Skip to content

[修正]9.1 Widnows自带的加密库 #4

@BigGan

Description

@BigGan

补充一

读者反馈说在运行 “9.1 Widnows自带的加密库” 小节的程序时候,WIN32 API 函数 CryptAcquireContext 一直在报错:0x80090016(NTE_BAD_KEYSET),表示密钥容器不存在。如果计算机从未设置过密钥容器,则会报这个错误!

解决方法是:只需指定 CRYPT_NEWKEYSET 标志,为系统创建一个新的密钥容器即可。那么,原来的调用 CryptAcquireContext 示例代码:

bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0);
if (FALSE == bRet)
{
	ShowError("CryptAcquireContext");
	break;
}

修改如下所示,设置 CRYPT_NEWKEYSET 标志即可:

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

补充二

“臣妾做不到” 反馈说 “9.1 Widnows自带的加密库” 小节中 RSA 加解密部分的程序,用公钥加密的数据,居然使用公钥可以解密!!!这是不符合 RSA 算法的要求,因为 RSA 算法中,公钥加密的数据,只能私钥解密!

后来经过我验证,我发现下面的一个问题就是:

  • 如果是在同一个系统上创建了密钥对,然后使用公钥调用 CryptEncrypt 去加密数据,然后在同一系统上使用公钥或者私钥调用 CryptDecrypt 去解密,都能得到正确的解密数据!

  • 如果是在一个系统上创建了密钥对,然后使用公钥调用 CryptEncrypt 去加密数据,然后在其他机器上或者虚拟机里,也就是在不同的系统上,只能使用私钥调用 CryptDecrypt 去解密,而不能使用公钥去解密!

上面测试是使用同一个程序测试的(测试程序上传在附件中),所以,其实程序并没有错误!上述用公钥加密的数据,可以用公钥可以解密的情况,实质上系统仍是调用私钥去解密的!

按照我的理解是,当我们在创建公私密钥对的时候,系统会将这些密钥对存储在系统密钥容器中,加密解密的时候,会从中获取密钥!虽然,我们指定了用公钥解密数据,但是,当调用 CryptDecrypt 函数解密数据时,系统仍会默认从密钥容器中获取私钥进行解密的。

验证方法如下

我们可以在一台计算机上面生成公私密钥对,用公钥加密数据并生成密文文件;这时,系统上的密钥容器中已经存储有我们的公私密钥对了,所以为了验证上述猜想,我们把公私密钥对文件以及密文数据文件拷贝到其他机器或者虚拟中进行解密操作!这时会发现,我们在调用 CryptDecrypt 用公钥进行解密的时候,一直报错:0x8009000D(NTE_NO_KEY),表示密钥不存在;而当我们使用私钥进行解密的时候,可以成功解密!

所以,出现上述问题,这应该是微软的密钥管理体系造成的结果吧。当然,要解决上述问题,也可以通过编程方式来解决,也就是在调用 CryptAcquireContext 函数获取CSP的密钥容器的句柄的时候,我们通过设置参数来获取不同的密钥容器句柄或者创建新的密钥容器句柄等,来规避上述问题。

修改后的源码,重新上传在下面的附件中了!!!

CryptoApi_Rsa_Test_修改.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions