1 // ==++== 2 // 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // 5 // ==--== 6 7 using System; 8 using System.Collections.Generic; 9 using System.Diagnostics; 10 using System.Diagnostics.CodeAnalysis; 11 #if !MONO 12 using System.Numerics; 13 #endif 14 using System.Runtime.CompilerServices; 15 using System.Runtime.ConstrainedExecution; 16 using System.Runtime.InteropServices; 17 using System.Security.Permissions; 18 using System.Text; 19 using System.Diagnostics.Contracts; 20 using Microsoft.Win32; 21 using Microsoft.Win32.SafeHandles; 22 23 namespace System.Security.Cryptography { 24 25 // 26 // Public facing enumerations 27 // 28 29 /// <summary> 30 /// Flags to control how often and in which format a key is allowed to be exported 31 /// </summary> 32 [Flags] 33 public enum CngExportPolicies { 34 None = 0x00000000, 35 AllowExport = 0x00000001, // NCRYPT_ALLOW_EXPORT_FLAG 36 AllowPlaintextExport = 0x00000002, // NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG 37 AllowArchiving = 0x00000004, // NCRYPT_ALLOW_ARCHIVING_FLAG 38 AllowPlaintextArchiving = 0x00000008 // NCRYPT_ALLOW_PLAINTEXT_ARCHIVING_FLAG 39 } 40 41 /// <summary> 42 /// Flags controlling how the key is created 43 /// </summary> 44 [Flags] 45 public enum CngKeyCreationOptions { 46 None = 0x00000000, 47 MachineKey = 0x00000020, // NCRYPT_MACHINE_KEY_FLAG 48 OverwriteExistingKey = 0x00000080 // NCRYPT_OVERWRITE_KEY_FLAG 49 } 50 51 /// <summary> 52 /// Flags controlling how a key is opened 53 /// </summary> 54 [Flags] 55 [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "Approved API exception to have an easy way to express user keys")] 56 public enum CngKeyOpenOptions { 57 None = 0x00000000, 58 UserKey = 0x00000000, 59 MachineKey = 0x00000020, // NCRYPT_MACHINE_KEY_FLAG 60 Silent = 0x00000040 // NCRYPT_SILENT_FLAG 61 } 62 63 /// <summary> 64 /// Flags indicating the type of key 65 /// </summary> 66 [Flags] 67 internal enum CngKeyTypes { 68 None = 0x00000000, 69 MachineKey = 0x00000020 // NCRYPT_MACHINE_KEY_FLAG 70 } 71 72 /// <summary> 73 /// Bits defining what operations are valid to use a key with 74 /// </summary> 75 [Flags] 76 [SuppressMessage("Microsoft.Usage", "CA2217:DoNotMarkEnumsWithFlags", Justification = "Flags are defined by the native ncrypt API")] 77 public enum CngKeyUsages { 78 None = 0x00000000, 79 Decryption = 0x00000001, // NCRYPT_ALLOW_DECRYPT_FLAG 80 Signing = 0x00000002, // NCRYPT_ALLOW_SIGNING_FLAG 81 KeyAgreement = 0x00000004, // NCRYPT_ALLOW_KEY_AGREEMENT_FLAG 82 AllUsages = 0x00ffffff // NCRYPT_ALLOW_ALL_USAGES 83 } 84 85 /// <summary> 86 /// Options affecting how a property is interpreted by CNG 87 /// </summary> 88 [Flags] 89 [SuppressMessage("Microsoft.Usage", "CA2217:DoNotMarkEnumsWithFlags", Justification = "Flags are defined by the native ncrypt API")] 90 public enum CngPropertyOptions { 91 None = 0x00000000, 92 CustomProperty = 0x40000000, // NCRYPT_PERSIST_ONLY_FLAG 93 Persist = unchecked((int)0x80000000) // NCRYPT_PERSIST_FLAG 94 } 95 96 /// <summary> 97 /// Levels of UI protection available for a key 98 /// </summary> 99 [Flags] 100 public enum CngUIProtectionLevels { 101 None = 0x00000000, 102 ProtectKey = 0x00000001, // NCRYPT_UI_PROTECT_KEY_FLAG 103 ForceHighProtection = 0x00000002 // NCRYPT_UI_FORCE_HIGH_PROTECTION_FLAG 104 } 105 #if !MONO 106 /// <summary> 107 /// Native interop with CNG's NCrypt layer. Native definitions are in ncrypt.h 108 /// </summary> 109 internal static class NCryptNative { 110 // 111 // Enumerations 112 // 113 114 /// <summary> 115 /// Types of NCryptBuffers 116 /// </summary> 117 internal enum BufferType { 118 KdfHashAlgorithm = 0x00000000, // KDF_HASH_ALGORITHM 119 KdfSecretPrepend = 0x00000001, // KDF_SECRET_PREPEND 120 KdfSecretAppend = 0x00000002, // KDF_SECRET_APPEND 121 KdfHmacKey = 0x00000003, // KDF_HMAC_KEY 122 KdfTlsLabel = 0x00000004, // KDF_TLS_PRF_LABEL 123 KdfTlsSeed = 0x00000005 // KDF_TLS_PRF_SEED 124 } 125 126 /// <summary> 127 /// Result codes from NCrypt APIs 128 /// </summary> 129 internal enum ErrorCode { 130 Success = 0, // ERROR_SUCCESS 131 BadSignature = unchecked((int)0x80090006), // NTE_BAD_SIGNATURE 132 NotFound = unchecked((int)0x80090011), // NTE_NOT_FOUND 133 KeyDoesNotExist = unchecked((int)0x80090016), // NTE_BAD_KEYSET 134 BufferTooSmall = unchecked((int)0x80090028), // NTE_BUFFER_TOO_SMALL 135 NoMoreItems = unchecked((int)0x8009002a) // NTE_NO_MORE_ITEMS 136 } 137 138 /// <summary> 139 /// Well known names of key properties 140 /// </summary> 141 internal static class KeyPropertyName { 142 internal const string Algorithm = "Algorithm Name"; // NCRYPT_ALGORITHM_PROPERTY 143 internal const string AlgorithmGroup = "Algorithm Group"; // NCRYPT_ALGORITHM_GROUP_PROPERTY 144 internal const string ExportPolicy = "Export Policy"; // NCRYPT_EXPORT_POLICY_PROPERTY 145 internal const string KeyType = "Key Type"; // NCRYPT_KEY_TYPE_PROPERTY 146 internal const string KeyUsage = "Key Usage"; // NCRYPT_KEY_USAGE_PROPERTY 147 internal const string Length = "Length"; // NCRYPT_LENGTH_PROPERTY 148 internal const string Name = "Name"; // NCRYPT_NAME_PROPERTY 149 internal const string ParentWindowHandle = "HWND Handle"; // NCRYPT_WINDOW_HANDLE_PROPERTY 150 internal const string PublicKeyLength = "PublicKeyLength"; // NCRYPT_PUBLIC_KEY_LENGTH (Win10+) 151 internal const string ProviderHandle = "Provider Handle"; // NCRYPT_PROVIDER_HANDLE_PROPERTY 152 internal const string UIPolicy = "UI Policy"; // NCRYPT_UI_POLICY_PROPERTY 153 internal const string UniqueName = "Unique Name"; // NCRYPT_UNIQUE_NAME_PROPERTY 154 internal const string UseContext = "Use Context"; // NCRYPT_USE_CONTEXT_PROPERTY 155 156 // 157 // Properties defined by the CLR 158 // 159 160 /// <summary> 161 /// Is the key a CLR created ephemeral key, it will contain a single byte with value 1 if the 162 /// key was created by the CLR as an ephemeral key. 163 /// </summary> 164 internal const string ClrIsEphemeral = "CLR IsEphemeral"; 165 } 166 167 /// <summary> 168 /// Well known names of provider properties 169 /// </summary> 170 internal static class ProviderPropertyName { 171 internal const string Name = "Name"; // NCRYPT_NAME_PROPERTY 172 } 173 174 /// <summary> 175 /// Flags for code:System.Security.Cryptography.NCryptNative.UnsafeNativeMethods.NCryptSecretAgreement 176 /// </summary> 177 [Flags] 178 internal enum SecretAgreementFlags { 179 None = 0x00000000, 180 UseSecretAsHmacKey = 0x00000001 // KDF_USE_SECRET_AS_HMAC_KEY_FLAG 181 } 182 183 // 184 // Structures 185 // 186 187 [StructLayout(LayoutKind.Sequential)] 188 internal struct NCRYPT_UI_POLICY { 189 public int dwVersion; 190 public CngUIProtectionLevels dwFlags; 191 192 [MarshalAs(UnmanagedType.LPWStr)] 193 public string pszCreationTitle; 194 195 [MarshalAs(UnmanagedType.LPWStr)] 196 public string pszFriendlyName; 197 198 [MarshalAs(UnmanagedType.LPWStr)] 199 public string pszDescription; 200 } 201 202 [StructLayout(LayoutKind.Sequential)] 203 internal struct NCryptBuffer { 204 public int cbBuffer; 205 public BufferType BufferType; 206 public IntPtr pvBuffer; 207 } 208 209 [StructLayout(LayoutKind.Sequential)] 210 internal struct NCryptBufferDesc { 211 public int ulVersion; 212 public int cBuffers; 213 public IntPtr pBuffers; // NCryptBuffer[cBuffers] 214 } 215 216 [SuppressUnmanagedCodeSecurity] 217 #pragma warning disable 618 // System.Core.dll still uses SecurityRuleSet.Level1 218 [SecurityCritical(SecurityCriticalScope.Everything)] 219 #pragma warning restore 618 220 internal static class UnsafeNativeMethods { 221 /// <summary> 222 /// Create an NCrypt key 223 /// </summary> 224 [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)] NCryptCreatePersistedKey(SafeNCryptProviderHandle hProvider, [Out] out SafeNCryptKeyHandle phKey, string pszAlgId, string pszKeyName, int dwLegacyKeySpec, CngKeyCreationOptions dwFlags)225 internal static extern ErrorCode NCryptCreatePersistedKey(SafeNCryptProviderHandle hProvider, 226 [Out] out SafeNCryptKeyHandle phKey, 227 string pszAlgId, 228 string pszKeyName, 229 int dwLegacyKeySpec, 230 CngKeyCreationOptions dwFlags); 231 232 /// <summary> 233 /// Delete a key 234 /// </summary> 235 [DllImport("ncrypt.dll")] NCryptDeleteKey(SafeNCryptKeyHandle hKey, int flags)236 internal static extern ErrorCode NCryptDeleteKey(SafeNCryptKeyHandle hKey, int flags); 237 238 /// <summary> 239 /// Generate a key from a secret agreement 240 /// </summary> 241 [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)] NCryptDeriveKey(SafeNCryptSecretHandle hSharedSecret, string pwszKDF, [In] ref NCryptBufferDesc pParameterList, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbDerivedKey, int cbDerivedKey, [Out] out int pcbResult, SecretAgreementFlags dwFlags)242 internal static extern ErrorCode NCryptDeriveKey(SafeNCryptSecretHandle hSharedSecret, 243 string pwszKDF, 244 [In] ref NCryptBufferDesc pParameterList, 245 [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbDerivedKey, 246 int cbDerivedKey, 247 [Out] out int pcbResult, 248 SecretAgreementFlags dwFlags); 249 250 /// <summary> 251 /// Export a key from the KSP 252 /// </summary> 253 [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)] NCryptExportKey(SafeNCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, IntPtr pParameterList, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, int dwFlags)254 internal static extern ErrorCode NCryptExportKey(SafeNCryptKeyHandle hKey, 255 IntPtr hExportKey, // NCRYPT_KEY_HANDLE 256 string pszBlobType, 257 IntPtr pParameterList, // NCryptBufferDesc * 258 [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, 259 int cbOutput, 260 [Out] out int pcbResult, 261 int dwFlags); 262 263 /// <summary> 264 /// Finalize a key to prepare it for use 265 /// </summary> 266 [DllImport("ncrypt.dll")] NCryptFinalizeKey(SafeNCryptKeyHandle hKey, int dwFlags)267 internal static extern ErrorCode NCryptFinalizeKey(SafeNCryptKeyHandle hKey, int dwFlags); 268 269 /// <summary> 270 /// Get the value of a property of an NCrypt object 271 /// </summary> 272 [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)] NCryptGetProperty(SafeNCryptHandle hObject, string pszProperty, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, CngPropertyOptions dwFlags)273 internal static extern ErrorCode NCryptGetProperty(SafeNCryptHandle hObject, 274 string pszProperty, 275 [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, 276 int cbOutput, 277 [Out] out int pcbResult, 278 CngPropertyOptions dwFlags); 279 280 /// <summary> 281 /// Get the value of a property of an NCrypt object 282 /// </summary> 283 [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)] NCryptGetProperty(SafeNCryptHandle hObject, string pszProperty, ref int pbOutput, int cbOutput, [Out] out int pcbResult, CngPropertyOptions dwFlags)284 internal static extern ErrorCode NCryptGetProperty(SafeNCryptHandle hObject, 285 string pszProperty, 286 ref int pbOutput, 287 int cbOutput, 288 [Out] out int pcbResult, 289 CngPropertyOptions dwFlags); 290 291 /// <summary> 292 /// Get the value of a pointer property of an NCrypt object 293 /// </summary> 294 [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)] 295 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] NCryptGetProperty(SafeNCryptHandle hObject, string pszProperty, [Out] out IntPtr pbOutput, int cbOutput, [Out] out int pcbResult, CngPropertyOptions dwFlags)296 internal static extern ErrorCode NCryptGetProperty(SafeNCryptHandle hObject, 297 string pszProperty, 298 [Out] out IntPtr pbOutput, 299 int cbOutput, 300 [Out] out int pcbResult, 301 CngPropertyOptions dwFlags); 302 303 /// <summary> 304 /// Import a key into the KSP 305 /// </summary> 306 [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)] NCryptImportKey(SafeNCryptProviderHandle hProvider, IntPtr hImportKey, string pszBlobType, IntPtr pParameterList, [Out] out SafeNCryptKeyHandle phKey, [MarshalAs(UnmanagedType.LPArray)] byte[] pbData, int cbData, int dwFlags)307 internal static extern ErrorCode NCryptImportKey(SafeNCryptProviderHandle hProvider, 308 IntPtr hImportKey, // NCRYPT_KEY_HANDLE 309 string pszBlobType, 310 IntPtr pParameterList, // NCryptBufferDesc * 311 [Out] out SafeNCryptKeyHandle phKey, 312 [MarshalAs(UnmanagedType.LPArray)] byte[] pbData, 313 int cbData, 314 int dwFlags); 315 316 /// <summary> 317 /// Open an existing key 318 /// </summary> 319 [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)] NCryptOpenKey(SafeNCryptProviderHandle hProvider, [Out] out SafeNCryptKeyHandle phKey, string pszKeyName, int dwLegacyKeySpec, CngKeyOpenOptions dwFlags)320 internal static extern ErrorCode NCryptOpenKey(SafeNCryptProviderHandle hProvider, 321 [Out] out SafeNCryptKeyHandle phKey, 322 string pszKeyName, 323 int dwLegacyKeySpec, 324 CngKeyOpenOptions dwFlags); 325 326 /// <summary> 327 /// Acquire a handle to a key storage provider 328 /// </summary> 329 [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)] NCryptOpenStorageProvider([Out] out SafeNCryptProviderHandle phProvider, string pszProviderName, int dwFlags)330 internal static extern ErrorCode NCryptOpenStorageProvider([Out] out SafeNCryptProviderHandle phProvider, 331 string pszProviderName, 332 int dwFlags); 333 334 /// <summary> 335 /// Generate a secret agreement for generating shared key material 336 /// </summary> 337 [DllImport("ncrypt.dll")] NCryptSecretAgreement(SafeNCryptKeyHandle hPrivKey, SafeNCryptKeyHandle hPubKey, [Out] out SafeNCryptSecretHandle phSecret, int dwFlags)338 internal static extern ErrorCode NCryptSecretAgreement(SafeNCryptKeyHandle hPrivKey, 339 SafeNCryptKeyHandle hPubKey, 340 [Out] out SafeNCryptSecretHandle phSecret, 341 int dwFlags); 342 343 /// <summary> 344 /// Set a property value on an NCrypt object 345 /// </summary> 346 [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)] NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, [MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, CngPropertyOptions dwFlags)347 internal static extern ErrorCode NCryptSetProperty(SafeNCryptHandle hObject, 348 string pszProperty, 349 [MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, 350 int cbInput, 351 CngPropertyOptions dwFlags); 352 353 /// <summary> 354 /// Set a string property value on an NCrypt object 355 /// </summary> 356 [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)] NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, string pbInput, int cbInput, CngPropertyOptions dwFlags)357 internal static extern ErrorCode NCryptSetProperty(SafeNCryptHandle hObject, 358 string pszProperty, 359 string pbInput, 360 int cbInput, 361 CngPropertyOptions dwFlags); 362 363 /// <summary> 364 /// Set a property value on an NCrypt object when a pointer to the buffer already exists in 365 /// managed code. To set a pointer valued property, use the ref IntPtr overload. 366 /// </summary> 367 [DllImport("ncrypt.dll", CharSet = CharSet.Unicode)] NCryptSetProperty(SafeNCryptHandle hObject, string pszProperty, IntPtr pbInput, int cbInput, CngPropertyOptions dwFlags)368 internal static extern ErrorCode NCryptSetProperty(SafeNCryptHandle hObject, 369 string pszProperty, 370 IntPtr pbInput, 371 int cbInput, 372 CngPropertyOptions dwFlags); 373 374 /// <summary> 375 /// Create a signature for a hash value 376 /// </summary> 377 [DllImport("ncrypt.dll")] NCryptSignHash(SafeNCryptKeyHandle hKey, IntPtr pPaddingInfo, [MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, [Out] out int pcbResult, int dwFlags)378 internal static extern ErrorCode NCryptSignHash(SafeNCryptKeyHandle hKey, 379 IntPtr pPaddingInfo, 380 [MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, 381 int cbHashValue, 382 [MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, 383 int cbSignature, 384 [Out] out int pcbResult, 385 int dwFlags); 386 387 /// <summary> 388 /// Verify a signature over a hash value 389 /// </summary> 390 [DllImport("ncrypt.dll")] NCryptVerifySignature(SafeNCryptKeyHandle hKey, IntPtr pPaddingInfo, [MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, int dwFlags)391 internal static extern ErrorCode NCryptVerifySignature(SafeNCryptKeyHandle hKey, 392 IntPtr pPaddingInfo, 393 [MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, 394 int cbHashValue, 395 [MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, 396 int cbSignature, 397 int dwFlags); 398 399 [DllImport("ncrypt.dll")] NCryptSignHash(SafeNCryptKeyHandle hKey, [In] ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pPaddingInfo, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, [Out] out int pcbResult, AsymmetricPaddingMode dwFlags)400 internal static extern ErrorCode NCryptSignHash(SafeNCryptKeyHandle hKey, 401 [In] ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pPaddingInfo, 402 [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, 403 int cbHashValue, 404 [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, 405 int cbSignature, 406 [Out] out int pcbResult, 407 AsymmetricPaddingMode dwFlags); 408 409 [DllImport("ncrypt.dll")] NCryptSignHash(SafeNCryptKeyHandle hKey, [In] ref BCryptNative.BCRYPT_PSS_PADDING_INFO pPaddingInfo, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, [Out] out int pcbResult, AsymmetricPaddingMode dwFlags)410 internal static extern ErrorCode NCryptSignHash(SafeNCryptKeyHandle hKey, 411 [In] ref BCryptNative.BCRYPT_PSS_PADDING_INFO pPaddingInfo, 412 [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, 413 int cbHashValue, 414 [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, 415 int cbSignature, 416 [Out] out int pcbResult, 417 AsymmetricPaddingMode dwFlags); 418 [DllImport("ncrypt.dll")] NCryptVerifySignature(SafeNCryptKeyHandle hKey, [In] ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pPaddingInfo, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, AsymmetricPaddingMode dwFlags)419 internal static extern ErrorCode NCryptVerifySignature(SafeNCryptKeyHandle hKey, 420 [In] ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pPaddingInfo, 421 [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, 422 int cbHashValue, 423 [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, 424 int cbSignature, 425 AsymmetricPaddingMode dwFlags); 426 427 [DllImport("ncrypt.dll")] NCryptVerifySignature(SafeNCryptKeyHandle hKey, [In] ref BCryptNative.BCRYPT_PSS_PADDING_INFO pPaddingInfo, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, AsymmetricPaddingMode dwFlags)428 internal static extern ErrorCode NCryptVerifySignature(SafeNCryptKeyHandle hKey, 429 [In] ref BCryptNative.BCRYPT_PSS_PADDING_INFO pPaddingInfo, 430 [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, 431 int cbHashValue, 432 [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, 433 int cbSignature, 434 AsymmetricPaddingMode dwFlags); 435 436 [DllImport("ncrypt.dll")] NCryptDecrypt(SafeNCryptKeyHandle hKey, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, [In] ref BCryptNative.BCRYPT_OAEP_PADDING_INFO pvPadding, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, AsymmetricPaddingMode dwFlags)437 internal static extern ErrorCode NCryptDecrypt(SafeNCryptKeyHandle hKey, 438 [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, 439 int cbInput, 440 [In] ref BCryptNative.BCRYPT_OAEP_PADDING_INFO pvPadding, 441 [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, 442 int cbOutput, 443 [Out] out int pcbResult, 444 AsymmetricPaddingMode dwFlags); 445 446 [DllImport("ncrypt.dll")] NCryptDecrypt(SafeNCryptKeyHandle hKey, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, IntPtr pvPaddingZero, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, AsymmetricPaddingMode dwFlags)447 internal static extern ErrorCode NCryptDecrypt(SafeNCryptKeyHandle hKey, 448 [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, 449 int cbInput, 450 IntPtr pvPaddingZero, 451 [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, 452 int cbOutput, 453 [Out] out int pcbResult, 454 AsymmetricPaddingMode dwFlags); 455 456 [DllImport("ncrypt.dll")] NCryptEncrypt(SafeNCryptKeyHandle hKey, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, [In] ref BCryptNative.BCRYPT_OAEP_PADDING_INFO pvPadding, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, AsymmetricPaddingMode dwFlags)457 internal static extern ErrorCode NCryptEncrypt(SafeNCryptKeyHandle hKey, 458 [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, 459 int cbInput, 460 [In] ref BCryptNative.BCRYPT_OAEP_PADDING_INFO pvPadding, 461 [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, 462 int cbOutput, 463 [Out] out int pcbResult, 464 AsymmetricPaddingMode dwFlags); 465 466 [DllImport("ncrypt.dll")] NCryptEncrypt(SafeNCryptKeyHandle hKey, [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, int cbInput, IntPtr pvPaddingZero, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, [Out] out int pcbResult, AsymmetricPaddingMode dwFlags)467 internal static extern ErrorCode NCryptEncrypt(SafeNCryptKeyHandle hKey, 468 [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput, 469 int cbInput, 470 IntPtr pvPaddingZero, 471 [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, 472 int cbOutput, 473 [Out] out int pcbResult, 474 AsymmetricPaddingMode dwFlags); 475 } 476 477 478 /// <summary> 479 /// Adapter to wrap specific NCryptDecrypt P/Invokes with specific padding info 480 /// </summary> 481 [SecuritySafeCritical] NCryptDecryptor(SafeNCryptKeyHandle hKey, byte[] pbInput, int cbInput, ref T pvPadding, byte[] pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags)482 private delegate ErrorCode NCryptDecryptor<T>(SafeNCryptKeyHandle hKey, 483 byte[] pbInput, 484 int cbInput, 485 ref T pvPadding, 486 byte[] pbOutput, 487 int cbOutput, 488 out int pcbResult, 489 AsymmetricPaddingMode dwFlags); 490 491 /// <summary> 492 /// Adapter to wrap specific NCryptEncrypt P/Invokes with specific padding info 493 /// </summary> 494 [SecuritySafeCritical] NCryptEncryptor(SafeNCryptKeyHandle hKey, byte[] pbInput, int cbInput, ref T pvPadding, byte[] pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags)495 private delegate ErrorCode NCryptEncryptor<T>(SafeNCryptKeyHandle hKey, 496 byte[] pbInput, 497 int cbInput, 498 ref T pvPadding, 499 byte[] pbOutput, 500 int cbOutput, 501 out int pcbResult, 502 AsymmetricPaddingMode dwFlags); 503 504 /// <summary> 505 /// Adapter to wrap specific NCryptSignHash P/Invokes with a specific padding info 506 /// </summary> 507 [SecuritySafeCritical] NCryptHashSigner(SafeNCryptKeyHandle hKey, ref T pvPaddingInfo, byte[] pbHashValue, int cbHashValue, byte[] pbSignature, int cbSignature, out int pcbResult, AsymmetricPaddingMode dwFlags)508 private delegate ErrorCode NCryptHashSigner<T>(SafeNCryptKeyHandle hKey, 509 ref T pvPaddingInfo, 510 byte[] pbHashValue, 511 int cbHashValue, 512 byte[] pbSignature, 513 int cbSignature, 514 out int pcbResult, 515 AsymmetricPaddingMode dwFlags); 516 517 /// <summary> 518 /// Adapter to wrap specific NCryptVerifySignature P/Invokes with a specific padding info 519 /// </summary> 520 [SecuritySafeCritical] 521 private delegate ErrorCode NCryptSignatureVerifier<T>(SafeNCryptKeyHandle hKey, 522 ref T pvPaddingInfo, 523 byte[] pbHashValue, 524 int cbHashValue, 525 byte[] pbSignature, 526 int cbSignature, 527 AsymmetricPaddingMode dwFlags) where T : struct; 528 529 // 530 // Utility and wrapper functions 531 // 532 533 private static volatile bool s_haveNcryptSupported; 534 private static volatile bool s_ncryptSupported; 535 536 537 /// <summary> 538 /// Generic decryption method, wrapped by decryption calls for specific padding modes 539 /// </summary> 540 [SecuritySafeCritical] 541 private static byte[] DecryptData<T>(SafeNCryptKeyHandle key, 542 byte[] data, 543 ref T paddingInfo, 544 AsymmetricPaddingMode paddingMode, 545 NCryptDecryptor<T> decryptor) where T : struct { 546 Debug.Assert(key != null, "key != null"); 547 Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid"); 548 Debug.Assert(data != null, "data != null"); 549 Debug.Assert(decryptor != null, "decryptor != null"); 550 551 // Figure out how big of a buffer is needed to store the decrypted data 552 int decryptedSize = 0; 553 ErrorCode error = decryptor(key, 554 data, 555 data.Length, 556 ref paddingInfo, 557 null, 558 0, 559 out decryptedSize, 560 paddingMode); 561 if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) { 562 throw new CryptographicException((int)error); 563 } 564 565 // Do the decryption 566 byte[] decrypted = new byte[decryptedSize]; 567 error = decryptor(key, 568 data, 569 data.Length, 570 ref paddingInfo, 571 decrypted, 572 decrypted.Length, 573 out decryptedSize, 574 paddingMode); 575 if (error != ErrorCode.Success) { 576 throw new CryptographicException((int)error); 577 } 578 579 // Sometimes decryptedSize can be less than the allocated buffer size 580 // So resize the array to the actual returned plaintext Array.ResizeSystem.Security.Cryptography.NCryptNative.__anon1581 Array.Resize(ref decrypted, decryptedSize); 582 583 return decrypted; 584 } 585 586 /// <summary> 587 /// Decrypt data using PKCS1 padding 588 /// </summary> 589 [SecuritySafeCritical] DecryptDataPkcs1(SafeNCryptKeyHandle key, byte[] data)590 internal static byte[] DecryptDataPkcs1(SafeNCryptKeyHandle key, byte[] data) { 591 BCryptNative.BCRYPT_PKCS1_PADDING_INFO pkcs1Info = new BCryptNative.BCRYPT_PKCS1_PADDING_INFO(); 592 593 return DecryptData(key, 594 data, 595 ref pkcs1Info, 596 AsymmetricPaddingMode.Pkcs1, 597 Pkcs1PaddingDecryptionWrapper); 598 } 599 600 /// <summary> 601 /// Decrypt data using OAEP padding 602 /// </summary> 603 [SecuritySafeCritical] DecryptDataOaep(SafeNCryptKeyHandle key, byte[] data, string hashAlgorithm)604 internal static byte[] DecryptDataOaep(SafeNCryptKeyHandle key, 605 byte[] data, 606 string hashAlgorithm) { 607 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)"); 608 609 BCryptNative.BCRYPT_OAEP_PADDING_INFO oaepInfo = new BCryptNative.BCRYPT_OAEP_PADDING_INFO(); 610 oaepInfo.pszAlgId = hashAlgorithm; 611 612 return DecryptData(key, 613 data, 614 ref oaepInfo, 615 AsymmetricPaddingMode.Oaep, 616 UnsafeNativeMethods.NCryptDecrypt); 617 } 618 619 [SecurityCritical] Pkcs1PaddingDecryptionWrapper(SafeNCryptKeyHandle hKey, byte[] pbInput, int cbInput, ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding, byte[] pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags)620 private static ErrorCode Pkcs1PaddingDecryptionWrapper(SafeNCryptKeyHandle hKey, 621 byte[] pbInput, 622 int cbInput, 623 ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding, 624 byte[] pbOutput, 625 int cbOutput, 626 out int pcbResult, 627 AsymmetricPaddingMode dwFlags) 628 { 629 Debug.Assert(dwFlags == AsymmetricPaddingMode.Pkcs1, "dwFlags == AsymmetricPaddingMode.Pkcs1"); 630 631 // This method exists to match a generic-based delegate (the ref parameter), but in PKCS#1 mode 632 // the value for pvPadding must be NULL with keys in the Smart Card KSP. 633 // 634 // Passing the ref PKCS1 (signature) padding info will work for software keys, which ignore the value; 635 // but hardware keys fail if it's any value other than NULL (and PKCS#1 was specified). 636 637 return UnsafeNativeMethods.NCryptDecrypt(hKey, pbInput, cbInput, IntPtr.Zero, pbOutput, cbOutput, out pcbResult, dwFlags); 638 } 639 640 /// <summary> 641 /// Generic encryption method, wrapped by decryption calls for specific padding modes 642 /// </summary> 643 [SecuritySafeCritical] 644 private static byte[] EncryptData<T>(SafeNCryptKeyHandle key, 645 byte[] data, 646 ref T paddingInfo, 647 AsymmetricPaddingMode paddingMode, 648 NCryptEncryptor<T> encryptor) where T : struct { 649 Debug.Assert(key != null, "key != null"); 650 Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid"); 651 Debug.Assert(data != null, "data != null"); 652 Debug.Assert(encryptor != null, "encryptor != null"); 653 654 // Figure out how big of a buffer is to encrypt the data 655 int encryptedSize = 0; 656 ErrorCode error = encryptor(key, 657 data, 658 data.Length, 659 ref paddingInfo, 660 null, 661 0, 662 out encryptedSize, 663 paddingMode); 664 if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) { 665 throw new CryptographicException((int)error); 666 } 667 668 // Do the encryption 669 byte[] encrypted = new byte[encryptedSize]; 670 error = encryptor(key, 671 data, 672 data.Length, 673 ref paddingInfo, 674 encrypted, 675 encrypted.Length, 676 out encryptedSize, 677 paddingMode); 678 if (error != ErrorCode.Success) { 679 throw new CryptographicException((int)error); 680 } 681 682 return encrypted; 683 } 684 685 /// <summary> 686 /// Encrypt data using OAEP padding 687 /// </summary> 688 [SecuritySafeCritical] EncryptDataOaep(SafeNCryptKeyHandle key, byte[] data, string hashAlgorithm)689 internal static byte[] EncryptDataOaep(SafeNCryptKeyHandle key, 690 byte[] data, 691 string hashAlgorithm) { 692 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)"); 693 694 BCryptNative.BCRYPT_OAEP_PADDING_INFO oaepInfo = new BCryptNative.BCRYPT_OAEP_PADDING_INFO(); 695 oaepInfo.pszAlgId = hashAlgorithm; 696 697 return EncryptData(key, 698 data, 699 ref oaepInfo, 700 AsymmetricPaddingMode.Oaep, 701 UnsafeNativeMethods.NCryptEncrypt); 702 } 703 704 /// <summary> 705 /// Encrypt data using PKCS1 padding 706 /// </summary> 707 [SecuritySafeCritical] EncryptDataPkcs1(SafeNCryptKeyHandle key, byte[] data)708 internal static byte[] EncryptDataPkcs1(SafeNCryptKeyHandle key, byte[] data) { 709 BCryptNative.BCRYPT_PKCS1_PADDING_INFO pkcs1Info = new BCryptNative.BCRYPT_PKCS1_PADDING_INFO(); 710 711 return EncryptData(key, 712 data, 713 ref pkcs1Info, 714 AsymmetricPaddingMode.Pkcs1, 715 Pkcs1PaddingEncryptionWrapper); 716 } 717 718 [SecurityCritical] Pkcs1PaddingEncryptionWrapper(SafeNCryptKeyHandle hKey, byte[] pbInput, int cbInput, ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding, byte[] pbOutput, int cbOutput, out int pcbResult, AsymmetricPaddingMode dwFlags)719 private static ErrorCode Pkcs1PaddingEncryptionWrapper(SafeNCryptKeyHandle hKey, 720 byte[] pbInput, 721 int cbInput, 722 ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding, 723 byte[] pbOutput, 724 int cbOutput, 725 out int pcbResult, 726 AsymmetricPaddingMode dwFlags) { 727 Debug.Assert(dwFlags == AsymmetricPaddingMode.Pkcs1, "dwFlags == AsymmetricPaddingMode.Pkcs1"); 728 729 // This method exists to match a generic-based delegate (the ref parameter), but in PKCS#1 mode 730 // the value for pvPadding must be NULL with keys in the Smart Card KSP. 731 // 732 // Passing the ref PKCS1 (signature) padding info will work for software keys, which ignore the value; 733 // but hardware keys fail if it's any value other than NULL (and PKCS#1 was specified). 734 735 return UnsafeNativeMethods.NCryptEncrypt(hKey, pbInput, cbInput, IntPtr.Zero, pbOutput, cbOutput, out pcbResult, dwFlags); 736 } 737 738 /// <summary> 739 /// Generic signature method, wrapped by signature calls for specific padding modes 740 /// </summary> 741 [SecuritySafeCritical] 742 private static byte[] SignHash<T>(SafeNCryptKeyHandle key, 743 byte[] hash, 744 ref T paddingInfo, 745 AsymmetricPaddingMode paddingMode, 746 NCryptHashSigner<T> signer) where T : struct { 747 Debug.Assert(key != null, "key != null"); 748 Debug.Assert(!key.IsInvalid && !key.IsClosed, "!key.IsInvalid && !key.IsClosed"); 749 Debug.Assert(hash != null, "hash != null"); 750 Debug.Assert(signer != null, "signer != null"); 751 752 // Figure out how big the signature is 753 int signatureSize = 0; 754 ErrorCode error = signer(key, 755 ref paddingInfo, 756 hash, 757 hash.Length, 758 null, 759 0, 760 out signatureSize, 761 paddingMode); 762 if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) { 763 throw new CryptographicException((int)error); 764 } 765 766 // Sign the hash 767 byte[] signature = new byte[signatureSize]; 768 error = signer(key, 769 ref paddingInfo, 770 hash, 771 hash.Length, 772 signature, 773 signature.Length, 774 out signatureSize, 775 paddingMode); 776 if (error != ErrorCode.Success) { 777 throw new CryptographicException((int)error); 778 } 779 return signature; 780 } 781 782 /// <summary> 783 /// Sign a hash, using PKCS1 padding 784 /// </summary> 785 [SecuritySafeCritical] SignHashPkcs1(SafeNCryptKeyHandle key, byte[] hash, string hashAlgorithm)786 internal static byte[] SignHashPkcs1(SafeNCryptKeyHandle key, 787 byte[] hash, 788 string hashAlgorithm) { 789 Debug.Assert(key != null, "key != null"); 790 Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid"); 791 Debug.Assert(hash != null, "hash != null"); 792 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)"); 793 794 BCryptNative.BCRYPT_PKCS1_PADDING_INFO pkcs1Info = new BCryptNative.BCRYPT_PKCS1_PADDING_INFO(); 795 pkcs1Info.pszAlgId = hashAlgorithm; 796 797 return SignHash(key, 798 hash, 799 ref pkcs1Info, 800 AsymmetricPaddingMode.Pkcs1, 801 UnsafeNativeMethods.NCryptSignHash); 802 } 803 804 /// <summary> 805 /// Sign a hash, using PSS padding 806 /// </summary> 807 [SecuritySafeCritical] SignHashPss(SafeNCryptKeyHandle key, byte[] hash, string hashAlgorithm, int saltBytes)808 internal static byte[] SignHashPss(SafeNCryptKeyHandle key, 809 byte[] hash, 810 string hashAlgorithm, 811 int saltBytes) { 812 Debug.Assert(key != null, "key != null"); 813 Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid"); 814 Debug.Assert(hash != null, "hash != null"); 815 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)"); 816 Debug.Assert(saltBytes >= 0, "saltBytes >= 0"); 817 818 BCryptNative.BCRYPT_PSS_PADDING_INFO pssInfo = new BCryptNative.BCRYPT_PSS_PADDING_INFO(); 819 pssInfo.pszAlgId = hashAlgorithm; 820 pssInfo.cbSalt = saltBytes; 821 822 return SignHash(key, 823 hash, 824 ref pssInfo, 825 AsymmetricPaddingMode.Pss, 826 UnsafeNativeMethods.NCryptSignHash); 827 } 828 829 /// <summary> 830 /// Generic signature verification method, wrapped by verification calls for specific padding modes 831 /// </summary> 832 [SecuritySafeCritical] 833 private static bool VerifySignature<T>(SafeNCryptKeyHandle key, 834 byte[] hash, 835 byte[] signature, 836 ref T paddingInfo, 837 AsymmetricPaddingMode paddingMode, 838 NCryptSignatureVerifier<T> verifier) where T : struct { 839 Debug.Assert(key != null, "key != null"); 840 Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid"); 841 Debug.Assert(hash != null, "hash != null"); 842 Debug.Assert(signature != null, "signature != null"); 843 Debug.Assert(verifier != null, "verifier != null"); 844 845 ErrorCode error = verifier(key, 846 ref paddingInfo, 847 hash, 848 hash.Length, 849 signature, 850 signature.Length, 851 paddingMode); 852 return error == ErrorCode.Success; 853 } 854 855 /// <summary> 856 /// Verify the signature of a hash using PKCS #1 padding 857 /// </summary> 858 [SecuritySafeCritical] VerifySignaturePkcs1(SafeNCryptKeyHandle key, byte[] hash, string hashAlgorithm, byte[] signature)859 internal static bool VerifySignaturePkcs1(SafeNCryptKeyHandle key, 860 byte[] hash, 861 string hashAlgorithm, 862 byte[] signature) { 863 Debug.Assert(key != null, "key != null"); 864 Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid"); 865 Debug.Assert(hash != null, "hash != null"); 866 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)"); 867 Debug.Assert(signature != null, "signature != null"); 868 869 BCryptNative.BCRYPT_PKCS1_PADDING_INFO pkcs1Info = new BCryptNative.BCRYPT_PKCS1_PADDING_INFO(); 870 pkcs1Info.pszAlgId = hashAlgorithm; 871 872 return VerifySignature(key, 873 hash, 874 signature, 875 ref pkcs1Info, 876 AsymmetricPaddingMode.Pkcs1, 877 UnsafeNativeMethods.NCryptVerifySignature); 878 } 879 880 /// <summary> 881 /// Verify the signature of a hash using PSS padding 882 /// </summary> 883 [SecuritySafeCritical] VerifySignaturePss(SafeNCryptKeyHandle key, byte[] hash, string hashAlgorithm, int saltBytes, byte[] signature)884 internal static bool VerifySignaturePss(SafeNCryptKeyHandle key, 885 byte[] hash, 886 string hashAlgorithm, 887 int saltBytes, 888 byte[] signature) { 889 Debug.Assert(key != null, "key != null"); 890 Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid"); 891 Debug.Assert(hash != null, "hash != null"); 892 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)"); 893 Debug.Assert(signature != null, "signature != null"); 894 895 BCryptNative.BCRYPT_PSS_PADDING_INFO pssInfo = new BCryptNative.BCRYPT_PSS_PADDING_INFO(); 896 pssInfo.pszAlgId = hashAlgorithm; 897 pssInfo.cbSalt = saltBytes; 898 899 return VerifySignature(key, 900 hash, 901 signature, 902 ref pssInfo, 903 AsymmetricPaddingMode.Pss, 904 UnsafeNativeMethods.NCryptVerifySignature); 905 } 906 907 /// <summary> 908 /// Determine if NCrypt is supported on the current machine 909 /// </summary> 910 internal static bool NCryptSupported { 911 [SecuritySafeCritical] 912 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")] 913 get { 914 if (!s_haveNcryptSupported) 915 { 916 // Attempt to load ncrypt.dll to see if the NCrypt CNG APIs are available on the machine 917 using (SafeLibraryHandle ncrypt = Microsoft.Win32.UnsafeNativeMethods.LoadLibraryEx("ncrypt", IntPtr.Zero, 0)) { 918 s_ncryptSupported = !ncrypt.IsInvalid; 919 s_haveNcryptSupported = true; 920 } 921 } 922 923 return s_ncryptSupported; 924 } 925 } 926 927 /// <summary> 928 /// Build an ECC public key blob to represent the given parameters 929 /// </summary> BuildEccPublicBlob(string algorithm, BigInteger x, BigInteger y)930 internal static byte[] BuildEccPublicBlob(string algorithm, BigInteger x, BigInteger y) { 931 Contract.Requires(!String.IsNullOrEmpty(algorithm)); 932 Contract.Ensures(Contract.Result<byte[]>() != null); 933 934 // 935 // #ECCPublicBlobFormat 936 // The ECC public key blob format is as follows: 937 // 938 // DWORD dwMagic 939 // DWORD cbKey 940 // X parameter (cbKey bytes long, byte-reversed) 941 // Y parameter (cbKey bytes long, byte-reversed) 942 // 943 944 // First map the algorithm name to its magic number and key size 945 BCryptNative.KeyBlobMagicNumber algorithmMagic; 946 int keySize; 947 BCryptNative.MapAlgorithmIdToMagic(algorithm, out algorithmMagic, out keySize); 948 949 // Next generate the public key parameters 950 byte[] xBytes = ReverseBytes(FillKeyParameter(x.ToByteArray(), keySize)); 951 byte[] yBytes = ReverseBytes(FillKeyParameter(y.ToByteArray(), keySize)); 952 953 // Finally, lay out the structure itself 954 byte[] blob = new byte[2 * sizeof(int) + xBytes.Length + yBytes.Length]; 955 Buffer.BlockCopy(BitConverter.GetBytes((int)algorithmMagic), 0, blob, 0, sizeof(int)); 956 Buffer.BlockCopy(BitConverter.GetBytes(xBytes.Length), 0, blob, sizeof(int), sizeof(int)); 957 Buffer.BlockCopy(xBytes, 0, blob, 2 * sizeof(int), xBytes.Length); 958 Buffer.BlockCopy(yBytes, 0, blob, 2 * sizeof(int) + xBytes.Length, yBytes.Length); 959 960 return blob; 961 } 962 963 /// <summary> 964 /// Create a random CNG key 965 /// </summary> 966 [System.Security.SecurityCritical] CreatePersistedKey(SafeNCryptProviderHandle provider, string algorithm, string name, CngKeyCreationOptions options)967 internal static SafeNCryptKeyHandle CreatePersistedKey(SafeNCryptProviderHandle provider, 968 string algorithm, 969 string name, 970 CngKeyCreationOptions options) { 971 Contract.Requires(provider != null && !provider.IsInvalid && !provider.IsClosed); 972 Contract.Requires(!String.IsNullOrEmpty(algorithm)); 973 Contract.Ensures(Contract.Result<SafeNCryptKeyHandle>() != null && 974 !Contract.Result<SafeNCryptKeyHandle>().IsInvalid && 975 !Contract.Result<SafeNCryptKeyHandle>().IsClosed); 976 977 SafeNCryptKeyHandle keyHandle = null; 978 ErrorCode error = UnsafeNativeMethods.NCryptCreatePersistedKey(provider, 979 out keyHandle, 980 algorithm, 981 name, 982 0, 983 options); 984 if (error != ErrorCode.Success) { 985 throw new CryptographicException((int)error); 986 } 987 988 return keyHandle; 989 } 990 991 /// <summary> 992 /// Delete a key 993 /// </summary> 994 [System.Security.SecurityCritical] DeleteKey(SafeNCryptKeyHandle key)995 internal static void DeleteKey(SafeNCryptKeyHandle key) { 996 Contract.Requires(key != null); 997 998 ErrorCode error = UnsafeNativeMethods.NCryptDeleteKey(key, 0); 999 if (error != ErrorCode.Success) { 1000 throw new CryptographicException((int)error); 1001 } 1002 key.SetHandleAsInvalid(); 1003 } 1004 1005 /// <summary> 1006 /// Derive key material from a hash or HMAC KDF 1007 /// </summary> 1008 /// <returns></returns> 1009 [System.Security.SecurityCritical] 1010 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")] DeriveKeyMaterial(SafeNCryptSecretHandle secretAgreement, string kdf, string hashAlgorithm, byte[] hmacKey, byte[] secretPrepend, byte[] secretAppend, SecretAgreementFlags flags)1011 private static byte[] DeriveKeyMaterial(SafeNCryptSecretHandle secretAgreement, 1012 string kdf, 1013 string hashAlgorithm, 1014 byte[] hmacKey, 1015 byte[] secretPrepend, 1016 byte[] secretAppend, 1017 SecretAgreementFlags flags) { 1018 Contract.Requires(secretAgreement != null); 1019 Contract.Requires(!String.IsNullOrEmpty(kdf)); 1020 Contract.Requires(!String.IsNullOrEmpty(hashAlgorithm)); 1021 Contract.Requires(hmacKey == null || kdf == BCryptNative.KeyDerivationFunction.Hmac); 1022 Contract.Ensures(Contract.Result<byte[]>() != null); 1023 1024 List<NCryptBuffer> parameters = new List<NCryptBuffer>(); 1025 1026 // First marshal the hash algoritm 1027 IntPtr hashAlgorithmString = IntPtr.Zero; 1028 1029 // Run in a CER so that we know we'll free the memory for the marshaled string 1030 RuntimeHelpers.PrepareConstrainedRegions(); 1031 try { 1032 // Assign in a CER so we don't fail between allocating the memory and assigning the result 1033 // back to the string variable. 1034 RuntimeHelpers.PrepareConstrainedRegions(); 1035 try { } 1036 finally { 1037 hashAlgorithmString = Marshal.StringToCoTaskMemUni(hashAlgorithm); 1038 } 1039 1040 // We always need to marshal the hashing function 1041 NCryptBuffer hashAlgorithmBuffer = new NCryptBuffer(); 1042 hashAlgorithmBuffer.cbBuffer = (hashAlgorithm.Length + 1) * sizeof(char); 1043 hashAlgorithmBuffer.BufferType = BufferType.KdfHashAlgorithm; 1044 hashAlgorithmBuffer.pvBuffer = hashAlgorithmString; 1045 parameters.Add(hashAlgorithmBuffer); 1046 1047 unsafe { 1048 fixed (byte* pHmacKey = hmacKey, pSecretPrepend = secretPrepend, pSecretAppend = secretAppend) { 1049 // 1050 // Now marshal the other parameters 1051 // 1052 1053 if (pHmacKey != null) { 1054 NCryptBuffer hmacKeyBuffer = new NCryptBuffer(); 1055 hmacKeyBuffer.cbBuffer = hmacKey.Length; 1056 hmacKeyBuffer.BufferType = BufferType.KdfHmacKey; 1057 hmacKeyBuffer.pvBuffer = new IntPtr(pHmacKey); 1058 parameters.Add(hmacKeyBuffer); 1059 } 1060 1061 if (pSecretPrepend != null) { 1062 NCryptBuffer secretPrependBuffer = new NCryptBuffer(); 1063 secretPrependBuffer.cbBuffer = secretPrepend.Length; 1064 secretPrependBuffer.BufferType = BufferType.KdfSecretPrepend; 1065 secretPrependBuffer.pvBuffer = new IntPtr(pSecretPrepend); 1066 parameters.Add(secretPrependBuffer); 1067 } 1068 1069 if (pSecretAppend != null) { 1070 NCryptBuffer secretAppendBuffer = new NCryptBuffer(); 1071 secretAppendBuffer.cbBuffer = secretAppend.Length; 1072 secretAppendBuffer.BufferType = BufferType.KdfSecretAppend; 1073 secretAppendBuffer.pvBuffer = new IntPtr(pSecretAppend); 1074 parameters.Add(secretAppendBuffer); 1075 } 1076 1077 return DeriveKeyMaterial(secretAgreement, 1078 kdf, 1079 parameters.ToArray(), 1080 flags); 1081 } 1082 } 1083 } 1084 finally { 1085 if (hashAlgorithmString != IntPtr.Zero) { 1086 Marshal.FreeCoTaskMem(hashAlgorithmString); 1087 } 1088 } 1089 } 1090 1091 /// <summary> 1092 /// Derive key material using a given KDF and secret agreement 1093 /// </summary> 1094 [System.Security.SecurityCritical] DeriveKeyMaterial(SafeNCryptSecretHandle secretAgreement, string kdf, NCryptBuffer[] parameters, SecretAgreementFlags flags)1095 private static byte[] DeriveKeyMaterial(SafeNCryptSecretHandle secretAgreement, 1096 string kdf, 1097 NCryptBuffer[] parameters, 1098 SecretAgreementFlags flags) { 1099 Contract.Requires(secretAgreement != null); 1100 Contract.Requires(!String.IsNullOrEmpty(kdf)); 1101 Contract.Requires(parameters != null); 1102 Contract.Ensures(Contract.Result<byte[]>() != null); 1103 1104 unsafe { 1105 fixed (NCryptBuffer* pParameters = parameters) { 1106 NCryptBufferDesc parameterDesc = new NCryptBufferDesc(); 1107 parameterDesc.ulVersion = 0; 1108 parameterDesc.cBuffers = parameters.Length; 1109 parameterDesc.pBuffers = new IntPtr(pParameters); 1110 1111 // Figure out how big the key material is 1112 int keySize = 0; 1113 ErrorCode error = UnsafeNativeMethods.NCryptDeriveKey(secretAgreement, 1114 kdf, 1115 ref parameterDesc, 1116 null, 1117 0, 1118 out keySize, 1119 flags); 1120 if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) { 1121 throw new CryptographicException((int)error); 1122 } 1123 1124 // Allocate memory for the key material and generate it 1125 byte[] keyMaterial = new byte[keySize]; 1126 error = UnsafeNativeMethods.NCryptDeriveKey(secretAgreement, 1127 kdf, 1128 ref parameterDesc, 1129 keyMaterial, 1130 keyMaterial.Length, 1131 out keySize, 1132 flags); 1133 1134 if (error != ErrorCode.Success) { 1135 throw new CryptographicException((int)error); 1136 } 1137 1138 return keyMaterial; 1139 } 1140 } 1141 } 1142 1143 /// <summary> 1144 /// Derive key material from a secret agreement using a hash KDF 1145 /// </summary> 1146 [System.Security.SecurityCritical] DeriveKeyMaterialHash(SafeNCryptSecretHandle secretAgreement, string hashAlgorithm, byte[] secretPrepend, byte[] secretAppend, SecretAgreementFlags flags)1147 internal static byte[] DeriveKeyMaterialHash(SafeNCryptSecretHandle secretAgreement, 1148 string hashAlgorithm, 1149 byte[] secretPrepend, 1150 byte[] secretAppend, 1151 SecretAgreementFlags flags) { 1152 Contract.Requires(secretAgreement != null); 1153 Contract.Requires(!String.IsNullOrEmpty(hashAlgorithm)); 1154 Contract.Ensures(Contract.Result<byte[]>() != null); 1155 1156 return DeriveKeyMaterial(secretAgreement, 1157 BCryptNative.KeyDerivationFunction.Hash, 1158 hashAlgorithm, 1159 null, 1160 secretPrepend, 1161 secretAppend, 1162 flags); 1163 } 1164 1165 /// <summary> 1166 /// Derive key material from a secret agreement using a HMAC KDF 1167 /// </summary> 1168 [System.Security.SecurityCritical] DeriveKeyMaterialHmac(SafeNCryptSecretHandle secretAgreement, string hashAlgorithm, byte[] hmacKey, byte[] secretPrepend, byte[] secretAppend, SecretAgreementFlags flags)1169 internal static byte[] DeriveKeyMaterialHmac(SafeNCryptSecretHandle secretAgreement, 1170 string hashAlgorithm, 1171 byte[] hmacKey, 1172 byte[] secretPrepend, 1173 byte[] secretAppend, 1174 SecretAgreementFlags flags) { 1175 Contract.Requires(secretAgreement != null); 1176 Contract.Requires(!String.IsNullOrEmpty(hashAlgorithm)); 1177 Contract.Ensures(Contract.Result<byte[]>() != null); 1178 1179 return DeriveKeyMaterial(secretAgreement, 1180 BCryptNative.KeyDerivationFunction.Hmac, 1181 hashAlgorithm, 1182 hmacKey, 1183 secretPrepend, 1184 secretAppend, 1185 flags); 1186 } 1187 1188 /// <summary> 1189 /// Derive key material from a secret agreeement using the TLS KDF 1190 /// </summary> 1191 [System.Security.SecurityCritical] DeriveKeyMaterialTls(SafeNCryptSecretHandle secretAgreement, byte[] label, byte[] seed, SecretAgreementFlags flags)1192 internal static byte[] DeriveKeyMaterialTls(SafeNCryptSecretHandle secretAgreement, 1193 byte[] label, 1194 byte[] seed, 1195 SecretAgreementFlags flags) { 1196 Contract.Requires(secretAgreement != null); 1197 Contract.Requires(label != null && seed != null); 1198 Contract.Ensures(Contract.Result<byte[]>() != null); 1199 1200 NCryptBuffer[] buffers = new NCryptBuffer[2]; 1201 1202 unsafe { 1203 fixed (byte* pLabel = label, pSeed = seed) { 1204 NCryptBuffer labelBuffer = new NCryptBuffer(); 1205 labelBuffer.cbBuffer = label.Length; 1206 labelBuffer.BufferType = BufferType.KdfTlsLabel; 1207 labelBuffer.pvBuffer = new IntPtr(pLabel); 1208 buffers[0] = labelBuffer; 1209 1210 NCryptBuffer seedBuffer = new NCryptBuffer(); 1211 seedBuffer.cbBuffer = seed.Length; 1212 seedBuffer.BufferType = BufferType.KdfTlsSeed; 1213 seedBuffer.pvBuffer = new IntPtr(pSeed); 1214 buffers[1] = seedBuffer; 1215 1216 return DeriveKeyMaterial(secretAgreement, 1217 BCryptNative.KeyDerivationFunction.Tls, 1218 buffers, 1219 flags); 1220 } 1221 } 1222 } 1223 1224 /// <summary> 1225 /// Generate a secret agreement value for between two parties 1226 /// </summary> 1227 [System.Security.SecurityCritical] DeriveSecretAgreement(SafeNCryptKeyHandle privateKey, SafeNCryptKeyHandle otherPartyPublicKey)1228 internal static SafeNCryptSecretHandle DeriveSecretAgreement(SafeNCryptKeyHandle privateKey, 1229 SafeNCryptKeyHandle otherPartyPublicKey) { 1230 Contract.Requires(privateKey != null); 1231 Contract.Requires(otherPartyPublicKey != null); 1232 Contract.Ensures(Contract.Result<SafeNCryptSecretHandle>() != null && 1233 !Contract.Result<SafeNCryptSecretHandle>().IsClosed && 1234 !Contract.Result<SafeNCryptSecretHandle>().IsInvalid); 1235 1236 SafeNCryptSecretHandle secretAgreement; 1237 ErrorCode error = UnsafeNativeMethods.NCryptSecretAgreement(privateKey, 1238 otherPartyPublicKey, 1239 out secretAgreement, 1240 0); 1241 1242 if (error != ErrorCode.Success) { 1243 throw new CryptographicException((int)error); 1244 } 1245 1246 return secretAgreement; 1247 } 1248 1249 /// <summary> 1250 /// Export a key from the KSP 1251 /// </summary> 1252 [System.Security.SecurityCritical] ExportKey(SafeNCryptKeyHandle key, string format)1253 internal static byte[] ExportKey(SafeNCryptKeyHandle key, string format) { 1254 Contract.Requires(key != null); 1255 Contract.Requires(!String.IsNullOrEmpty(format)); 1256 Contract.Ensures(Contract.Result<byte[]>() != null); 1257 1258 // Figure out how big of a buffer we need to export into 1259 int bufferSize = 0; 1260 ErrorCode error = UnsafeNativeMethods.NCryptExportKey(key, 1261 IntPtr.Zero, 1262 format, 1263 IntPtr.Zero, 1264 null, 1265 0, 1266 out bufferSize, 1267 0); 1268 1269 if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) { 1270 throw new CryptographicException((int)error); 1271 } 1272 1273 // Export the key 1274 Debug.Assert(bufferSize > 0, "bufferSize > 0"); 1275 byte[] keyBlob = new byte[bufferSize]; 1276 error = UnsafeNativeMethods.NCryptExportKey(key, 1277 IntPtr.Zero, 1278 format, 1279 IntPtr.Zero, 1280 keyBlob, 1281 keyBlob.Length, 1282 out bufferSize, 1283 0); 1284 1285 if (error != ErrorCode.Success) { 1286 throw new CryptographicException((int)error); 1287 } 1288 1289 return keyBlob; 1290 } 1291 1292 /// <summary> 1293 /// Make sure that a key is padded out to be its full size 1294 /// </summary> FillKeyParameter(byte[] key, int keySize)1295 private static byte[] FillKeyParameter(byte[] key, int keySize) { 1296 Contract.Requires(key != null); 1297 Contract.Requires(keySize > 0); 1298 Contract.Ensures(Contract.Result<byte[]>() != null && Contract.Result<byte[]>().Length >= keySize / 8); 1299 1300 int bytesRequired = (keySize / 8) + (keySize % 8 == 0 ? 0 : 1); 1301 if (key.Length == bytesRequired) { 1302 return key; 1303 } 1304 1305 #if DEBUG 1306 // If the key is longer than required, it should have been padded out with zeros 1307 if (key.Length > bytesRequired) { 1308 for (int i = bytesRequired; i < key.Length; i++) { 1309 Debug.Assert(key[i] == 0, "key[i] == 0"); 1310 } 1311 } 1312 #endif 1313 byte[] fullKey = new byte[bytesRequired]; 1314 Buffer.BlockCopy(key, 0, fullKey, 0, Math.Min(key.Length, fullKey.Length)); 1315 return fullKey; 1316 } 1317 1318 /// <summary> 1319 /// Finalize a key and prepare it for use 1320 /// </summary> 1321 [System.Security.SecurityCritical] FinalizeKey(SafeNCryptKeyHandle key)1322 internal static void FinalizeKey(SafeNCryptKeyHandle key) { 1323 Contract.Requires(key != null && !key.IsInvalid && !key.IsClosed); 1324 1325 ErrorCode error = UnsafeNativeMethods.NCryptFinalizeKey(key, 0); 1326 if (error != ErrorCode.Success) { 1327 throw new CryptographicException((int)error); 1328 } 1329 } 1330 1331 /// <summary> 1332 /// Get the value of an NCrypt property 1333 /// </summary> 1334 [System.Security.SecurityCritical] GetProperty(SafeNCryptHandle ncryptObject, string propertyName, CngPropertyOptions propertyOptions, out bool foundProperty)1335 internal static byte[] GetProperty(SafeNCryptHandle ncryptObject, 1336 string propertyName, 1337 CngPropertyOptions propertyOptions, 1338 out bool foundProperty) { 1339 Contract.Requires(ncryptObject != null); 1340 Contract.Requires(propertyName != null); 1341 1342 // Find out how big of a buffer we need to store the property in 1343 int bufferSize = 0; 1344 ErrorCode error = UnsafeNativeMethods.NCryptGetProperty(ncryptObject, 1345 propertyName, 1346 null, 1347 0, 1348 out bufferSize, 1349 propertyOptions); 1350 1351 // 1352 // NTE_NOT_FOUND means this property does not exist, any other error besides NTE_BUFFER_TOO_SMALL 1353 // indicates a real problem. 1354 // 1355 1356 if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall && error != ErrorCode.NotFound) { 1357 throw new CryptographicException((int)error); 1358 } 1359 1360 foundProperty = error != ErrorCode.NotFound; 1361 1362 // Pull back the property value 1363 byte[] value = null; 1364 if (error != ErrorCode.NotFound && bufferSize > 0) { 1365 value = new byte[bufferSize]; 1366 error = UnsafeNativeMethods.NCryptGetProperty(ncryptObject, 1367 propertyName, 1368 value, 1369 value.Length, 1370 out bufferSize, 1371 propertyOptions); 1372 if (error != ErrorCode.Success) { 1373 throw new CryptographicException((int)error); 1374 } 1375 1376 foundProperty = true; 1377 } 1378 1379 return value; 1380 } 1381 1382 /// <summary> 1383 /// Get the value of a DWORD NCrypt property 1384 /// </summary> 1385 [System.Security.SecurityCritical] GetPropertyAsDWord(SafeNCryptHandle ncryptObject, string propertyName, CngPropertyOptions propertyOptions)1386 internal static int GetPropertyAsDWord(SafeNCryptHandle ncryptObject, 1387 string propertyName, 1388 CngPropertyOptions propertyOptions) { 1389 Contract.Requires(ncryptObject != null); 1390 Contract.Requires(propertyName != null); 1391 1392 bool foundProperty; 1393 byte[] valueBytes = GetProperty(ncryptObject, propertyName, propertyOptions, out foundProperty); 1394 1395 if (!foundProperty || valueBytes == null) { 1396 return 0; 1397 } 1398 else { 1399 return BitConverter.ToInt32(valueBytes, 0); 1400 } 1401 } 1402 1403 [SecurityCritical] GetPropertyAsInt(SafeNCryptHandle ncryptObject, string propertyName, CngPropertyOptions propertyOptions, ref int propertyValue)1404 internal static ErrorCode GetPropertyAsInt(SafeNCryptHandle ncryptObject, 1405 string propertyName, 1406 CngPropertyOptions propertyOptions, 1407 ref int propertyValue) { 1408 Contract.Requires(ncryptObject != null); 1409 Contract.Requires(propertyName != null); 1410 1411 int cbResult; 1412 1413 ErrorCode errorCode = UnsafeNativeMethods.NCryptGetProperty( 1414 ncryptObject, 1415 propertyName, 1416 ref propertyValue, 1417 sizeof(int), 1418 out cbResult, 1419 propertyOptions); 1420 1421 if (errorCode == ErrorCode.Success) 1422 { 1423 System.Diagnostics.Debug.Assert(cbResult == sizeof(int), "Expected cbResult=4, got " + cbResult); 1424 } 1425 1426 return errorCode; 1427 } 1428 1429 /// <summary> 1430 /// Get the value of a pointer NCrypt property 1431 /// </summary> 1432 [System.Security.SecurityCritical] 1433 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] GetPropertyAsIntPtr(SafeNCryptHandle ncryptObject, string propertyName, CngPropertyOptions propertyOptions)1434 internal static IntPtr GetPropertyAsIntPtr(SafeNCryptHandle ncryptObject, 1435 string propertyName, 1436 CngPropertyOptions propertyOptions) { 1437 Contract.Requires(ncryptObject != null); 1438 Contract.Requires(propertyName != null); 1439 1440 // Find out how big of a buffer we need to store the property in 1441 int bufferSize = IntPtr.Size; 1442 IntPtr value = IntPtr.Zero; 1443 ErrorCode error = UnsafeNativeMethods.NCryptGetProperty(ncryptObject, 1444 propertyName, 1445 out value, 1446 IntPtr.Size, 1447 out bufferSize, 1448 propertyOptions); 1449 1450 // NTE_NOT_FOUND means this property was not set, so return a NULL pointer 1451 if (error == ErrorCode.NotFound) { 1452 return IntPtr.Zero; 1453 } 1454 1455 if (error != ErrorCode.Success) { 1456 throw new CryptographicException((int)error); 1457 } 1458 1459 return value; 1460 } 1461 1462 /// <summary> 1463 /// Get the value of a string NCrypt property 1464 /// </summary> 1465 [System.Security.SecurityCritical] 1466 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")] GetPropertyAsString(SafeNCryptHandle ncryptObject, string propertyName, CngPropertyOptions propertyOptions)1467 internal static string GetPropertyAsString(SafeNCryptHandle ncryptObject, 1468 string propertyName, 1469 CngPropertyOptions propertyOptions) { 1470 Contract.Requires(ncryptObject != null); 1471 Contract.Requires(propertyName != null); 1472 1473 bool foundProperty; 1474 byte[] valueBytes = GetProperty(ncryptObject, propertyName, propertyOptions, out foundProperty); 1475 1476 if (!foundProperty || valueBytes == null) { 1477 return null; 1478 } 1479 else if (valueBytes.Length == 0) { 1480 return String.Empty; 1481 } 1482 else { 1483 unsafe { 1484 fixed (byte* pValueBytes = valueBytes) { 1485 return Marshal.PtrToStringUni(new IntPtr(pValueBytes)); 1486 } 1487 } 1488 } 1489 } 1490 1491 /// <summary> 1492 /// Get the value of an NCrypt structure property -- this will return an empty structure if the 1493 /// property does not exist. 1494 /// </summary> 1495 [System.Security.SecurityCritical] 1496 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")] 1497 internal static T GetPropertyAsStruct<T>(SafeNCryptHandle ncryptObject, 1498 string propertyName, 1499 CngPropertyOptions propertyOptions) where T : struct { 1500 Contract.Requires(ncryptObject != null); 1501 Contract.Requires(propertyName != null); 1502 1503 bool foundProperty; 1504 byte[] valueBytes = GetProperty(ncryptObject, propertyName, propertyOptions, out foundProperty); 1505 1506 if (!foundProperty || valueBytes == null) { 1507 return new T(); 1508 } 1509 1510 unsafe { 1511 fixed (byte *pValue = valueBytes) { 1512 return (T)Marshal.PtrToStructure(new IntPtr(pValue), typeof(T)); 1513 } 1514 } 1515 } 1516 1517 /// <summary> 1518 /// Import a key into the KSP 1519 /// </summary> 1520 [System.Security.SecurityCritical] ImportKey(SafeNCryptProviderHandle provider, byte[] keyBlob, string format)1521 internal static SafeNCryptKeyHandle ImportKey(SafeNCryptProviderHandle provider, 1522 byte[] keyBlob, 1523 string format) { 1524 Contract.Requires(provider != null); 1525 Contract.Requires(keyBlob != null); 1526 Contract.Requires(!String.IsNullOrEmpty(format)); 1527 Contract.Ensures(Contract.Result<SafeNCryptKeyHandle>() != null && 1528 !Contract.Result<SafeNCryptKeyHandle>().IsInvalid && 1529 !Contract.Result<SafeNCryptKeyHandle>().IsClosed); 1530 1531 SafeNCryptKeyHandle keyHandle = null; 1532 ErrorCode error = UnsafeNativeMethods.NCryptImportKey(provider, 1533 IntPtr.Zero, 1534 format, 1535 IntPtr.Zero, 1536 out keyHandle, 1537 keyBlob, 1538 keyBlob.Length, 1539 0); 1540 1541 if (error != ErrorCode.Success) { 1542 throw new CryptographicException((int)error); 1543 } 1544 1545 return keyHandle; 1546 } 1547 1548 [System.Security.SecurityCritical] ImportKey(SafeNCryptProviderHandle provider, byte[] keyBlob, string format, IntPtr pParametersList)1549 internal static SafeNCryptKeyHandle ImportKey(SafeNCryptProviderHandle provider, 1550 byte[] keyBlob, 1551 string format, 1552 IntPtr pParametersList) { 1553 Contract.Requires(provider != null); 1554 Contract.Requires(keyBlob != null); 1555 Contract.Requires(!String.IsNullOrEmpty(format)); 1556 Contract.Ensures(Contract.Result<SafeNCryptKeyHandle>() != null && 1557 !Contract.Result<SafeNCryptKeyHandle>().IsInvalid && 1558 !Contract.Result<SafeNCryptKeyHandle>().IsClosed); 1559 1560 SafeNCryptKeyHandle keyHandle = null; 1561 ErrorCode error = UnsafeNativeMethods.NCryptImportKey(provider, 1562 IntPtr.Zero, 1563 format, 1564 pParametersList, 1565 out keyHandle, 1566 keyBlob, 1567 keyBlob.Length, 1568 0); 1569 1570 if (error != ErrorCode.Success) 1571 { 1572 throw new CryptographicException((int)error); 1573 } 1574 1575 return keyHandle; 1576 } 1577 1578 /// <summary> 1579 /// Open an existing key 1580 /// </summary> 1581 [System.Security.SecurityCritical] OpenKey(SafeNCryptProviderHandle provider, string name, CngKeyOpenOptions options)1582 internal static SafeNCryptKeyHandle OpenKey(SafeNCryptProviderHandle provider, 1583 string name, 1584 CngKeyOpenOptions options) { 1585 Contract.Requires(provider != null && !provider.IsInvalid && !provider.IsClosed); 1586 Contract.Requires(name != null); 1587 1588 SafeNCryptKeyHandle key = null; 1589 ErrorCode error = UnsafeNativeMethods.NCryptOpenKey(provider, out key, name, 0, options); 1590 1591 if (error != ErrorCode.Success) { 1592 throw new CryptographicException((int)error); 1593 } 1594 1595 return key; 1596 } 1597 1598 /// <summary> 1599 /// Open the specified key storage provider 1600 /// </summary> 1601 [System.Security.SecurityCritical] OpenStorageProvider(string providerName)1602 internal static SafeNCryptProviderHandle OpenStorageProvider(string providerName) { 1603 Contract.Requires(!String.IsNullOrEmpty(providerName)); 1604 Contract.Ensures(Contract.Result<SafeNCryptProviderHandle>() != null && 1605 !Contract.Result<SafeNCryptProviderHandle>().IsInvalid && 1606 !Contract.Result<SafeNCryptProviderHandle>().IsClosed); 1607 1608 SafeNCryptProviderHandle providerHandle = null; 1609 ErrorCode error = UnsafeNativeMethods.NCryptOpenStorageProvider(out providerHandle, 1610 providerName, 1611 0); 1612 1613 if (error != ErrorCode.Success) { 1614 throw new CryptographicException((int)error); 1615 } 1616 1617 return providerHandle; 1618 } 1619 1620 /// <summary> 1621 /// Reverse the bytes in a buffer 1622 /// </summary> ReverseBytes(byte[] buffer)1623 private static byte[] ReverseBytes(byte[] buffer) { 1624 Contract.Requires(buffer != null); 1625 Contract.Ensures(Contract.Result<byte[]>() != null && Contract.Result<byte[]>().Length == buffer.Length); 1626 return ReverseBytes(buffer, 0, buffer.Length, false); 1627 } 1628 1629 /// <summary> 1630 /// Reverse a section of bytes within a buffer 1631 /// </summary> ReverseBytes(byte[] buffer, int offset, int count)1632 private static byte[] ReverseBytes(byte[] buffer, int offset, int count) { 1633 return ReverseBytes(buffer, offset, count, false); 1634 } 1635 ReverseBytes(byte[] buffer, int offset, int count, bool padWithZeroByte)1636 private static byte[] ReverseBytes(byte[] buffer, int offset, int count, bool padWithZeroByte) { 1637 Contract.Requires(buffer != null); 1638 Contract.Requires(offset >= 0 && offset < buffer.Length); 1639 Contract.Requires(count >= 0 && buffer.Length - count >= offset); 1640 Contract.Ensures(Contract.Result<byte[]>() != null); 1641 Contract.Ensures(Contract.Result<byte[]>().Length == (padWithZeroByte ? count + 1 : count)); 1642 Contract.Ensures(padWithZeroByte ? Contract.Result<byte[]>()[count] == 0 : true); 1643 1644 byte[] reversed; 1645 if(padWithZeroByte) 1646 { 1647 reversed = new byte[count+1]; // the last (most-significant) byte will be left as 0x00 1648 } 1649 else 1650 { 1651 reversed = new byte[count]; 1652 } 1653 1654 int lastByte = offset + count - 1; 1655 for (int i = 0; i < count; i++) { 1656 reversed[i] = buffer[lastByte - i]; 1657 } 1658 1659 return reversed; 1660 } 1661 1662 /// <summary> 1663 /// Set a DWORD property on an NCrypt object 1664 /// </summary> 1665 [System.Security.SecurityCritical] SetProperty(SafeNCryptHandle ncryptObject, string propertyName, int value, CngPropertyOptions propertyOptions)1666 internal static void SetProperty(SafeNCryptHandle ncryptObject, 1667 string propertyName, 1668 int value, 1669 CngPropertyOptions propertyOptions) { 1670 Contract.Requires(ncryptObject != null); 1671 Contract.Requires(propertyName != null); 1672 1673 SetProperty(ncryptObject, propertyName, BitConverter.GetBytes(value), propertyOptions); 1674 } 1675 1676 /// <summary> 1677 /// Set a string property 1678 /// </summary> 1679 [System.Security.SecurityCritical] SetProperty(SafeNCryptHandle ncryptObject, string propertyName, string value, CngPropertyOptions propertyOptions)1680 internal static void SetProperty(SafeNCryptHandle ncryptObject, 1681 string propertyName, 1682 string value, 1683 CngPropertyOptions propertyOptions) { 1684 Contract.Requires(ncryptObject != null); 1685 Contract.Requires(propertyName != null); 1686 1687 ErrorCode error = UnsafeNativeMethods.NCryptSetProperty(ncryptObject, 1688 propertyName, 1689 value, 1690 (value.Length + 1) * sizeof(char), 1691 propertyOptions); 1692 1693 if (error != ErrorCode.Success) { 1694 throw new CryptographicException((int)error); 1695 } 1696 } 1697 1698 /// <summary> 1699 /// Set a structure property 1700 /// </summary> 1701 [System.Security.SecurityCritical] 1702 internal static void SetProperty<T>(SafeNCryptHandle ncryptObject, 1703 string propertyName, 1704 T value, 1705 CngPropertyOptions propertyOptions) where T : struct { 1706 Contract.Requires(ncryptObject != null); 1707 Contract.Requires(propertyName != null); 1708 1709 byte[] buffer = new byte[Marshal.SizeOf(typeof(T))]; 1710 1711 unsafe { 1712 fixed (byte *pBuffer = buffer) { 1713 1714 bool marshaledStructure = false; 1715 RuntimeHelpers.PrepareConstrainedRegions(); 1716 try { 1717 // If we successfully marshal into the buffer, make sure to destroy the buffer when we're done 1718 RuntimeHelpers.PrepareConstrainedRegions(); 1719 try { } 1720 finally { 1721 Marshal.StructureToPtr(value, new IntPtr(pBuffer), false); 1722 marshaledStructure = true; 1723 } 1724 1725 SetProperty(ncryptObject, propertyName, buffer, propertyOptions); 1726 } 1727 finally { 1728 if (marshaledStructure) { 1729 Marshal.DestroyStructure(new IntPtr(pBuffer), typeof(T)); 1730 } 1731 } 1732 } 1733 } 1734 } 1735 1736 /// <summary> 1737 /// Set a property on an NCrypt object 1738 /// </summary> 1739 [System.Security.SecurityCritical] SetProperty(SafeNCryptHandle ncryptObject, string propertyName, byte[] value, CngPropertyOptions propertyOptions)1740 internal static void SetProperty(SafeNCryptHandle ncryptObject, 1741 string propertyName, 1742 byte[] value, 1743 CngPropertyOptions propertyOptions) { 1744 Contract.Requires(ncryptObject != null); 1745 Contract.Requires(propertyName != null); 1746 1747 ErrorCode error = UnsafeNativeMethods.NCryptSetProperty(ncryptObject, 1748 propertyName, 1749 value, 1750 value != null ? value.Length : 0, 1751 propertyOptions); 1752 1753 if (error != ErrorCode.Success) { 1754 throw new CryptographicException((int)error); 1755 } 1756 } 1757 1758 /// <summary> 1759 /// Sign a hash using no padding 1760 /// </summary> 1761 [System.Security.SecurityCritical] SignHash(SafeNCryptKeyHandle key, byte[] hash)1762 internal static byte[] SignHash(SafeNCryptKeyHandle key, byte[] hash) { 1763 Contract.Requires(key != null); 1764 Contract.Requires(hash != null); 1765 Contract.Ensures(Contract.Result<byte[]>() != null); 1766 1767 // Figure out how big the signature is 1768 int signatureSize = 0; 1769 ErrorCode error = UnsafeNativeMethods.NCryptSignHash(key, 1770 IntPtr.Zero, 1771 hash, 1772 hash.Length, 1773 null, 1774 0, 1775 out signatureSize, 1776 0); 1777 1778 if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) { 1779 throw new CryptographicException((int)error); 1780 } 1781 1782 // Sign the data 1783 Debug.Assert(signatureSize > 0, "signatureSize > 0"); 1784 byte[] signature = new byte[signatureSize]; 1785 1786 error = UnsafeNativeMethods.NCryptSignHash(key, 1787 IntPtr.Zero, 1788 hash, 1789 hash.Length, 1790 signature, 1791 signature.Length, 1792 out signatureSize, 1793 0); 1794 1795 if (error != ErrorCode.Success) { 1796 throw new CryptographicException((int)error); 1797 } 1798 1799 return signature; 1800 } 1801 1802 /// <summary> 1803 /// Sign a hash using no padding 1804 /// </summary> 1805 [System.Security.SecurityCritical] SignHash(SafeNCryptKeyHandle key, byte[] hash, int expectedSize)1806 internal static byte[] SignHash(SafeNCryptKeyHandle key, byte[] hash, int expectedSize) 1807 { 1808 Contract.Requires(key != null); 1809 Contract.Requires(hash != null); 1810 Contract.Ensures(Contract.Result<byte[]>() != null); 1811 1812 #if DEBUG 1813 expectedSize = 1; 1814 #endif 1815 1816 // Figure out how big the signature is 1817 byte[] signature = new byte[expectedSize]; 1818 int signatureSize = 0; 1819 ErrorCode error = UnsafeNativeMethods.NCryptSignHash(key, 1820 IntPtr.Zero, 1821 hash, 1822 hash.Length, 1823 signature, 1824 signature.Length, 1825 out signatureSize, 1826 0); 1827 1828 if (error == ErrorCode.BufferTooSmall) 1829 { 1830 signature = new byte[signatureSize]; 1831 1832 error = UnsafeNativeMethods.NCryptSignHash(key, 1833 IntPtr.Zero, 1834 hash, 1835 hash.Length, 1836 signature, 1837 signature.Length, 1838 out signatureSize, 1839 0); 1840 } 1841 1842 if (error != ErrorCode.Success) 1843 { 1844 throw new CryptographicException((int)error); 1845 } 1846 1847 Array.Resize(ref signature, signatureSize); 1848 return signature; 1849 } 1850 1851 /// <summary> 1852 /// Unpack a key blob in ECC public blob format into its X and Y parameters 1853 /// 1854 /// This method expects that the blob be in the correct format -- blobs accepted from partially 1855 /// trusted code need to be validated before being unpacked. 1856 /// </summary> UnpackEccPublicBlob(byte[] blob, out BigInteger x, out BigInteger y)1857 internal static void UnpackEccPublicBlob(byte[] blob, out BigInteger x, out BigInteger y) { 1858 Contract.Requires(blob != null && blob.Length > 2 * sizeof(int)); 1859 1860 // 1861 // See code:System.Security.Cryptography.NCryptNative#ECCPublicBlobFormat for details about the 1862 // format of the ECC public key blob. 1863 // 1864 1865 // read the size of each parameter 1866 int parameterSize = BitConverter.ToInt32(blob, sizeof(int)); 1867 Debug.Assert(parameterSize > 0, "parameterSize > 0"); 1868 Debug.Assert(blob.Length >= 2 * sizeof(int) + 2 * parameterSize, "blob.Length >= 2 * sizeof(int) + 2 * parameterSize"); 1869 1870 // read out the X and Y parameters, in memory reversed form 1871 // add 0x00 padding to force BigInteger to interpret these as positive numbers 1872 x = new BigInteger(ReverseBytes(blob, 2 * sizeof(int), parameterSize, true)); 1873 y = new BigInteger(ReverseBytes(blob, 2 * sizeof(int) + parameterSize, parameterSize, true)); 1874 } 1875 1876 /// <summary> 1877 /// Verify a signature created with no padding 1878 /// </summary> 1879 [System.Security.SecurityCritical] VerifySignature(SafeNCryptKeyHandle key, byte[] hash, byte[] signature)1880 internal static bool VerifySignature(SafeNCryptKeyHandle key, byte[] hash, byte[] signature) { 1881 Contract.Requires(key != null); 1882 Contract.Requires(hash != null); 1883 Contract.Requires(signature != null); 1884 1885 ErrorCode error = UnsafeNativeMethods.NCryptVerifySignature(key, 1886 IntPtr.Zero, 1887 hash, 1888 hash.Length, 1889 signature, 1890 signature.Length, 1891 0); 1892 1893 return error == ErrorCode.Success; 1894 } 1895 } 1896 #endif 1897 } 1898