This repository has been archived by the owner on May 9, 2020. It is now read-only.
/
CngKeyExtensionMethods.cs
145 lines (138 loc) · 8.4 KB
/
CngKeyExtensionMethods.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright (c) Microsoft Corporation. All rights reserved.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Permissions;
using Microsoft.Win32.SafeHandles;
using Security.Cryptography.X509Certificates;
namespace Security.Cryptography
{
/// <summary>
/// <para>
/// The CngKeyExtensionMethods class provides several extension methods for the
/// <see cref="CngKey" />. This type is in the Security.Cryptography namespace (not the
/// System.Security.Cryptography namespace), so in order to use these extension methods, you will
/// need to make sure you include this namespace as well as a reference to
/// Security.Cryptography.dll.
/// </para>
/// <para>
/// CngKey uses the NCrypt layer of CNG, and requires Windows Vista and the .NET Framework 3.5.
/// </para>
/// </summary>
public static class CngKeyExtensionMethods
{
/// <summary>
/// <para>
/// CreateSelfSignedCertificate creates a new self signed certificate issued to the specified
/// subject. The certificate will contain the key used to create the self signed certificate.
/// Since the certificate needs to be signed, the CngKey used must be usable for signing, which
/// means it must also contain a private key. If there is no private key, the operation will fail
/// with a CryptographicException indicating that "The key does not exist."
/// </para>
/// <para>
/// This overload creates a certificate which does take ownership of the underlying key - which
/// means that the input CngKey will be disposed before this method exits and should no longer
/// be used by the caller.
/// </para>
/// </summary>
/// <param name="key">key to wrap in a self signed certificate</param>
/// <param name="subjectName">the name of hte subject the self-signed certificate will be issued to</param>
/// <exception cref="ArgumentNullException">if <paramref name="subjectName" /> is null</exception>
/// <exception cref="CryptographicException">if the certificate cannot be created</exception>
public static X509Certificate2 CreateSelfSignedCertificate(this CngKey key,
X500DistinguishedName subjectName)
{
X509CertificateCreationParameters creationParameters = new X509CertificateCreationParameters(subjectName);
creationParameters.TakeOwnershipOfKey = true;
return CreateSelfSignedCertificate(key, creationParameters);
}
/// <summary>
/// <para>
/// CreateSelfSignedCertificate creates a new self signed certificate issued to the specified
/// subject. The certificate will contain the key used to create the self signed certificate.
/// Since the certificate needs to be signed, the CngKey used must be usable for signing, which
/// means it must also contain a private key. If there is no private key, the operation will fail
/// with a CryptographicException indicating that "The key does not exist."
/// </para>
/// <para>
/// If <paramref name="creationParameters"/> have TakeOwnershipOfKey set to true, the certificate
/// generated will own the key and the input CngKey will be disposed to ensure that the caller
/// doesn't accidentally use it beyond its lifetime (which is now controlled by the certificate
/// object).
/// </para>
/// <para>
/// Conversely, if TakeOwnershipOfKey is set to false, the API requires full trust to use, and
/// also requires that the caller ensure that the generated certificate does not outlive the
/// input CngKey object.
/// </para>
/// </summary>
/// <param name="key">key to wrap in a self signed certificate</param>
/// <param name="creationParameters">parameters to customize the self-signed certificate</param>
/// <exception cref="ArgumentNullException">if <paramref name="creationParameters" /> is null</exception>
/// <exception cref="CryptographicException">if the certificate cannot be created</exception>
/// <permission cref="PermissionSet">
/// This API requries full trust if <paramref name="creationParameters"/> specifies TakeOwnershipOfKey
/// to be false.
/// </permission>
[SecurityCritical]
[SecuritySafeCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Safe use of LinkDemand protected methods")]
[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Runtime.InteropServices.SafeHandle.DangerousGetHandle", Justification = "Used in a CER block with AddRef and Release")]
public static X509Certificate2 CreateSelfSignedCertificate(this CngKey key,
X509CertificateCreationParameters creationParameters)
{
if (creationParameters == null)
throw new ArgumentNullException("creationParameters");
// If we are not being asked to hand ownership of the key over to the certificate, then we need
// ensure that we are running in a trusted context as we have no way to ensure that the caller
// will not force the key to be cleaned up and then continue to use the dangling handle left in
// the certificate.
if (!creationParameters.TakeOwnershipOfKey)
{
new PermissionSet(PermissionState.Unrestricted).Demand();
}
using (SafeCertContextHandle selfSignedCertHandle =
X509Native.CreateSelfSignedCertificate(key,
creationParameters.TakeOwnershipOfKey,
creationParameters.SubjectName.RawData,
creationParameters.CertificateCreationOptions,
X509Native.MapCertificateSignatureAlgorithm(creationParameters.SignatureAlgorithm),
creationParameters.StartTime,
creationParameters.EndTime,
creationParameters.ExtensionsNoDemand))
{
// We need to get the raw handle out of the safe handle because X509Certificate2 only
// exposes an IntPtr constructor. To do that we'll temporarially bump the ref count on
// the handle.
//
// X509Certificate2 will duplicate the handle value in the .ctor, so once we've created
// the certificate object, we can safely drop the ref count and dispose of our handle.
X509Certificate2 certificate = null;
bool addedRef = false;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
selfSignedCertHandle.DangerousAddRef(ref addedRef);
certificate = new X509Certificate2(selfSignedCertHandle.DangerousGetHandle());
}
finally
{
if (addedRef)
{
selfSignedCertHandle.DangerousRelease();
}
}
// If we passed ownership of the key to the certificate, than destroy the key
// now so that we don't continue to use it beyond the liftime of the cert.
if (creationParameters.TakeOwnershipOfKey)
{
key.Dispose();
}
return certificate;
}
}
}
}