Java数字签名和C ++ CryptSignMessage导致不同的结果

时间:2014-11-26 15:57:19

标签: java c++ windows cryptography digital-signature

我正在尝试将数字签名的生成从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==

我确信使用相同的证书对邮件进行签名。

非常感谢你的想法。

1 个答案:

答案 0 :(得分:3)

您使用的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。