|
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Diagnostics.Contracts;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
namespace System.Security.Cryptography {
//
// Public facing enumerations
//
/// <summary>
/// Flags to control how often and in which format a key is allowed to be exported
/// </summary>
[Flags]
public enum CngExportPolicies {
None = 0x00000000,
AllowExport = 0x00000001, // NCRYPT_ALLOW_EXPORT_FLAG
AllowPlaintextExport = 0x00000002, // NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG
AllowArchiving = 0x00000004, // NCRYPT_ALLOW_ARCHIVING_FLAG
AllowPlaintextArchiving = 0x00000008 // NCRYPT_ALLOW_PLAINTEXT_ARCHIVING_FLAG
}
/// <summary>
/// Flags controlling how the key is created
/// </summary>
[Flags]
public enum CngKeyCreationOptions {
None = 0x00000000,
MachineKey = 0x00000020, // NCRYPT_MACHINE_KEY_FLAG
OverwriteExistingKey = 0x00000080 // NCRYPT_OVERWRITE_KEY_FLAG
}
/// <summary>
/// Flags controlling how a key is opened
/// </summary>
[Flags]
[SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "Approved API exception to have an easy way to express user keys")]
public enum CngKeyOpenOptions {
None = 0x00000000,
UserKey = 0x00000000,
MachineKey = 0x00000020, // NCRYPT_MACHINE_KEY_FLAG
Silent = 0x00000040 // NCRYPT_SILENT_FLAG
}
/// <summary>
/// Flags indicating the type of key
/// </summary>
[Flags]
internal enum CngKeyTypes {
None = 0x00000000,
MachineKey = 0x00000020 // NCRYPT_MACHINE_KEY_FLAG
}
/// <summary>
/// Bits defining what operations are valid to use a key with
/// </summary>
[Flags]
[SuppressMessage("Microsoft.Usage", "CA2217:DoNotMarkEnumsWithFlags", Justification = "Flags are defined by the native ncrypt API")]
public enum CngKeyUsages {
None = 0x00000000,
Decryption = 0x00000001, // NCRYPT_ALLOW_DECRYPT_FLAG
Signing = 0x00000002, // NCRYPT_ALLOW_SIGNING_FLAG
KeyAgreement = 0x00000004, // NCRYPT_ALLOW_KEY_AGREEMENT_FLAG
AllUsages = 0x00ffffff // NCRYPT_ALLOW_ALL_USAGES
}
/// <summary>
/// Options affecting how a property is interpreted by CNG
/// </summary>
[Flags]
[SuppressMessage("Microsoft.Usage", "CA2217:DoNotMarkEnumsWithFlags", Justification = "Flags are defined by the native ncrypt API")]
public enum CngPropertyOptions {
None = 0x00000000,
CustomProperty = 0x40000000, // NCRYPT_PERSIST_ONLY_FLAG
Persist = unchecked((int)0x80000000) // NCRYPT_PERSIST_FLAG
}
/// <summary>
/// Levels of UI protection available for a key
/// </summary>
[Flags]
public enum CngUIProtectionLevels {
None = 0x00000000,
ProtectKey = 0x00000001, // NCRYPT_UI_PROTECT_KEY_FLAG
ForceHighProtection = 0x00000002 // NCRYPT_UI_FORCE_HIGH_PROTECTION_FLAG
}
/// <summary>
/// Native interop with CNG's NCrypt layer. Native definitions are in ncrypt.h
/// </summary>
internal static class NCryptNative {
//
// Enumerations
//
/// <summary>
/// Types of NCryptBuffers
/// </summary>
internal enum BufferType {
KdfHashAlgorithm = 0x00000000, // KDF_HASH_ALGORITHM
KdfSecretPrepend = 0x00000001, // KDF_SECRET_PREPEND
KdfSecretAppend = 0x00000002, // KDF_SECRET_APPEND
KdfHmacKey = 0x00000003, // KDF_HMAC_KEY
KdfTlsLabel = 0x00000004, // KDF_TLS_PRF_LABEL
KdfTlsSeed = 0x00000005 // KDF_TLS_PRF_SEED
}
/// <summary>
/// Result codes from NCrypt APIs
/// </summary>
internal enum ErrorCode {
Success = 0, // ERROR_SUCCESS
BadSignature = unchecked((int)0x80090006), // NTE_BAD_SIGNATURE
NotFound = unchecked((int)0x80090011), // NTE_NOT_FOUND
KeyDoesNotExist = unchecked((int)0x80090016), // NTE_BAD_KEYSET
BufferTooSmall = unchecked((int)0x80090028), // NTE_BUFFER_TOO_SMALL
NoMoreItems = unchecked((int)0x8009002a) // NTE_NO_MORE_ITEMS
}
/// <summary>
/// Well known names of key properties
/// </summary>
internal static class KeyPropertyName {
internal const string Algorithm = "Algorithm Name"; // NCRYPT_ALGORITHM_PROPERTY
internal const string AlgorithmGroup = "Algorithm Group"; // NCRYPT_ALGORITHM_GROUP_PROPERTY
internal const string ExportPolicy = "Export Policy"; // NCRYPT_EXPORT_POLICY_PROPERTY
internal const string KeyType = "Key Type"; // NCRYPT_KEY_TYPE_PROPERTY
internal const string KeyUsage = "Key Usage"; // NCRYPT_KEY_USAGE_PROPERTY
internal const string Length = "Length"; // NCRYPT_LENGTH_PROPERTY
internal const string Name = "Name"; // NCRYPT_NAME_PROPERTY
internal const string ParentWindowHandle = "HWND Handle"; // NCRYPT_WINDOW_HANDLE_PROPERTY
internal const string PublicKeyLength = "PublicKeyLength"; // NCRYPT_PUBLIC_KEY_LENGTH (Win10+)
internal const string ProviderHandle = "Provider Handle"; // NCRYPT_PROVIDER_HANDLE_PROPERTY
internal const string UIPolicy = "UI Policy"; // NCRYPT_UI_POLICY_PROPERTY
internal const string UniqueName = "Unique Name"; // NCRYPT_UNIQUE_NAME_PROPERTY
internal const string UseContext = "Use Context"; // NCRYPT_USE_CONTEXT_PROPERTY
//
// Properties defined by the CLR
//
/// <summary>
/// Is the key a CLR created ephemeral key, it will contain a single byte with value 1 if the
/// key was created by the CLR as an ephemeral key.
/// </summary>
internal const string ClrIsEphemeral = "CLR IsEphemeral";
}
/// <summary>
/// Well known names of provider properties
/// </summary>
internal static class ProviderPropertyName {
internal const string Name = "Name"; // NCRYPT_NAME_PROPERTY
}
/// <summary>
/// Flags for code:System.Security.Cryptography.NCryptNative.UnsafeNativeMethods.NCryptSecretAgreement
/// </summary>
[Flags]
internal enum SecretAgreementFlags {
None = 0x00000000,
UseSecretAsHmacKey = 0x00000001 // KDF_USE_SECRET_AS_HMAC_KEY_FLAG
}
//
// Structures
//
[StructLayout(LayoutKind.Sequential)]
internal struct NCRYPT_UI_POLICY {
public int dwVersion;
public CngUIProtectionLevels dwFlags;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszCreationTitle;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszFriendlyName;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszDescription;
}
[StructLayout(LayoutKind.Sequential)]
internal struct NCryptBuffer {
public int cbBuffer;
public BufferType BufferType;
public IntPtr pvBuffer;
}
[StructLayout(LayoutKind.Sequential)]
internal struct NCryptBufferDesc {
public int ulVersion;
public int cBuffers;
public IntPtr pBuffers; // NCryptBuffer[cBuffers]
}
[SuppressUnmanagedCodeSecurity]
#pragma warning disable 618 // System.Core.dll still uses SecurityRuleSet.Level1
[SecurityCritical(SecurityCriticalScope.Everything)]
#pragma warning restore 618
internal static class UnsafeNativeMethods {
/// <summary>
/// Create an NCrypt key
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode NCryptCreatePersistedKey(SafeNCryptProviderHandle hProvider,
[Out] out SafeNCryptKeyHandle phKey,
string pszAlgId,
string pszKeyName,
int dwLegacyKeySpec,
CngKeyCreationOptions dwFlags);
/// <summary>
/// Delete a key
/// </summary>
[DllImport("ncrypt.dll")]
internal static extern ErrorCode NCryptDeleteKey(SafeNCryptKeyHandle hKey, int flags);
/// <summary>
/// Generate a key from a secret agreement
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode NCryptDeriveKey(SafeNCryptSecretHandle hSharedSecret,
string pwszKDF,
[In] ref NCryptBufferDesc pParameterList,
[Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbDerivedKey,
int cbDerivedKey,
[Out] out int pcbResult,
SecretAgreementFlags dwFlags);
/// <summary>
/// Export a key from the KSP
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode NCryptExportKey(SafeNCryptKeyHandle hKey,
IntPtr hExportKey, // NCRYPT_KEY_HANDLE
string pszBlobType,
IntPtr pParameterList, // NCryptBufferDesc *
[Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
int cbOutput,
[Out] out int pcbResult,
int dwFlags);
/// <summary>
/// Finalize a key to prepare it for use
/// </summary>
[DllImport("ncrypt.dll")]
internal static extern ErrorCode NCryptFinalizeKey(SafeNCryptKeyHandle hKey, int dwFlags);
/// <summary>
/// Get the value of a property of an NCrypt object
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode NCryptGetProperty(SafeNCryptHandle hObject,
string pszProperty,
[Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
int cbOutput,
[Out] out int pcbResult,
CngPropertyOptions dwFlags);
/// <summary>
/// Get the value of a property of an NCrypt object
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode NCryptGetProperty(SafeNCryptHandle hObject,
string pszProperty,
ref int pbOutput,
int cbOutput,
[Out] out int pcbResult,
CngPropertyOptions dwFlags);
/// <summary>
/// Get the value of a pointer property of an NCrypt object
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern ErrorCode NCryptGetProperty(SafeNCryptHandle hObject,
string pszProperty,
[Out] out IntPtr pbOutput,
int cbOutput,
[Out] out int pcbResult,
CngPropertyOptions dwFlags);
/// <summary>
/// Import a key into the KSP
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode NCryptImportKey(SafeNCryptProviderHandle hProvider,
IntPtr hImportKey, // NCRYPT_KEY_HANDLE
string pszBlobType,
IntPtr pParameterList, // NCryptBufferDesc *
[Out] out SafeNCryptKeyHandle phKey,
[MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
int cbData,
int dwFlags);
/// <summary>
/// Open an existing key
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode NCryptOpenKey(SafeNCryptProviderHandle hProvider,
[Out] out SafeNCryptKeyHandle phKey,
string pszKeyName,
int dwLegacyKeySpec,
CngKeyOpenOptions dwFlags);
/// <summary>
/// Acquire a handle to a key storage provider
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode NCryptOpenStorageProvider([Out] out SafeNCryptProviderHandle phProvider,
string pszProviderName,
int dwFlags);
/// <summary>
/// Generate a secret agreement for generating shared key material
/// </summary>
[DllImport("ncrypt.dll")]
internal static extern ErrorCode NCryptSecretAgreement(SafeNCryptKeyHandle hPrivKey,
SafeNCryptKeyHandle hPubKey,
[Out] out SafeNCryptSecretHandle phSecret,
int dwFlags);
/// <summary>
/// Set a property value on an NCrypt object
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode NCryptSetProperty(SafeNCryptHandle hObject,
string pszProperty,
[MarshalAs(UnmanagedType.LPArray)] byte[] pbInput,
int cbInput,
CngPropertyOptions dwFlags);
/// <summary>
/// Set a string property value on an NCrypt object
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode NCryptSetProperty(SafeNCryptHandle hObject,
string pszProperty,
string pbInput,
int cbInput,
CngPropertyOptions dwFlags);
/// <summary>
/// Set a property value on an NCrypt object when a pointer to the buffer already exists in
/// managed code. To set a pointer valued property, use the ref IntPtr overload.
/// </summary>
[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode NCryptSetProperty(SafeNCryptHandle hObject,
string pszProperty,
IntPtr pbInput,
int cbInput,
CngPropertyOptions dwFlags);
/// <summary>
/// Create a signature for a hash value
/// </summary>
[DllImport("ncrypt.dll")]
internal static extern ErrorCode 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);
/// <summary>
/// Verify a signature over a hash value
/// </summary>
[DllImport("ncrypt.dll")]
internal static extern ErrorCode NCryptVerifySignature(SafeNCryptKeyHandle hKey,
IntPtr pPaddingInfo,
[MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue,
int cbHashValue,
[MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature,
int cbSignature,
int dwFlags);
[DllImport("ncrypt.dll")]
internal static extern ErrorCode 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);
[DllImport("ncrypt.dll")]
internal static extern ErrorCode 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);
[DllImport("ncrypt.dll")]
internal static extern ErrorCode 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);
[DllImport("ncrypt.dll")]
internal static extern ErrorCode 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);
[DllImport("ncrypt.dll")]
internal static extern ErrorCode 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);
[DllImport("ncrypt.dll")]
internal static extern ErrorCode 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);
[DllImport("ncrypt.dll")]
internal static extern ErrorCode 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);
[DllImport("ncrypt.dll")]
internal static extern ErrorCode 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);
}
/// <summary>
/// Adapter to wrap specific NCryptDecrypt P/Invokes with specific padding info
/// </summary>
[SecuritySafeCritical]
private delegate ErrorCode NCryptDecryptor<T>(SafeNCryptKeyHandle hKey,
byte[] pbInput,
int cbInput,
ref T pvPadding,
byte[] pbOutput,
int cbOutput,
out int pcbResult,
AsymmetricPaddingMode dwFlags);
/// <summary>
/// Adapter to wrap specific NCryptEncrypt P/Invokes with specific padding info
/// </summary>
[SecuritySafeCritical]
private delegate ErrorCode NCryptEncryptor<T>(SafeNCryptKeyHandle hKey,
byte[] pbInput,
int cbInput,
ref T pvPadding,
byte[] pbOutput,
int cbOutput,
out int pcbResult,
AsymmetricPaddingMode dwFlags);
/// <summary>
/// Adapter to wrap specific NCryptSignHash P/Invokes with a specific padding info
/// </summary>
[SecuritySafeCritical]
private delegate ErrorCode NCryptHashSigner<T>(SafeNCryptKeyHandle hKey,
ref T pvPaddingInfo,
byte[] pbHashValue,
int cbHashValue,
byte[] pbSignature,
int cbSignature,
out int pcbResult,
AsymmetricPaddingMode dwFlags);
/// <summary>
/// Adapter to wrap specific NCryptVerifySignature P/Invokes with a specific padding info
/// </summary>
[SecuritySafeCritical]
private delegate ErrorCode NCryptSignatureVerifier<T>(SafeNCryptKeyHandle hKey,
ref T pvPaddingInfo,
byte[] pbHashValue,
int cbHashValue,
byte[] pbSignature,
int cbSignature,
AsymmetricPaddingMode dwFlags) where T : struct;
//
// Utility and wrapper functions
//
private static volatile bool s_haveNcryptSupported;
private static volatile bool s_ncryptSupported;
/// <summary>
/// Generic decryption method, wrapped by decryption calls for specific padding modes
/// </summary>
[SecuritySafeCritical]
private static byte[] DecryptData<T>(SafeNCryptKeyHandle key,
byte[] data,
ref T paddingInfo,
AsymmetricPaddingMode paddingMode,
NCryptDecryptor<T> decryptor) where T : struct {
Debug.Assert(key != null, "key != null");
Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
Debug.Assert(data != null, "data != null");
Debug.Assert(decryptor != null, "decryptor != null");
// Figure out how big of a buffer is needed to store the decrypted data
int decryptedSize = 0;
ErrorCode error = decryptor(key,
data,
data.Length,
ref paddingInfo,
null,
0,
out decryptedSize,
paddingMode);
if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) {
throw new CryptographicException((int)error);
}
// Do the decryption
byte[] decrypted = new byte[decryptedSize];
error = decryptor(key,
data,
data.Length,
ref paddingInfo,
decrypted,
decrypted.Length,
out decryptedSize,
paddingMode);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
// Sometimes decryptedSize can be less than the allocated buffer size
// So resize the array to the actual returned plaintext
//
// After the Resize call the array into which the data was originally
// decrypted should be cleared to reduce the number of copies of the
// decrypted data (which is presumably a secret) in memory, and also
// reduces the likelihood that the value would be present in a heap dump.
if (decrypted.Length != decryptedSize)
{
byte[] clear = decrypted;
Array.Resize(ref decrypted, decryptedSize);
Array.Clear(clear, 0, clear.Length);
}
return decrypted;
}
/// <summary>
/// Decrypt data using PKCS1 padding
/// </summary>
[SecuritySafeCritical]
internal static byte[] DecryptDataPkcs1(SafeNCryptKeyHandle key, byte[] data) {
BCryptNative.BCRYPT_PKCS1_PADDING_INFO pkcs1Info = new BCryptNative.BCRYPT_PKCS1_PADDING_INFO();
return DecryptData(key,
data,
ref pkcs1Info,
AsymmetricPaddingMode.Pkcs1,
Pkcs1PaddingDecryptionWrapper);
}
/// <summary>
/// Decrypt data using OAEP padding
/// </summary>
[SecuritySafeCritical]
internal static byte[] DecryptDataOaep(SafeNCryptKeyHandle key,
byte[] data,
string hashAlgorithm) {
Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)");
BCryptNative.BCRYPT_OAEP_PADDING_INFO oaepInfo = new BCryptNative.BCRYPT_OAEP_PADDING_INFO();
oaepInfo.pszAlgId = hashAlgorithm;
return DecryptData(key,
data,
ref oaepInfo,
AsymmetricPaddingMode.Oaep,
UnsafeNativeMethods.NCryptDecrypt);
}
[SecurityCritical]
private static ErrorCode Pkcs1PaddingDecryptionWrapper(SafeNCryptKeyHandle hKey,
byte[] pbInput,
int cbInput,
ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding,
byte[] pbOutput,
int cbOutput,
out int pcbResult,
AsymmetricPaddingMode dwFlags)
{
Debug.Assert(dwFlags == AsymmetricPaddingMode.Pkcs1, "dwFlags == AsymmetricPaddingMode.Pkcs1");
// This method exists to match a generic-based delegate (the ref parameter), but in PKCS#1 mode
// the value for pvPadding must be NULL with keys in the Smart Card KSP.
//
// Passing the ref PKCS1 (signature) padding info will work for software keys, which ignore the value;
// but hardware keys fail if it's any value other than NULL (and PKCS#1 was specified).
return UnsafeNativeMethods.NCryptDecrypt(hKey, pbInput, cbInput, IntPtr.Zero, pbOutput, cbOutput, out pcbResult, dwFlags);
}
/// <summary>
/// Generic encryption method, wrapped by decryption calls for specific padding modes
/// </summary>
[SecuritySafeCritical]
private static byte[] EncryptData<T>(SafeNCryptKeyHandle key,
byte[] data,
ref T paddingInfo,
AsymmetricPaddingMode paddingMode,
NCryptEncryptor<T> encryptor) where T : struct {
Debug.Assert(key != null, "key != null");
Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
Debug.Assert(data != null, "data != null");
Debug.Assert(encryptor != null, "encryptor != null");
// Figure out how big of a buffer is to encrypt the data
int encryptedSize = 0;
ErrorCode error = encryptor(key,
data,
data.Length,
ref paddingInfo,
null,
0,
out encryptedSize,
paddingMode);
if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) {
throw new CryptographicException((int)error);
}
// Do the encryption
byte[] encrypted = new byte[encryptedSize];
error = encryptor(key,
data,
data.Length,
ref paddingInfo,
encrypted,
encrypted.Length,
out encryptedSize,
paddingMode);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return encrypted;
}
/// <summary>
/// Encrypt data using OAEP padding
/// </summary>
[SecuritySafeCritical]
internal static byte[] EncryptDataOaep(SafeNCryptKeyHandle key,
byte[] data,
string hashAlgorithm) {
Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)");
BCryptNative.BCRYPT_OAEP_PADDING_INFO oaepInfo = new BCryptNative.BCRYPT_OAEP_PADDING_INFO();
oaepInfo.pszAlgId = hashAlgorithm;
return EncryptData(key,
data,
ref oaepInfo,
AsymmetricPaddingMode.Oaep,
UnsafeNativeMethods.NCryptEncrypt);
}
/// <summary>
/// Encrypt data using PKCS1 padding
/// </summary>
[SecuritySafeCritical]
internal static byte[] EncryptDataPkcs1(SafeNCryptKeyHandle key, byte[] data) {
BCryptNative.BCRYPT_PKCS1_PADDING_INFO pkcs1Info = new BCryptNative.BCRYPT_PKCS1_PADDING_INFO();
return EncryptData(key,
data,
ref pkcs1Info,
AsymmetricPaddingMode.Pkcs1,
Pkcs1PaddingEncryptionWrapper);
}
[SecurityCritical]
private static ErrorCode Pkcs1PaddingEncryptionWrapper(SafeNCryptKeyHandle hKey,
byte[] pbInput,
int cbInput,
ref BCryptNative.BCRYPT_PKCS1_PADDING_INFO pvPadding,
byte[] pbOutput,
int cbOutput,
out int pcbResult,
AsymmetricPaddingMode dwFlags) {
Debug.Assert(dwFlags == AsymmetricPaddingMode.Pkcs1, "dwFlags == AsymmetricPaddingMode.Pkcs1");
// This method exists to match a generic-based delegate (the ref parameter), but in PKCS#1 mode
// the value for pvPadding must be NULL with keys in the Smart Card KSP.
//
// Passing the ref PKCS1 (signature) padding info will work for software keys, which ignore the value;
// but hardware keys fail if it's any value other than NULL (and PKCS#1 was specified).
return UnsafeNativeMethods.NCryptEncrypt(hKey, pbInput, cbInput, IntPtr.Zero, pbOutput, cbOutput, out pcbResult, dwFlags);
}
/// <summary>
/// Generic signature method, wrapped by signature calls for specific padding modes
/// </summary>
[SecuritySafeCritical]
private static byte[] SignHash<T>(SafeNCryptKeyHandle key,
byte[] hash,
ref T paddingInfo,
AsymmetricPaddingMode paddingMode,
NCryptHashSigner<T> signer) where T : struct {
Debug.Assert(key != null, "key != null");
Debug.Assert(!key.IsInvalid && !key.IsClosed, "!key.IsInvalid && !key.IsClosed");
Debug.Assert(hash != null, "hash != null");
Debug.Assert(signer != null, "signer != null");
// Figure out how big the signature is
int signatureSize = 0;
ErrorCode error = signer(key,
ref paddingInfo,
hash,
hash.Length,
null,
0,
out signatureSize,
paddingMode);
if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) {
throw new CryptographicException((int)error);
}
// Sign the hash
byte[] signature = new byte[signatureSize];
error = signer(key,
ref paddingInfo,
hash,
hash.Length,
signature,
signature.Length,
out signatureSize,
paddingMode);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return signature;
}
/// <summary>
/// Sign a hash, using PKCS1 padding
/// </summary>
[SecuritySafeCritical]
internal static byte[] SignHashPkcs1(SafeNCryptKeyHandle key,
byte[] hash,
string hashAlgorithm) {
Debug.Assert(key != null, "key != null");
Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
Debug.Assert(hash != null, "hash != null");
Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)");
BCryptNative.BCRYPT_PKCS1_PADDING_INFO pkcs1Info = new BCryptNative.BCRYPT_PKCS1_PADDING_INFO();
pkcs1Info.pszAlgId = hashAlgorithm;
return SignHash(key,
hash,
ref pkcs1Info,
AsymmetricPaddingMode.Pkcs1,
UnsafeNativeMethods.NCryptSignHash);
}
/// <summary>
/// Sign a hash, using PSS padding
/// </summary>
[SecuritySafeCritical]
internal static byte[] SignHashPss(SafeNCryptKeyHandle key,
byte[] hash,
string hashAlgorithm,
int saltBytes) {
Debug.Assert(key != null, "key != null");
Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
Debug.Assert(hash != null, "hash != null");
Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)");
Debug.Assert(saltBytes >= 0, "saltBytes >= 0");
BCryptNative.BCRYPT_PSS_PADDING_INFO pssInfo = new BCryptNative.BCRYPT_PSS_PADDING_INFO();
pssInfo.pszAlgId = hashAlgorithm;
pssInfo.cbSalt = saltBytes;
return SignHash(key,
hash,
ref pssInfo,
AsymmetricPaddingMode.Pss,
UnsafeNativeMethods.NCryptSignHash);
}
/// <summary>
/// Generic signature verification method, wrapped by verification calls for specific padding modes
/// </summary>
[SecuritySafeCritical]
private static bool VerifySignature<T>(SafeNCryptKeyHandle key,
byte[] hash,
byte[] signature,
ref T paddingInfo,
AsymmetricPaddingMode paddingMode,
NCryptSignatureVerifier<T> verifier) where T : struct {
Debug.Assert(key != null, "key != null");
Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
Debug.Assert(hash != null, "hash != null");
Debug.Assert(signature != null, "signature != null");
Debug.Assert(verifier != null, "verifier != null");
ErrorCode error = verifier(key,
ref paddingInfo,
hash,
hash.Length,
signature,
signature.Length,
paddingMode);
return error == ErrorCode.Success;
}
/// <summary>
/// Verify the signature of a hash using PKCS #1 padding
/// </summary>
[SecuritySafeCritical]
internal static bool VerifySignaturePkcs1(SafeNCryptKeyHandle key,
byte[] hash,
string hashAlgorithm,
byte[] signature) {
Debug.Assert(key != null, "key != null");
Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
Debug.Assert(hash != null, "hash != null");
Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)");
Debug.Assert(signature != null, "signature != null");
BCryptNative.BCRYPT_PKCS1_PADDING_INFO pkcs1Info = new BCryptNative.BCRYPT_PKCS1_PADDING_INFO();
pkcs1Info.pszAlgId = hashAlgorithm;
return VerifySignature(key,
hash,
signature,
ref pkcs1Info,
AsymmetricPaddingMode.Pkcs1,
UnsafeNativeMethods.NCryptVerifySignature);
}
/// <summary>
/// Verify the signature of a hash using PSS padding
/// </summary>
[SecuritySafeCritical]
internal static bool VerifySignaturePss(SafeNCryptKeyHandle key,
byte[] hash,
string hashAlgorithm,
int saltBytes,
byte[] signature) {
Debug.Assert(key != null, "key != null");
Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
Debug.Assert(hash != null, "hash != null");
Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm), "!String.IsNullOrEmpty(hashAlgorithm)");
Debug.Assert(signature != null, "signature != null");
BCryptNative.BCRYPT_PSS_PADDING_INFO pssInfo = new BCryptNative.BCRYPT_PSS_PADDING_INFO();
pssInfo.pszAlgId = hashAlgorithm;
pssInfo.cbSalt = saltBytes;
return VerifySignature(key,
hash,
signature,
ref pssInfo,
AsymmetricPaddingMode.Pss,
UnsafeNativeMethods.NCryptVerifySignature);
}
/// <summary>
/// Determine if NCrypt is supported on the current machine
/// </summary>
internal static bool NCryptSupported {
[SecuritySafeCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
get {
if (!s_haveNcryptSupported)
{
// Attempt to load ncrypt.dll to see if the NCrypt CNG APIs are available on the machine
using (SafeLibraryHandle ncrypt = Microsoft.Win32.UnsafeNativeMethods.LoadLibraryEx("ncrypt", IntPtr.Zero, 0)) {
s_ncryptSupported = !ncrypt.IsInvalid;
s_haveNcryptSupported = true;
}
}
return s_ncryptSupported;
}
}
/// <summary>
/// Build an ECC public key blob to represent the given parameters
/// </summary>
internal static byte[] BuildEccPublicBlob(string algorithm, BigInteger x, BigInteger y) {
Contract.Requires(!String.IsNullOrEmpty(algorithm));
Contract.Ensures(Contract.Result<byte[]>() != null);
//
// #ECCPublicBlobFormat
// The ECC public key blob format is as follows:
//
// DWORD dwMagic
// DWORD cbKey
// X parameter (cbKey bytes long, byte-reversed)
// Y parameter (cbKey bytes long, byte-reversed)
//
// First map the algorithm name to its magic number and key size
BCryptNative.KeyBlobMagicNumber algorithmMagic;
int keySize;
BCryptNative.MapAlgorithmIdToMagic(algorithm, out algorithmMagic, out keySize);
// Next generate the public key parameters
byte[] xBytes = ReverseBytes(FillKeyParameter(x.ToByteArray(), keySize));
byte[] yBytes = ReverseBytes(FillKeyParameter(y.ToByteArray(), keySize));
// Finally, lay out the structure itself
byte[] blob = new byte[2 * sizeof(int) + xBytes.Length + yBytes.Length];
Buffer.BlockCopy(BitConverter.GetBytes((int)algorithmMagic), 0, blob, 0, sizeof(int));
Buffer.BlockCopy(BitConverter.GetBytes(xBytes.Length), 0, blob, sizeof(int), sizeof(int));
Buffer.BlockCopy(xBytes, 0, blob, 2 * sizeof(int), xBytes.Length);
Buffer.BlockCopy(yBytes, 0, blob, 2 * sizeof(int) + xBytes.Length, yBytes.Length);
return blob;
}
/// <summary>
/// Create a random CNG key
/// </summary>
[System.Security.SecurityCritical]
internal static SafeNCryptKeyHandle CreatePersistedKey(SafeNCryptProviderHandle provider,
string algorithm,
string name,
CngKeyCreationOptions options) {
Contract.Requires(provider != null && !provider.IsInvalid && !provider.IsClosed);
Contract.Requires(!String.IsNullOrEmpty(algorithm));
Contract.Ensures(Contract.Result<SafeNCryptKeyHandle>() != null &&
!Contract.Result<SafeNCryptKeyHandle>().IsInvalid &&
!Contract.Result<SafeNCryptKeyHandle>().IsClosed);
SafeNCryptKeyHandle keyHandle = null;
ErrorCode error = UnsafeNativeMethods.NCryptCreatePersistedKey(provider,
out keyHandle,
algorithm,
name,
0,
options);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return keyHandle;
}
/// <summary>
/// Delete a key
/// </summary>
[System.Security.SecurityCritical]
internal static void DeleteKey(SafeNCryptKeyHandle key) {
Contract.Requires(key != null);
ErrorCode error = UnsafeNativeMethods.NCryptDeleteKey(key, 0);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
key.SetHandleAsInvalid();
}
/// <summary>
/// Derive key material from a hash or HMAC KDF
/// </summary>
/// <returns></returns>
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
private static byte[] DeriveKeyMaterial(SafeNCryptSecretHandle secretAgreement,
string kdf,
string hashAlgorithm,
byte[] hmacKey,
byte[] secretPrepend,
byte[] secretAppend,
SecretAgreementFlags flags) {
Contract.Requires(secretAgreement != null);
Contract.Requires(!String.IsNullOrEmpty(kdf));
Contract.Requires(!String.IsNullOrEmpty(hashAlgorithm));
Contract.Requires(hmacKey == null || kdf == BCryptNative.KeyDerivationFunction.Hmac);
Contract.Ensures(Contract.Result<byte[]>() != null);
List<NCryptBuffer> parameters = new List<NCryptBuffer>();
// First marshal the hash algoritm
IntPtr hashAlgorithmString = IntPtr.Zero;
// Run in a CER so that we know we'll free the memory for the marshaled string
RuntimeHelpers.PrepareConstrainedRegions();
try {
// Assign in a CER so we don't fail between allocating the memory and assigning the result
// back to the string variable.
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally {
hashAlgorithmString = Marshal.StringToCoTaskMemUni(hashAlgorithm);
}
// We always need to marshal the hashing function
NCryptBuffer hashAlgorithmBuffer = new NCryptBuffer();
hashAlgorithmBuffer.cbBuffer = (hashAlgorithm.Length + 1) * sizeof(char);
hashAlgorithmBuffer.BufferType = BufferType.KdfHashAlgorithm;
hashAlgorithmBuffer.pvBuffer = hashAlgorithmString;
parameters.Add(hashAlgorithmBuffer);
unsafe {
fixed (byte* pHmacKey = hmacKey, pSecretPrepend = secretPrepend, pSecretAppend = secretAppend) {
//
// Now marshal the other parameters
//
if (pHmacKey != null) {
NCryptBuffer hmacKeyBuffer = new NCryptBuffer();
hmacKeyBuffer.cbBuffer = hmacKey.Length;
hmacKeyBuffer.BufferType = BufferType.KdfHmacKey;
hmacKeyBuffer.pvBuffer = new IntPtr(pHmacKey);
parameters.Add(hmacKeyBuffer);
}
if (pSecretPrepend != null) {
NCryptBuffer secretPrependBuffer = new NCryptBuffer();
secretPrependBuffer.cbBuffer = secretPrepend.Length;
secretPrependBuffer.BufferType = BufferType.KdfSecretPrepend;
secretPrependBuffer.pvBuffer = new IntPtr(pSecretPrepend);
parameters.Add(secretPrependBuffer);
}
if (pSecretAppend != null) {
NCryptBuffer secretAppendBuffer = new NCryptBuffer();
secretAppendBuffer.cbBuffer = secretAppend.Length;
secretAppendBuffer.BufferType = BufferType.KdfSecretAppend;
secretAppendBuffer.pvBuffer = new IntPtr(pSecretAppend);
parameters.Add(secretAppendBuffer);
}
return DeriveKeyMaterial(secretAgreement,
kdf,
parameters.ToArray(),
flags);
}
}
}
finally {
if (hashAlgorithmString != IntPtr.Zero) {
Marshal.FreeCoTaskMem(hashAlgorithmString);
}
}
}
/// <summary>
/// Derive key material using a given KDF and secret agreement
/// </summary>
[System.Security.SecurityCritical]
private static byte[] DeriveKeyMaterial(SafeNCryptSecretHandle secretAgreement,
string kdf,
NCryptBuffer[] parameters,
SecretAgreementFlags flags) {
Contract.Requires(secretAgreement != null);
Contract.Requires(!String.IsNullOrEmpty(kdf));
Contract.Requires(parameters != null);
Contract.Ensures(Contract.Result<byte[]>() != null);
unsafe {
fixed (NCryptBuffer* pParameters = parameters) {
NCryptBufferDesc parameterDesc = new NCryptBufferDesc();
parameterDesc.ulVersion = 0;
parameterDesc.cBuffers = parameters.Length;
parameterDesc.pBuffers = new IntPtr(pParameters);
// Figure out how big the key material is
int keySize = 0;
ErrorCode error = UnsafeNativeMethods.NCryptDeriveKey(secretAgreement,
kdf,
ref parameterDesc,
null,
0,
out keySize,
flags);
if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) {
throw new CryptographicException((int)error);
}
// Allocate memory for the key material and generate it
byte[] keyMaterial = new byte[keySize];
error = UnsafeNativeMethods.NCryptDeriveKey(secretAgreement,
kdf,
ref parameterDesc,
keyMaterial,
keyMaterial.Length,
out keySize,
flags);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return keyMaterial;
}
}
}
/// <summary>
/// Derive key material from a secret agreement using a hash KDF
/// </summary>
[System.Security.SecurityCritical]
internal static byte[] DeriveKeyMaterialHash(SafeNCryptSecretHandle secretAgreement,
string hashAlgorithm,
byte[] secretPrepend,
byte[] secretAppend,
SecretAgreementFlags flags) {
Contract.Requires(secretAgreement != null);
Contract.Requires(!String.IsNullOrEmpty(hashAlgorithm));
Contract.Ensures(Contract.Result<byte[]>() != null);
return DeriveKeyMaterial(secretAgreement,
BCryptNative.KeyDerivationFunction.Hash,
hashAlgorithm,
null,
secretPrepend,
secretAppend,
flags);
}
/// <summary>
/// Derive key material from a secret agreement using a HMAC KDF
/// </summary>
[System.Security.SecurityCritical]
internal static byte[] DeriveKeyMaterialHmac(SafeNCryptSecretHandle secretAgreement,
string hashAlgorithm,
byte[] hmacKey,
byte[] secretPrepend,
byte[] secretAppend,
SecretAgreementFlags flags) {
Contract.Requires(secretAgreement != null);
Contract.Requires(!String.IsNullOrEmpty(hashAlgorithm));
Contract.Ensures(Contract.Result<byte[]>() != null);
return DeriveKeyMaterial(secretAgreement,
BCryptNative.KeyDerivationFunction.Hmac,
hashAlgorithm,
hmacKey,
secretPrepend,
secretAppend,
flags);
}
/// <summary>
/// Derive key material from a secret agreeement using the TLS KDF
/// </summary>
[System.Security.SecurityCritical]
internal static byte[] DeriveKeyMaterialTls(SafeNCryptSecretHandle secretAgreement,
byte[] label,
byte[] seed,
SecretAgreementFlags flags) {
Contract.Requires(secretAgreement != null);
Contract.Requires(label != null && seed != null);
Contract.Ensures(Contract.Result<byte[]>() != null);
NCryptBuffer[] buffers = new NCryptBuffer[2];
unsafe {
fixed (byte* pLabel = label, pSeed = seed) {
NCryptBuffer labelBuffer = new NCryptBuffer();
labelBuffer.cbBuffer = label.Length;
labelBuffer.BufferType = BufferType.KdfTlsLabel;
labelBuffer.pvBuffer = new IntPtr(pLabel);
buffers[0] = labelBuffer;
NCryptBuffer seedBuffer = new NCryptBuffer();
seedBuffer.cbBuffer = seed.Length;
seedBuffer.BufferType = BufferType.KdfTlsSeed;
seedBuffer.pvBuffer = new IntPtr(pSeed);
buffers[1] = seedBuffer;
return DeriveKeyMaterial(secretAgreement,
BCryptNative.KeyDerivationFunction.Tls,
buffers,
flags);
}
}
}
/// <summary>
/// Generate a secret agreement value for between two parties
/// </summary>
[System.Security.SecurityCritical]
internal static SafeNCryptSecretHandle DeriveSecretAgreement(SafeNCryptKeyHandle privateKey,
SafeNCryptKeyHandle otherPartyPublicKey) {
Contract.Requires(privateKey != null);
Contract.Requires(otherPartyPublicKey != null);
Contract.Ensures(Contract.Result<SafeNCryptSecretHandle>() != null &&
!Contract.Result<SafeNCryptSecretHandle>().IsClosed &&
!Contract.Result<SafeNCryptSecretHandle>().IsInvalid);
SafeNCryptSecretHandle secretAgreement;
ErrorCode error = UnsafeNativeMethods.NCryptSecretAgreement(privateKey,
otherPartyPublicKey,
out secretAgreement,
0);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return secretAgreement;
}
/// <summary>
/// Export a key from the KSP
/// </summary>
[System.Security.SecurityCritical]
internal static byte[] ExportKey(SafeNCryptKeyHandle key, string format) {
Contract.Requires(key != null);
Contract.Requires(!String.IsNullOrEmpty(format));
Contract.Ensures(Contract.Result<byte[]>() != null);
// Figure out how big of a buffer we need to export into
int bufferSize = 0;
ErrorCode error = UnsafeNativeMethods.NCryptExportKey(key,
IntPtr.Zero,
format,
IntPtr.Zero,
null,
0,
out bufferSize,
0);
if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) {
throw new CryptographicException((int)error);
}
// Export the key
Debug.Assert(bufferSize > 0, "bufferSize > 0");
byte[] keyBlob = new byte[bufferSize];
error = UnsafeNativeMethods.NCryptExportKey(key,
IntPtr.Zero,
format,
IntPtr.Zero,
keyBlob,
keyBlob.Length,
out bufferSize,
0);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return keyBlob;
}
/// <summary>
/// Make sure that a key is padded out to be its full size
/// </summary>
private static byte[] FillKeyParameter(byte[] key, int keySize) {
Contract.Requires(key != null);
Contract.Requires(keySize > 0);
Contract.Ensures(Contract.Result<byte[]>() != null && Contract.Result<byte[]>().Length >= keySize / 8);
int bytesRequired = (keySize / 8) + (keySize % 8 == 0 ? 0 : 1);
if (key.Length == bytesRequired) {
return key;
}
#if DEBUG
// If the key is longer than required, it should have been padded out with zeros
if (key.Length > bytesRequired) {
for (int i = bytesRequired; i < key.Length; i++) {
Debug.Assert(key[i] == 0, "key[i] == 0");
}
}
#endif
byte[] fullKey = new byte[bytesRequired];
Buffer.BlockCopy(key, 0, fullKey, 0, Math.Min(key.Length, fullKey.Length));
return fullKey;
}
/// <summary>
/// Finalize a key and prepare it for use
/// </summary>
[System.Security.SecurityCritical]
internal static void FinalizeKey(SafeNCryptKeyHandle key) {
Contract.Requires(key != null && !key.IsInvalid && !key.IsClosed);
ErrorCode error = UnsafeNativeMethods.NCryptFinalizeKey(key, 0);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
}
/// <summary>
/// Get the value of an NCrypt property
/// </summary>
[System.Security.SecurityCritical]
internal static byte[] GetProperty(SafeNCryptHandle ncryptObject,
string propertyName,
CngPropertyOptions propertyOptions,
out bool foundProperty) {
Contract.Requires(ncryptObject != null);
Contract.Requires(propertyName != null);
// Find out how big of a buffer we need to store the property in
int bufferSize = 0;
ErrorCode error = UnsafeNativeMethods.NCryptGetProperty(ncryptObject,
propertyName,
null,
0,
out bufferSize,
propertyOptions);
//
// NTE_NOT_FOUND means this property does not exist, any other error besides NTE_BUFFER_TOO_SMALL
// indicates a real problem.
//
if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall && error != ErrorCode.NotFound) {
throw new CryptographicException((int)error);
}
foundProperty = error != ErrorCode.NotFound;
// Pull back the property value
byte[] value = null;
if (error != ErrorCode.NotFound && bufferSize > 0) {
value = new byte[bufferSize];
error = UnsafeNativeMethods.NCryptGetProperty(ncryptObject,
propertyName,
value,
value.Length,
out bufferSize,
propertyOptions);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
foundProperty = true;
}
return value;
}
/// <summary>
/// Get the value of a DWORD NCrypt property
/// </summary>
[System.Security.SecurityCritical]
internal static int GetPropertyAsDWord(SafeNCryptHandle ncryptObject,
string propertyName,
CngPropertyOptions propertyOptions) {
Contract.Requires(ncryptObject != null);
Contract.Requires(propertyName != null);
bool foundProperty;
byte[] valueBytes = GetProperty(ncryptObject, propertyName, propertyOptions, out foundProperty);
if (!foundProperty || valueBytes == null) {
return 0;
}
else {
return BitConverter.ToInt32(valueBytes, 0);
}
}
[SecurityCritical]
internal static ErrorCode GetPropertyAsInt(SafeNCryptHandle ncryptObject,
string propertyName,
CngPropertyOptions propertyOptions,
ref int propertyValue) {
Contract.Requires(ncryptObject != null);
Contract.Requires(propertyName != null);
int cbResult;
ErrorCode errorCode = UnsafeNativeMethods.NCryptGetProperty(
ncryptObject,
propertyName,
ref propertyValue,
sizeof(int),
out cbResult,
propertyOptions);
if (errorCode == ErrorCode.Success)
{
System.Diagnostics.Debug.Assert(cbResult == sizeof(int), "Expected cbResult=4, got " + cbResult);
}
return errorCode;
}
/// <summary>
/// Get the value of a pointer NCrypt property
/// </summary>
[System.Security.SecurityCritical]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static IntPtr GetPropertyAsIntPtr(SafeNCryptHandle ncryptObject,
string propertyName,
CngPropertyOptions propertyOptions) {
Contract.Requires(ncryptObject != null);
Contract.Requires(propertyName != null);
// Find out how big of a buffer we need to store the property in
int bufferSize = IntPtr.Size;
IntPtr value = IntPtr.Zero;
ErrorCode error = UnsafeNativeMethods.NCryptGetProperty(ncryptObject,
propertyName,
out value,
IntPtr.Size,
out bufferSize,
propertyOptions);
// NTE_NOT_FOUND means this property was not set, so return a NULL pointer
if (error == ErrorCode.NotFound) {
return IntPtr.Zero;
}
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return value;
}
/// <summary>
/// Get the value of a string NCrypt property
/// </summary>
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
internal static string GetPropertyAsString(SafeNCryptHandle ncryptObject,
string propertyName,
CngPropertyOptions propertyOptions) {
Contract.Requires(ncryptObject != null);
Contract.Requires(propertyName != null);
bool foundProperty;
byte[] valueBytes = GetProperty(ncryptObject, propertyName, propertyOptions, out foundProperty);
if (!foundProperty || valueBytes == null) {
return null;
}
else if (valueBytes.Length == 0) {
return String.Empty;
}
else {
unsafe {
fixed (byte* pValueBytes = valueBytes) {
return Marshal.PtrToStringUni(new IntPtr(pValueBytes));
}
}
}
}
/// <summary>
/// Get the value of an NCrypt structure property -- this will return an empty structure if the
/// property does not exist.
/// </summary>
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
internal static T GetPropertyAsStruct<T>(SafeNCryptHandle ncryptObject,
string propertyName,
CngPropertyOptions propertyOptions) where T : struct {
Contract.Requires(ncryptObject != null);
Contract.Requires(propertyName != null);
bool foundProperty;
byte[] valueBytes = GetProperty(ncryptObject, propertyName, propertyOptions, out foundProperty);
if (!foundProperty || valueBytes == null) {
return new T();
}
unsafe {
fixed (byte *pValue = valueBytes) {
return (T)Marshal.PtrToStructure(new IntPtr(pValue), typeof(T));
}
}
}
/// <summary>
/// Import a key into the KSP
/// </summary>
[System.Security.SecurityCritical]
internal static SafeNCryptKeyHandle ImportKey(SafeNCryptProviderHandle provider,
byte[] keyBlob,
string format) {
Contract.Requires(provider != null);
Contract.Requires(keyBlob != null);
Contract.Requires(!String.IsNullOrEmpty(format));
Contract.Ensures(Contract.Result<SafeNCryptKeyHandle>() != null &&
!Contract.Result<SafeNCryptKeyHandle>().IsInvalid &&
!Contract.Result<SafeNCryptKeyHandle>().IsClosed);
SafeNCryptKeyHandle keyHandle = null;
ErrorCode error = UnsafeNativeMethods.NCryptImportKey(provider,
IntPtr.Zero,
format,
IntPtr.Zero,
out keyHandle,
keyBlob,
keyBlob.Length,
0);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return keyHandle;
}
[System.Security.SecurityCritical]
internal static SafeNCryptKeyHandle ImportKey(SafeNCryptProviderHandle provider,
byte[] keyBlob,
string format,
IntPtr pParametersList) {
Contract.Requires(provider != null);
Contract.Requires(keyBlob != null);
Contract.Requires(!String.IsNullOrEmpty(format));
Contract.Ensures(Contract.Result<SafeNCryptKeyHandle>() != null &&
!Contract.Result<SafeNCryptKeyHandle>().IsInvalid &&
!Contract.Result<SafeNCryptKeyHandle>().IsClosed);
SafeNCryptKeyHandle keyHandle = null;
ErrorCode error = UnsafeNativeMethods.NCryptImportKey(provider,
IntPtr.Zero,
format,
pParametersList,
out keyHandle,
keyBlob,
keyBlob.Length,
0);
if (error != ErrorCode.Success)
{
throw new CryptographicException((int)error);
}
return keyHandle;
}
/// <summary>
/// Open an existing key
/// </summary>
[System.Security.SecurityCritical]
internal static SafeNCryptKeyHandle OpenKey(SafeNCryptProviderHandle provider,
string name,
CngKeyOpenOptions options) {
Contract.Requires(provider != null && !provider.IsInvalid && !provider.IsClosed);
Contract.Requires(name != null);
SafeNCryptKeyHandle key = null;
ErrorCode error = UnsafeNativeMethods.NCryptOpenKey(provider, out key, name, 0, options);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return key;
}
/// <summary>
/// Open the specified key storage provider
/// </summary>
[System.Security.SecurityCritical]
internal static SafeNCryptProviderHandle OpenStorageProvider(string providerName) {
Contract.Requires(!String.IsNullOrEmpty(providerName));
Contract.Ensures(Contract.Result<SafeNCryptProviderHandle>() != null &&
!Contract.Result<SafeNCryptProviderHandle>().IsInvalid &&
!Contract.Result<SafeNCryptProviderHandle>().IsClosed);
SafeNCryptProviderHandle providerHandle = null;
ErrorCode error = UnsafeNativeMethods.NCryptOpenStorageProvider(out providerHandle,
providerName,
0);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return providerHandle;
}
/// <summary>
/// Reverse the bytes in a buffer
/// </summary>
private static byte[] ReverseBytes(byte[] buffer) {
Contract.Requires(buffer != null);
Contract.Ensures(Contract.Result<byte[]>() != null && Contract.Result<byte[]>().Length == buffer.Length);
return ReverseBytes(buffer, 0, buffer.Length, false);
}
/// <summary>
/// Reverse a section of bytes within a buffer
/// </summary>
private static byte[] ReverseBytes(byte[] buffer, int offset, int count) {
return ReverseBytes(buffer, offset, count, false);
}
private static byte[] ReverseBytes(byte[] buffer, int offset, int count, bool padWithZeroByte) {
Contract.Requires(buffer != null);
Contract.Requires(offset >= 0 && offset < buffer.Length);
Contract.Requires(count >= 0 && buffer.Length - count >= offset);
Contract.Ensures(Contract.Result<byte[]>() != null);
Contract.Ensures(Contract.Result<byte[]>().Length == (padWithZeroByte ? count + 1 : count));
Contract.Ensures(padWithZeroByte ? Contract.Result<byte[]>()[count] == 0 : true);
byte[] reversed;
if(padWithZeroByte)
{
reversed = new byte[count+1]; // the last (most-significant) byte will be left as 0x00
}
else
{
reversed = new byte[count];
}
int lastByte = offset + count - 1;
for (int i = 0; i < count; i++) {
reversed[i] = buffer[lastByte - i];
}
return reversed;
}
/// <summary>
/// Set a DWORD property on an NCrypt object
/// </summary>
[System.Security.SecurityCritical]
internal static void SetProperty(SafeNCryptHandle ncryptObject,
string propertyName,
int value,
CngPropertyOptions propertyOptions) {
Contract.Requires(ncryptObject != null);
Contract.Requires(propertyName != null);
SetProperty(ncryptObject, propertyName, BitConverter.GetBytes(value), propertyOptions);
}
/// <summary>
/// Set a string property
/// </summary>
[System.Security.SecurityCritical]
internal static void SetProperty(SafeNCryptHandle ncryptObject,
string propertyName,
string value,
CngPropertyOptions propertyOptions) {
Contract.Requires(ncryptObject != null);
Contract.Requires(propertyName != null);
ErrorCode error = UnsafeNativeMethods.NCryptSetProperty(ncryptObject,
propertyName,
value,
(value.Length + 1) * sizeof(char),
propertyOptions);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
}
/// <summary>
/// Set a structure property
/// </summary>
[System.Security.SecurityCritical]
internal static void SetProperty<T>(SafeNCryptHandle ncryptObject,
string propertyName,
T value,
CngPropertyOptions propertyOptions) where T : struct {
Contract.Requires(ncryptObject != null);
Contract.Requires(propertyName != null);
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
unsafe {
fixed (byte *pBuffer = buffer) {
bool marshaledStructure = false;
RuntimeHelpers.PrepareConstrainedRegions();
try {
// If we successfully marshal into the buffer, make sure to destroy the buffer when we're done
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally {
Marshal.StructureToPtr(value, new IntPtr(pBuffer), false);
marshaledStructure = true;
}
SetProperty(ncryptObject, propertyName, buffer, propertyOptions);
}
finally {
if (marshaledStructure) {
Marshal.DestroyStructure(new IntPtr(pBuffer), typeof(T));
}
}
}
}
}
/// <summary>
/// Set a property on an NCrypt object
/// </summary>
[System.Security.SecurityCritical]
internal static void SetProperty(SafeNCryptHandle ncryptObject,
string propertyName,
byte[] value,
CngPropertyOptions propertyOptions) {
Contract.Requires(ncryptObject != null);
Contract.Requires(propertyName != null);
ErrorCode error = UnsafeNativeMethods.NCryptSetProperty(ncryptObject,
propertyName,
value,
value != null ? value.Length : 0,
propertyOptions);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
}
/// <summary>
/// Sign a hash using no padding
/// </summary>
[System.Security.SecurityCritical]
internal static byte[] SignHash(SafeNCryptKeyHandle key, byte[] hash) {
Contract.Requires(key != null);
Contract.Requires(hash != null);
Contract.Ensures(Contract.Result<byte[]>() != null);
// Figure out how big the signature is
int signatureSize = 0;
ErrorCode error = UnsafeNativeMethods.NCryptSignHash(key,
IntPtr.Zero,
hash,
hash.Length,
null,
0,
out signatureSize,
0);
if (error != ErrorCode.Success && error != ErrorCode.BufferTooSmall) {
throw new CryptographicException((int)error);
}
// Sign the data
Debug.Assert(signatureSize > 0, "signatureSize > 0");
byte[] signature = new byte[signatureSize];
error = UnsafeNativeMethods.NCryptSignHash(key,
IntPtr.Zero,
hash,
hash.Length,
signature,
signature.Length,
out signatureSize,
0);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return signature;
}
/// <summary>
/// Sign a hash using no padding
/// </summary>
[System.Security.SecurityCritical]
internal static byte[] SignHash(SafeNCryptKeyHandle key, byte[] hash, int expectedSize)
{
Contract.Requires(key != null);
Contract.Requires(hash != null);
Contract.Ensures(Contract.Result<byte[]>() != null);
#if DEBUG
expectedSize = 1;
#endif
// Figure out how big the signature is
byte[] signature = new byte[expectedSize];
int signatureSize = 0;
ErrorCode error = UnsafeNativeMethods.NCryptSignHash(key,
IntPtr.Zero,
hash,
hash.Length,
signature,
signature.Length,
out signatureSize,
0);
if (error == ErrorCode.BufferTooSmall)
{
signature = new byte[signatureSize];
error = UnsafeNativeMethods.NCryptSignHash(key,
IntPtr.Zero,
hash,
hash.Length,
signature,
signature.Length,
out signatureSize,
0);
}
if (error != ErrorCode.Success)
{
throw new CryptographicException((int)error);
}
Array.Resize(ref signature, signatureSize);
return signature;
}
/// <summary>
/// Unpack a key blob in ECC public blob format into its X and Y parameters
///
/// This method expects that the blob be in the correct format -- blobs accepted from partially
/// trusted code need to be validated before being unpacked.
/// </summary>
internal static void UnpackEccPublicBlob(byte[] blob, out BigInteger x, out BigInteger y) {
Contract.Requires(blob != null && blob.Length > 2 * sizeof(int));
//
// See code:System.Security.Cryptography.NCryptNative#ECCPublicBlobFormat for details about the
// format of the ECC public key blob.
//
// read the size of each parameter
int parameterSize = BitConverter.ToInt32(blob, sizeof(int));
Debug.Assert(parameterSize > 0, "parameterSize > 0");
Debug.Assert(blob.Length >= 2 * sizeof(int) + 2 * parameterSize, "blob.Length >= 2 * sizeof(int) + 2 * parameterSize");
// read out the X and Y parameters, in memory reversed form
// add 0x00 padding to force BigInteger to interpret these as positive numbers
x = new BigInteger(ReverseBytes(blob, 2 * sizeof(int), parameterSize, true));
y = new BigInteger(ReverseBytes(blob, 2 * sizeof(int) + parameterSize, parameterSize, true));
}
/// <summary>
/// Verify a signature created with no padding
/// </summary>
[System.Security.SecurityCritical]
internal static bool VerifySignature(SafeNCryptKeyHandle key, byte[] hash, byte[] signature) {
Contract.Requires(key != null);
Contract.Requires(hash != null);
Contract.Requires(signature != null);
ErrorCode error = UnsafeNativeMethods.NCryptVerifySignature(key,
IntPtr.Zero,
hash,
hash.Length,
signature,
signature.Length,
0);
return error == ErrorCode.Success;
}
}
}
|