我正在尝试将数字签名的生成从 Java 移植到 C++。
除了从 C++ 创建的数字签名似乎包含比 Java 创建的信息更多的信息外,我尝试的大多数事情都有效。我不知道这些字节是什么,也不知道如何防止它们被写入。
这是两个代码片段。首先是现有的 Java 代码:
KeyStore keyStore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
keyStore.load(null);
PrivateKey key = (PrivateKey) keyStore.getKey(alias, null);
Signature sign = Signature.getInstance("SHA1withRSA", keyStore.getProvider());
sign.initSign(key);
String msg = "message";
sign.update(msg.getBytes("UTF-8"));
byte[] ba = sign.sign();
String signature = new String(Base64.encodeBase64(ba), "UTF-8");
这是我在 C++ 中发现的上述问题。代码是按照 MSDN 示例 Signing a Message and Verifying a Message Signature 编写的:
HCERTSTORE hCertStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
L"MY"));
PCCERT_CONTEXT pCertContext = CertFindCertificateInStore(
hCertStore,
MY_ENCODING_TYPE,
0,
CERT_FIND_SUBJECT_STR,
alias,
NULL);
CRYPT_SIGN_MESSAGE_PARA SigParams;
SigParams.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
SigParams.dwMsgEncodingType = MY_ENCODING_TYPE;
SigParams.pSigningCert = pCertContext;
SigParams.HashAlgorithm.pszObjId = szOID_RSA_SHA1RSA;
SigParams.HashAlgorithm.Parameters.cbData = NULL;
SigParams.dwFlags = CRYPT_MESSAGE_KEYID_SIGNER_FLAG;
SigParams.dwInnerContentType = 0;
SigParams.pvHashAuxInfo = nullptr;
SigParams.rgAuthAttr = NULL;
SigParams.cMsgCert = 0;
SigParams.cAuthAttr = 0;
SigParams.cMsgCrl = 0;
SigParams.cUnauthAttr = 0;
std::string msg("message");
const BYTE* MessageArray[] = {(BYTE*)msg.c_str()};
DWORD MessageSizeArray[] = {msg.size()};
// First, get the size of the signed BLOB.
DWORD cbSignedMessageBlob = 0;
CryptSignMessage(
&SigParams,
TRUE,
1,
MessageArray,
MessageSizeArray,
NULL,
&cbSignedMessageBlob);
// Allocate memory for the signed BLOB.
BYTE* pbSignedMessageBlob = (BYTE*)malloc(cbSignedMessageBlob);
CryptSignMessage(
&SigParams,
TRUE,
1,
MessageArray,
MessageSizeArray,
pbSignedMessageBlob,
&cbSignedMessageBlob);
const std::string signedMessageBase64 =
Base64::Encode(pbSignedMessageBlob, cbSignedMessageBlob);
从 Java 代码创建的签名:
messageyoPTn33Z/c1P05BoY6COW+VrbG5MTsog2YhNrXkbVy3PfXQtERQ4j9BXKnPAidYmMPaOxyT/Lh+D3ZyiXmtBwgV4oMMIp4PnMj5MO77ZCGc86NzYTbyk0FqLJFiMAR/+2h9fEsVd3NQlci3gxFHSO2tlDDppQBePjl39nXPlkrfUqRxtr7cGDLV6mX7iI5nuKXLKgbywmkVB4NT15vbTqLQaCMMJrRpNp5jg3NG17u1LthfeOwrkNk4SE6fxfoyZOU6mQ+ACbYCIn3lYCwVtHLDvoMDhmjWvgyBQwfSNr5SlPx5qiPSZrPg7AO2svqmNeEibvW1YPpfilNg83MWeOg==
从 C++ 代码创建的签名:
messageMIIBdwYJKoZIhvcNAQcCoIIBaDCCAWQCAQMxDzANBgkqhkiG9w0BAQUFADALBgkqhkiG9w0BBwExggE/MIIBOwIBA4AUn87cY1dT9wKLDn9zdZu0Rrm0j54wDQYJKoZIhvcNAQEFBQAwDQYJKoZIhvcNAQEBBQAEggEAyoPTn33Z/c1P05BoY6COW+VrbG5MTsog2YhNrXkbVy3PfXQtERQ4j9BXKnPAidYmMPaOxyT/Lh+D3ZyiXmtBwgV4oMMIp4PnMj5MO77ZCGc86NzYTbyk0FqLJFiMAR/+2h9fEsVd3NQlci3gxFHSO2tlDDppQBePjl39nXPlkrfUqRxtr7cGDLV6mX7iI5nuKXLKgbywmkVB4NT15vbTqLQaCMMJrRpNp5jg3NG17u1LthfeOwrkNk4SE6fxfoyZOU6mQ+ACbYCIn3lYCwVtHLDvoMDhmjWvgyBQwfSNr5SlPx5qiPSZrPg7AO2svqmNeEibvW1YPpfilNg83MWeOg==
我确信同一个证书用于对消息进行签名。
非常感谢您的想法。
最佳答案
您使用的 Java 代码生成 PKCS#1(使用 PKCS#1 v1.5 签名方案)和 SHA-1 中定义的“原始”签名。您的 C++ 代码生成 PKCS#7 (CMS) 签名。这是一个与消息一起以容器格式包装的签名,如您所见here .
如果您对包含在 PKCS#7 签名中的签名进行十六进制编码,您将看到与您在 Java 中生成的值相同的值。它们都生成以下签名(使用十六进制表示):
CA83D39F7DD9FDCD4FD3906863A08E5BE56B6C6E4C4ECA20D9884DAD791B572DCF7D742D1114388FD0572A73C089D62630F68EC724FF2E1F83DD9CA25E6B41C20578A0C308A783E7323E4C3BBED908673CE8DCD84DBCA4D05A8B24588C011FFEDA1F5F12C55DDCD425722DE0C451D23B6B650C3A6940178F8E5DFD9D73E592B7D4A91C6DAFB7060CB57A997EE22399EE2972CA81BCB09A4541E0D4F5E6F6D3A8B41A08C309AD1A4DA798E0DCD1B5EEED4BB617DE3B0AE4364E1213A7F17E8C99394EA643E0026D80889F79580B056D1CB0EFA0C0E19A35AF832050C1F48DAF94A53F1E6A88F499ACF83B00EDACBEA98D78489BBD6D583E97E294D83CDCC59E3A
您需要生成 PKCS#1 签名而不是 PKCS#7 (CMS) 容器的代码,例如尝试 CryptSignHash 的示例代码这也默认为 PKCS#1 v1.5 签名方案和 SHA-1。
关于Java数字签名和C++ CryptSignMessage导致的结果不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27153549/