• [Windows编程笔记]RSA加解密


    《Windows黑客编程技术详解》学习笔记

    本文代码均来自https://www.jb51.net/books/755116.html

    写了3个函数

    生成公钥和私钥

    // 生成公钥和私钥
    BOOL GenerateKey(BYTE **ppPublicKey, DWORD *pdwPublicKeyLength, BYTE **ppPrivateKey, DWORD *pdwPrivateKeyLength)
    {
    	BOOL bRet = TRUE;
    	HCRYPTPROV hCryptProv = NULL;
    	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, CRYPT_VERIFYCONTEXT);
    		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;
    }
    

    具体流程如下

    首先,调用CryptAcquireContext函数来获取加密服务提供程序所需的CSP句柄。由于本程序实现的是使用RSA非对称加密算法加/解密数据,所以将提供程序类型设置为PROV_RSA_FULL,该类型支持RSA非对称加密算法。需要注意的是最后一个参数dwFlags需要设置为CRYPT_VERIFYCONTEXT,如果设置为0的话运行程序会报错,原因未知。

    // 获取CSP句柄
    bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
    

    然后,可以直接调用CryptGenKey函数随机生成AT_KEYEXCHANGE交换密钥对,并设置生成的密钥对类型为CRYPT_EXPORTABLE,它是可导出的,可以使用CryptExportKey函数导出密钥。

    // 随机生成公钥/私钥对
    BOOL CryptGenKey(
      HCRYPTPROV hProv,  // CryptAcquireContext创建的CSP句柄
      ALG_ID     Algid,  // AT_KEYEXCHANGE表示生成的是交换密钥对
      DWORD      dwFlags,  // CRYPT_EXPORTABLE表示密钥可导出,可以使用CryptExportKey函数导出密钥
      HCRYPTKEY  *phKey  // 返回的密钥对句柄
    );
    
    // 生成公私密钥对
    bRet = ::CryptGenKey(hCryptProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hCryptKey);
    

    经过上述两步操作后,RSA密钥对就已经成功生成。但是,为了方便后续使用公钥和私钥密钥对,需要通过CryptExportKey函数来导出密钥。由于密钥长度都不是固定的,所以在获取密钥之前,应该先确定密钥的长度。将输出缓冲区置为NULL(即可返回实际所需的缓冲区大小),这样可以申请足够的密钥缓冲区。若要导出公钥,则要将导出类型置为PUBLICKEYBLOB;若要导出私钥,则要将导出类型置为PRIVATEKEYBLOB。

    // 导出密钥对
    BOOL CryptExportKey(
      HCRYPTKEY hKey,  // 要导出的密钥句柄
      HCRYPTKEY hExpKey,  // NULL
      DWORD     dwBlobType,  // PUBLICKEYBLOB表示导出公钥,PRIVATEKEYBLOB表示导出私钥
      DWORD     dwFlags,  // 0
      BYTE      *pbData,  // 若要确定密钥长度,置为NULL,若要导出公私钥,置为接收公私钥的缓冲区
      DWORD     *pdwDataLen  // 在入口处包含pbData指向的缓冲区大小,返回时包含存储在pbData中的字节数
    );
    
    // 获取公钥密钥的长度和内容
    bRet = ::CryptExportKey(hCryptKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwPublicKeyLength);
    bRet = ::CryptExportKey(hCryptKey, NULL, PUBLICKEYBLOB, 0, pPublicKey, &dwPublicKeyLength);
    
    // 获取私钥密钥的长度和内容
    bRet = ::CryptExportKey(hCryptKey, NULL, PRIVATEKEYBLOB, 0, NULL, &dwPrivateKeyLength);
    bRet = ::CryptExportKey(hCryptKey, NULL, PRIVATEKEYBLOB, 0, pPrivateKey, &dwPrivateKeyLength);
    

    最后,调用CryptDestroyKey函数来删除密钥的句柄,调用CryptReleaseContext函数释放CSP句柄。

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

    公钥加密数据

    // 公钥加密数据
    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, CRYPT_VERIFYCONTEXT);
    		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;
    }
    

    具体实现流程如下

    首先,依然是通过调用CryptAcquireContext函数来获取加密程序所需的CSP句柄。将提供程序类型设置为PROV_RSA_FULL,最后一个参数dwFlags设置为CRYPT_VERIFYCONTEXT。

    // 获取CSP句柄
    bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
    

    其次,需要把公钥导入到CSP中以方便后续进行加密操作。调用CryptImportKey函数可将密钥导入CSP中,并获取导入的公钥密钥句柄。

    // 将密钥从密钥BLOB导入到加密程序中
    BOOL CryptImportKey(
      HCRYPTPROV hProv,  // CryptAcquireContext函数获取的CSP句柄
      const BYTE *pbData,  // CryptExportKey函数创建的密钥
      DWORD      dwDataLen,  // 密钥的长度
      HCRYPTKEY  hPubKey,  // NULL
      DWORD      dwFlags,  // 0
      HCRYPTKEY  *phKey  // 导入键的句柄
    );
    
    // 导入公钥
    bRet = ::CryptImportKey(hCryptProv, pPublicKey, dwPublicKeyLength, NULL, 0, &hCryptKey);
    

    最后,可以直接通过调用CryptEncrypt函数来对数据进行加密,由于数据的输入和密文的输出使用同一个缓冲区,所以一定要确保缓冲区足够大。因为RSA非对称加密算法也是一种分组加密算法,所以要指定Final参数为TRUE来指定加密数据是最后一组加密数据。

    // 用来加密数据
    BOOL CryptEncrypt(
      HCRYPTKEY  hKey,  // CryptImportKey函数导入的密钥
      HCRYPTHASH hHash,  // NULL
      BOOL       Final,  // TRUE
      DWORD      dwFlags,  // 0
      BYTE       *pbData,  // 要加密的明文,该缓冲区中的纯文本会被密文覆盖
      DWORD      *pdwDataLen,  // 入口处是要加密的明文长度,退出时是写入到pbData中的密文长度
      DWORD      dwBufLen  // 指定输入pbData缓冲区的总大小,注意:密文长度可能大于明文长度
    );
    
    // 加密数据
    bRet = ::CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength);
    

    调用CryptDestroyKey函数来删除密钥的句柄,调用CryptReleaseContext函数释放CSP句柄。

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

    私钥解密数据

    // 私钥解密数据
    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, CRYPT_VERIFYCONTEXT);
    		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;
    }
    

    使用RSA私钥解密密文的实现流程与使用公钥加密数据的流程很相似。不同点是CryptImportKey函数的第二和第三个参数要设置为私钥和私钥的长度

    // 导入私钥
    bRet = ::CryptImportKey(hCryptProv, pPrivateKey, dwProvateKeyLength, NULL, 0, &hCryptKey);
    

    解密时要用CryptDecrypt函数,Final参数也要设置为TRUE

    // 用来解密数据
    BOOL CryptDecrypt(
      HCRYPTKEY  hKey,  // 密钥句柄
      HCRYPTHASH hHash,  // NULL
      BOOL       Final,  // TRUE
      DWORD      dwFlags,  // 0
      BYTE       *pbData,  // 要解密的数据,解密完成后,明文会放回到该缓冲区
      DWORD      *pdwDataLen  // 调用此函数前,表示密文长度,返回时,表示明文长度
    );
    
    // 解密数据
    bRet = ::CryptDecrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength);
    

    完整代码

    // CryptoApi_Rsa_Test.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <Windows.h>
    
    
    void ShowError(char *pszText)
    {
    	char szErr[MAX_PATH] = { 0 };
    	::wsprintf(szErr, "%s Error[0x%x]
    ", pszText, ::GetLastError());
    #ifdef _DEBUG
    	::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR);
    #endif
    }
    
    
    // 生成公钥和私钥
    BOOL GenerateKey(BYTE **ppPublicKey, DWORD *pdwPublicKeyLength, BYTE **ppPrivateKey, DWORD *pdwPrivateKeyLength)
    {
    	BOOL bRet = TRUE;
    	HCRYPTPROV hCryptProv = NULL;
    	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;
    }
    
    
    // 公钥加密数据
    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;
    }
    
    
    // 私钥解密数据
    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;
    }
    
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	BYTE *pPublicKey = NULL;
    	DWORD dwPublicKeyLength = 0;
    	BYTE *pPrivateKey = NULL;
    	DWORD dwPrivateKeyLength = 0;
    	BYTE *pData = NULL;
    	DWORD dwDataLength = 0;
    	DWORD dwBufferLength = 4096;
    	DWORD i = 0;
    
    	pData = new BYTE[dwBufferLength];
    	if (NULL == pData)
    	{
    		return 1;
    	}
    	::RtlZeroMemory(pData, dwBufferLength);
    	::lstrcpy((char *)pData, "What is your name? DemonGan");
    	dwDataLength = 1 + ::lstrlen((char *)pData);
    	printf("Text[%d]
    ", dwDataLength);
    	for (i = 0; i < dwDataLength; i++)
    	{
    		printf("%x", pData[i]);
    	}
    	printf("
    
    ");
    
    	// 生成公钥和私钥
    	GenerateKey(&pPublicKey, &dwPublicKeyLength, &pPrivateKey, &dwPrivateKeyLength);
    	printf("Public Key[%d]
    ", dwPublicKeyLength);
    	for (i = 0; i < dwPublicKeyLength; i++)
    	{
    		printf("%.2x", pPublicKey[i]);
    	}
    	printf("
    ");
    	printf("Private Key[%d]
    ", dwPrivateKeyLength);
    	for (i = 0; i < dwPrivateKeyLength; i++)
    	{
    		printf("%.2x", pPrivateKey[i]);
    	}
    	printf("
    
    ");
    
    	// 公钥加密
    	RsaEncrypt(pPublicKey, dwPublicKeyLength, pData, dwDataLength, dwBufferLength);
    	printf("RSA Encrypt[%d]
    ", dwDataLength);
    	for (i = 0; i < dwDataLength; i++)
    	{
    		printf("%x", pData[i]);
    	}
    	printf("
    
    ");
    
    	// 私钥解密
    	RsaDecrypt(pPrivateKey, dwPrivateKeyLength, pData, dwDataLength);
    	printf("RSA Decrypt[%d]
    ", dwDataLength);
    	for (i = 0; i < dwDataLength; i++)
    	{
    		printf("%x", pData[i]);
    	}
    	printf("
    
    ");
    
    	// 释放
    	if (pData)
    	{
    		delete[]pData;
    		pData = NULL;
    	}
    	if (pPrivateKey)
    	{
    		delete[]pPrivateKey;
    		pPrivateKey = NULL;
    	}
    	if (pPublicKey)
    	{
    		delete[]pPublicKey;
    		pPublicKey = NULL;
    	}
    
    	system("pause");
    	return 0;
    }
    

    小结

    生成公钥和私钥:调用CryptAcquireContext函数获取CSP句柄,调用CryptGenKey函数随机生成AT_KEYEXCHANGE交换密钥对,通过CryptExportKey函数导出公钥和私钥。由于密钥长度并不是固定的,所以需要先确定密钥长度的大小,以申请足够的密钥存放缓冲区。最后,调用CryptDestroyKey函数来删除密钥的句柄,调用CryptReleaseContext函数释放CSP句柄。

    公钥加密数据:通过调用CryptAcquireContext函数来获取加密程序所需的CSP句柄,调用CryptImportKey函数将密钥导入CSP中,并获取导入的公钥密钥句柄,调用CryptEncrypt函数来对数据进行加密,最后,调用CryptDestroyKey函数来删除密钥的句柄,调用CryptReleaseContext函数释放CSP句柄。

    私钥解密数据:和公钥加密数据流程基本相同,除了CryptImportKey函数的第二和第三个参数要设置为私钥和私钥的长度,以及解密时要用CryptDecrypt函数。

  • 相关阅读:
    LinuxMySQL主从复制原理图
    flask中间件请求流程
    flask02
    flask01
    笔记本电脑播放声音前后会有吱吱声
    筛法欧拉函数
    矩阵快速幂
    最短路 P1144 最短路计数【Dijkstra堆优化/SPFA】
    SPFA算法以及负环判断【模板】
    最短路 P1629 邮递员送信 【反向图求最短路】
  • 原文地址:https://www.cnblogs.com/rnss/p/15339000.html
Copyright © 2020-2023  润新知