Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Core / System / Security / Cryptography / CapiNative.cs / 1305376 / CapiNative.cs
// ==++==
// ==--==
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Diagnostics.Contracts;
using Microsoft.Win32.SafeHandles;
namespace System.Security.Cryptography {
/// Native interop with CAPI. Native definitions can be found in wincrypt.h or msaxlapi.h
internal static class CapiNative {
internal enum AlgorithmClass {
DataEncryption = (3 << 13), // ALG_CLASS_DATA_ENCRYPT
Hash = (4 << 13) // ALG_CLASS_HASH
internal enum AlgorithmType {
Any = (0 << 9), // ALG_TYPE_ANY
Block = (3 << 9) // ALG_TYPE_BLOCK
internal enum AlgorithmSubId {
MD5 = 3, // ALG_SID_MD5
Sha1 = 4, // ALG_SID_SHA1
Sha256 = 12, // ALG_SID_SHA_256
Sha384 = 13, // ALG_SID_SHA_384
Sha512 = 14, // ALG_SID_SHA_512
Aes128 = 14, // ALG_SID_AES_128
Aes192 = 15, // ALG_SID_AES_192
Aes256 = 16 // ALG_SID_AES_256
internal enum AlgorithmId {
None = 0,
Aes128 = (AlgorithmClass.DataEncryption | AlgorithmType.Block | AlgorithmSubId.Aes128), // CALG_AES_128
Aes192 = (AlgorithmClass.DataEncryption | AlgorithmType.Block | AlgorithmSubId.Aes192), // CALG_AES_192
Aes256 = (AlgorithmClass.DataEncryption | AlgorithmType.Block | AlgorithmSubId.Aes256), // CALG_AES_256
MD5 = (AlgorithmClass.Hash | AlgorithmType.Any | AlgorithmSubId.MD5), // CALG_MD5
Sha1 = (AlgorithmClass.Hash | AlgorithmType.Any | AlgorithmSubId.Sha1), // CALG_SHA1
Sha256 = (AlgorithmClass.Hash | AlgorithmType.Any | AlgorithmSubId.Sha256), // CALG_SHA_256
Sha384 = (AlgorithmClass.Hash | AlgorithmType.Any | AlgorithmSubId.Sha384), // CALG_SHA_384
Sha512 = (AlgorithmClass.Hash | AlgorithmType.Any | AlgorithmSubId.Sha512) // CALG_SHA_512
/// Flags for the CryptAcquireContext API
[Flags]
internal enum CryptAcquireContextFlags {
None = 0x00000000,
VerifyContext = unchecked((int)0xF0000000) // CRYPT_VERIFYCONTEXT
/// Error codes returned from CAPI
internal enum ErrorCode {
Success = 0x00000000, // ERROR_SUCCESS
MoreData = 0x00000ea, // ERROR_MORE_DATA
NoMoreItems = 0x00000103, // ERROR_NO_MORE_ITEMS
BadData = unchecked((int)0x80090005), // NTE_BAD_DATA
BadAlgorithmId = unchecked((int)0x80090008), // NTE_BAD_ALGID
ProviderTypeNotDefined = unchecked((int)0x80090017), // NTE_PROV_TYPE_NOT_DEF
KeysetNotDefined = unchecked((int)0x80090019) // NTE_KEYSET_NOT_DEF
/// Parameters that GetHashParam can query
internal enum HashParameter {
None = 0x0000,
AlgorithmId = 0x0001, // HP_ALGID
HashValue = 0x0002, // HP_HASHVAL
HashSize = 0x0004 // HP_HASHSIZE
/// Formats of blobs that keys can appear in
internal enum KeyBlobType : byte {
PlainText = 0x8 // PLAINTEXTKEYBLOB
/// Flags for CryptGenKey and CryptImportKey
[Flags]
internal enum KeyFlags {
None = 0x0000,
Exportable = 0x0001 // CRYPT_EXPORTABLE
/// Parameters of a cryptographic key used by SetKeyParameter
internal enum KeyParameter {
None = 0,
IV = 1, // KP_IV
Mode = 4, // KP_MODE
ModeBits = 5 // KP_MODE_BITS
/// Well-known names of crypto service providers
internal static class ProviderNames {
// MS_ENH_RSA_AES_PROV
public const string MicrosoftEnhancedRsaAes = "Microsoft Enhanced RSA and AES Cryptographic Provider";
public const string MicrosoftEnhancedRsaAesPrototype = "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)";
/// Parameters exposed by a CSP
internal enum ProviderParameter {
None = 0,
EnumerateAlgorithms = 1 // PP_ENUMALGS
/// Flags controlling information retrieved about a provider parameter
[Flags]
internal enum ProviderParameterFlags {
None = 0x00000000,
RestartEnumeration = 0x00000001 // CRYPT_FIRST
/// Provider type accessed in a crypto service provider. These provide the set of algorithms
/// available to use for an application.
internal enum ProviderType {
None = 0,
RsaAes = 24 // PROV_RSA_AES
[StructLayout(LayoutKind.Sequential)]
internal struct BLOBHEADER {
public KeyBlobType bType;
public byte bVersion;
public short reserved;
public AlgorithmId aiKeyAlg;
[StructLayout(LayoutKind.Sequential)]
internal struct CRYPTOAPI_BLOB {
public int cbData;
public IntPtr pbData;
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct PROV_ENUMALGS {
public AlgorithmId aiAlgId;
public int dwBitLen;
public int dwNameLen;
public fixed byte szName[20];
#pragma warning disable 618 // Have not migrated to v4 transparency yet
[SecurityCritical(SecurityCriticalScope.Everything)]
#pragma warning restore 618
[SuppressUnmanagedCodeSecurity]
internal static class UnsafeNativeMethods {
/// Calculate the public key token for a given public key
[DllImport("clr")]
public static extern int _AxlPublicKeyBlobToPublicKeyToken(ref CRYPTOAPI_BLOB pCspPublicKeyBlob,
[Out] out SafeAxlBufferHandle ppwszPublicKeyToken);
/// Open a crypto service provider, if a key container is specified KeyContainerPermission
/// should be demanded.
[DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptAcquireContext([Out] out SafeCspHandle phProv,
string pszContainer,
string pszProvider,
ProviderType dwProvType,
CryptAcquireContextFlags dwFlags);
/// Prepare a new hash algorithm for use
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptCreateHash(SafeCspHandle hProv,
AlgorithmId Algid,
SafeCapiKeyHandle hKey,
int dwFlags,
[Out] out SafeCapiHashHandle phHash);
/// Decrypt a block of data
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptDecrypt(SafeCapiKeyHandle hKey,
SafeCapiHashHandle hHash,
[MarshalAs(UnmanagedType.Bool)] bool Final,
int dwFlags,
IntPtr pbData, // BYTE *
[In, Out] ref int pdwDataLen);
/// Duplicate a key handle
[DllImport("advapi32")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptDuplicateKey(SafeCapiKeyHandle hKey,
IntPtr pdwReserved,
int dwFlags,
[Out] out SafeCapiKeyHandle phKey);
/// Encrypt a block of data
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptEncrypt(SafeCapiKeyHandle hKey,
SafeCapiHashHandle hHash,
[MarshalAs(UnmanagedType.Bool)] bool Final,
int dwFlags,
IntPtr pbData, // BYTE *
[In, Out] ref int pdwDataLen,
int dwBufLen);
/// Export a key into a byte array
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptExportKey(SafeCapiKeyHandle hKey,
SafeCapiKeyHandle hExpKey,
int dwBlobType, // (int)KeyBlobType
int dwExportFlags,
[Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
[In, Out] ref int pdwDataLen);
/// Generate a random key
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptGenKey(SafeCspHandle hProv,
AlgorithmId Algid,
KeyFlags dwFlags,
[Out] out SafeCapiKeyHandle phKey);
/// Fill a buffer with cryptographically random bytes
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptGenRandom(SafeCspHandle hProv,
int dwLen,
[Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbBuffer);
/// Get information about a hash algorithm, including the current value
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptGetHashParam(SafeCapiHashHandle hHash,
HashParameter dwParam,
[Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
[In, Out] ref int pdwDataLen,
int dwFlags);
/// Get information about a CSP
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptGetProvParam(SafeCspHandle hProv,
ProviderParameter dwParam,
IntPtr pbData,
[In, Out] ref int pdwDataLen,
ProviderParameterFlags dwFlags);
/// Add a block of data to a hash
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptHashData(SafeCapiHashHandle hHash,
[MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
int dwDataLen,
int dwFlags);
/// Import a key into a CSP
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptImportKey(SafeCspHandle hProv,
[MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
int dwDataLen,
SafeCapiKeyHandle hPubKey,
KeyFlags dwFlags,
[Out] out SafeCapiKeyHandle phKey);
/// Set a property of a key
[DllImport("advapi32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptSetKeyParam(SafeCapiKeyHandle hKey,
KeyParameter dwParam,
[MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
int dwFlags);
// Utility and wrapper functions
/// Acquire a crypto service provider
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
internal static SafeCspHandle AcquireCsp(string keyContainer,
string providerName,
ProviderType providerType,
CryptAcquireContextFlags flags,
bool throwPlatformException) {
Contract.Ensures(Contract.Result
() != null &&
!Contract.Result
().IsInvalid &&
!Contract.Result
().IsClosed);
SafeCspHandle cspHandle = null;
if (!UnsafeNativeMethods.CryptAcquireContext(out cspHandle,
keyContainer,
providerName,
providerType,
flags)) {
// If the platform doesn't have the specified CSP, we'll either get a ProviderTypeNotDefined
// or a KeysetNotDefined error depending on the CAPI version.
int error = Marshal.GetLastWin32Error();
if (throwPlatformException && (error == (int)CapiNative.ErrorCode.ProviderTypeNotDefined ||
error == (int)CapiNative.ErrorCode.KeysetNotDefined)) {
throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
else {
throw new CryptographicException(error);
return cspHandle;
/// Export a symmetric algorithm key into a byte array
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
internal static byte[] ExportSymmetricKey(SafeCapiKeyHandle key) {
Contract.Requires(key != null);
Contract.Ensures(Contract.Result
() != null && Contract.Result
().Length > 0);
// Figure out how big the key blob is, and export it
int keySize = 0;
if (!UnsafeNativeMethods.CryptExportKey(key,
SafeCapiKeyHandle.InvalidHandle,
(int)KeyBlobType.PlainText,
null,
ref keySize)) {
int error = Marshal.GetLastWin32Error();
if (error != (int)ErrorCode.MoreData) {
throw new CryptographicException(error);
byte[] keyBlob = new byte[keySize];
if (!UnsafeNativeMethods.CryptExportKey(key,
SafeCapiKeyHandle.InvalidHandle,
(int)KeyBlobType.PlainText,
keyBlob,
ref keySize)) {
throw new CryptographicException(Marshal.GetLastWin32Error());
// Strip the headers from the key to access the raw data
// A PLAINTEXTBLOB is laid out as follows:
// BLOBHEADER hdr
// DWORD cbKeySize
// BYTE rbgKeyData[]
int keyDataOffset = Marshal.SizeOf(typeof(BLOBHEADER)) + Marshal.SizeOf(typeof(int));
Debug.Assert(keyBlob.Length > keyDataOffset, "Key blob is in an unexpected format.");
int keyLength = BitConverter.ToInt32(keyBlob, Marshal.SizeOf(typeof(BLOBHEADER)));
Debug.Assert(keyLength > 0, "Unexpected key length.");
Debug.Assert(keyBlob.Length >= keyDataOffset + keyLength, "Key blob is in an unexpected format.");
byte[] keyData = new byte[keyLength];
Buffer.BlockCopy(keyBlob, keyDataOffset, keyData, 0, keyData.Length);
return keyData;
/// Map an algorithm ID to a string name
internal static string GetAlgorithmName(AlgorithmId algorithm) {
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result
()));
return algorithm.ToString().ToUpper(CultureInfo.InvariantCulture);
/// Get the value of a specific hash parameter
[System.Security.SecurityCritical]
internal static byte[] GetHashParameter(SafeCapiHashHandle hashHandle, CapiNative.HashParameter parameter) {
Contract.Requires(hashHandle != null);
Contract.Requires(CapiNative.HashParameter.AlgorithmId <= parameter && parameter <= CapiNative.HashParameter.HashSize);
Contract.Ensures(Contract.Result
() != null);
// Determine the maximum size of the parameter and retrieve it
int parameterSize = 0;
if (!CapiNative.UnsafeNativeMethods.CryptGetHashParam(hashHandle, parameter, null, ref parameterSize, 0)) {
throw new CryptographicException(Marshal.GetLastWin32Error());
Debug.Assert(0 < parameterSize, "Invalid parameter size returned");
byte[] parameterValue = new byte[parameterSize];
if (!CapiNative.UnsafeNativeMethods.CryptGetHashParam(hashHandle, parameter, parameterValue, ref parameterSize, 0)) {
throw new CryptographicException(Marshal.GetLastWin32Error());
// CAPI may have asked for a larger buffer than it used, so only copy the used bytes
if (parameterSize != parameterValue.Length) {
byte[] realValue = new byte[parameterSize];
Buffer.BlockCopy(parameterValue, 0, realValue, 0, parameterSize);
parameterValue = realValue;
return parameterValue;
/// Get information about a CSP. This should only be used for calls where the returned information
/// is in the form of a structure.
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
internal static T GetProviderParameterStruct
(SafeCspHandle provider,
ProviderParameter parameter,
ProviderParameterFlags flags) where T : struct {
Contract.Requires(provider != null);
Contract.Requires(parameter == ProviderParameter.EnumerateAlgorithms);
// Figure out how big the parameter is
int bufferSize = 0;
IntPtr buffer = IntPtr.Zero;
if (!UnsafeNativeMethods.CryptGetProvParam(provider, parameter, buffer, ref bufferSize, flags)) {
int errorCode = Marshal.GetLastWin32Error();
// NoMoreItems means that we've finished the enumeration we're currently working on, this is
// not a real error, so return an empty structure to mark the end.
if (errorCode == (int)ErrorCode.NoMoreItems) {
return new T();
else if (errorCode != (int)ErrorCode.MoreData) {
throw new CryptographicException(errorCode);
Debug.Assert(Marshal.SizeOf(typeof(T)) <= bufferSize, "Buffer size does not match structure size");
// Pull the parameter back and marshal it into the return structure
RuntimeHelpers.PrepareConstrainedRegions();
try {
// Allocate in a CER because we could fail between the alloc and the assignment
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally {
buffer = Marshal.AllocCoTaskMem(bufferSize);
if (!UnsafeNativeMethods.CryptGetProvParam(provider, parameter, buffer, ref bufferSize, flags)) {
throw new CryptographicException(Marshal.GetLastWin32Error());
return (T)Marshal.PtrToStructure(buffer, typeof(T));
finally {
if (buffer != IntPtr.Zero) {
Marshal.FreeCoTaskMem(buffer);
/// Map a verification result to a matching HRESULT
internal static int HResultForVerificationResult(SignatureVerificationResult verificationResult) {
switch (verificationResult) {
case SignatureVerificationResult.AssemblyIdentityMismatch:
case SignatureVerificationResult.PublicKeyTokenMismatch:
case SignatureVerificationResult.PublisherMismatch:
return (int)SignatureVerificationResult.BadSignatureFormat;
case SignatureVerificationResult.ContainingSignatureInvalid:
return (int)SignatureVerificationResult.BadDigest;
default:
return (int)verificationResult;
/// Import a symmetric key into a CSP
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
internal static SafeCapiKeyHandle ImportSymmetricKey(SafeCspHandle provider, AlgorithmId algorithm, byte[] key) {
Contract.Requires(provider != null);
Contract.Requires(((int)algorithm & (int)AlgorithmClass.DataEncryption) == (int)AlgorithmClass.DataEncryption);
Contract.Requires(key != null);
Contract.Ensures(Contract.Result
() != null &&
!Contract.Result
().IsInvalid &&
!Contract.Result
().IsClosed);
// Setup a PLAINTEXTKEYBLOB (v2) which has the following format:
// BLOBHEADER hdr
// DWORD cbKeySize
// BYTE rbgKeyData[]
int blobSize = Marshal.SizeOf(typeof(BLOBHEADER)) + Marshal.SizeOf(typeof(int)) + key.Length;
byte[] keyBlob = new byte[blobSize];
unsafe {
fixed (byte *pBlob = keyBlob) {
BLOBHEADER* pHeader = (BLOBHEADER*)pBlob;
pHeader->bType = KeyBlobType.PlainText;
pHeader->bVersion = 2;
pHeader->reserved = 0;
pHeader->aiKeyAlg = algorithm;
int* pSize = (int *)(pBlob + Marshal.SizeOf(*pHeader));
*pSize = key.Length;
Buffer.BlockCopy(key, 0, keyBlob, Marshal.SizeOf(typeof(BLOBHEADER)) + Marshal.SizeOf(typeof(int)), key.Length);
// Import the PLAINTEXTKEYBLOB into the CSP
SafeCapiKeyHandle importedKey = null;
if (!UnsafeNativeMethods.CryptImportKey(provider,
keyBlob,
keyBlob.Length,
SafeCapiKeyHandle.InvalidHandle,
KeyFlags.Exportable,
out importedKey)) {
throw new CryptographicException(Marshal.GetLastWin32Error());
return importedKey;
/// Set a DWORD key parameter (KP_MODE and KP_MODE_BITS)
[System.Security.SecurityCritical]
internal static void SetKeyParameter(SafeCapiKeyHandle key, KeyParameter parameter, int value) {
Contract.Requires(key != null);
Contract.Requires(parameter == KeyParameter.Mode || parameter == KeyParameter.ModeBits);
SetKeyParameter(key, parameter, BitConverter.GetBytes(value));
/// Set the value of one of a key's parameters
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
internal static void SetKeyParameter(SafeCapiKeyHandle key, KeyParameter parameter, byte[] value) {
Contract.Requires(key != null && !key.IsInvalid && !key.IsClosed);
Contract.Requires(value != null);
if (!UnsafeNativeMethods.CryptSetKeyParam(key, parameter, value, 0)) {
throw new CryptographicException(Marshal.GetLastWin32Error());
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.