⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cryptnativehelper.cs

📁 Microsoft Mobile Development Handbook的代码,有C#,VB,C++的
💻 CS
📖 第 1 页 / 共 3 页
字号:
//===============================================================================
// 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 + -