📄 cryptnativehelper.cs
字号:
//===============================================================================
// Microsoft patterns & practices
// Mobile Client Software Factory - July 2006
//===============================================================================
// Copyright Microsoft Corporation. All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious. No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Globalization;
namespace Microsoft.Practices.Mobile.PasswordAuthentication
{
/// <summary>
/// This class provides a wrapper around native calls to Crypt APIs that we need for authentication and
/// key generation.
/// </summary>
public class CryptNativeHelper
{
private const ProviderType providerType = ProviderType.PROV_RSA_AES;
private RsaAesCryptographyProvider provider;
private IntPtr hProvider;
/// <summary>
/// Creates a new instance.
/// </summary>
/// <param name="provider">
/// The cryptographic provider that provides both the key store and the algorithm.
/// </param>
public CryptNativeHelper(RsaAesCryptographyProvider provider)
{
Guard.ArgumentNotNull(provider, "provider");
this.provider = provider;
this.hProvider = provider.ProviderHandle;
}
/// <summary>
/// This is a "managed" wrapper around the CryptExportKey function. It basically calls this function
/// and returns a byte array with the key data.
/// </summary>
/// <param name="hKey">The private key to export.</param>
/// <param name="hExchangeKey">
/// The key used to encrypt the private key, or IntPtr.Zero if you don't want to encrypt the key.
/// </param>
/// <param name="type">The type of blob you want returned.</param>
/// <returns>The key data.</returns>
/// <exception cref="CryptNativeException">If one of the native calls failed.</exception>
private byte[] ExportKeyBlob(IntPtr hKey, IntPtr hExchangeKey, BlobType type)
{
//
// First ask for the length of the key, in bytes.
//
int dataLength = 0;
IntPtr pData;
if (!CryptExportKey(hKey, hExchangeKey, type, 0, IntPtr.Zero, ref dataLength))
ThrowWin32Exception("CryptExportKey");
//
// Retrieve the key and copy it over into a managed array.
//
pData = Marshal.AllocHGlobal(dataLength);
try
{
if (!CryptExportKey(hKey, hExchangeKey, type, 0, pData, ref dataLength))
ThrowWin32Exception("CryptExportKey");
byte[] data = new byte[dataLength];
Marshal.Copy(pData, data, 0, dataLength);
return data;
}
finally
{
Marshal.FreeHGlobal(pData);
}
}
/// <summary>
/// This method takes the key and returns a simple blob. A simple blob is basically a header, an
/// algorithm ID, and then the key data itself. This is all defined in an article on MSDN:
/// <para>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/seccrypto/security/simple_key_blobs.asp</para>
/// </summary>
/// <param name="keyData"></param>
/// <param name="algid"></param>
/// <returns></returns>
private byte[] GetKeyBlobFromKeyData(byte[] keyData, AlgorithmId algid)
{
byte[] blob = new byte[keyData.Length + 12];
//
// BLOBHEADER:
// Offset Type Name Description
// ------ ---- ---- -----------
// 0 byte bType Type of Blobk, such as SIMPLEBLOB
// 1 byte bVersion 0x02
// 2-3 short reserved
// 4-7 int Algorithm ID.
//
blob[0] = (byte)BlobType.PLAINTEXTKEYBLOB;
blob[1] = 2;
byte[] algidBytes = CryptographyUtility.GetBytes((uint)algid);
Array.Copy(algidBytes, 0, blob, 4, 4);
//
// SIMPLEBLOB format:
// Offset Contents Description
// ------ -------- -----------
// 0-7 BLOBHEADER
// 8-11 ALG_ID ID of algorithm used to encrypt the key
// 11- Key data
//
blob[8] = 32; // This is what the value is when we export a simple key
Array.Copy(keyData, 0, blob, 12, keyData.Length);
return blob;
}
private IntPtr GetDerivedKey(string password)
{
IntPtr hHash;
if (!CryptCreateHash(hProvider, AlgorithmId.CALG_SHA1, IntPtr.Zero, 0, out hHash))
ThrowWin32Exception("CryptCreateHash");
byte[] data = null;
try
{
data = CryptographyUtility.GetBytes(password);
if (!CryptHashData(hHash, data, data.Length, 0))
ThrowWin32Exception("CryptHashData");
IntPtr hKey;
CryptGenFlags genFlags = CryptGenFlags.CRYPT_EXPORTABLE | CryptGenFlags.CRYPT_NO_SALT;
if (!CryptDeriveKey(hProvider, AlgorithmId.CALG_AES_256, hHash, genFlags, out hKey))
ThrowWin32Exception("CryptGenKey");
return hKey;
}
finally
{
CryptographyUtility.ZeroOutBytes(data);
CryptDestroyHash(hHash);
}
}
/// <summary>
/// Generates an keyed hash (HMAC) from data, salt, and a key. To obtain
/// the same hash you must supply the same data, salt, and key. This function
/// uses HMAC hashing with SHA1.
/// </summary>
/// <param name="data">The data to hash.</param>
/// <param name="salt">
/// Salt to add to the hash to mitigate dictionary attacks. Recommended length
/// is 8 bytes (64 bits).
/// </param>
/// <param name="key">The key to use. Recommended length is 32 bytes (256 bits).</param>
/// <returns>A keyed hash value.</returns>
public byte[] GetHmacHash(byte[] data, byte[] salt, byte[] key)
{
//
// Turns the provided key into a blob that can be imported into the key store and then import
// this key into the key store.
//
IntPtr hKey;
byte[] keyBlob = GetKeyBlobFromKeyData(key, AlgorithmId.CALG_AES_256);
if (!CryptImportKey(hProvider, keyBlob, keyBlob.Length, IntPtr.Zero, CryptGenFlags.CRYPT_EXPORTABLE, out hKey))
ThrowWin32Exception("CryptImportKey");
try
{
//
// Create an HMAC hash set to use the SHA1 algorithm. Then hash the data followed by the salt.
//
IntPtr hHash;
if (!CryptCreateHash(hProvider, AlgorithmId.CALG_HMAC, hKey, 0, out hHash))
ThrowWin32Exception("CryptCreateHash");
try
{
int[] hmacInfo = new int[5];
hmacInfo[0] = (int)AlgorithmId.CALG_SHA1;
if (!CryptSetHashParam(hHash, HashParam.HP_HMAC_INFO, hmacInfo, 0))
ThrowWin32Exception("CryptSetHashParam");
if (!CryptHashData(hHash, data, data.Length, 0))
ThrowWin32Exception("CryptHashData");
if (!CryptHashData(hHash, salt, salt.Length, 0))
ThrowWin32Exception("CryptHashData");
int hashSize;
int dataLength = 4;
if (!CryptGetHashParam(hHash, HashParam.HP_HASHSIZE, out hashSize, ref dataLength, 0))
ThrowWin32Exception("CryptGetHashParam");
byte[] hashData = new byte[hashSize];
dataLength = hashSize;
if (!CryptGetHashParam(hHash, HashParam.HP_HASHVAL, hashData, ref dataLength, 0))
ThrowWin32Exception("CryptGetHashParam");
return hashData;
}
finally
{
CryptDestroyHash(hHash);
}
}
finally
{
CryptDestroyKey(hKey);
}
}
/// <summary>
/// Obtains a key from a password that you supply.
/// </summary>
/// <param name="password">A password.</param>
/// <returns>Returns a key derived from this password.</returns>
public byte[] GetPasswordDerivedKey(string password)
{
IntPtr hUserKey = GetDerivedKey(password);
try
{
byte[] data = ExportKeyBlob(hUserKey, IntPtr.Zero, BlobType.PLAINTEXTKEYBLOB);
return ExtractKeyFromBlob(hUserKey, data);
}
finally
{
CryptDestroyKey(hUserKey);
}
}
//
// The key blob contains data in addition to the actual key. The following article defines the
// format for the blob.
//
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/seccrypto/security/simple_key_blobs.asp
//
private byte[] ExtractKeyFromBlob(IntPtr hKey, byte[] blobData)
{
int keyLength; // The length of the key, in bits
int dataLength = 4;
if (!CryptGetKeyParam(hKey, KeyParam.KP_KEYLEN, out keyLength, ref dataLength, 0))
ThrowWin32Exception("CryptGetKeyParam");
keyLength /= 8; // Convert to key size in bytes
byte[] keyData = new byte[keyLength];
int index = 12; // sizeof(BLOBHEADER) + sizeof(ALG_ID)
Array.Copy(blobData, index, keyData, 0, keyData.Length);
return keyData;
}
private static void ThrowWin32Exception(string functionName)
{
try
{
int error = Marshal.GetLastWin32Error();
Marshal.ThrowExceptionForHR(error);
throw new CryptNativeException(String.Format(CultureInfo.CurrentCulture, Properties.Resources.Win32Error, functionName, error), error);
}
catch (Exception ex)
{
throw new CryptNativeException(String.Format(CultureInfo.CurrentCulture, Properties.Resources.Win32HResultMessage, functionName, ex), ex);
}
}
// exported key blob definitions
private enum BlobType
{
SIMPLEBLOB = 0x1,
PUBLICKEYBLOB = 0x6,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -