Cryptography API: Next Generation(CNG)使用梳理——非对称加密算法应用(二)非对称密钥导入并保存及公钥加密私钥解密

CNG中NCrypt创建的密钥对会默认会保存在本地,而BCrypt创建的密钥对并不会保存

NCrypt创建的密钥默认不能导出私钥

此篇主要聊一下,

一、如何保存BCrypt创建并导出的私钥(公钥能导入CNG但不能保存)

二、RSA公钥加密私钥解密(BCRYPT_PAD_PKCS1填充

三、导出(备份)NCrypt创建的密钥(私钥)

另附:RSA公钥加密私钥解密(BCRYPT_PAD_OAEP填充)代码

理论上基于BCrypt导出密钥的内存BLOB结构,任何其它加密导出的密钥都是可以被导入的,后面我会单独写一篇导入OpenSSL3生成的密钥(RSA和ECC)与CNG内存BLOB结构之间做转换

关于导入持久化密钥,MSDN中导入持久化密钥的流程为:

    使用 NCryptCreatePersistedKey 函数创建持久密钥。
    使用 NCryptSetProperty 函数设置键对象上的任何所需属性。
    将导入密钥 BLOB 设置为密钥上的属性,将 BLOB 类型设置为属性名称。
    使用 NCryptFinalizeKey 函数完成持久密钥导入。

此流程或许我解读有误,实际操作后没有成功,一下为我成功的流程

以RSA加密(公钥加密,私钥解密)为例,基本流程

1、生成密钥对(BCrypt)

2、加密数据

3、导出私钥

4、导入私钥(NCrypt),非私钥NCrypt不会保存

5、解密

相关函数和结构

//导入密钥函数
SECURITY_STATUS NCryptImportKey(
  [in]           NCRYPT_PROV_HANDLE hProvider,
  [in, optional] NCRYPT_KEY_HANDLE  hImportKey,
  [in]           LPCWSTR            pszBlobType,
  [in, optional] NCryptBufferDesc   *pParameterList,
  [out]          NCRYPT_KEY_HANDLE  *phKey,
  [in]           PBYTE              pbData,
  [in]           DWORD              cbData,
  [in]           DWORD              dwFlags
);

//NCryptBufferDesc结构,NCryptBufferDesc结构为BCryptBufferDesc结构的别名
typedef struct _BCryptBufferDesc {
  ULONG         ulVersion;
  ULONG         cBuffers;
  PBCryptBuffer pBuffers;
} BCryptBufferDesc, *PBCryptBufferDesc;

//BCryptBuffer结构,部分MSDN中使用NCryptBuffer来描述,实际NCryptBuffer为此结构的别名
typedef struct _BCryptBuffer {
  ULONG cbBuffer;
  ULONG BufferType;
  PVOID pvBuffer;
} BCryptBuffer, *PBCryptBuffer;

NCryptBufferDesc结构很简单

ulVersion 结构的版本
    BCRYPTBUFFER_VERSION     默认版本号。

cBuffers pBuffers数组中的元素数。

pBuffers 包含缓冲区的 BCryptBuffer 结构的数组的地址。 cBuffers 包含此数组中的元素数。

而NCryptBuffer(BCryptBuffer)就比较复杂了,根据不同应用场景,具体参数不同,此处主要填写BufferType、pvBuffer、cbBuffer,分别对应NCryptBuffer(BCryptBuffer)内容的类型、密钥名字和密钥名字长度(包含字符串最后的\0结束符)

BufferType = NCRYPTBUFFER_PKCS_KEY_NAME;//设置密钥名
pvBuffer = (PVOID)pszKeyName;
cbBuffer = (::lstrlenW(pszKeyName) + 1) * sizeof(wchar_t);//需要包含\0

实际代码如下:(只包含基本流程的代码,并未对每个函数的返回值进行判断)

#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>
#include <ncrypt.h>

#pragma comment(lib,"Bcrypt.lib")
#pragma comment(lib,"Ncrypt.lib")

#define NT_SUCCESS(Status)          (((NTSTATUS)(Status)) >= 0)

#define STATUS_UNSUCCESSFUL         ((NTSTATUS)0xC0000001L)

const BYTE rgbPlaintext[] =
{
	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};

int main()
{
	BCRYPT_ALG_HANDLE       hRSAAlg         = NULL;
	NCRYPT_PROV_HANDLE      hProv           = NULL;
	BCRYPT_KEY_HANDLE       hKey            = NULL;
	NCRYPT_KEY_HANDLE       hTmpKey         = NULL;

	NTSTATUS                status          = STATUS_UNSUCCESSFUL;
	DWORD                   cbData          = 0,
						    cbBlob          = 0,
						    cbPlainText     = 0,
						    cbCipherText    = 0,
						    cbKeyLength     = 2048;
	PBYTE                   pbPlainText     = NULL,
						    pbBlob          = NULL,
						    pbCipherText    = NULL;

	//加载并初始化BCrypt提供程序
	BCryptOpenAlgorithmProvider(&hRSAAlg, BCRYPT_RSA_ALGORITHM, NULL, 0);

	//创建密钥对
	BCryptGenerateKeyPair(hRSAAlg, &hKey, cbKeyLength, 0);

	//完成
	BCryptFinalizeKeyPair(hKey, 0);

	// 复制明文
	cbPlainText = sizeof(rgbPlaintext);
	pbPlainText = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbPlainText);
	memcpy(pbPlainText, rgbPlaintext, sizeof(rgbPlaintext));

	// 获取密文所需空间
	BCryptEncrypt(hKey, pbPlainText, cbPlainText, NULL, NULL, 0, NULL, 0, &cbCipherText, BCRYPT_PAD_PKCS1);

	// 分配密文所需空间
	pbCipherText = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbCipherText);

	// 加密数据(填充方式为BCRYPT_PAD_PKCS1)
	BCryptEncrypt(hKey, pbPlainText, cbPlainText, NULL, NULL, 0, pbCipherText, cbCipherText, &cbData, BCRYPT_PAD_PKCS1);

	// 获取导出密钥所需大小(此处导出私钥)
	BCryptExportKey(hKey, NULL, BCRYPT_RSAPRIVATE_BLOB, NULL, 0, &cbBlob, 0);

	// 为导出密钥分配空间
	pbBlob = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbBlob);

	// 导出密钥
	BCryptExportKey(hKey, NULL, BCRYPT_RSAPRIVATE_BLOB, pbBlob, cbBlob, &cbBlob, 0);

	// 销毁当前密钥
	if(hKey) {
		BCryptDestroyKey(hKey);
	}

	if(hRSAAlg) {
		BCryptCloseAlgorithmProvider(hRSAAlg, 0);
	}

	//加载并初始化 CNG 密钥存储提供程序
	NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0);

	LPCWSTR pszKeyName = L"my rsa key";
	NCryptBuffer cb[1];
	cb[0].BufferType = NCRYPTBUFFER_PKCS_KEY_NAME;//设置密钥名
	cb[0].pvBuffer = (PVOID)pszKeyName;
	cb[0].cbBuffer = (::lstrlenW(pszKeyName) + 1) * sizeof(wchar_t);//需要包含\0
	NCryptBufferDesc desc;
	desc.ulVersion = NCRYPTBUFFER_VERSION;//版本,NCRYPTBUFFER_VERSION = 0
	desc.pBuffers = cb;
	desc.cBuffers = 1;

	//导入密钥,默认自动保存,如果需要设置属性,可以这是最后的标志为NCRYPT_DO_NOT_FINALIZE_FLAG
	NCryptImportKey(hProv, NULL, BCRYPT_RSAPRIVATE_BLOB, &desc, &hTmpKey, pbBlob, cbBlob, 0);

	//如果设置了NCRYPT_DO_NOT_FINALIZE_FLAG,则需要手动调用NCryptFinalizeKey(此处不需要)
	//NCryptFinalizeKey(hTmpKey, 0);

	// 清理原先复制的明文空间
	HeapFree(GetProcessHeap(), 0, pbPlainText);
	pbPlainText = NULL;

	// 获取解密后的明文大小,需要设置与加密时一样的填充方式
	NCryptDecrypt(hTmpKey, pbCipherText, cbCipherText, NULL, NULL, 0, &cbPlainText, BCRYPT_PAD_PKCS1);

	// 分配明文所需空间
	pbPlainText = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbPlainText);

	// 解密
	NCryptDecrypt(hTmpKey, pbCipherText, cbCipherText, NULL, pbPlainText, cbPlainText, &cbData, BCRYPT_PAD_PKCS1);

	//验证指定的签名是否与指定的哈希匹配
	if (0 != memcmp(pbPlainText, (PBYTE)rgbPlaintext, sizeof(rgbPlaintext))) {
		wprintf(L"Error Expected decrypted text comparison failed.\n");
	}
	else {
		wprintf(L"Success!\n");
	}

	if(pbBlob) {
		HeapFree(GetProcessHeap(), 0, pbBlob);
	}

	if(pbCipherText) {
		HeapFree(GetProcessHeap(), 0, pbCipherText);
	}

	if(pbPlainText) {
		HeapFree(GetProcessHeap(), 0, pbPlainText);
	}

	if (hTmpKey) {
		NCryptDeleteKey(hTmpKey, 0);
	}

	if (hProv) {
		NCryptFreeObject(hProv);
	}

	return 0;
}

最后关于NCrypt生成的密钥对导出,默认情况下NCrypt不允许导出私钥,如果需要导出则需要对NCRYPT_EXPORT_POLICY_PROPERTY设置对应属性,比如NCRYPT_ALLOW_ARCHIVING_FLAG | NCRYPT_ALLOW_PLAINTEXT_ARCHIVING_FLAG,这两个属性只能应用于原始密钥句柄。 关闭密钥句柄后,无法再出于存档目的导出密钥

NCryptCreatePersistedKey(hNCryptProv, &hKey, CryptType::get_alg_id(), pszKeyName, 0, 0);
DWORD dwPolicy = NCRYPT_ALLOW_ARCHIVING_FLAG | NCRYPT_ALLOW_PLAINTEXT_ARCHIVING_FLAG;
::NCryptSetProperty(hKey, NCRYPT_EXPORT_POLICY_PROPERTY, (PBYTE)&dwPolicy, sizeof(DWORD), 0);
NCryptFinalizeKey(hKey, 0);

NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG外加NCRYPT_PERSIST_FLAG标志的话,就可以将属性保存到密钥文件中去了,实现永久允许导出的目的

NCryptCreatePersistedKey(hNCryptProv, &hKey, CryptType::get_alg_id(), pszKeyName, 0, 0);
DWORD dwPolicy = NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
::NCryptSetProperty(hKey, NCRYPT_EXPORT_POLICY_PROPERTY, (PBYTE)&dwPolicy, sizeof(DWORD), NCRYPT_PERSIST_FLAG);
NCryptFinalizeKey(hKey, 0);

另附,使用BCRYPT_OAEP_PADDING作为加密填充的代码:

#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>
#include <ncrypt.h>

#pragma comment(lib,"Bcrypt.lib")
#pragma comment(lib,"Ncrypt.lib")

#define NT_SUCCESS(Status)          (((NTSTATUS)(Status)) >= 0)

#define STATUS_UNSUCCESSFUL         ((NTSTATUS)0xC0000001L)

const BYTE rgbPlaintext[] =
{
	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};

int main()
{
	BCRYPT_ALG_HANDLE       hRSAAlg        = NULL;
	NCRYPT_PROV_HANDLE      hProv           = NULL;
	BCRYPT_KEY_HANDLE       hKey            = NULL;
	NCRYPT_KEY_HANDLE       hTmpKey         = NULL;

	NTSTATUS                status          = STATUS_UNSUCCESSFUL;
	DWORD                   cbData          = 0,
						    cbBlob          = 0,
						    cbPlainText          = 0,
						    cbCipherText          = 0,
						    cbKeyLength     = 2048;
	PBYTE                   pbPlainText          = NULL,
						    pbBlob          = NULL,
						    pbCipherText     = NULL;

	//加载并初始化BCrypt提供程序
	BCryptOpenAlgorithmProvider(&hRSAAlg, BCRYPT_RSA_ALGORITHM, NULL, 0);

	//创建密钥对
	BCryptGenerateKeyPair(hRSAAlg, &hKey, cbKeyLength, 0);

	//完成
	BCryptFinalizeKeyPair(hKey, 0);

	// 复制明文
	cbPlainText = sizeof(rgbPlaintext);
	pbPlainText = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbPlainText);
	memcpy(pbPlainText, rgbPlaintext, sizeof(rgbPlaintext));

    //使用BCRYPT_OAEP_PADDING填充
	BYTE buffer[32] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
					0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
					0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
					0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
	BCRYPT_OAEP_PADDING_INFO padding;
	padding.pszAlgId = BCRYPT_SHA256_ALGORITHM;
	padding.pbLabel = buffer;
	padding.cbLabel = sizeof(buffer);

	// 获取密文所需空间
	BCryptEncrypt(hKey, pbPlainText, cbPlainText, &padding, NULL, 0, NULL, 0, &cbCipherText, BCRYPT_PAD_OAEP);

	// 分配密文所需空间
	pbCipherText = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbCipherText);

	// 加密数据(填充方式为BCRYPT_PAD_PKCS1)
	BCryptEncrypt(hKey, pbPlainText, cbPlainText, &padding, NULL, 0, pbCipherText, cbCipherText, &cbData, BCRYPT_PAD_OAEP);

	// 获取导出密钥所需大小
	BCryptExportKey(hKey, NULL, BCRYPT_RSAPRIVATE_BLOB, NULL, 0, &cbBlob, 0);

	// 为导出密钥分配空间
	pbBlob = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbBlob);

	// 导出密钥
	BCryptExportKey(hKey, NULL, BCRYPT_RSAPRIVATE_BLOB, pbBlob, cbBlob, &cbBlob, 0);

	// 销毁当前密钥
	if(hKey) {
		BCryptDestroyKey(hKey);
	}

	if(hRSAAlg) {
		BCryptCloseAlgorithmProvider(hRSAAlg, 0);
	}

	//加载并初始化 CNG 密钥存储提供程序
	NCryptOpenStorageProvider(&hProv, MS_KEY_STORAGE_PROVIDER, 0);

	LPCWSTR pszKeyName = L"my rsa key";
	NCryptBuffer cb[1];
	cb[0].BufferType = NCRYPTBUFFER_PKCS_KEY_NAME;//设置密钥名
	cb[0].pvBuffer = (PVOID)pszKeyName;
	cb[0].cbBuffer = (::lstrlenW(pszKeyName) + 1) * sizeof(wchar_t);//需要包含\0
	NCryptBufferDesc desc;
	desc.ulVersion = NCRYPTBUFFER_VERSION;//版本,NCRYPTBUFFER_VERSION = 0
	desc.pBuffers = cb;
	desc.cBuffers = 1;

	//导入密钥,默认自动保存,如果需要设置属性,可以这是最后的标志为NCRYPT_DO_NOT_FINALIZE_FLAG
	NCryptImportKey(hProv, NULL, BCRYPT_RSAPRIVATE_BLOB, &desc, &hTmpKey, pbBlob, cbBlob, 0);

	//如果设置了NCRYPT_DO_NOT_FINALIZE_FLAG,则需要手动调用NCryptFinalizeKey(此处不需要)
	//NCryptFinalizeKey(hTmpKey, 0);

	// 清理原先复制的明文空间
	HeapFree(GetProcessHeap(), 0, pbPlainText);
	pbPlainText = NULL;

	// 获取解密后的明文大小,需要设置与加密时一样的填充方式
	NCryptDecrypt(hTmpKey, pbCipherText, cbCipherText, &padding, NULL, 0, &cbPlainText, NCRYPT_PAD_OAEP_FLAG);

	// 分配明文所需空间
	pbPlainText = (PBYTE)HeapAlloc (GetProcessHeap (), 0, cbPlainText);

	// 解密
	NCryptDecrypt(hTmpKey, pbCipherText, cbCipherText, &padding, pbPlainText, cbPlainText, &cbData, NCRYPT_PAD_OAEP_FLAG);

	//验证指定的签名是否与指定的哈希匹配
	if (0 != memcmp(pbPlainText, (PBYTE)rgbPlaintext, sizeof(rgbPlaintext))) {
		wprintf(L"Error Expected decrypted text comparison failed.\n");
	}
	else {
		wprintf(L"Success!\n");
	}

	if(pbBlob) {
		HeapFree(GetProcessHeap(), 0, pbBlob);
	}

	if(pbCipherText) {
		HeapFree(GetProcessHeap(), 0, pbCipherText);
	}

	if(pbPlainText) {
		HeapFree(GetProcessHeap(), 0, pbPlainText);
	}

	if (hTmpKey) {
		NCryptDeleteKey(hTmpKey, 0);
	}

	if (hProv) {
		NCryptFreeObject(hProv);
	}

	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对称加密算法非对称加密算法是两种常用的加密算法,它们各自的算法有以下几种: 对称加密算法: 1. DES(Data Encryption Standard):数据加密标准,是一种较为古老的对称加密算法。 2. 3DES(Triple DES):三重DES加密算法,是一种对DES算法的加强版,提高了安全性。 3. AES(Advanced Encryption Standard):高级加密标准,是一种目前应用最为广泛的对称加密算法,具有高安全性和高效率的特点。 4. Blowfish:一种高效的对称加密算法,被广泛应用于信息安全领域。 非对称加密算法: 1. RSA:一种非对称加密算法,广泛应用于数字签名、身份验证等方面。 2. DSA(Digital Signature Algorithm):数字签名算法,是一种非对称加密算法,被广泛应用于数字签名领域。 3. ECC(Elliptic Curve Cryptography):椭圆曲线加密算法,是一种比RSA更加高效的非对称加密算法。 对称加密算法的优势在于加密解密速度快,加密效率高。但由于它采用的是同一个密钥进行加密解密,因此密钥安全性较差,容易被破解。 非对称加密算法的优势在于密钥安全性较高,可以采用公钥加密私钥解密的方式来保障数据安全。但由于加密解密过程较为复杂,加密效率比对称加密算法低。 综上所述,对称加密算法非对称加密算法各有优劣,可以根据具体的应用场景来选择合适的加密算法。在实际应用,通常会将对称加密算法非对称加密算法结合起来使用,以充分发挥它们各自的优势。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值