简体   繁体   中英

Java digital signature and C++ CryptSignMessage lead to different results

I am trying to port the generation of a digital signature from Java to C++.

Most of the things I tried work except that the digital signature created from C++ seems to contain more information than Java creates. I do not know what these bytes are neither do I know how to prevent them from being written.

Here are both code snippets. First the existing Java code:

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");

Here is what I found in C++ with the above mentioned issue. The code was written following the MSDN example 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);

Signature created from Java code:

messageyoPTn33Z/c1P05BoY6COW+VrbG5MTsog2YhNrXkbVy3PfXQtERQ4j9BXKnPAidYmMPaOxyT/Lh+D3ZyiXmtBwgV4oMMIp4PnMj5MO77ZCGc86NzYTbyk0FqLJFiMAR/+2h9fEsVd3NQlci3gxFHSO2tlDDppQBePjl39nXPlkrfUqRxtr7cGDLV6mX7iI5nuKXLKgbywmkVB4NT15vbTqLQaCMMJrRpNp5jg3NG17u1LthfeOwrkNk4SE6fxfoyZOU6mQ+ACbYCIn3lYCwVtHLDvoMDhmjWvgyBQwfSNr5SlPx5qiPSZrPg7AO2svqmNeEibvW1YPpfilNg83MWeOg==

Signature created from C++ code:

messageMIIBdwYJKoZIhvcNAQcCoIIBaDCCAWQCAQMxDzANBgkqhkiG9w0BAQUFADALBgkqhkiG9w0BBwExggE/MIIBOwIBA4AUn87cY1dT9wKLDn9zdZu0Rrm0j54wDQYJKoZIhvcNAQEFBQAwDQYJKoZIhvcNAQEBBQAEggEAyoPTn33Z/c1P05BoY6COW+VrbG5MTsog2YhNrXkbVy3PfXQtERQ4j9BXKnPAidYmMPaOxyT/Lh+D3ZyiXmtBwgV4oMMIp4PnMj5MO77ZCGc86NzYTbyk0FqLJFiMAR/+2h9fEsVd3NQlci3gxFHSO2tlDDppQBePjl39nXPlkrfUqRxtr7cGDLV6mX7iI5nuKXLKgbywmkVB4NT15vbTqLQaCMMJrRpNp5jg3NG17u1LthfeOwrkNk4SE6fxfoyZOU6mQ+ACbYCIn3lYCwVtHLDvoMDhmjWvgyBQwfSNr5SlPx5qiPSZrPg7AO2svqmNeEibvW1YPpfilNg83MWeOg==

I am sure that the same certificate is used for signing the message.

Thanks a lot for your thoughts.

The Java code you use generates a "raw" signature as defined in PKCS#1 (using the PKCS#1 v1.5 signature scheme) and SHA-1. Your C++ code generates a PKCS#7 (CMS) signature. This is a signature that is wrapped in a container format together with the message, as you can see here .

If you hex encode the signature that is contained in the PKCS#7 signature you will see the same value as you generated in Java. They both generate the following signature (using hex as representation):

CA83D39F7DD9FDCD4FD3906863A08E5BE56B6C6E4C4ECA20D9884DAD791B572DCF7D742D1114388FD0572A73C089D62630F68EC724FF2E1F83DD9CA25E6B41C20578A0C308A783E7323E4C3BBED908673CE8DCD84DBCA4D05A8B24588C011FFEDA1F5F12C55DDCD425722DE0C451D23B6B650C3A6940178F8E5DFD9D73E592B7D4A91C6DAFB7060CB57A997EE22399EE2972CA81BCB09A4541E0D4F5E6F6D3A8B41A08C309AD1A4DA798E0DCD1B5EEED4BB617DE3B0AE4364E1213A7F17E8C99394EA643E0026D80889F79580B056D1CB0EFA0C0E19A35AF832050C1F48DAF94A53F1E6A88F499ACF83B00EDACBEA98D78489BBD6D583E97E294D83CDCC59E3A

You need a code that generates a PKCS#1 signature instead of a PKCS#7 (CMS) container, for instance try the sample code of CryptSignHash that also defaults to the PKCS#1 v1.5 signature scheme and SHA-1.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM