我正在使用CryptEncryptMessage
生成一个PKCS#7
封装的消息。我使用szOID_NIST_AES256_CBC
作为加密算法。
生成的消息似乎是有效的,但它是密钥传输算法的RSAES-OAEP
,该算法在野外的支持有限(雷鸟、OpenSSL SMIME模块和许多其他模块不支持它)。
我希望CAPI恢复到较旧的RSAencryption
来进行密钥传输。
有没有可能做到这一点,如果有办法而不是使用CryptEncryptMessage
,我可以恢复到低级消息传递函数,但我找不到一种即使使用低级函数也无法做到的方法。
代码:
CRYPT_ENCRYPT_MESSAGE_PARA EncryptMessageParams;
EncryptMessageParams.cbSize = sizeof(CMSG_ENVELOPED_ENCODE_INFO);
EncryptMessageParams.dwMsgEncodingType = PKCS_7_ASN_ENCODING;
EncryptMessageParams.ContentEncryptionAlgorithm.pszObjId = szOID_NIST_AES256_CBC;
EncryptMessageParams.ContentEncryptionAlgorithm.Parameters.cbData = 0;
EncryptMessageParams.ContentEncryptionAlgorithm.Parameters.pbData = 0;
EncryptMessageParams.hCryptProv = NULL;
EncryptMessageParams.pvEncryptionAuxInfo = NULL;
EncryptMessageParams.dwFlags = 0;
EncryptMessageParams.dwInnerContentType = 0;
BYTE pbEncryptedBlob[640000];
DWORD pcbEncryptedBlob = 640000;
BOOL retval = CryptEncryptMessage(&EncryptMessageParams, cRecipientCert, pRecipCertContextArray, pbMsgText, dwMsgTextSize, pbEncryptedBlob, &pcbEncryptedBlob);
发布于 2014-10-28 07:23:41
Key Transport算法有点难以处理,而且可能达不到它的目的(我看到您注意到您希望CAPI支持RSAencryption
;相信我,我也希望如此)。看起来你已经发现了大部分问题--生成的消息是有效的,但是你的方法使得你必须使用 CryptEncryptMessage
**,,这在很长一段时间内都不能很好地工作。**
第1步-检查代码
CRYPT_ENCRYPT_MESSAGE_PARA EncryptMessageParams;
EncryptMessageParams.cbSize = sizeof(CMSG_ENVELOPED_ENCODE_INFO);
EncryptMessageParams.dwMsgEncodingType = PKCS_7_ASN_ENCODING;
EncryptMessageParams.ContentEncryptionAlgorithm.pszObjId = szOID_NIST_AES256_CBC;
EncryptMessageParams.ContentEncryptionAlgorithm.Parameters.cbData = 0;
EncryptMessageParams.ContentEncryptionAlgorithm.Parameters.pbData = 0;
EncryptMessageParams.hCryptProv = NULL;
EncryptMessageParams.pvEncryptionAuxInfo = NULL;
EncryptMessageParams.dwFlags = 0;
EncryptMessageParams.dwInnerContentType = 0;
BYTE pbEncryptedBlob[640000];
DWORD pcbEncryptedBlob = 640000;
BOOL retval = CryptEncryptMessage(&EncryptMessageParams, cRecipientCert, pRecipCertContextArray, pbMsgText, dwMsgTextSize, pbEncryptedBlob, &pcbEncryptedBlob);
非常基础,不是吗?虽然效率很高,但它并没有真正解决问题。如果你看这个:
EncryptMessageParams.dwFlags = 0;
EncryptMessageParams.dwInnerContentType = 0;
您将看到它是预定义的,但仅在retval
的定义中使用。然而,我可以肯定地认为这是一种微优化,如果我们要重写代码,这并不是真正有用的。然而,我已经概述了在不完全重做代码的情况下集成这一点的基本步骤(因此您可以继续使用相同的参数):
第2步-编辑参数
正如@owlstead在他的评论中提到的,Crypto API对用户不是很友好。然而,您已经用有限的资源完成了一项伟大的工作。您需要添加的是一个Cryptographic Enumeration Provider来帮助缩小键的范围。确保您拥有Microsoft Base Cryptographic Provider 1.0版或Microsoft Enhanced Cryptographic Provider 1.0版,以便有效地使用它们。否则,您需要像这样添加函数:
DWORD cbName;
DWORD dwType;
DWORD dwIndex;
CHAR *pszName = NULL;
(regular crypt calls here)
这主要用于防止NTE_BAD_FLAGS
错误,尽管从技术上讲,您可以通过更低级的声明来避免这一点。如果您愿意,您还可以创建一个全新的散列(尽管只有在上面的实现不能扩展到必要的时间/速度因素时,才需要这样做):
DWORD dwBufferLen = strlen((char *)pbBuffer)+1*(0+5);
HCRYPTHASH hHash;
HCRYPTKEY hKey;
HCRYPTKEY hPubKey;
BYTE *pbKeyBlob;
BYTE *pbSignature;
DWORD dwSigLen;
DWORD dwBlobLen;
(use hash as normal w/ crypt calls and the pbKeyBlobs/Signatures)
在继续之前,请确保此代码段有效。您可以像这样轻松地做到这一点:
if(CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
printf("CSP context acquired.\n");
}
如果您正在记录或发布,可能希望添加一个void MyHandleError(char *s)
来捕获错误,以便编辑但失败的人可以快速捕获它。
顺便说一句,当你第一次运行它的时候,你必须创建一个新的集合,因为它没有默认值。下面是一个可以弹出到if
中的漂亮的一行代码:
CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)
请记住,同步服务器资源将不会像我在第一步中建议的那样高效。这是我将在下面解释的:
第3步-重新编码并重新启动
作为一名程序员,重新编码可能看起来像是浪费时间,但从长远来看,它肯定会对您有所帮助。请记住,在编码/同步时,您仍然需要在自定义参数中进行编码;我不会像婴儿一样手动给您提供所有代码。它应该足以向您展示基本的轮廓。
我绝对假设您正在尝试处理特定CSP中的当前用户的密钥容器;否则,我看不出这有什么用。如果没有,您可以进行一些基本的编辑以满足您的需要。
请记住,我们将使用CryptReleaseContext
绕过CryptEncryptMessage
,这将直接释放由CryptAcquireContext
函数获取的句柄。微软在CAC上的标准如下:
BOOL WINAPI CryptAcquireContext(
_Out_ HCRYPTPROV *phProv,
_In_ LPCTSTR pszContainer,
_In_ LPCTSTR pszProvider,
_In_ DWORD dwProvType,
_In_ DWORD dwFlags
);
请注意,如果您使用的是用户界面,Microsoft会责备您:
如果CSP必须显示UI才能运行,则调用失败,并将NTE_SILENT_CONTEXT错误代码设置为最后一个错误,则为
。另外,如果使用CRYPT_USER_PROTECTED标志对CryptGenKey进行调用,而上下文是用CRYPT_SILENT标志获取的,则呼叫失败,并且CSP设置NTE_SILENT_CONTEXT。
这主要是服务端代码,当有多个连接,特别是延时较高的连接时,ERROR_BUSY
肯定会显示给新用户。超过300ms会导致调用NTE_BAD_KEYSET_PARAM
或类似的错误,因为超时甚至没有接收到适当的错误。(传输问题,有人和我一起吗?)
除非你关心多个动态链接库(由于NTE_PROVIDER_DLL_FAIL
错误这是不支持的),抓取加密服务客户端的基本设置如下(直接从微软的例子中复制):
if (GetLastError() == NTE_BAD_KEYSET)
{
if(CryptAcquireContext(
&hCryptProv,
UserName,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET))
{
printf("A new key container has been created.\n");
}
else
{
printf("Could not create a new key container.\n");
exit(1);
}
}
else
{
printf("A cryptographic service handle could not be "
"acquired.\n");
exit(1);
}
无论这看起来多么简单,您肯定不希望将其传递给密钥交换算法(或其他任何处理此问题的算法)。除非您使用对称会话密钥(Diffie-Hellman/KEA),否则可以使用交换密钥对会话密钥进行加密,以便可以安全地存储它们并与其他用户进行交换。
一个叫John Howard的人写了一个很好的Hyper-V远程管理配置实用程序(HVRemote),它是这里讨论的技术的一个大型汇编。除了使用基本的加密和密钥对之外,它们还可用于允许ANONYMOUS LOGON
远程DCOM
访问(具体地说,cscript hvremote.wsf
)。你可以在他的博客上看到他最新的加密中的许多函数和技术(你必须缩小查询范围):
http://blogs.technet.com/b/jhoward/
如果你需要更多的基础知识的帮助,只需留下评论或请求私人聊天。
结论
虽然一旦您了解了哈希的基本服务器端方法和客户端如何获取“加密”,它就非常简单,但您会问为什么要在传输过程中尝试加密。然而,如果没有加密客户端,加密肯定是传输已经散列的内容的唯一安全方式。
尽管您可能会争辩说,可以对数据包进行解密并对其进行散列,但请考虑到,传入和传出都必须以重新散列客户端所需的正确时间和顺序进行处理和存储。
https://stackoverflow.com/questions/13090176
复制相似问题