3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 using System.Diagnostics;
9 using System.Diagnostics.CodeAnalysis;
10 using System.Runtime.CompilerServices;
11 using System.Runtime.ConstrainedExecution;
12 using System.Runtime.InteropServices;
13 using System.Diagnostics.Contracts;
14 using Microsoft.Win32;
15 using Microsoft.Win32.SafeHandles;
16 using System.Security.Cryptography.X509Certificates;
18 namespace System.Security.Cryptography {
20 internal enum AsymmetricPaddingMode {
24 None = 1, // BCRYPT_PAD_NONE
29 Pkcs1 = 2, // BCRYPT_PAD_PKCS1
32 /// Optimal Asymmetric Encryption Padding
34 Oaep = 4, // BCRYPT_PAD_OAEP
37 /// Probabilistic Signature Scheme padding
39 Pss = 8 // BCRYPT_PAD_PSS
42 /// Native interop with CNG's BCrypt layer. Native definitions can be found in bcrypt.h
44 internal static class BCryptNative {
46 /// Well known algorithm names
48 internal static class AlgorithmName {
49 public const string ECDHP256 = "ECDH_P256"; // BCRYPT_ECDH_P256_ALGORITHM
50 public const string ECDHP384 = "ECDH_P384"; // BCRYPT_ECDH_P384_ALGORITHM
51 public const string ECDHP521 = "ECDH_P521"; // BCRYPT_ECDH_P521_ALGORITHM
52 public const string ECDsaP256 = "ECDSA_P256"; // BCRYPT_ECDSA_P256_ALGORITHM
53 public const string ECDsaP384 = "ECDSA_P384"; // BCRYPT_ECDSA_P384_ALGORITHM
54 public const string ECDsaP521 = "ECDSA_P521"; // BCRYPT_ECDSA_P521_ALGORITHM
55 public const string MD5 = "MD5"; // BCRYPT_MD5_ALGORITHM
56 public const string Sha1 = "SHA1"; // BCRYPT_SHA1_ALGORITHM
57 public const string Sha256 = "SHA256"; // BCRYPT_SHA256_ALGORITHM
58 public const string Sha384 = "SHA384"; // BCRYPT_SHA384_ALGORITHM
59 public const string Sha512 = "SHA512"; // BCRYPT_SHA512_ALGORITHM
60 internal const string Rsa = "RSA"; // BCRYPT_RSA_ALGORITHM
64 /// Well known key blob tyes
66 internal static class KeyBlobType {
67 //During Win8 Windows introduced BCRYPT_PUBLIC_KEY_BLOB L"PUBLICBLOB"
68 //and #define BCRYPT_PRIVATE_KEY_BLOB L"PRIVATEBLOB". We should use the
69 //same on ProjectN and ProjectK
70 internal const string RsaFullPrivateBlob = "RSAFULLPRIVATEBLOB"; // BCRYPT_RSAFULLPRIVATE_BLOB
71 internal const string RsaPrivateBlob = "RSAPRIVATEBLOB"; // BCRYPT_RSAPRIVATE_BLOB
72 internal const string RsaPublicBlob = "RSAPUBLICBLOB"; // BCRYPT_PUBLIC_KEY_BLOB
75 [StructLayout(LayoutKind.Sequential)]
76 internal struct BCRYPT_RSAKEY_BLOB {
77 internal KeyBlobMagicNumber Magic;
78 internal int BitLength;
79 internal int cbPublicExp;
80 internal int cbModulus;
81 internal int cbPrime1;
82 internal int cbPrime2;
86 /// Result codes from BCrypt APIs
88 internal enum ErrorCode {
89 Success = 0x00000000, // STATUS_SUCCESS
90 BufferToSmall = unchecked((int)0xC0000023), // STATUS_BUFFER_TOO_SMALL
91 ObjectNameNotFound = unchecked((int)0xC0000034) // SATUS_OBJECT_NAME_NOT_FOUND
95 /// Well known BCrypt hash property names
97 internal static class HashPropertyName {
98 public const string HashLength = "HashDigestLength"; // BCRYPT_HASH_LENGTH
102 /// Magic numbers identifying blob types
104 internal enum KeyBlobMagicNumber {
105 ECDHPublicP256 = 0x314B4345, // BCRYPT_ECDH_PUBLIC_P256_MAGIC
106 ECDHPublicP384 = 0x334B4345, // BCRYPT_ECDH_PUBLIC_P384_MAGIC
107 ECDHPublicP521 = 0x354B4345, // BCRYPT_ECDH_PUBLIC_P521_MAGIC
108 ECDsaPublicP256 = 0x31534345, // BCRYPT_ECDSA_PUBLIC_P256_MAGIC
109 ECDsaPublicP384 = 0x33534345, // BCRYPT_ECDSA_PUBLIC_P384_MAGIC
110 ECDsaPublicP521 = 0x35534345, // BCRYPT_ECDSA_PUBLIC_P521_MAGIC
111 RsaPublic = 0x31415352, // BCRYPT_RSAPUBLIC_MAGIC
112 RsaPrivate = 0x32415352, // BCRYPT_RSAPRIVATE_MAGIC
113 RsaFullPrivateMagic = 0x33415352, //BCRYPT_RSAFULLPRIVATE_MAGIC
114 KeyDataBlob = 0x4d42444b // BCRYPT_KEY_DATA_BLOB_MAGIC
117 [StructLayout(LayoutKind.Sequential)]
118 internal struct BCRYPT_OAEP_PADDING_INFO {
119 [MarshalAs(UnmanagedType.LPWStr)]
120 internal string pszAlgId;
122 internal IntPtr pbLabel;
124 internal int cbLabel;
127 [StructLayout(LayoutKind.Sequential)]
128 internal struct BCRYPT_PKCS1_PADDING_INFO {
129 [MarshalAs(UnmanagedType.LPWStr)]
130 internal string pszAlgId;
133 [StructLayout(LayoutKind.Sequential)]
134 internal struct BCRYPT_PSS_PADDING_INFO {
135 [MarshalAs(UnmanagedType.LPWStr)]
136 internal string pszAlgId;
142 /// Well known KDF names
144 internal static class KeyDerivationFunction {
145 public const string Hash = "HASH"; // BCRYPT_KDF_HASH
146 public const string Hmac = "HMAC"; // BCRYPT_KDF_HMAC
147 public const string Tls = "TLS_PRF"; // BCRYPT_KDF_TLS_PRF
150 internal const string BCRYPT_ECCPUBLIC_BLOB = "ECCPUBLICBLOB";
151 internal const string BCRYPT_ECCPRIVATE_BLOB = "ECCPRIVATEBLOB";
154 /// Well known BCrypt provider names
156 internal static class ProviderName {
157 public const string MicrosoftPrimitiveProvider = "Microsoft Primitive Provider"; // MS_PRIMITIVE_PROVIDER
161 /// Well known BCrypt object property names
163 internal static class ObjectPropertyName {
164 public const string ObjectLength = "ObjectLength"; // BCRYPT_OBJECT_LENGTH
167 #pragma warning disable 618 // Have not migrated to v4 transparency yet
168 [SecurityCritical(SecurityCriticalScope.Everything)]
169 #pragma warning restore 618
170 [SuppressUnmanagedCodeSecurity]
171 internal static class UnsafeNativeMethods {
173 /// Create a hash object
175 [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
176 internal static extern ErrorCode BCryptCreateHash(SafeBCryptAlgorithmHandle hAlgorithm,
177 [Out] out SafeBCryptHashHandle phHash,
185 /// Get a property from a BCrypt algorithm object
187 [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
188 internal static extern ErrorCode BCryptGetProperty(SafeBCryptAlgorithmHandle hObject,
190 [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
192 [In, Out] ref int pcbResult,
196 /// Get a property from a BCrypt algorithm object
198 [DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
199 internal static extern ErrorCode BCryptGetAlgorithmProperty(SafeBCryptAlgorithmHandle hObject,
201 [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
203 [In, Out] ref int pcbResult,
207 /// Get a property from a BCrypt hash object
209 [DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
210 internal static extern ErrorCode BCryptGetHashProperty(SafeBCryptHashHandle hObject,
212 [MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
214 [In, Out] ref int pcbResult,
218 /// Get the hash value of the data
220 [DllImport("bcrypt.dll")]
221 internal static extern ErrorCode BCryptFinishHash(SafeBCryptHashHandle hHash,
222 [MarshalAs(UnmanagedType.LPArray), Out] byte[] pbInput,
227 /// Hash a block of data
229 [DllImport("bcrypt.dll")]
230 internal static extern ErrorCode BCryptHashData(SafeBCryptHashHandle hHash,
231 [MarshalAs(UnmanagedType.LPArray), In] byte[] pbInput,
236 /// Get a handle to an algorithm provider
238 [DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
239 internal static extern ErrorCode BCryptOpenAlgorithmProvider([Out] out SafeBCryptAlgorithmHandle phAlgorithm,
240 string pszAlgId, // BCryptAlgorithm
241 string pszImplementation, // ProviderNames
244 [DllImport("bcrypt.dll", SetLastError = true)]
245 internal static extern ErrorCode BCryptExportKey([In]SafeBCryptKeyHandle hKey,
246 [In]IntPtr hExportKey,
247 [In][MarshalAs(UnmanagedType.LPWStr)] string pszBlobType,
248 [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
250 [In]ref int pcbResult,
253 [DllImport("Crypt32.dll", SetLastError = true)]
254 internal static extern int CryptImportPublicKeyInfoEx2([In] uint dwCertEncodingType,
255 [In] ref X509Native.CERT_PUBLIC_KEY_INFO pInfo,
257 [In] IntPtr pvAuxInfo,
258 [Out] out SafeBCryptKeyHandle phKey);
262 // Wrapper and utility functions
266 /// Adapter to wrap specific BCryptGetProperty P/Invokes with a generic handle type
268 #pragma warning disable 618 // System.Core.dll still uses SecurityRuleSet.Level1
269 [SecurityCritical(SecurityCriticalScope.Everything)]
270 #pragma warning restore 618
271 private delegate ErrorCode BCryptPropertyGetter<T>(T hObject,
276 int dwFlags) where T : SafeHandle;
278 private static volatile bool s_haveBcryptSupported;
279 private static volatile bool s_bcryptSupported;
282 /// Determine if BCrypt is supported on the current machine
284 internal static bool BCryptSupported {
285 [SecuritySafeCritical]
286 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
288 if (!s_haveBcryptSupported)
290 // Attempt to load bcrypt.dll to see if the BCrypt CNG APIs are available on the machine
291 using (SafeLibraryHandle bcrypt = Microsoft.Win32.UnsafeNativeMethods.LoadLibraryEx("bcrypt", IntPtr.Zero, 0)) {
292 s_bcryptSupported = !bcrypt.IsInvalid;
293 s_haveBcryptSupported = true;
297 return s_bcryptSupported;
302 /// Get the value of a DWORD property of a BCrypt object
304 [System.Security.SecurityCritical]
305 internal static int GetInt32Property<T>(T algorithm, string property) where T : SafeHandle {
306 Contract.Requires(algorithm != null);
307 Contract.Requires(property == HashPropertyName.HashLength ||
308 property == ObjectPropertyName.ObjectLength);
310 return BitConverter.ToInt32(GetProperty(algorithm, property), 0);
314 /// Get the value of a property of a BCrypt object
316 [System.Security.SecurityCritical]
317 internal static byte[] GetProperty<T>(T algorithm, string property) where T : SafeHandle {
318 Contract.Requires(algorithm != null);
319 Contract.Requires(!String.IsNullOrEmpty(property));
320 Contract.Ensures(Contract.Result<byte[]>() != null);
322 BCryptPropertyGetter<T> getter = null;
323 if (typeof(T) == typeof(SafeBCryptAlgorithmHandle)) {
324 getter = new BCryptPropertyGetter<SafeBCryptAlgorithmHandle>(UnsafeNativeMethods.BCryptGetAlgorithmProperty)
325 as BCryptPropertyGetter<T>;
327 else if (typeof(T) == typeof(SafeBCryptHashHandle)) {
328 getter = new BCryptPropertyGetter<SafeBCryptHashHandle>(UnsafeNativeMethods.BCryptGetHashProperty)
329 as BCryptPropertyGetter<T>;
332 Debug.Assert(getter != null, "Unknown handle type");
334 // Figure out how big the property is
336 ErrorCode error = getter(algorithm, property, null, 0, ref bufferSize, 0);
338 if (error != ErrorCode.BufferToSmall && error != ErrorCode.Success) {
339 throw new CryptographicException((int)error);
342 // Allocate the buffer, and return the property
343 Debug.Assert(bufferSize > 0, "bufferSize > 0");
344 byte[] buffer = new byte[bufferSize];
345 error = getter(algorithm, property, buffer, buffer.Length, ref bufferSize, 0);
347 if (error != ErrorCode.Success) {
348 throw new CryptographicException((int)error);
356 /// Map an algorithm identifier to a key size and magic number
358 internal static void MapAlgorithmIdToMagic(string algorithm,
359 out KeyBlobMagicNumber algorithmMagic,
361 Contract.Requires(!String.IsNullOrEmpty(algorithm));
364 case AlgorithmName.ECDHP256:
365 algorithmMagic = KeyBlobMagicNumber.ECDHPublicP256;
369 case AlgorithmName.ECDHP384:
370 algorithmMagic = KeyBlobMagicNumber.ECDHPublicP384;
374 case AlgorithmName.ECDHP521:
375 algorithmMagic = KeyBlobMagicNumber.ECDHPublicP521;
379 case AlgorithmName.ECDsaP256:
380 algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP256;
384 case AlgorithmName.ECDsaP384:
385 algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP384;
389 case AlgorithmName.ECDsaP521:
390 algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP521;
395 throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurveAlgorithm));
400 /// Open a handle to an algorithm provider
402 [System.Security.SecurityCritical]
403 internal static SafeBCryptAlgorithmHandle OpenAlgorithm(string algorithm, string implementation) {
404 Contract.Requires(!String.IsNullOrEmpty(algorithm));
405 Contract.Requires(!String.IsNullOrEmpty(implementation));
406 Contract.Ensures(Contract.Result<SafeBCryptAlgorithmHandle>() != null &&
407 !Contract.Result<SafeBCryptAlgorithmHandle>().IsInvalid &&
408 !Contract.Result<SafeBCryptAlgorithmHandle>().IsClosed);
410 SafeBCryptAlgorithmHandle algorithmHandle = null;
411 ErrorCode error = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algorithmHandle,
416 if (error != ErrorCode.Success) {
417 throw new CryptographicException((int)error);
420 return algorithmHandle;
423 [SecuritySafeCritical]
424 internal static SafeBCryptKeyHandle ImportAsymmetricPublicKey(X509Native.CERT_PUBLIC_KEY_INFO certPublicKeyInfo, int dwFlag) {
425 SafeBCryptKeyHandle keyHandle = null;
426 int error = UnsafeNativeMethods.CryptImportPublicKeyInfoEx2(
427 X509Native.X509_ASN_ENCODING,
428 ref certPublicKeyInfo,
433 throw new CryptographicException(Marshal.GetLastWin32Error());
438 [SecuritySafeCritical]
439 internal static byte[] ExportBCryptKey(SafeBCryptKeyHandle hKey, string blobType) {
440 byte[] keyBlob = null;
443 ErrorCode error = UnsafeNativeMethods.BCryptExportKey(hKey, IntPtr.Zero, blobType, null, 0, ref length, 0);
445 if (error != ErrorCode.BufferToSmall && error != ErrorCode.Success)
447 throw new CryptographicException(Marshal.GetLastWin32Error());
450 keyBlob = new byte[length];
451 error = UnsafeNativeMethods.BCryptExportKey(hKey, IntPtr.Zero, blobType, keyBlob, length, ref length, 0);
452 if (error != ErrorCode.Success) {
453 throw new CryptographicException(Marshal.GetLastWin32Error());