windows黑客编程技术之加密技术:windows自带的加密库

经典的加解密算法有AES,DES,RSA等,通过加密我们可以保护我们的数据。

windows自带加密库

windows提供了一组CryptoAPI来对用户的敏感私钥数据提供保护,并以灵活的方式对数据进行加密或签名。接下来介绍HASH值计算、AES加解密、RSA加解密。
CSP:加密服务提供程序

HASH值的计算

Hash算法是一种单向加密算法,HASH就是把任意长度的输入通过HASH算法变换成固定长度的输出,该值就是HASH值。HASH值的空间远小于输入值的空间,不同的输入可能得到相同的输出,所以无法利用HASH值来确定唯一的输入值。基于这种特性,HASH值一般用来执行数据完整性校验。如MD5,SHA等。

实现过程

//用于获取指定CSP内特定密钥容器的句柄。
BOOL CryptAcquireContext(
HCRYPTPROV *phProv,//指向csp句柄的指针,通过CryptReleaseContext释放。
LPCSTR szContainer,//密钥容器名称,一般设置NULL
LPCSTR szProvider,//包含要使用CSP名称的空终止字符串,一般设置NULL。
DWORD dwProvType,//指定要获取提供程序的类型,如PROV_RSA_AES支持RSA签名算法,AES加密算法以及HASH算法。
DWORD dwFlags);//标志值,一般设置0。

//创建一个空HASH对象
BOOL CryptCreateHash(
HCRYPTPROV hProv,//调用CryptAcquireContext创建的CSP句柄
ALG_ID Algid,//标识要使用的HASH算法的ALG_ID值。
HCRYPTKEY  hKey,//对于非键控算法,设置为0.
DWORD dwFlags,//通常为0.
HCRYPTHASH *phHash);//HASH对象句柄。

//将数据添加到HASH对象,并进行HASH计算。
BOOL WINAPI CryptHashData(
HCRYPTHASH hHash, //HASH对象句柄
BYTE* pbData,//指向要添加到HASH对象数据缓冲区的指针
DWORD dwDataLen, //要添加到HASH对象缓冲区的指针
DWORD dwFlags//通常为0
);

//从HASH对象获取指定参数值
BOOL CRYPTFUNC CryptGethashParam(
HCRYPTHASH hHash,//HASH对象句柄
DWORD dwParam, //查询类型 HP_ALGID,HP_HASHSIZE,HP_HASHVAL。
BYTE *pbData, //指向接受指定值的数据缓冲区的指针。
DWORD *pdwDataLen, //指向指定的pdData缓冲区大小的DWORD值得指针,
DWORD dwFlags//0
);

//释放HASH对象句柄
BOOL CRYPTFUNC CryptDestroyHash(
HCRYPTHASH hHash
);

//释放CSP句柄
BOOL WINAPI CryptReleaseContext(
HCRYPTPROV hProv,
DWORD dwFlag
); 

1 任何程序在使用CryptoAPI函数来执行数据计算或是加/解密之前,都需要调用CryptAcquireContext来获取CSP句柄。因为这里是计算数据HASH值,所以将提供程序类型设置为PROV_RSA_AES,该类型支持常用HASH算法。
2 调用CryptCreateHash在CSP中创建一个空的HASH对象并获取句柄,并可以指定HASH对象使用的HASH算法,如CALG_MD5表示MD5 HASH算法。
3 调用CryptGetHashParam从HASH对象获取指定参数,可以获取的参数有三种:
HP_ALGID:HASH算法
HP_HASHSIZE:HASH值的数据长度
HP_HASHVAL:HASH值
因为HASH算法不同,HASH值大小不固定,所以在获取HASH值之前,需要先获取HASH值长度,并申请足够的缓冲区存放HASH值。

编码实现

/*
Function:HASH值计算
pData:需要计算的内容
dwDataLength:内容长度
algHashType:HASH算法类型 如CALG_MD5,CALG_SHA1,CALG_SHA256等
ppHashData:存放计算的的HASH值
pdwHashDataLength:计算的HASH值长度
*/
BOOL CalculateHash(BYTE *pData, DWORD dwDataLength, ALG_ID algHashType, BYTE **ppHashData, DWORD *pdwHashDataLength)
{
	HCRYPTPROV hCryptProv = NULL;//CSP句柄
	HCRYPTHASH hCryptHash = NULL;//HASH对象句柄
	BYTE *pHashData = NULL;
	DWORD dwHashDataLength = 0;
	DWORD dwTemp = 0;
	BOOL bRet = FALSE;


	do
	{
		// 获得指定CSP的密钥容器的句柄		
		bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
		if (FALSE == bRet)
		{
			ShowError("CryptAcquireContext");
			break;
		}

		// 创建一个HASH对象, 指定HASH算法		
		bRet = ::CryptCreateHash(hCryptProv, algHashType, NULL, NULL, &hCryptHash);
		if (FALSE == bRet)
		{
			ShowError("CryptCreateHash");
			break;
		}

		// 计算HASH数据
		bRet = ::CryptHashData(hCryptHash, pData, dwDataLength, 0);
		if (FALSE == bRet)
		{
			ShowError("CryptHashData");
			break;
		}
		
		
		
		// 获取HASH结果的大小
		dwTemp = sizeof(dwHashDataLength);
		bRet = ::CryptGetHashParam(hCryptHash, HP_HASHSIZE, (BYTE *)(&dwHashDataLength), &dwTemp, 0);
		if (FALSE == bRet)
		{
			ShowError("CryptGetHashParam");
			break;
		}

		// 申请内存
		pHashData = new BYTE[dwHashDataLength];
		if (NULL == pHashData)
		{
			bRet = FALSE;
			ShowError("new");
			break;
		}
		::RtlZeroMemory(pHashData, dwHashDataLength);

		// 获取HASH结果数据
		bRet = ::CryptGetHashParam(hCryptHash, HP_HASHVAL, pHashData, &dwHashDataLength, 0);
		if (FALSE == bRet)
		{
			ShowError("CryptGetHashParam");
			break;
		}

		// 返回数据
		*ppHashData = pHashData;
		*pdwHashDataLength = dwHashDataLength;

	} while (FALSE);

	// 释放关闭
	if (FALSE == bRet)
	{
		if (pHashData)
		{
			delete[]pHashData;
			pHashData = NULL;
		}
	}
	if (hCryptHash)
	{
		::CryptDestroyHash(hCryptHash);
	}
	if (hCryptProv)
	{	
		::CryptReleaseContext(hCryptProv, 0);
	}

	return bRet;
}

测试结果
在这里插入图片描述

AES加解密

AES是最常见的对称加密算法,所谓对称加密算法就是加密和解密使用相同密钥的加密算法。AES为分组密码,就是把明文分成一组一组的,每组长度相同,每次加密一组数据,直到加密完整个明文。AES对称加密算法的优势在于算法公开,计算量小,加密效率高。
在AES标准规范中,分组长度只能是128位(16个字节)。密钥长度可以是:
128:CALG_AES_128(16字节)
192:CALG_AES_192(24字节)
256:CALG_AES_256(32字节)

实现过程

//CryptDeriveKey:生成从基础数据值(可以是密码或者别的)派生出的加密会话密钥。
BOOL CRYPTFUNC CryptDeriveKey(
HCRYPTPROV hProv,//CSP句柄
ALG_ID Algid,//标识要使用的HASH算法的ALG_ID值
HCRYPTHASH hBaseData,//HASH对象句柄
DWORD dwFlags,//指定生成密钥的类型
HCRYPTKEY* phKey);//加密会话密钥

//对数据进行加密
BOOL CRYPTFUNC CryptEncrypt
(HCRYPTKEY hKey,//加密会话密钥
HCRYPTHASH hHash,//HASH对象句柄
BOOL Final,//指定它是否是加密系列中的最后一部分
DWORD dwFlags, //0
BYTE* pbData,//指向要加密的明文缓冲区的指针,该缓冲区文本会被生成的密文所覆盖。
DWORD* pdwDataLen,//pbData缓冲区中明文长度
DWORD dwBufLen//指定输入pbData缓冲区的总大小
);

BOOL CryptDecrypt(
HCRYPTKEY  hKey,//加密会话密钥
HCRYPTHASH hHash,//HASH对象句柄
BOOL       Final,//指定它是否是加密系列中的最后一部分
DWORD      dwFlags,//0
BYTE       *pbData,//指向要解密的明文缓冲区的指针,该缓冲区文本会被生成的明文所覆盖。
DWORD      *pdwDataLen//pbData缓冲区的总大小
);

//释放密钥句柄
BOOL CRYPTFUNC CryptDestroyKey(HCRYPTKEY hKey);

1 任何程序在使用CryptoAPI函数来执行数据计算或是加/解密之前,都需要调用CryptAcquireContext来获取CSP句柄.由于这里使用的是AES对称加密算法,所以将提供程序类型设置为PROV_RSA_AES,该类型支持AES算法。
2 下面并不直接使用明文密码作为AES的加密密码,而是把明文密码的MD5值作为密码,再调用CryptDeriveKey函数来派生出AES的加密密钥。先CryptCreateHash创建HASH对象,然后CryptHashData对传入的密码进行MD5值计算,再CryptDeriveKey通过MD5来派生密钥,CALG_AES_128表示用于128位AES加密。
3 派生完密钥之后,可以调用CryptEncrypt来根据派生密钥中指定的加密算法进行加密。

AES加密编码实现

/*
Function:AES加密
pPassword:加密密码
dwPasswordLength:密码长度
pData:指向要加密的明文缓冲区的指针
dwDataLength:pData缓冲区中明文的长度 
dwBufferLength:pData缓冲区总大小:大于明文长度,因为加密完成后密文也会写道这个缓冲区。
*/ 
BOOL AesEncrypt(BYTE *pPassword, DWORD dwPasswordLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength)
{
	BOOL bRet = TRUE;
	HCRYPTPROV hCryptProv = NULL;//CSP句柄
	HCRYPTHASH hCryptHash = NULL;//HASH对象句柄
	HCRYPTKEY hCryptKey = NULL;//加密密钥句柄

	do
	{
		// 获取CSP句柄
		//CryptAcquireContext:用于获取指定CSP内特定密钥容器的句柄。
		bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
		if (FALSE == bRet)
		{
			ShowError("CryptAcquireContext");
			break;
		}

		// 创建HASH对象
		bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash);
		if (FALSE == bRet)
		{
			ShowError("CryptCreateHash");
			break;
		}

		// 对密钥进行HASH计算
		bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0);
		if (FALSE == bRet)
		{
			ShowError("CryptHashData");
			break;
		}

		// 使用HASH来生成密钥
		bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey);
		if (FALSE == bRet)
		{
			ShowError("CryptDeriveKey");
			break;
		}

		// 加密数据
		//CryptEncrypt:由CSP模块保存的密钥指定的加密算法来加密数据。
		//TRUE:表示该加密是AES加密数据中最后一组,这样系统会自动按照分组长度对数据进行填充并计算。
		bRet = ::CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength);
		if (FALSE == bRet)
		{
			ShowError("CryptEncrypt");
			break;
		}

	} while (FALSE);

	// 关闭释放
	if (hCryptKey)
	{
		::CryptDestroyKey(hCryptKey);//释放密钥句柄
	}
	if (hCryptHash)
	{
		::CryptDestroyHash(hCryptHash);//释放HASH对象句柄
	}
	if (hCryptProv)
	{
		::CryptReleaseContext(hCryptProv, 0);//释放CSP句柄
	}

	return bRet;
}

由于AES是对称加密,所以加密解密都是同一个密码。为了获取相同的解密密钥,需要根据明文密码来计算出派生密钥。过程与上面相同。

AES解码编码实现

/*
Function:AES解密
pPassword:解密密码
dwPasswordLength:密码长度
pData:指向要解密的明文缓冲区的指针
dwDataLength:pData缓冲区中明文的长度 
dwBufferLength:pData缓冲区总大小:大于明文长度,因为加密完成后密文也会写道这个缓冲区。
*/ 
BOOL AesDecrypt(BYTE *pPassword, DWORD dwPasswordLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength)
{
	BOOL bRet = TRUE;
	HCRYPTPROV hCryptProv = NULL;
	HCRYPTHASH hCryptHash = NULL;
	HCRYPTKEY hCryptKey = NULL;

	do
	{
		// 获取CSP句柄
		bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
		if (FALSE == bRet)
		{
			ShowError("CryptAcquireContext");
			break;
		}

		// 创建HASH对象
		bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash);
		if (FALSE == bRet)
		{
			ShowError("CryptCreateHash");
			break;
		}

		// 对密钥进行HASH计算
		bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0);
		if (FALSE == bRet)
		{
			ShowError("CryptHashData");
			break;
		}

		// 使用HASH来生成密钥
		bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey);
		if (FALSE == bRet)
		{
			ShowError("CryptDeriveKey");
			break;
		}

		// 解密数据
		bRet = ::CryptDecrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength);
		if (FALSE == bRet)
		{
			ShowError("CryptDecrypt");
			break;
		}

	} while (FALSE);

	// 关闭释放
	if (hCryptKey)
	{
		::CryptDestroyKey(hCryptKey);
	}
	if (hCryptHash)
	{
		::CryptDestroyHash(hCryptHash);
	}
	if (hCryptProv)
	{
		::CryptReleaseContext(hCryptProv, 0);
	}

	return bRet;
}

测试结果

![在这里插入图片描述](https://img-blog.csdnimg.cn/fc168fffb1744789afb6e707a223ec70.pn

RSA加解密

RSA是一种非对称加密算法,加密密钥和解密密钥不相同。RSA算法的安全性非常高,极大整数进行因数分解的难度决定了RSA算法的可靠性。但是由于RSA进行的都是大数运算,所以RSA速度很慢,是同安全级别的对称密码算法的1/1000左右,一般RSA用于少量数据加密。

实现过程

//随机生成加密会话密钥或公钥/私钥对,密钥或密钥对的句柄在phKey返回。
BOOL CRYPTFUNC CryptGenKey( 
HCRYPTPROV  hProv,//CSP句柄
ALG_ID Algid,//标识要使用的HASH算法的ALG_ID值
DWORD dwFlags,//指定生成的密钥类型
HCRYPTKEY* phKey//密钥句柄
);

//以安全的方式从加密服务提供程序中导出加密密钥或密钥对。
BOOL CRYPTFUNC CryptExportKey(
HCRYPTKEY hKey,//指向要导出的密钥句柄
HCRYPTKEY hExpKey,//指向目标用户的密钥句柄,通常为NULL
DWORD dwBlobType,//指定要在pdData中导出键BLOB的类型。PUBLICKEYBLOB表示导出公钥,PRIVATEKEYBLOB表示导出私钥。
DWORD dwFlags,//0
BYTE* pbData,//指向接收关键BLOB数据的缓冲区的指针
DWORD* pdwbDataLen//pddate缓冲区大小
);

//将加密密钥从密钥 BLOB传输到加密服务提供程序(CSP)
BOOL CryptImportKey(
HCRYPTPROV hProv,//CSP句柄
const BYTE *pbData,//指定一个BYTE数组,此密钥BOLO由CryptExportKey创建
DWORD dwDataLen,//包含关键BLOB长度
HCRYPTKEY  hPubKey,//解密存储在pdData中的加密密钥句柄
DWORD  dwFlags,//目前仅在将PRIVATEKEYBLOB形式的公钥/私钥对导入 CSP 时使用
HCRYPTKEY  *phKey//指向接受导入键句柄的指针
);

通常情况下,公钥用来加密数据,私钥用来解密数据,因此在数据加解/密前,需要先产生公钥和私钥密钥对。公钥和私钥成对,使用公钥加密的数据只能用唯一的私钥来解密。

1 首先,任何程序在使用CryptoAPI函数来执行数据计算或是加/解密之前,都需要调用CryptAcquireContext来获取CSP句柄.由于这里使用的是RSA非对称加密算法,所以将提供程序类型设置为PROV_RSA_FULL,该类型支持RSA算法。
2 调用CryptGenKey函数随机生成AT_KEYEXCHANGE交换密钥对,并设置生成的密钥对类型为CRYPT_EXPORTABLE,它是可导出的,可以使用CryptExportKey导出密钥。此时RSA密钥对已经生成。

编码实现

/*
Function:生成公钥和私钥
ppPublicKey:公钥
pdwPublicKeyLength:公钥长度
ppPrivateKey:私钥
pdwPrivateKeyLength:私钥长度
*/ 
BOOL GenerateKey(BYTE **ppPublicKey, DWORD *pdwPublicKeyLength, BYTE **ppPrivateKey, DWORD *pdwPrivateKeyLength)
{
	BOOL bRet = TRUE;
	HCRYPTPROV hCryptProv = NULL;//CSP句柄
	HCRYPTKEY hCryptKey = NULL;//密钥句柄
	BYTE *pPublicKey = NULL;//公钥内容
	DWORD dwPublicKeyLength = 0;//公钥长度
	BYTE *pPrivateKey = NULL;//私钥内容
	DWORD dwPrivateKeyLength = 0;//私钥长度

	do
	{
		// 获取CSP句柄
		bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0);
		if (FALSE == bRet)
		{
			ShowError("CryptAcquireContext");
			break;
		}

		// 生成公私密钥对
		bRet = ::CryptGenKey(hCryptProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hCryptKey);
		if (FALSE == bRet)
		{
			ShowError("CryptGenKey");
			break;
		}

		// 获取公钥密钥的长度和内容
		bRet = ::CryptExportKey(hCryptKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwPublicKeyLength);
		if (FALSE == bRet)
		{
			ShowError("CryptExportKey");
			break;
		}
		pPublicKey = new BYTE[dwPublicKeyLength];
		::RtlZeroMemory(pPublicKey, dwPublicKeyLength);
		bRet = ::CryptExportKey(hCryptKey, NULL, PUBLICKEYBLOB, 0, pPublicKey, &dwPublicKeyLength);
		if (FALSE == bRet)
		{
			ShowError("CryptExportKey");
			break;
		}

		// 获取私钥密钥的长度和内容
		bRet = ::CryptExportKey(hCryptKey, NULL, PRIVATEKEYBLOB, 0, NULL, &dwPrivateKeyLength);
		if (FALSE == bRet)
		{
			ShowError("CryptExportKey");
			break;
		}
		pPrivateKey = new BYTE[dwPrivateKeyLength];
		::RtlZeroMemory(pPrivateKey, dwPrivateKeyLength);
		bRet = ::CryptExportKey(hCryptKey, NULL, PRIVATEKEYBLOB, 0, pPrivateKey, &dwPrivateKeyLength);
		if (FALSE == bRet)
		{
			ShowError("CryptExportKey");
			break;
		}

		// 返回数据
		*ppPublicKey = pPublicKey;
		*pdwPublicKeyLength = dwPublicKeyLength;
		*ppPrivateKey = pPrivateKey;
		*pdwPrivateKeyLength = dwPrivateKeyLength;

	} while (FALSE);

	// 释放关闭
	if (hCryptKey)
	{
		::CryptDestroyKey(hCryptKey);
	}
	if (hCryptProv)
	{
		::CryptReleaseContext(hCryptProv, 0);
	}

	return bRet;
}


/*
Function:公钥加密数据
*/ 
BOOL RsaEncrypt(BYTE *pPublicKey, DWORD dwPublicKeyLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength)
{
	BOOL bRet = TRUE;
	HCRYPTPROV hCryptProv = NULL;
	HCRYPTKEY hCryptKey = NULL;

	do
	{
		// 获取CSP句柄
		bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0);
		if (FALSE == bRet)
		{
			ShowError("CryptAcquireContext");
			break;
		}

		// 导入公钥
		bRet = ::CryptImportKey(hCryptProv, pPublicKey, dwPublicKeyLength, NULL, 0, &hCryptKey);
		if (FALSE == bRet)
		{
			ShowError("CryptImportKey");
			break;
		}

		// 加密数据
		bRet = ::CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength);
		if (FALSE == bRet)
		{
			ShowError("CryptImportKey");
			break;
		}

	} while (FALSE);

	// 释放并关闭
	if (hCryptKey)
	{
		::CryptDestroyKey(hCryptKey);
	}
	if (hCryptProv)
	{
		::CryptReleaseContext(hCryptProv, 0);
	}

	return bRet;
}

/*
Function:私钥解密数据
*/ 
BOOL RsaDecrypt(BYTE *pPrivateKey, DWORD dwProvateKeyLength, BYTE *pData, DWORD &dwDataLength)
{
	BOOL bRet = TRUE;
	HCRYPTPROV hCryptProv = NULL;
	HCRYPTKEY hCryptKey = NULL;

	do
	{
		// 获取CSP句柄
		bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0);
		if (FALSE == bRet)
		{
			ShowError("CryptAcquireContext");
			break;
		}

		// 导入私钥
		bRet = ::CryptImportKey(hCryptProv, pPrivateKey, dwProvateKeyLength, NULL, 0, &hCryptKey);
		if (FALSE == bRet)
		{
			ShowError("CryptImportKey");
			break;
		}

		// 解密数据
		bRet = ::CryptDecrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength);
		if (FALSE == bRet)
		{
			ShowError("CryptDecrypt");
			break;
		}

	} while (FALSE);

	// 释放并关闭
	if (hCryptKey)
	{
		::CryptDestroyKey(hCryptKey);
	}
	if (hCryptProv)
	{
		::CryptReleaseContext(hCryptProv, 0);
	}

	return bRet;
}

测试结果

在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值