//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1995 - 1999 // // File: sca.cpp // // Contents: Simplified Cryptographic APIs (SCA) // // This implementation layers upon the CryptMsg and CertStore // APIs. // // Functions: // CryptSignMessage // CryptVerifyMessageSignature // CryptVerifyDetachedMessageSignature // CryptGetMessageSignerCount // CryptGetMessageCertificates // CryptDecodeMessage // CryptEncryptMessage // CryptDecryptMessage // CryptSignAndEncryptMessage // CryptDecryptAndVerifyMessageSignature // CryptHashMessage // CryptVerifyMessageHash // CryptVerifyDetachedMessageHash // CryptSignMessageWithKey // CryptVerifyMessageSignatureWithKey // // History: 14-Feb-96 philh created // 21-Feb-96 phil redid to reflect changes made to sca.h // 19-Jan-97 philh removed SET stuff // //-------------------------------------------------------------------------- #include "global.hxx" #include // #define ENABLE_SCA_STREAM_TEST 1 #define SCA_STREAM_ENABLE_FLAG 0x80000000 #define SCA_INDEFINITE_STREAM_FLAG 0x40000000 static const CRYPT_OBJID_TABLE MsgTypeObjIdTable[] = { CMSG_DATA, szOID_RSA_data , CMSG_SIGNED, szOID_RSA_signedData , CMSG_ENVELOPED, szOID_RSA_envelopedData , CMSG_SIGNED_AND_ENVELOPED, szOID_RSA_signEnvData , CMSG_HASHED, szOID_RSA_digestedData , CMSG_ENCRYPTED, szOID_RSA_encryptedData }; #define MSG_TYPE_OBJID_CNT (sizeof(MsgTypeObjIdTable)/sizeof(MsgTypeObjIdTable[0])) //+------------------------------------------------------------------------- // Convert the MsgType to the ASN.1 Object Identifier string // // Returns NULL if there isn't an ObjId corresponding to the MsgType. //-------------------------------------------------------------------------- static LPCSTR MsgTypeToOID( IN DWORD dwMsgType ) { int i; for (i = 0; i < MSG_TYPE_OBJID_CNT; i++) if (MsgTypeObjIdTable[i].dwAlgId == dwMsgType) return MsgTypeObjIdTable[i].pszObjId; return NULL; } //+------------------------------------------------------------------------- // Convert the ASN.1 Object Identifier string to the MsgType // // Returns 0 if there isn't a MsgType corresponding to the ObjId. //-------------------------------------------------------------------------- static DWORD OIDToMsgType( IN LPCSTR pszObjId ) { int i; for (i = 0; i < MSG_TYPE_OBJID_CNT; i++) if (_stricmp(pszObjId, MsgTypeObjIdTable[i].pszObjId) == 0) return MsgTypeObjIdTable[i].dwAlgId; return 0; } //+------------------------------------------------------------------------- // SCA allocation and free routines //-------------------------------------------------------------------------- static void *SCAAlloc( IN size_t cbBytes ); static void SCAFree( IN void *pv ); //+------------------------------------------------------------------------- // Null implementation of the get signer certificate //-------------------------------------------------------------------------- static PCCERT_CONTEXT WINAPI NullGetSignerCertificate( IN void *pvGetArg, IN DWORD dwCertEncodingType, IN PCERT_INFO pSignerId, IN HCERTSTORE hMsgCertStore ); //+------------------------------------------------------------------------- // Functions for initializing message encode information //-------------------------------------------------------------------------- static PCMSG_SIGNER_ENCODE_INFO InitSignerEncodeInfo( IN PCRYPT_SIGN_MESSAGE_PARA pSignPara ); static void FreeSignerEncodeInfo( IN PCMSG_SIGNER_ENCODE_INFO pSigner ); static BOOL InitSignedCertAndCrl( IN PCRYPT_SIGN_MESSAGE_PARA pSignPara, OUT PCERT_BLOB *ppCertEncoded, OUT PCRL_BLOB *ppCrlEncoded ); static void FreeSignedCertAndCrl( IN PCERT_BLOB pCertEncoded, IN PCRL_BLOB pCrlEncoded ); static BOOL InitSignedMsgEncodeInfo( IN PCRYPT_SIGN_MESSAGE_PARA pSignPara, OUT PCMSG_SIGNED_ENCODE_INFO pSignedMsgEncodeInfo ); static void FreeSignedMsgEncodeInfo( IN PCRYPT_SIGN_MESSAGE_PARA pSignPara, IN PCMSG_SIGNED_ENCODE_INFO pSignedMsgEncodeInfo ); #ifdef CMS_PKCS7 // Returned array of CMSG_RECIPIENT_ENCODE_INFOs needs to be SCAFree'd static PCMSG_RECIPIENT_ENCODE_INFO InitCmsRecipientEncodeInfo( IN PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, IN DWORD cRecipientCert, IN PCCERT_CONTEXT rgpRecipientCert[], IN DWORD dwFlags ); #else // Returned array of PCERT_INFOs needs to be SCAFree'd static PCERT_INFO *InitRecipientEncodeInfo( IN DWORD cRecipientCert, IN PCCERT_CONTEXT rgpRecipientCert[] ); #endif // CMS_PKCS7 static BOOL InitEnvelopedMsgEncodeInfo( IN PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, IN DWORD cRecipientCert, IN PCCERT_CONTEXT rgpRecipientCert[], OUT PCMSG_ENVELOPED_ENCODE_INFO pEnvelopedMsgEncodeInfo ); static void FreeEnvelopedMsgEncodeInfo( IN PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, IN PCMSG_ENVELOPED_ENCODE_INFO pEnvelopedMsgEncodeInfo ); //+------------------------------------------------------------------------- // Encodes the message. //-------------------------------------------------------------------------- static BOOL EncodeMsg( IN DWORD dwMsgEncodingType, IN DWORD dwFlags, IN DWORD dwMsgType, IN void *pvMsgEncodeInfo, IN DWORD cToBeEncoded, IN const BYTE *rgpbToBeEncoded[], IN DWORD rgcbToBeEncoded[], IN BOOL fBareContent, IN DWORD dwInnerContentType, OUT BYTE *pbEncodedBlob, IN OUT DWORD *pcbEncodedBlob ); //+------------------------------------------------------------------------- // Decodes the message types: // CMSG_SIGNED // CMSG_ENVELOPED // CMSG_SIGNED_AND_ENVELOPED // CMSG_HASHED //-------------------------------------------------------------------------- static BOOL DecodeMsg( IN DWORD dwMsgTypeFlags, IN OPTIONAL PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, IN OPTIONAL PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, IN DWORD dwSignerIndex, IN const BYTE *pbEncodedBlob, IN DWORD cbEncodedBlob, IN DWORD cToBeEncoded, IN OPTIONAL const BYTE *rgpbToBeEncoded[], IN OPTIONAL DWORD rgcbToBeEncoded[], IN DWORD dwPrevInnerContentType, OUT OPTIONAL DWORD *pdwMsgType, OUT OPTIONAL DWORD *pdwInnerContentType, OUT OPTIONAL BYTE *pbDecoded, IN OUT OPTIONAL DWORD *pcbDecoded, OUT OPTIONAL PCCERT_CONTEXT *ppXchgCert, OUT OPTIONAL PCCERT_CONTEXT *ppSignerCert ); #ifdef ENABLE_SCA_STREAM_TEST //+------------------------------------------------------------------------- // Encodes the message using streaming. //-------------------------------------------------------------------------- static BOOL StreamEncodeMsg( IN DWORD dwMsgEncodingType, IN DWORD dwFlags, IN DWORD dwMsgType, IN void *pvMsgEncodeInfo, IN DWORD cToBeEncoded, IN const BYTE *rgpbToBeEncoded[], IN DWORD rgcbToBeEncoded[], IN BOOL fBareContent, IN DWORD dwInnerContentType, OUT BYTE *pbEncodedBlob, IN OUT DWORD *pcbEncodedBlob ); //+------------------------------------------------------------------------- // Decodes the message types: // CMSG_SIGNED // CMSG_ENVELOPED // CMSG_SIGNED_AND_ENVELOPED // CMSG_HASHED // // Uses streaming. //-------------------------------------------------------------------------- static BOOL StreamDecodeMsg( IN DWORD dwMsgTypeFlags, IN OPTIONAL PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, IN OPTIONAL PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, IN DWORD dwSignerIndex, IN const BYTE *pbEncodedBlob, IN DWORD cbEncodedBlob, IN DWORD cToBeEncoded, IN OPTIONAL const BYTE *rgpbToBeEncoded[], IN OPTIONAL DWORD rgcbToBeEncoded[], IN DWORD dwPrevInnerContentType, OUT OPTIONAL DWORD *pdwMsgType, OUT OPTIONAL DWORD *pdwInnerContentType, OUT OPTIONAL BYTE *pbDecoded, IN OUT OPTIONAL DWORD *pcbDecoded, OUT OPTIONAL PCCERT_CONTEXT *ppXchgCert, OUT OPTIONAL PCCERT_CONTEXT *ppSignerCert ); #endif //+------------------------------------------------------------------------- // Decodes the HASHED message type //-------------------------------------------------------------------------- static BOOL DecodeHashMsg( IN PCRYPT_HASH_MESSAGE_PARA pHashPara, IN const BYTE *pbEncodedBlob, IN DWORD cbEncodedBlob, IN DWORD cToBeHashed, IN OPTIONAL const BYTE *rgpbToBeHashed[], IN OPTIONAL DWORD rgcbToBeHashed[], OUT OPTIONAL BYTE *pbDecoded, IN OUT OPTIONAL DWORD *pcbDecoded, OUT OPTIONAL BYTE *pbComputedHash, IN OUT OPTIONAL DWORD *pcbComputedHash ); //+------------------------------------------------------------------------- // Get certificate for and verify the message's signer. //-------------------------------------------------------------------------- static BOOL GetSignerCertAndVerify( IN PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, IN DWORD dwSignerIndex, IN HCRYPTMSG hMsg, OUT OPTIONAL PCCERT_CONTEXT *ppSignerCert ); //+------------------------------------------------------------------------- // Get a certificate with a key provider property for one of the message's // recipients and use to decrypt the message. //-------------------------------------------------------------------------- static BOOL GetXchgCertAndDecrypt( IN PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, IN HCRYPTMSG hMsg, OUT OPTIONAL PCCERT_CONTEXT *ppXchgCert ); //+------------------------------------------------------------------------- // Allocate and get message parameter //-------------------------------------------------------------------------- static void * AllocAndMsgGetParam( IN HCRYPTMSG hMsg, IN DWORD dwParamType, IN DWORD dwIndex ); //+------------------------------------------------------------------------- // Sign the message. // // If fDetachedSignature is TRUE, the "to be signed" content isn't included // in the encoded signed blob. //-------------------------------------------------------------------------- BOOL WINAPI CryptSignMessage( IN PCRYPT_SIGN_MESSAGE_PARA pSignPara, IN BOOL fDetachedSignature, IN DWORD cToBeSigned, IN const BYTE *rgpbToBeSigned[], IN DWORD rgcbToBeSigned[], OUT BYTE *pbSignedBlob, IN OUT DWORD *pcbSignedBlob ) { BOOL fResult; CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo; fResult = InitSignedMsgEncodeInfo( pSignPara, &SignedMsgEncodeInfo ); if (fResult) { BOOL fBareContent; DWORD dwInnerContentType; DWORD dwFlags = 0; if (fDetachedSignature) dwFlags |= CMSG_DETACHED_FLAG; if (pSignPara->cbSize >= STRUCT_CBSIZE(CRYPT_SIGN_MESSAGE_PARA, dwInnerContentType)) { fBareContent = pSignPara->dwFlags & CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG; dwInnerContentType = pSignPara->dwInnerContentType; #ifdef CMS_PKCS7 if (pSignPara->dwFlags & CRYPT_MESSAGE_ENCAPSULATED_CONTENT_OUT_FLAG) dwFlags |= CMSG_CMS_ENCAPSULATED_CONTENT_FLAG; #endif // CMS_PKCS7 } else { fBareContent = FALSE; dwInnerContentType = 0; } #ifdef ENABLE_SCA_STREAM_TEST if (pSignPara->cbSize >= STRUCT_CBSIZE(CRYPT_SIGN_MESSAGE_PARA, dwFlags) && (pSignPara->dwFlags & SCA_STREAM_ENABLE_FLAG)) { dwFlags |= pSignPara->dwFlags & SCA_INDEFINITE_STREAM_FLAG; fResult = StreamEncodeMsg( pSignPara->dwMsgEncodingType, dwFlags, CMSG_SIGNED, &SignedMsgEncodeInfo, cToBeSigned, rgpbToBeSigned, rgcbToBeSigned, fBareContent, dwInnerContentType, pbSignedBlob, pcbSignedBlob ); } else #endif fResult = EncodeMsg( pSignPara->dwMsgEncodingType, dwFlags, CMSG_SIGNED, &SignedMsgEncodeInfo, cToBeSigned, rgpbToBeSigned, rgcbToBeSigned, fBareContent, dwInnerContentType, pbSignedBlob, pcbSignedBlob ); FreeSignedMsgEncodeInfo(pSignPara, &SignedMsgEncodeInfo); } else *pcbSignedBlob = 0; return fResult; } //+------------------------------------------------------------------------- // Verify a signed message. // // For *pcbDecoded == 0 on input, the signer isn't verified. // // A message might have more than one signer. Set dwSignerIndex to iterate // through all the signers. dwSignerIndex == 0 selects the first signer. // // pVerifyPara's pfnGetSignerCertificate is called to get the signer's // certificate. // // For a verified signer and message, *ppSignerCert is updated // with the CertContext of the signer. It must be freed by calling // CertFreeCertificateContext. Otherwise, *ppSignerCert is set to NULL. // For *pbcbDecoded == 0 on input, *ppSignerCert is always set to // NULL. // // ppSignerCert can be NULL, indicating the caller isn't interested // in getting the CertContext of the signer. // // pcbDecoded can be NULL, indicating the caller isn't interested in getting // the decoded content. Furthermore, if the message doesn't contain any // content or signers, then, pcbDecoded must be set to NULL, to allow the // pVerifyPara->pfnGetSignerCertificate to be called. Normally, this would be // the case when the signed message contains only certficates and CRLs. // If pcbDecoded is NULL and the message doesn't have the indicated signer, // pfnGetSignerCertificate is called with pSignerId set to NULL. //-------------------------------------------------------------------------- BOOL WINAPI CryptVerifyMessageSignature( IN PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, IN DWORD dwSignerIndex, IN const BYTE *pbSignedBlob, IN DWORD cbSignedBlob, OUT OPTIONAL BYTE *pbDecoded, IN OUT OPTIONAL DWORD *pcbDecoded, OUT OPTIONAL PCCERT_CONTEXT *ppSignerCert ) { #ifdef ENABLE_SCA_STREAM_TEST if (pVerifyPara->dwMsgAndCertEncodingType & SCA_STREAM_ENABLE_FLAG) return StreamDecodeMsg( CMSG_SIGNED_FLAG, NULL, // pDecryptPara pVerifyPara, dwSignerIndex, pbSignedBlob, cbSignedBlob, 0, // cToBeEncoded NULL, // rgpbToBeEncoded NULL, // rgcbToBeEncoded 0, // dwPrevInnerContentType NULL, // pdwMsgType NULL, // pdwInnerContentType pbDecoded, pcbDecoded, NULL, // ppXchgCert ppSignerCert ); else #endif return DecodeMsg( CMSG_SIGNED_FLAG, NULL, // pDecryptPara pVerifyPara, dwSignerIndex, pbSignedBlob, cbSignedBlob, 0, // cToBeEncoded NULL, // rgpbToBeEncoded NULL, // rgcbToBeEncoded 0, // dwPrevInnerContentType NULL, // pdwMsgType NULL, // pdwInnerContentType pbDecoded, pcbDecoded, NULL, // ppXchgCert ppSignerCert ); } //+------------------------------------------------------------------------- // Verify a signed message containing detached signature(s). // The "to be signed" content is passed in separately. No // decoded output. Otherwise, identical to CryptVerifyMessageSignature. //-------------------------------------------------------------------------- BOOL WINAPI CryptVerifyDetachedMessageSignature( IN PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, IN DWORD dwSignerIndex, IN const BYTE *pbDetachedSignBlob, IN DWORD cbDetachedSignBlob, IN DWORD cToBeSigned, IN const BYTE *rgpbToBeSigned[], IN DWORD rgcbToBeSigned[], OUT OPTIONAL PCCERT_CONTEXT *ppSignerCert ) { #ifdef ENABLE_SCA_STREAM_TEST if (pVerifyPara->dwMsgAndCertEncodingType & SCA_STREAM_ENABLE_FLAG) return StreamDecodeMsg( CMSG_SIGNED_FLAG, NULL, // pDecryptPara pVerifyPara, dwSignerIndex, pbDetachedSignBlob, cbDetachedSignBlob, cToBeSigned, rgpbToBeSigned, rgcbToBeSigned, 0, // dwPrevInnerContentType NULL, // pdwMsgType NULL, // pdwInnerContentType NULL, // pbDecoded NULL, // pcbDecoded NULL, // ppXchgCert ppSignerCert ); else #endif return DecodeMsg( CMSG_SIGNED_FLAG, NULL, // pDecryptPara pVerifyPara, dwSignerIndex, pbDetachedSignBlob, cbDetachedSignBlob, cToBeSigned, rgpbToBeSigned, rgcbToBeSigned, 0, // dwPrevInnerContentType NULL, // pdwMsgType NULL, // pdwInnerContentType NULL, // pbDecoded NULL, // pcbDecoded NULL, // ppXchgCert ppSignerCert ); } //+------------------------------------------------------------------------- // Returns the count of signers in the signed message. For no signers, returns // 0. For an error returns -1 with LastError updated accordingly. //-------------------------------------------------------------------------- LONG WINAPI CryptGetMessageSignerCount( IN DWORD dwMsgEncodingType, IN const BYTE *pbSignedBlob, IN DWORD cbSignedBlob ) { HCRYPTMSG hMsg = NULL; LONG lSignerCount; DWORD cbData; if (NULL == (hMsg = CryptMsgOpenToDecode( dwMsgEncodingType, 0, // dwFlags 0, // dwMsgType 0, // hCryptProv, NULL, // pRecipientInfo NULL // pStreamInfo ))) goto ErrorReturn; if (!CryptMsgUpdate( hMsg, pbSignedBlob, cbSignedBlob, TRUE // fFinal )) goto ErrorReturn; lSignerCount = 0; cbData = sizeof(lSignerCount); if (!CryptMsgGetParam( hMsg, CMSG_SIGNER_COUNT_PARAM, 0, // dwIndex &lSignerCount, &cbData )) goto ErrorReturn; goto CommonReturn; ErrorReturn: lSignerCount = -1; CommonReturn: if (hMsg) CryptMsgClose(hMsg); return lSignerCount; } //+------------------------------------------------------------------------- // Returns the cert store containing the message's certs and CRLs. // For an error, returns NULL with LastError updated. //-------------------------------------------------------------------------- HCERTSTORE WINAPI CryptGetMessageCertificates( IN DWORD dwMsgAndCertEncodingType, IN HCRYPTPROV hCryptProv, // passed to CertOpenStore IN DWORD dwFlags, // passed to CertOpenStore IN const BYTE *pbSignedBlob, IN DWORD cbSignedBlob ) { CRYPT_DATA_BLOB SignedBlob; SignedBlob.pbData = (BYTE *) pbSignedBlob; SignedBlob.cbData = cbSignedBlob; return CertOpenStore( CERT_STORE_PROV_PKCS7, dwMsgAndCertEncodingType, hCryptProv, dwFlags, (const void *) &SignedBlob ); } //+------------------------------------------------------------------------- // Encrypts the message for the recipient(s). //-------------------------------------------------------------------------- BOOL WINAPI CryptEncryptMessage( IN PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, IN DWORD cRecipientCert, IN PCCERT_CONTEXT rgpRecipientCert[], IN const BYTE *pbToBeEncrypted, IN DWORD cbToBeEncrypted, OUT BYTE *pbEncryptedBlob, IN OUT DWORD *pcbEncryptedBlob ) { BOOL fResult; CMSG_ENVELOPED_ENCODE_INFO EnvelopedMsgEncodeInfo; fResult = InitEnvelopedMsgEncodeInfo( pEncryptPara, cRecipientCert, rgpRecipientCert, &EnvelopedMsgEncodeInfo ); if (fResult) { BOOL fBareContent; DWORD dwInnerContentType; DWORD dwFlags = 0; if (pEncryptPara->cbSize >= sizeof(CRYPT_ENCRYPT_MESSAGE_PARA)) { fBareContent = pEncryptPara->dwFlags & CRYPT_MESSAGE_BARE_CONTENT_OUT_FLAG; dwInnerContentType = pEncryptPara->dwInnerContentType; #ifdef CMS_PKCS7 if (pEncryptPara->dwFlags & CRYPT_MESSAGE_ENCAPSULATED_CONTENT_OUT_FLAG) dwFlags |= CMSG_CMS_ENCAPSULATED_CONTENT_FLAG; #endif // CMS_PKCS7 } else { fBareContent = FALSE; dwInnerContentType = 0; } #ifdef ENABLE_SCA_STREAM_TEST if (pEncryptPara->cbSize >= STRUCT_CBSIZE(CRYPT_ENCRYPT_MESSAGE_PARA, dwFlags) && (pEncryptPara->dwFlags & SCA_STREAM_ENABLE_FLAG)) { dwFlags |= pEncryptPara->dwFlags & SCA_INDEFINITE_STREAM_FLAG; fResult = StreamEncodeMsg( pEncryptPara->dwMsgEncodingType, dwFlags, CMSG_ENVELOPED, &EnvelopedMsgEncodeInfo, 1, // cToBeEncrypted &pbToBeEncrypted, &cbToBeEncrypted, fBareContent, dwInnerContentType, pbEncryptedBlob, pcbEncryptedBlob ); } else #endif fResult = EncodeMsg( pEncryptPara->dwMsgEncodingType, dwFlags, CMSG_ENVELOPED, &EnvelopedMsgEncodeInfo, 1, // cToBeEncrypted &pbToBeEncrypted, &cbToBeEncrypted, fBareContent, dwInnerContentType, pbEncryptedBlob, pcbEncryptedBlob ); FreeEnvelopedMsgEncodeInfo(pEncryptPara, &EnvelopedMsgEncodeInfo); } else *pcbEncryptedBlob = 0; return fResult; } //+------------------------------------------------------------------------- // Decrypts the message. // // For *pcbDecrypted == 0 on input, the message isn't decrypted. // // For a successfully decrypted message, *ppXchgCert is updated // with the CertContext used to decrypt. It must be freed by calling // CertFreeCertificateContext. Otherwise, *ppXchgCert is set to NULL. // For *pbcbDecrypted == 0 on input, *ppXchgCert is always set to // NULL. // // ppXchgCert can be NULL, indicating the caller isn't interested // in getting the CertContext used to decrypt. // // pcbDecrypted can be NULL, indicating the caller isn't interested in // getting the decrypted content. However, when pcbDecrypted is NULL, // the message is still decrypted. //-------------------------------------------------------------------------- BOOL WINAPI CryptDecryptMessage( IN PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, IN const BYTE *pbEncryptedBlob, IN DWORD cbEncryptedBlob, OUT OPTIONAL BYTE *pbDecrypted, IN OUT OPTIONAL DWORD *pcbDecrypted, OUT OPTIONAL PCCERT_CONTEXT *ppXchgCert ) { #ifdef ENABLE_SCA_STREAM_TEST if (pDecryptPara->dwMsgAndCertEncodingType & SCA_STREAM_ENABLE_FLAG) return StreamDecodeMsg( CMSG_ENVELOPED_FLAG, pDecryptPara, NULL, // pVerifyPara 0, // dwSignerIndex pbEncryptedBlob, cbEncryptedBlob, 0, // cToBeEncoded NULL, // rgpbToBeEncoded NULL, // rgcbToBeEncoded 0, // dwPrevInnerContentType NULL, // pdwMsgType NULL, // pdwInnerContentType pbDecrypted, pcbDecrypted, ppXchgCert, NULL // ppSignerCert ); else #endif return DecodeMsg( CMSG_ENVELOPED_FLAG, pDecryptPara, NULL, // pVerifyPara 0, // dwSignerIndex pbEncryptedBlob, cbEncryptedBlob, 0, // cToBeEncoded NULL, // rgpbToBeEncoded NULL, // rgcbToBeEncoded 0, // dwPrevInnerContentType NULL, // pdwMsgType NULL, // pdwInnerContentType pbDecrypted, pcbDecrypted, ppXchgCert, NULL // ppSignerCert ); } //+------------------------------------------------------------------------- // Sign the message and encrypt for the recipient(s) //-------------------------------------------------------------------------- BOOL WINAPI CryptSignAndEncryptMessage( IN PCRYPT_SIGN_MESSAGE_PARA pSignPara, IN PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, IN DWORD cRecipientCert, IN PCCERT_CONTEXT rgpRecipientCert[], IN const BYTE *pbToBeSignedAndEncrypted, IN DWORD cbToBeSignedAndEncrypted, OUT BYTE *pbSignedAndEncryptedBlob, IN OUT DWORD *pcbSignedAndEncryptedBlob ) { #if 1 BOOL fResult; DWORD cbSigned; DWORD cbSignedDelta = 0; BYTE *pbSigned = NULL; if (pbSignedAndEncryptedBlob == NULL) *pcbSignedAndEncryptedBlob = 0; cbSigned = 0; CryptSignMessage( pSignPara, FALSE, // fDetachedSignature 1, // cToBeSigned &pbToBeSignedAndEncrypted, &cbToBeSignedAndEncrypted, NULL, // pbSignedBlob &cbSigned ); if (cbSigned == 0) goto ErrorReturn; if (*pcbSignedAndEncryptedBlob) { DWORD cbSignedMax; pbSigned = (BYTE *) SCAAlloc(cbSigned); if (pbSigned == NULL) goto ErrorReturn; cbSignedMax = cbSigned; if (!CryptSignMessage( pSignPara, FALSE, // fDetachedSignature 1, // cToBeSigned &pbToBeSignedAndEncrypted, &cbToBeSignedAndEncrypted, pbSigned, &cbSigned )) goto ErrorReturn; if (cbSignedMax > cbSigned) // For DSS, the signature length varies since it consists of // a sequence of unsigned integers. cbSignedDelta = cbSignedMax - cbSigned; } fResult = CryptEncryptMessage( pEncryptPara, cRecipientCert, rgpRecipientCert, pbSigned, cbSigned, pbSignedAndEncryptedBlob, pcbSignedAndEncryptedBlob ); if (!fResult && 0 != *pcbSignedAndEncryptedBlob) // Adjust if necessary for DSS signature length *pcbSignedAndEncryptedBlob += cbSignedDelta; goto CommonReturn; ErrorReturn: *pcbSignedAndEncryptedBlob = 0; fResult = FALSE; CommonReturn: if (pbSigned) SCAFree(pbSigned); return fResult; #else BOOL fResult; CMSG_SIGNED_AND_ENVELOPED_ENCODE_INFO SignedAndEnvelopedMsgEncodeInfo; SignedAndEnvelopedMsgEncodeInfo.cbSize = sizeof(CMSG_SIGNED_AND_ENVELOPED_ENCODE_INFO); fResult = InitSignedMsgEncodeInfo( pSignPara, &SignedAndEnvelopedMsgEncodeInfo.SignedInfo ); if (fResult) { fResult = InitEnvelopedMsgEncodeInfo( pEncryptPara, cRecipientCert, rgpRecipientCert, &SignedAndEnvelopedMsgEncodeInfo.EnvelopedInfo ); if (fResult) { fResult = EncodeMsg( pSignPara->dwMsgEncodingType, CMSG_SIGNED_AND_ENVELOPED, &SignedAndEnvelopedMsgEncodeInfo, pbToBeSignedAndEncrypted, cbToBeSignedAndEncrypted, FALSE, // fBareContent 0, // dwInnerContentType pbSignedAndEncryptedBlob, pcbSignedAndEncryptedBlob ); FreeEnvelopedMsgEncodeInfo(pEncryptPara, &SignedAndEnvelopedMsgEncodeInfo.EnvelopedInfo); } FreeSignedMsgEncodeInfo(pSignPara, &SignedAndEnvelopedMsgEncodeInfo.SignedInfo); } return fResult; #endif } //+------------------------------------------------------------------------- // Decrypts the message and verifies the signer. // // For *pcbDecrypted == 0 on input, the message isn't decrypted and the // signer isn't verified. // // A message might have more than one signer. Set dwSignerIndex to iterate // through all the signers. dwSignerIndex == 0 selects the first signer. // // The hVerify's GetSignerCertificate is called to verify the signer's // certificate. // // For a successfully decrypted and verified message, *ppXchgCert and // *ppSignerCert are updated. They must be freed by calling // CertFreeCertificateContext. Otherwise, they are set to NULL. // For *pbcbDecrypted == 0 on input, both are always set to NULL. // // ppXchgCert and/or ppSignerCert can be NULL, indicating the // caller isn't interested in getting the CertContext. // // pcbDecrypted can be NULL, indicating the caller isn't interested in // getting the decrypted content. However, when pcbDecrypted is NULL, // the message is still decrypted and verified. //-------------------------------------------------------------------------- BOOL WINAPI CryptDecryptAndVerifyMessageSignature( IN PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, IN PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, IN DWORD dwSignerIndex, IN const BYTE *pbEncryptedBlob, IN DWORD cbEncryptedBlob, OUT OPTIONAL BYTE *pbDecrypted, IN OUT OPTIONAL DWORD *pcbDecrypted, OUT OPTIONAL PCCERT_CONTEXT *ppXchgCert, OUT OPTIONAL PCCERT_CONTEXT *ppSignerCert ) { #if 1 BOOL fResult; DWORD cbSignedBlob; BYTE *pbSignedBlob = NULL; DWORD dwEnvelopeInnerContentType; if (ppXchgCert) *ppXchgCert = NULL; if (ppSignerCert) *ppSignerCert = NULL; #ifdef ENABLE_SCA_STREAM_TEST if (pDecryptPara->dwMsgAndCertEncodingType & SCA_STREAM_ENABLE_FLAG) { cbSignedBlob = 0; StreamDecodeMsg( CMSG_ENVELOPED_FLAG, pDecryptPara, NULL, // pVerifyPara 0, // dwSignerIndex pbEncryptedBlob, cbEncryptedBlob, 0, // cToBeEncoded NULL, // rgpbToBeEncoded NULL, // rgcbToBeEncoded 0, // dwPrevInnerContentType NULL, // pdwMsgType NULL, // pdwInnerContentType NULL, // pbDecrypted &cbSignedBlob, NULL, // ppXchgCert NULL // ppSignerCert ); if (cbSignedBlob == 0) goto ErrorReturn; pbSignedBlob = (BYTE *) SCAAlloc(cbSignedBlob); if (pbSignedBlob == NULL) goto ErrorReturn; if (!StreamDecodeMsg( CMSG_ENVELOPED_FLAG, pDecryptPara, NULL, // pVerifyPara 0, // dwSignerIndex pbEncryptedBlob, cbEncryptedBlob, 0, // cToBeEncoded NULL, // rgpbToBeEncoded NULL, // rgcbToBeEncoded 0, // dwPrevInnerContentType NULL, // pdwMsgType &dwEnvelopeInnerContentType, pbSignedBlob, &cbSignedBlob, ppXchgCert, NULL // ppSignerCert )) goto ErrorReturn; } else { #endif cbSignedBlob = 0; DecodeMsg( CMSG_ENVELOPED_FLAG, pDecryptPara, NULL, // pVerifyPara 0, // dwSignerIndex pbEncryptedBlob, cbEncryptedBlob, 0, // cToBeEncoded NULL, // rgpbToBeEncoded NULL, // rgcbToBeEncoded 0, // dwPrevInnerContentType NULL, // pdwMsgType NULL, // pdwInnerContentType NULL, // pbDecrypted &cbSignedBlob, NULL, // ppXchgCert NULL // ppSignerCert ); if (cbSignedBlob == 0) goto ErrorReturn; pbSignedBlob = (BYTE *) SCAAlloc(cbSignedBlob); if (pbSignedBlob == NULL) goto ErrorReturn; if (!DecodeMsg( CMSG_ENVELOPED_FLAG, pDecryptPara, NULL, // pVerifyPara 0, // dwSignerIndex pbEncryptedBlob, cbEncryptedBlob, 0, // cToBeEncoded NULL, // rgpbToBeEncoded NULL, // rgcbToBeEncoded 0, // dwPrevInnerContentType NULL, // pdwMsgType &dwEnvelopeInnerContentType, pbSignedBlob, &cbSignedBlob, ppXchgCert, NULL // ppSignerCert )) goto ErrorReturn; #ifdef ENABLE_SCA_STREAM_TEST } #endif #ifdef ENABLE_SCA_STREAM_TEST if (pVerifyPara->dwMsgAndCertEncodingType & SCA_STREAM_ENABLE_FLAG) fResult = StreamDecodeMsg( CMSG_SIGNED_FLAG, NULL, // pDecryptPara pVerifyPara, dwSignerIndex, pbSignedBlob, cbSignedBlob, 0, // cToBeEncoded NULL, // rgpbToBeEncoded NULL, // rgcbToBeEncoded dwEnvelopeInnerContentType, NULL, // pdwMsgType NULL, // pdwInnerContentType pbDecrypted, pcbDecrypted, NULL, // ppXchgCert ppSignerCert ); else #endif fResult = DecodeMsg( CMSG_SIGNED_FLAG, NULL, // pDecryptPara pVerifyPara, dwSignerIndex, pbSignedBlob, cbSignedBlob, 0, // cToBeEncoded NULL, // rgpbToBeEncoded NULL, // rgcbToBeEncoded dwEnvelopeInnerContentType, NULL, // pdwMsgType NULL, // pdwInnerContentType pbDecrypted, pcbDecrypted, NULL, // ppXchgCert ppSignerCert ); if (!fResult) goto VerifyError; goto CommonReturn; ErrorReturn: if (pcbDecrypted) *pcbDecrypted = 0; VerifyError: if (ppXchgCert && *ppXchgCert) { CertFreeCertificateContext(*ppXchgCert); *ppXchgCert = NULL; } if (ppSignerCert && *ppSignerCert) { CertFreeCertificateContext(*ppSignerCert); *ppSignerCert = NULL; } fResult = FALSE; CommonReturn: if (pbSignedBlob) SCAFree(pbSignedBlob); return fResult; #else // This needs to be updated if we switch back to this option return DecodeMsg( CMSG_SIGNED_AND_ENVELOPED_FLAG, pDecryptPara, pVerifyPara, dwSignerIndex, pbEncryptedBlob, cbEncryptedBlob, 0, // dwPrevInnerContentType NULL, // pdwMsgType NULL, // pdwInnerContentType pbDecrypted, pcbDecrypted, ppXchgCert, ppSignerCert ); #endif } //+------------------------------------------------------------------------- // Get the hash length for the specified algorithm identifier. // // Returns 0 for an unknown identifier. //-------------------------------------------------------------------------- static DWORD GetComputedHashLength(PCRYPT_ALGORITHM_IDENTIFIER pAlgId) { DWORD cbHash; DWORD dwAlgId; dwAlgId = CertOIDToAlgId(pAlgId->pszObjId); switch (dwAlgId) { case CALG_SHA: cbHash = 20; break; case CALG_MD2: case CALG_MD4: case CALG_MD5: cbHash = 16; break; default: cbHash = 0; } return cbHash; } //+------------------------------------------------------------------------- // Hash the message. // // If fDetachedHash is TRUE, only the ComputedHash is encoded in the // pbHashedBlob. Otherwise, both the ToBeHashed and ComputedHash // are encoded. // // pcbHashedBlob or pcbComputedHash can be NULL, indicating the caller // isn't interested in getting the output. //-------------------------------------------------------------------------- BOOL WINAPI CryptHashMessage( IN PCRYPT_HASH_MESSAGE_PARA pHashPara, IN BOOL fDetachedHash, IN DWORD cToBeHashed, IN const BYTE *rgpbToBeHashed[], IN DWORD rgcbToBeHashed[], OUT OPTIONAL BYTE *pbHashedBlob, IN OUT OPTIONAL DWORD *pcbHashedBlob, OUT OPTIONAL BYTE *pbComputedHash, IN OUT OPTIONAL DWORD *pcbComputedHash ) { BOOL fResult; DWORD dwFlags = fDetachedHash ? CMSG_DETACHED_FLAG : 0; HCRYPTMSG hMsg = NULL; CMSG_HASHED_ENCODE_INFO HashedMsgEncodeInfo; DWORD cbHashedBlob; DWORD cbComputedHash; // Get input lengths and default return lengths to 0 cbHashedBlob = 0; if (pcbHashedBlob) { if (pbHashedBlob) cbHashedBlob = *pcbHashedBlob; *pcbHashedBlob = 0; } cbComputedHash = 0; if (pcbComputedHash) { if (pbComputedHash) cbComputedHash = *pcbComputedHash; *pcbComputedHash = 0; } assert(pHashPara->cbSize == sizeof(CRYPT_HASH_MESSAGE_PARA)); if (pHashPara->cbSize != sizeof(CRYPT_HASH_MESSAGE_PARA)) goto InvalidArg; HashedMsgEncodeInfo.cbSize = sizeof(CMSG_HASHED_ENCODE_INFO); HashedMsgEncodeInfo.hCryptProv = pHashPara->hCryptProv; HashedMsgEncodeInfo.HashAlgorithm = pHashPara->HashAlgorithm; HashedMsgEncodeInfo.pvHashAuxInfo = pHashPara->pvHashAuxInfo; fResult = TRUE; if (0 == cbHashedBlob && 0 == cbComputedHash && (NULL == pcbComputedHash || 0 != (*pcbComputedHash = GetComputedHashLength( &pHashPara->HashAlgorithm)))) { // Length only if (pcbHashedBlob) { DWORD c; DWORD cbTotal = 0; DWORD *pcb; for (c = cToBeHashed, pcb = rgcbToBeHashed; c > 0; c--, pcb++) cbTotal += *pcb; if (0 == (*pcbHashedBlob = CryptMsgCalculateEncodedLength( pHashPara->dwMsgEncodingType, dwFlags, CMSG_HASHED, &HashedMsgEncodeInfo, NULL, // pszInnerContentObjID cbTotal ))) goto CalculateEncodedLengthError; if (pbHashedBlob) goto LengthError; } if (pcbComputedHash && pbComputedHash) goto LengthError; } else { if (NULL == (hMsg = CryptMsgOpenToEncode( pHashPara->dwMsgEncodingType, dwFlags, CMSG_HASHED, &HashedMsgEncodeInfo, NULL, // pszInnerContentObjID NULL // pStreamInfo ))) goto OpenToEncodeError; if (0 == cToBeHashed) { if (!CryptMsgUpdate( hMsg, NULL, // pbData 0, // cbData TRUE // fFinal )) goto UpdateError; } else { DWORD c; DWORD *pcb; const BYTE **ppb; for (c = cToBeHashed, pcb = rgcbToBeHashed, ppb = rgpbToBeHashed; c > 0; c--, pcb++, ppb++) { if (!CryptMsgUpdate( hMsg, *ppb, *pcb, c == 1 // fFinal )) goto UpdateError; } } if (pcbHashedBlob) { fResult = CryptMsgGetParam( hMsg, CMSG_CONTENT_PARAM, 0, // dwIndex pbHashedBlob, &cbHashedBlob ); *pcbHashedBlob = cbHashedBlob; } if (pcbComputedHash) { DWORD dwErr = 0; BOOL fResult2; if (!fResult) dwErr = GetLastError(); fResult2 = CryptMsgGetParam( hMsg, CMSG_COMPUTED_HASH_PARAM, 0, // dwIndex pbComputedHash, &cbComputedHash ); *pcbComputedHash = cbComputedHash; if (!fResult2) fResult = FALSE; else if (!fResult) SetLastError(dwErr); } if (!fResult) goto ErrorReturn; // NO_TRACE } CommonReturn: if (hMsg) CryptMsgClose(hMsg); // for success, preserves LastError return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) SET_ERROR(LengthError, ERROR_MORE_DATA) TRACE_ERROR(CalculateEncodedLengthError) TRACE_ERROR(OpenToEncodeError) TRACE_ERROR(UpdateError) } //+------------------------------------------------------------------------- // Verify a hashed message. // // pcbToBeHashed or pcbComputedHash can be NULL, // indicating the caller isn't interested in getting the output. //-------------------------------------------------------------------------- BOOL WINAPI CryptVerifyMessageHash( IN PCRYPT_HASH_MESSAGE_PARA pHashPara, IN BYTE *pbHashedBlob, IN DWORD cbHashedBlob, OUT OPTIONAL BYTE *pbToBeHashed, IN OUT OPTIONAL DWORD *pcbToBeHashed, OUT OPTIONAL BYTE *pbComputedHash, IN OUT OPTIONAL DWORD *pcbComputedHash ) { return DecodeHashMsg( pHashPara, pbHashedBlob, cbHashedBlob, NULL, // cToBeHashed NULL, // rgpbToBeHashed NULL, // rgcbToBeHashed pbToBeHashed, pcbToBeHashed, pbComputedHash, pcbComputedHash ); } //+------------------------------------------------------------------------- // Verify a hashed message containing a detached hash. // The "to be hashed" content is passed in separately. No // decoded output. Otherwise, identical to CryptVerifyMessageHash. // // pcbComputedHash can be NULL, indicating the caller isn't interested // in getting the output. //-------------------------------------------------------------------------- BOOL WINAPI CryptVerifyDetachedMessageHash( IN PCRYPT_HASH_MESSAGE_PARA pHashPara, IN BYTE *pbDetachedHashBlob, IN DWORD cbDetachedHashBlob, IN DWORD cToBeHashed, IN const BYTE *rgpbToBeHashed[], IN DWORD rgcbToBeHashed[], OUT OPTIONAL BYTE *pbComputedHash, IN OUT OPTIONAL DWORD *pcbComputedHash ) { return DecodeHashMsg( pHashPara, pbDetachedHashBlob, cbDetachedHashBlob, cToBeHashed, rgpbToBeHashed, rgcbToBeHashed, NULL, // pbDecoded NULL, // pcbDecoded pbComputedHash, pcbComputedHash ); } //+------------------------------------------------------------------------- // Decodes a cryptographic message which may be one of the following types: // CMSG_DATA // CMSG_SIGNED // CMSG_ENVELOPED // CMSG_SIGNED_AND_ENVELOPED // CMSG_HASHED // // dwMsgTypeFlags specifies the set of allowable messages. For example, to // decode either SIGNED or ENVELOPED messages, set dwMsgTypeFlags to: // CMSG_SIGNED_FLAG | CMSG_ENVELOPED_FLAG. // // dwProvInnerContentType is only applicable when processing nested // crytographic messages. When processing an outer crytographic message // it must be set to 0. When decoding a nested cryptographic message // its the dwInnerContentType returned by a previous CryptDecodeMessage // of the outer message. The InnerContentType can be any of the CMSG types, // for example, CMSG_DATA, CMSG_SIGNED, ... // // The optional *pdwMsgType is updated with the type of message. // // The optional *pdwInnerContentType is updated with the type of the inner // message. Unless there is cryptographic message nesting, CMSG_DATA // is returned. // // For CMSG_DATA: returns decoded content. // For CMSG_SIGNED: same as CryptVerifyMessageSignature. // For CMSG_ENVELOPED: same as CryptDecryptMessage. // For CMSG_SIGNED_AND_ENVELOPED: same as CryptDecryptMessage plus // CryptVerifyMessageSignature. // For CMSG_HASHED: verifies the hash and returns decoded content. //-------------------------------------------------------------------------- BOOL WINAPI CryptDecodeMessage( IN DWORD dwMsgTypeFlags, IN PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, IN PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, IN DWORD dwSignerIndex, IN const BYTE *pbEncodedBlob, IN DWORD cbEncodedBlob, IN DWORD dwPrevInnerContentType, OUT OPTIONAL DWORD *pdwMsgType, OUT OPTIONAL DWORD *pdwInnerContentType, OUT OPTIONAL BYTE *pbDecoded, IN OUT OPTIONAL DWORD *pcbDecoded, OUT OPTIONAL PCCERT_CONTEXT *ppXchgCert, OUT OPTIONAL PCCERT_CONTEXT *ppSignerCert ) { #ifdef ENABLE_SCA_STREAM_TEST if ((pVerifyPara && (pVerifyPara->dwMsgAndCertEncodingType & SCA_STREAM_ENABLE_FLAG)) || (pDecryptPara && (pDecryptPara->dwMsgAndCertEncodingType & SCA_STREAM_ENABLE_FLAG))) return StreamDecodeMsg( dwMsgTypeFlags, pDecryptPara, pVerifyPara, dwSignerIndex, pbEncodedBlob, cbEncodedBlob, 0, // cToBeEncoded NULL, // rgpbToBeEncoded NULL, // rgcbToBeEncoded dwPrevInnerContentType, pdwMsgType, pdwInnerContentType, pbDecoded, pcbDecoded, ppXchgCert, ppSignerCert ); else #endif return DecodeMsg( dwMsgTypeFlags, pDecryptPara, pVerifyPara, dwSignerIndex, pbEncodedBlob, cbEncodedBlob, 0, // cToBeEncoded NULL, // rgpbToBeEncoded NULL, // rgcbToBeEncoded dwPrevInnerContentType, pdwMsgType, pdwInnerContentType, pbDecoded, pcbDecoded, ppXchgCert, ppSignerCert ); } //+------------------------------------------------------------------------- // Sign the message using the provider's private key specified in the // parameters. A dummy SignerId is created and stored in the message. // // Normally used until a certificate has been created for the key. //-------------------------------------------------------------------------- BOOL WINAPI CryptSignMessageWithKey( IN PCRYPT_KEY_SIGN_MESSAGE_PARA pSignPara, IN const BYTE *pbToBeSigned, IN DWORD cbToBeSigned, OUT BYTE *pbSignedBlob, IN OUT DWORD *pcbSignedBlob ) { BOOL fResult; CMSG_SIGNED_ENCODE_INFO SignedMsgEncodeInfo; CMSG_SIGNER_ENCODE_INFO SignerEncodeInfo; CERT_INFO CertInfo; DWORD dwSerialNumber = 0x12345678; #define NO_CERT_COMMON_NAME "NO CERT SIGNATURE" CERT_RDN rgRDN[1]; CERT_RDN_ATTR rgAttr[1]; CERT_NAME_INFO NameInfo; BYTE *pbNameEncoded = NULL; DWORD cbNameEncoded; assert(pSignPara->cbSize >= offsetof(CRYPT_KEY_SIGN_MESSAGE_PARA, pvHashAuxInfo) + sizeof(pSignPara->pvHashAuxInfo)); if (pSignPara->cbSize < offsetof(CRYPT_KEY_SIGN_MESSAGE_PARA, pvHashAuxInfo) + sizeof(pSignPara->pvHashAuxInfo)) goto InvalidArg; // Create a dummy issuer name NameInfo.cRDN = 1; NameInfo.rgRDN = rgRDN; rgRDN[0].cRDNAttr = 1; rgRDN[0].rgRDNAttr = rgAttr; rgAttr[0].pszObjId = szOID_COMMON_NAME; rgAttr[0].dwValueType = CERT_RDN_PRINTABLE_STRING; rgAttr[0].Value.pbData = (BYTE *) NO_CERT_COMMON_NAME; rgAttr[0].Value.cbData = strlen(NO_CERT_COMMON_NAME); cbNameEncoded = 0; CryptEncodeObject( pSignPara->dwMsgAndCertEncodingType, X509_NAME, &NameInfo, NULL, // pbEncoded &cbNameEncoded ); if (cbNameEncoded == 0) goto ErrorReturn; pbNameEncoded = (BYTE *) SCAAlloc(cbNameEncoded); if (pbNameEncoded == NULL) goto ErrorReturn; if (!CryptEncodeObject( pSignPara->dwMsgAndCertEncodingType, X509_NAME, &NameInfo, pbNameEncoded, &cbNameEncoded )) goto ErrorReturn; // CertInfo needs to only be initialized with issuer, serial number // and public key algorithm memset(&CertInfo, 0, sizeof(CertInfo)); CertInfo.Issuer.pbData = pbNameEncoded; CertInfo.Issuer.cbData = cbNameEncoded; CertInfo.SerialNumber.pbData = (BYTE *) &dwSerialNumber; CertInfo.SerialNumber.cbData = sizeof(dwSerialNumber); if (pSignPara->cbSize >= offsetof(CRYPT_KEY_SIGN_MESSAGE_PARA, PubKeyAlgorithm) + sizeof(pSignPara->PubKeyAlgorithm) && pSignPara->PubKeyAlgorithm.pszObjId && '\0' != *pSignPara->PubKeyAlgorithm.pszObjId) CertInfo.SubjectPublicKeyInfo.Algorithm = pSignPara->PubKeyAlgorithm; else CertInfo.SubjectPublicKeyInfo.Algorithm.pszObjId = CERT_DEFAULT_OID_PUBLIC_KEY_SIGN; memset(&SignerEncodeInfo, 0, sizeof(SignerEncodeInfo)); SignerEncodeInfo.cbSize = sizeof(SignerEncodeInfo); SignerEncodeInfo.pCertInfo = &CertInfo; SignerEncodeInfo.hCryptProv = pSignPara->hCryptProv; SignerEncodeInfo.dwKeySpec = pSignPara->dwKeySpec; SignerEncodeInfo.HashAlgorithm = pSignPara->HashAlgorithm; SignerEncodeInfo.pvHashAuxInfo = pSignPara->pvHashAuxInfo; memset(&SignedMsgEncodeInfo, 0, sizeof(SignedMsgEncodeInfo)); SignedMsgEncodeInfo.cbSize = sizeof(SignedMsgEncodeInfo); SignedMsgEncodeInfo.cSigners = 1; SignedMsgEncodeInfo.rgSigners = &SignerEncodeInfo; fResult = EncodeMsg( pSignPara->dwMsgAndCertEncodingType, 0, // dwFlags CMSG_SIGNED, &SignedMsgEncodeInfo, 1, // cToBeSigned &pbToBeSigned, &cbToBeSigned, FALSE, // fBareContent 0, // dwInnerContentType pbSignedBlob, pcbSignedBlob ); goto CommonReturn; InvalidArg: SetLastError((DWORD) E_INVALIDARG); ErrorReturn: fResult = FALSE; *pcbSignedBlob = 0; CommonReturn: if (pbNameEncoded) SCAFree(pbNameEncoded); return fResult; } //+------------------------------------------------------------------------- // Verify a signed message using the specified public key info. // // Normally called by a CA until it has created a certificate for the // key. // // pPublicKeyInfo contains the public key to use to verify the signed // message. If NULL, the signature isn't verified (for instance, the decoded // content may contain the PublicKeyInfo). // // pcbDecoded can be NULL, indicating the caller isn't interested // in getting the decoded content. //-------------------------------------------------------------------------- BOOL WINAPI CryptVerifyMessageSignatureWithKey( IN PCRYPT_KEY_VERIFY_MESSAGE_PARA pVerifyPara, IN OPTIONAL PCERT_PUBLIC_KEY_INFO pPublicKeyInfo, IN const BYTE *pbSignedBlob, IN DWORD cbSignedBlob, OUT OPTIONAL BYTE *pbDecoded, IN OUT OPTIONAL DWORD *pcbDecoded ) { BOOL fResult = TRUE; HCRYPTMSG hMsg = NULL; PCERT_INFO pCertInfo = NULL; DWORD cbData; DWORD dwMsgType; DWORD dwFlags; assert(pVerifyPara->cbSize == sizeof(CRYPT_KEY_VERIFY_MESSAGE_PARA)); if (pVerifyPara->cbSize != sizeof(CRYPT_KEY_VERIFY_MESSAGE_PARA)) goto InvalidArg; if (pbDecoded == NULL && pcbDecoded) *pcbDecoded = 0; if (pcbDecoded && *pcbDecoded == 0 && pPublicKeyInfo == NULL) dwFlags = CMSG_LENGTH_ONLY_FLAG; else dwFlags = 0; hMsg = CryptMsgOpenToDecode( pVerifyPara->dwMsgEncodingType, dwFlags, 0, // dwMsgType pVerifyPara->hCryptProv, NULL, // pRecipientInfo NULL // pStreamInfo ); if (hMsg == NULL) goto ErrorReturn; fResult = CryptMsgUpdate( hMsg, pbSignedBlob, cbSignedBlob, TRUE // fFinal ); if (!fResult) goto ErrorReturn; cbData = sizeof(dwMsgType); dwMsgType = 0; fResult = CryptMsgGetParam( hMsg, CMSG_TYPE_PARAM, 0, // dwIndex &dwMsgType, &cbData ); if (!fResult) goto ErrorReturn; if (dwMsgType != CMSG_SIGNED) { SetLastError((DWORD) CRYPT_E_UNEXPECTED_MSG_TYPE); goto ErrorReturn; } if (pPublicKeyInfo) { // Allocate and get the CERT_INFO containing the SignerId // (Issuer and SerialNumber) pCertInfo = (PCERT_INFO) AllocAndMsgGetParam( hMsg, CMSG_SIGNER_CERT_INFO_PARAM, 0 // dwSignerIndex ); if (pCertInfo == NULL) goto ErrorReturn; pCertInfo->SubjectPublicKeyInfo = *pPublicKeyInfo; fResult = CryptMsgControl( hMsg, 0, // dwFlags CMSG_CTRL_VERIFY_SIGNATURE, pCertInfo ); if (!fResult) goto ErrorReturn; } if (pcbDecoded) { fResult = CryptMsgGetParam( hMsg, CMSG_CONTENT_PARAM, 0, // dwIndex pbDecoded, pcbDecoded ); } goto CommonReturn; InvalidArg: SetLastError((DWORD) E_INVALIDARG); ErrorReturn: if (pcbDecoded) *pcbDecoded = 0; fResult = FALSE; CommonReturn: if (pCertInfo) SCAFree(pCertInfo); if (hMsg) CryptMsgClose(hMsg); // for success, preserves LastError return fResult; } //+------------------------------------------------------------------------- // SCA allocation and free routines //-------------------------------------------------------------------------- static void *SCAAlloc( IN size_t cbBytes ) { void *pv; pv = malloc(cbBytes); if (pv == NULL) SetLastError((DWORD) E_OUTOFMEMORY); return pv; } static void SCAFree( IN void *pv ) { if (pv) free(pv); } //+------------------------------------------------------------------------- // Null implementation of the callback get and verify signer certificate //-------------------------------------------------------------------------- static PCCERT_CONTEXT WINAPI NullGetSignerCertificate( IN void *pvGetArg, IN DWORD dwCertEncodingType, IN PCERT_INFO pSignerId, // Only the Issuer and SerialNumber // fields are used IN HCERTSTORE hMsgCertStore ) { return CertGetSubjectCertificateFromStore(hMsgCertStore, dwCertEncodingType, pSignerId); } //+------------------------------------------------------------------------- // Functions for initializing message encode information //-------------------------------------------------------------------------- static PCMSG_SIGNER_ENCODE_INFO InitSignerEncodeInfo( IN PCRYPT_SIGN_MESSAGE_PARA pSignPara ) { BOOL fResult; PCMSG_SIGNER_ENCODE_INFO pSigner = NULL; BOOL *pfDidCryptAcquire; DWORD cbSigner; #ifdef CMS_PKCS7 BYTE *pbHash; // not allocated #endif // CMS_PKCS7 DWORD dwAcquireFlags; if (pSignPara->pSigningCert == NULL) return NULL; // The flag indicating we did a CryptAcquireContext // follows the CMSG_SIGNER_ENCODE_INFO. If set, the HCRYPTPROV will need to be // released when SignerEncodeInfo is freed. cbSigner = sizeof(CMSG_SIGNER_ENCODE_INFO) + sizeof(BOOL); #ifdef CMS_PKCS7 if (pSignPara->dwFlags & CRYPT_MESSAGE_KEYID_SIGNER_FLAG) cbSigner += MAX_HASH_LEN; #endif // CMS_PKCS7 pSigner = (PCMSG_SIGNER_ENCODE_INFO) SCAAlloc(cbSigner); if (pSigner == NULL) goto ErrorReturn; memset(pSigner, 0, cbSigner); pSigner->cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO); pfDidCryptAcquire = (BOOL *) (((BYTE *) pSigner) + sizeof(CMSG_SIGNER_ENCODE_INFO)); pSigner->pCertInfo = pSignPara->pSigningCert->pCertInfo; pSigner->HashAlgorithm = pSignPara->HashAlgorithm; pSigner->pvHashAuxInfo = pSignPara->pvHashAuxInfo; dwAcquireFlags = CRYPT_ACQUIRE_USE_PROV_INFO_FLAG; if (pSignPara->dwFlags & CRYPT_MESSAGE_SILENT_KEYSET_FLAG) dwAcquireFlags |= CRYPT_ACQUIRE_SILENT_FLAG; fResult = CryptAcquireCertificatePrivateKey( pSignPara->pSigningCert, dwAcquireFlags, NULL, // pvReserved &pSigner->hCryptProv, &pSigner->dwKeySpec, pfDidCryptAcquire ); if (!fResult) goto ErrorReturn; if (pSignPara->cbSize >= STRUCT_CBSIZE(CRYPT_SIGN_MESSAGE_PARA, rgUnauthAttr)) { pSigner->cAuthAttr = pSignPara->cAuthAttr; pSigner->rgAuthAttr = pSignPara->rgAuthAttr; pSigner->cUnauthAttr = pSignPara->cUnauthAttr; pSigner->rgUnauthAttr = pSignPara->rgUnauthAttr; } #ifdef CMS_PKCS7 if (pSignPara->dwFlags & CRYPT_MESSAGE_KEYID_SIGNER_FLAG) { pbHash = (BYTE *) pfDidCryptAcquire + sizeof(*pfDidCryptAcquire); pSigner->SignerId.dwIdChoice = CERT_ID_KEY_IDENTIFIER; pSigner->SignerId.KeyId.pbData = pbHash; pSigner->SignerId.KeyId.cbData = MAX_HASH_LEN; if (!CertGetCertificateContextProperty( pSignPara->pSigningCert, CERT_KEY_IDENTIFIER_PROP_ID, pbHash, &pSigner->SignerId.KeyId.cbData )) goto ErrorReturn; } if (pSignPara->cbSize >= STRUCT_CBSIZE(CRYPT_SIGN_MESSAGE_PARA, pvHashEncryptionAuxInfo)) { pSigner->HashEncryptionAlgorithm = pSignPara->HashEncryptionAlgorithm; pSigner->pvHashEncryptionAuxInfo = pSignPara->pvHashEncryptionAuxInfo; } #endif // CMS_PKCS7 goto CommonReturn; ErrorReturn: if (pSigner) { FreeSignerEncodeInfo(pSigner); pSigner = NULL; } CommonReturn: return pSigner; } static void FreeSignerEncodeInfo( IN PCMSG_SIGNER_ENCODE_INFO pSigner ) { BOOL *pfDidCryptAcquire; if (pSigner == NULL) return; // The flag indicating we did a CryptAcquireContext // follows the CMSG_SIGNER_ENCODE_INFO. pfDidCryptAcquire = (BOOL *) (((BYTE *) pSigner) + sizeof(CMSG_SIGNER_ENCODE_INFO)); if (*pfDidCryptAcquire) { DWORD dwErr = GetLastError(); CryptReleaseContext(pSigner->hCryptProv, 0); SetLastError(dwErr); } SCAFree(pSigner); } static BOOL InitSignedCertAndCrl( IN PCRYPT_SIGN_MESSAGE_PARA pSignPara, OUT PCERT_BLOB *ppCertEncoded, OUT PCRL_BLOB *ppCrlEncoded ) { PCERT_BLOB pCertEncoded = NULL; PCRL_BLOB pCrlEncoded = NULL; DWORD cMsgCert = pSignPara->cMsgCert; DWORD cMsgCrl = pSignPara->cMsgCrl; BOOL fResult; DWORD dwIdx; if (cMsgCert) { pCertEncoded = (PCERT_BLOB) SCAAlloc(sizeof(CERT_BLOB) * cMsgCert); if (pCertEncoded == NULL) goto ErrorReturn; for (dwIdx = 0; dwIdx < cMsgCert; dwIdx++) { pCertEncoded[dwIdx].pbData = pSignPara->rgpMsgCert[dwIdx]->pbCertEncoded; pCertEncoded[dwIdx].cbData = pSignPara->rgpMsgCert[dwIdx]->cbCertEncoded; } } if (cMsgCrl) { pCrlEncoded = (PCRL_BLOB) SCAAlloc(sizeof(CRL_BLOB) * cMsgCrl); if (pCrlEncoded == NULL) goto ErrorReturn; for (dwIdx = 0; dwIdx < cMsgCrl; dwIdx++) { pCrlEncoded[dwIdx].pbData = pSignPara->rgpMsgCrl[dwIdx]->pbCrlEncoded; pCrlEncoded[dwIdx].cbData = pSignPara->rgpMsgCrl[dwIdx]->cbCrlEncoded; } } fResult = TRUE; goto CommonReturn; ErrorReturn: FreeSignedCertAndCrl(pCertEncoded, pCrlEncoded); pCertEncoded = NULL; pCrlEncoded = NULL; fResult = FALSE; CommonReturn: *ppCertEncoded = pCertEncoded; *ppCrlEncoded = pCrlEncoded; return fResult; } static void FreeSignedCertAndCrl( IN PCERT_BLOB pCertEncoded, IN PCRL_BLOB pCrlEncoded ) { if (pCertEncoded) SCAFree(pCertEncoded); if (pCrlEncoded) SCAFree(pCrlEncoded); } static BOOL InitSignedMsgEncodeInfo( IN PCRYPT_SIGN_MESSAGE_PARA pSignPara, OUT PCMSG_SIGNED_ENCODE_INFO pSignedMsgEncodeInfo ) { BOOL fResult = FALSE; assert(pSignPara->cbSize >= STRUCT_CBSIZE(CRYPT_SIGN_MESSAGE_PARA, rgpMsgCrl)); if (pSignPara->cbSize < STRUCT_CBSIZE(CRYPT_SIGN_MESSAGE_PARA, rgpMsgCrl)) SetLastError((DWORD) E_INVALIDARG); else { memset(pSignedMsgEncodeInfo, 0, sizeof(CMSG_SIGNED_ENCODE_INFO)); pSignedMsgEncodeInfo->cbSize = sizeof(CMSG_SIGNED_ENCODE_INFO); pSignedMsgEncodeInfo->cSigners = (pSignPara->pSigningCert != NULL) ? 1 : 0; pSignedMsgEncodeInfo->rgSigners = InitSignerEncodeInfo(pSignPara); if (pSignedMsgEncodeInfo->rgSigners || pSignedMsgEncodeInfo->cSigners == 0) { pSignedMsgEncodeInfo->cCertEncoded = pSignPara->cMsgCert; pSignedMsgEncodeInfo->cCrlEncoded = pSignPara->cMsgCrl; fResult = InitSignedCertAndCrl( pSignPara, &pSignedMsgEncodeInfo->rgCertEncoded, &pSignedMsgEncodeInfo->rgCrlEncoded ); if(!fResult) FreeSignerEncodeInfo(pSignedMsgEncodeInfo->rgSigners); } } if (!fResult) memset(pSignedMsgEncodeInfo, 0, sizeof(CMSG_SIGNED_ENCODE_INFO)); return fResult; } static void FreeSignedMsgEncodeInfo( IN PCRYPT_SIGN_MESSAGE_PARA pSignPara, IN PCMSG_SIGNED_ENCODE_INFO pSignedMsgEncodeInfo ) { FreeSignerEncodeInfo(pSignedMsgEncodeInfo->rgSigners); FreeSignedCertAndCrl( pSignedMsgEncodeInfo->rgCertEncoded, pSignedMsgEncodeInfo->rgCrlEncoded ); } #ifdef CMS_PKCS7 // Returned array of CMSG_RECIPIENT_ENCODE_INFOs needs to be SCAFree'd // // KeyAgree recipients use RC2 or 3DES wrap according // to the EncryptPara's ContentEncryptionAlgorithm static PCMSG_RECIPIENT_ENCODE_INFO InitCmsRecipientEncodeInfo( IN PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, IN DWORD cRecipientCert, IN PCCERT_CONTEXT rgpRecipientCert[], IN DWORD dwFlags ) { PCMSG_RECIPIENT_ENCODE_INFO pCmsRecipientEncodeInfo = NULL; DWORD cbCmsRecipientEncodeInfo; PCMSG_RECIPIENT_ENCODE_INFO pEncodeInfo; // not allocated PCMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO pKeyTrans; // not allocated PCMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO pKeyAgree; // not allocated PCMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO *ppEncryptedKey; // not allocated PCMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO pEncryptedKey; // not allocated PCCERT_CONTEXT *ppRecipientCert; // not allocated BYTE *pbHash = NULL; // not allocated assert(cRecipientCert); cbCmsRecipientEncodeInfo = sizeof(CMSG_RECIPIENT_ENCODE_INFO) * cRecipientCert + sizeof(CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO) * cRecipientCert + sizeof(CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO) * cRecipientCert + sizeof(CMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO *) * cRecipientCert + sizeof(CMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO) * cRecipientCert; if (dwFlags & CRYPT_MESSAGE_KEYID_RECIPIENT_FLAG) cbCmsRecipientEncodeInfo += MAX_HASH_LEN * cRecipientCert; pCmsRecipientEncodeInfo = (PCMSG_RECIPIENT_ENCODE_INFO) SCAAlloc(cbCmsRecipientEncodeInfo); if (NULL == pCmsRecipientEncodeInfo) goto OutOfMemory; memset(pCmsRecipientEncodeInfo, 0, cbCmsRecipientEncodeInfo); pEncodeInfo = pCmsRecipientEncodeInfo; pKeyTrans = (PCMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO) &pEncodeInfo[cRecipientCert]; pKeyAgree = (PCMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO) &pKeyTrans[cRecipientCert]; ppEncryptedKey = (PCMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO *) &pKeyAgree[cRecipientCert]; pEncryptedKey = (PCMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO) &ppEncryptedKey[cRecipientCert]; if (dwFlags & CRYPT_MESSAGE_KEYID_RECIPIENT_FLAG) pbHash = (BYTE *) &pEncryptedKey[cRecipientCert]; ppRecipientCert = rgpRecipientCert; for ( ; 0 < cRecipientCert; cRecipientCert--, pEncodeInfo++, pKeyTrans++, pKeyAgree++, ppEncryptedKey++, pEncryptedKey++, ppRecipientCert++) { PCERT_INFO pCertInfo = (*ppRecipientCert)->pCertInfo; PCERT_PUBLIC_KEY_INFO pPublicKeyInfo = &pCertInfo->SubjectPublicKeyInfo; PCCRYPT_OID_INFO pOIDInfo; PCERT_ID pRecipientId; ALG_ID aiPubKey; if (pOIDInfo = CryptFindOIDInfo( CRYPT_OID_INFO_OID_KEY, pPublicKeyInfo->Algorithm.pszObjId, CRYPT_PUBKEY_ALG_OID_GROUP_ID)) aiPubKey = pOIDInfo->Algid; else aiPubKey = 0; if (aiPubKey == CALG_DH_SF || aiPubKey == CALG_DH_EPHEM) { pEncodeInfo->dwRecipientChoice = CMSG_KEY_AGREE_RECIPIENT; pEncodeInfo->pKeyAgree = pKeyAgree; ALG_ID aiSymKey; pKeyAgree->cbSize = sizeof(*pKeyAgree); pKeyAgree->KeyEncryptionAlgorithm.pszObjId = szOID_RSA_SMIMEalgESDH; // pKeyAgree->pvKeyEncryptionAuxInfo = if (pOIDInfo = CryptFindOIDInfo( CRYPT_OID_INFO_OID_KEY, pEncryptPara->ContentEncryptionAlgorithm.pszObjId, CRYPT_ENCRYPT_ALG_OID_GROUP_ID)) aiSymKey = pOIDInfo->Algid; else aiSymKey = 0; if (CALG_3DES == aiSymKey) pKeyAgree->KeyWrapAlgorithm.pszObjId = szOID_RSA_SMIMEalgCMS3DESwrap; else { pKeyAgree->KeyWrapAlgorithm.pszObjId = szOID_RSA_SMIMEalgCMSRC2wrap; if (CALG_RC2 == aiSymKey) pKeyAgree->pvKeyWrapAuxInfo = pEncryptPara->pvEncryptionAuxInfo; } // pKeyAgree->hCryptProv = pKeyAgree->dwKeyChoice = CMSG_KEY_AGREE_EPHEMERAL_KEY_CHOICE; pKeyAgree->pEphemeralAlgorithm = &pPublicKeyInfo->Algorithm; // pKeyAgree->UserKeyingMaterial = pKeyAgree->cRecipientEncryptedKeys = 1; pKeyAgree->rgpRecipientEncryptedKeys = ppEncryptedKey; *ppEncryptedKey = pEncryptedKey; pEncryptedKey->cbSize = sizeof(*pEncryptedKey); pEncryptedKey->RecipientPublicKey = pPublicKeyInfo->PublicKey; pRecipientId = &pEncryptedKey->RecipientId; // pEncryptedKey->Date = // pEncryptedKey->pOtherAttr = } else { pEncodeInfo->dwRecipientChoice = CMSG_KEY_TRANS_RECIPIENT; pEncodeInfo->pKeyTrans = pKeyTrans; pKeyTrans->cbSize = sizeof(*pKeyTrans); pKeyTrans->KeyEncryptionAlgorithm = pPublicKeyInfo->Algorithm; // pKeyTrans->pvKeyEncryptionAuxInfo = // pKeyTrans->hCryptProv = pKeyTrans->RecipientPublicKey = pPublicKeyInfo->PublicKey; pRecipientId = &pKeyTrans->RecipientId; } if (dwFlags & CRYPT_MESSAGE_KEYID_RECIPIENT_FLAG) { pRecipientId->dwIdChoice = CERT_ID_KEY_IDENTIFIER; pRecipientId->KeyId.pbData = pbHash; pRecipientId->KeyId.cbData = MAX_HASH_LEN; if (!CertGetCertificateContextProperty( *ppRecipientCert, CERT_KEY_IDENTIFIER_PROP_ID, pbHash, &pRecipientId->KeyId.cbData )) goto GetKeyIdPropError; pbHash += MAX_HASH_LEN; } else { pRecipientId->dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER; pRecipientId->IssuerSerialNumber.Issuer = pCertInfo->Issuer; pRecipientId->IssuerSerialNumber.SerialNumber = pCertInfo->SerialNumber; } } CommonReturn: return pCmsRecipientEncodeInfo; ErrorReturn: SCAFree(pCmsRecipientEncodeInfo); pCmsRecipientEncodeInfo = NULL; goto CommonReturn; TRACE_ERROR(OutOfMemory) TRACE_ERROR(GetKeyIdPropError) } #else // Returned array of PCERT_INFOs needs to be SCAFree'd static PCERT_INFO *InitRecipientEncodeInfo( IN DWORD cRecipientCert, IN PCCERT_CONTEXT rgpRecipientCert[] ) { DWORD dwIdx; PCERT_INFO *ppRecipientEncodeInfo; if (cRecipientCert == 0) { SetLastError((DWORD) E_INVALIDARG); return NULL; } ppRecipientEncodeInfo = (PCERT_INFO *) SCAAlloc(sizeof(PCERT_INFO) * cRecipientCert); if (ppRecipientEncodeInfo != NULL) { for (dwIdx = 0; dwIdx < cRecipientCert; dwIdx++) ppRecipientEncodeInfo[dwIdx] = rgpRecipientCert[dwIdx]->pCertInfo; } return ppRecipientEncodeInfo; } #endif // CMS_PKCS7 static BOOL InitEnvelopedMsgEncodeInfo( IN PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, IN DWORD cRecipientCert, IN PCCERT_CONTEXT rgpRecipientCert[], OUT PCMSG_ENVELOPED_ENCODE_INFO pEnvelopedMsgEncodeInfo ) { BOOL fResult = FALSE; #ifdef CMS_PKCS7 PCMSG_RECIPIENT_ENCODE_INFO pCmsRecipientEncodeInfo = NULL; #else PCERT_INFO *ppRecipientEncodeInfo; #endif // CMS_PKCS7 assert(pEncryptPara->cbSize == sizeof(CRYPT_ENCRYPT_MESSAGE_PARA) || pEncryptPara->cbSize == offsetof(CRYPT_ENCRYPT_MESSAGE_PARA, dwFlags)); if (pEncryptPara->cbSize < offsetof(CRYPT_ENCRYPT_MESSAGE_PARA, dwFlags)) SetLastError((DWORD) E_INVALIDARG); else { #ifdef CMS_PKCS7 if (0 == cRecipientCert || (pCmsRecipientEncodeInfo = InitCmsRecipientEncodeInfo( pEncryptPara, cRecipientCert, rgpRecipientCert, pEncryptPara->cbSize >= sizeof(CRYPT_ENCRYPT_MESSAGE_PARA) ? pEncryptPara->dwFlags : 0 ))) { #else ppRecipientEncodeInfo = InitRecipientEncodeInfo( cRecipientCert, rgpRecipientCert ); if (ppRecipientEncodeInfo) { #endif // CMS_PKCS7 memset(pEnvelopedMsgEncodeInfo, 0, sizeof(CMSG_ENVELOPED_ENCODE_INFO)); pEnvelopedMsgEncodeInfo->cbSize = sizeof(CMSG_ENVELOPED_ENCODE_INFO); pEnvelopedMsgEncodeInfo->hCryptProv = pEncryptPara->hCryptProv; pEnvelopedMsgEncodeInfo->ContentEncryptionAlgorithm = pEncryptPara->ContentEncryptionAlgorithm; pEnvelopedMsgEncodeInfo->pvEncryptionAuxInfo = pEncryptPara->pvEncryptionAuxInfo; pEnvelopedMsgEncodeInfo->cRecipients = cRecipientCert; #ifdef CMS_PKCS7 pEnvelopedMsgEncodeInfo->rgCmsRecipients = pCmsRecipientEncodeInfo; #else pEnvelopedMsgEncodeInfo->rgpRecipients = ppRecipientEncodeInfo; #endif // CMS_PKCS7 fResult = TRUE; } else fResult = FALSE; } if (!fResult) memset(pEnvelopedMsgEncodeInfo, 0, sizeof(CMSG_ENVELOPED_ENCODE_INFO)); return fResult; } static void FreeEnvelopedMsgEncodeInfo( IN PCRYPT_ENCRYPT_MESSAGE_PARA pEncryptPara, IN PCMSG_ENVELOPED_ENCODE_INFO pEnvelopedMsgEncodeInfo ) { #ifdef CMS_PKCS7 if (pEnvelopedMsgEncodeInfo->rgCmsRecipients) SCAFree(pEnvelopedMsgEncodeInfo->rgCmsRecipients); #else if (pEnvelopedMsgEncodeInfo->rgpRecipients) SCAFree(pEnvelopedMsgEncodeInfo->rgpRecipients); #endif // CMS_PKCS7 } //+------------------------------------------------------------------------- // Encode the message. //-------------------------------------------------------------------------- static BOOL EncodeMsg( IN DWORD dwMsgEncodingType, IN DWORD dwFlags, IN DWORD dwMsgType, IN void *pvMsgEncodeInfo, IN DWORD cToBeEncoded, IN const BYTE *rgpbToBeEncoded[], IN DWORD rgcbToBeEncoded[], IN BOOL fBareContent, IN DWORD dwInnerContentType, OUT BYTE *pbEncodedBlob, IN OUT DWORD *pcbEncodedBlob ) { BOOL fResult; HCRYPTMSG hMsg = NULL; DWORD cbEncodedBlob; LPCSTR pszInnerContentOID; // Get input length and default return length to 0 if (pbEncodedBlob == NULL) cbEncodedBlob = 0; else cbEncodedBlob = *pcbEncodedBlob; *pcbEncodedBlob = 0; if (dwInnerContentType) pszInnerContentOID = MsgTypeToOID(dwInnerContentType); else pszInnerContentOID = NULL; if (0 == cbEncodedBlob) { DWORD c; DWORD cbTotal = 0; DWORD *pcb; for (c = cToBeEncoded, pcb = rgcbToBeEncoded; c > 0; c--, pcb++) cbTotal += *pcb; if (fBareContent) dwFlags |= CMSG_BARE_CONTENT_FLAG; if (0 == (*pcbEncodedBlob = CryptMsgCalculateEncodedLength( dwMsgEncodingType, dwFlags, dwMsgType, pvMsgEncodeInfo, (LPSTR) pszInnerContentOID, cbTotal ))) goto CalculateEncodedLengthError; if (pbEncodedBlob) goto LengthError; } else { if (NULL == (hMsg = CryptMsgOpenToEncode( dwMsgEncodingType, dwFlags, dwMsgType, pvMsgEncodeInfo, (LPSTR) pszInnerContentOID, NULL // pStreamInfo ))) goto OpenToEncodeError; if (0 == cToBeEncoded) { if (!CryptMsgUpdate( hMsg, NULL, // pbData 0, // cbData TRUE // fFinal )) goto UpdateError; } else { DWORD c; DWORD *pcb; const BYTE **ppb; for (c = cToBeEncoded, pcb = rgcbToBeEncoded, ppb = rgpbToBeEncoded; c > 0; c--, pcb++, ppb++) { if (!CryptMsgUpdate( hMsg, *ppb, *pcb, c == 1 // fFinal )) goto UpdateError; } } fResult = CryptMsgGetParam( hMsg, fBareContent ? CMSG_BARE_CONTENT_PARAM : CMSG_CONTENT_PARAM, 0, // dwIndex pbEncodedBlob, &cbEncodedBlob ); *pcbEncodedBlob = cbEncodedBlob; if (!fResult) goto ErrorReturn; // NO_TRACE } fResult = TRUE; CommonReturn: if (hMsg) CryptMsgClose(hMsg); // for success, preserves LastError return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(LengthError, ERROR_MORE_DATA) TRACE_ERROR(CalculateEncodedLengthError) TRACE_ERROR(OpenToEncodeError) TRACE_ERROR(UpdateError) } //+------------------------------------------------------------------------- // Decodes the message types: // CMSG_SIGNED // CMSG_ENVELOPED // CMSG_SIGNED_AND_ENVELOPED // CMSG_HASHED // // For detached signature (cToBeEncoded != 0), then, pcbDecoded == NULL. //-------------------------------------------------------------------------- static BOOL DecodeMsg( IN DWORD dwMsgTypeFlags, IN OPTIONAL PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, IN OPTIONAL PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, IN DWORD dwSignerIndex, IN const BYTE *pbEncodedBlob, IN DWORD cbEncodedBlob, IN DWORD cToBeEncoded, IN OPTIONAL const BYTE *rgpbToBeEncoded[], IN OPTIONAL DWORD rgcbToBeEncoded[], IN DWORD dwPrevInnerContentType, OUT OPTIONAL DWORD *pdwMsgType, OUT OPTIONAL DWORD *pdwInnerContentType, OUT OPTIONAL BYTE *pbDecoded, IN OUT OPTIONAL DWORD *pcbDecoded, OUT OPTIONAL PCCERT_CONTEXT *ppXchgCert, OUT OPTIONAL PCCERT_CONTEXT *ppSignerCert ) { BOOL fResult; HCRYPTMSG hMsg = NULL; DWORD cbData; DWORD dwMsgType; DWORD dwFlags; HCRYPTPROV hCryptProv; DWORD dwMsgEncodingType; DWORD cbDecoded; // Get input length and default return length to 0 cbDecoded = 0; if (pcbDecoded) { if (pbDecoded) cbDecoded = *pcbDecoded; *pcbDecoded = 0; } // Default optional return values to 0 if (pdwMsgType) *pdwMsgType = 0; if (pdwInnerContentType) *pdwInnerContentType = 0; if (ppXchgCert) *ppXchgCert = NULL; if (ppSignerCert) *ppSignerCert = NULL; if (pDecryptPara) { assert(pDecryptPara->cbSize >= STRUCT_CBSIZE(CRYPT_DECRYPT_MESSAGE_PARA, rghCertStore)); if (pDecryptPara->cbSize < STRUCT_CBSIZE(CRYPT_DECRYPT_MESSAGE_PARA, rghCertStore)) goto InvalidArg; } if (pVerifyPara) { assert(pVerifyPara->cbSize == sizeof(CRYPT_VERIFY_MESSAGE_PARA)); if (pVerifyPara->cbSize != sizeof(CRYPT_VERIFY_MESSAGE_PARA)) goto InvalidArg; hCryptProv = pVerifyPara->hCryptProv; dwMsgEncodingType = pVerifyPara->dwMsgAndCertEncodingType; } else { hCryptProv = 0; if (NULL == pDecryptPara) goto InvalidArg; dwMsgEncodingType = pDecryptPara->dwMsgAndCertEncodingType; } if (cToBeEncoded) dwFlags = CMSG_DETACHED_FLAG; else if (pcbDecoded && 0 == cbDecoded && NULL == ppXchgCert && NULL == ppSignerCert) dwFlags = CMSG_LENGTH_ONLY_FLAG; else dwFlags = 0; if (dwPrevInnerContentType) { dwMsgType = dwPrevInnerContentType; if (CMSG_DATA == dwMsgType) dwMsgType = 0; } else dwMsgType = 0; if (NULL == (hMsg = CryptMsgOpenToDecode( dwMsgEncodingType, dwFlags, dwMsgType, hCryptProv, NULL, // pRecipientInfo NULL // pStreamInfo ))) goto OpenToDecodeError; if (!CryptMsgUpdate( hMsg, pbEncodedBlob, cbEncodedBlob, TRUE // fFinal )) goto UpdateError; if (cToBeEncoded) { // Detached signature DWORD c; DWORD *pcb; const BYTE **ppb; for (c = cToBeEncoded, pcb = rgcbToBeEncoded, ppb = rgpbToBeEncoded; c > 0; c--, pcb++, ppb++) { if (!CryptMsgUpdate( hMsg, *ppb, *pcb, c == 1 // fFinal )) goto UpdateError; } } cbData = sizeof(dwMsgType); dwMsgType = 0; if (!CryptMsgGetParam( hMsg, CMSG_TYPE_PARAM, 0, // dwIndex &dwMsgType, &cbData )) goto GetTypeError; if (pdwMsgType) *pdwMsgType = dwMsgType; if (0 == ((1 << dwMsgType) & dwMsgTypeFlags)) goto UnexpectedMsgTypeError; if (pdwInnerContentType) { char szInnerContentType[128]; cbData = sizeof(szInnerContentType); if (!CryptMsgGetParam( hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, // dwIndex szInnerContentType, &cbData )) goto GetInnerContentTypeError; *pdwInnerContentType = OIDToMsgType(szInnerContentType); } if (0 == (dwFlags & CMSG_LENGTH_ONLY_FLAG)) { if (dwMsgType == CMSG_ENVELOPED || dwMsgType == CMSG_SIGNED_AND_ENVELOPED) { if (pDecryptPara == NULL) goto InvalidArg; if (!GetXchgCertAndDecrypt( pDecryptPara, hMsg, ppXchgCert )) goto GetXchgCertAndDecryptError; } if (dwMsgType == CMSG_SIGNED || dwMsgType == CMSG_SIGNED_AND_ENVELOPED) { if (pVerifyPara == NULL) goto InvalidArg; if (!GetSignerCertAndVerify( pVerifyPara, dwSignerIndex, hMsg, ppSignerCert )) goto GetSignerCertAndVerifyError; } } if (pcbDecoded) { fResult = CryptMsgGetParam( hMsg, CMSG_CONTENT_PARAM, 0, // dwIndex pbDecoded, &cbDecoded ); *pcbDecoded = cbDecoded; if (!fResult) goto ErrorReturn; // NO_TRACE } fResult = TRUE; CommonReturn: if (hMsg) CryptMsgClose(hMsg); // for success, preserves LastError return fResult; ErrorReturn: if (ppXchgCert && *ppXchgCert) { CertFreeCertificateContext(*ppXchgCert); *ppXchgCert = NULL; } if (ppSignerCert && *ppSignerCert) { CertFreeCertificateContext(*ppSignerCert); *ppSignerCert = NULL; } fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(OpenToDecodeError) TRACE_ERROR(UpdateError) TRACE_ERROR(GetTypeError) TRACE_ERROR(GetInnerContentTypeError) SET_ERROR(UnexpectedMsgTypeError, CRYPT_E_UNEXPECTED_MSG_TYPE) TRACE_ERROR(GetXchgCertAndDecryptError) TRACE_ERROR(GetSignerCertAndVerifyError) } #ifdef ENABLE_SCA_STREAM_TEST typedef struct _STREAM_OUTPUT_INFO { BYTE *pbData; DWORD cbData; DWORD cFinal; } STREAM_OUTPUT_INFO, *PSTREAM_OUTPUT_INFO; static void *SCARealloc( IN void *pvOrg, IN size_t cbBytes ) { void *pv; if (NULL == (pv = pvOrg ? realloc(pvOrg, cbBytes) : malloc(cbBytes))) SetLastError((DWORD) E_OUTOFMEMORY); return pv; } static BOOL WINAPI StreamOutputCallback( IN const void *pvArg, IN BYTE *pbData, IN DWORD cbData, IN BOOL fFinal ) { BOOL fResult = TRUE; PSTREAM_OUTPUT_INFO pInfo = (PSTREAM_OUTPUT_INFO) pvArg; if (fFinal) pInfo->cFinal++; if (cbData) { BYTE *pb; if (NULL == (pb = (BYTE *) SCARealloc(pInfo->pbData, pInfo->cbData + cbData))) fResult = FALSE; else { memcpy(pb + pInfo->cbData, pbData, cbData); pInfo->pbData = pb; pInfo->cbData += cbData; } } return fResult; } //+------------------------------------------------------------------------- // Encodes the message using streaming. //-------------------------------------------------------------------------- static BOOL StreamEncodeMsg( IN DWORD dwMsgEncodingType, IN DWORD dwFlags, IN DWORD dwMsgType, IN void *pvMsgEncodeInfo, IN DWORD cToBeEncoded, IN const BYTE *rgpbToBeEncoded[], IN DWORD rgcbToBeEncoded[], IN BOOL fBareContent, IN DWORD dwInnerContentType, OUT BYTE *pbEncodedBlob, IN OUT DWORD *pcbEncodedBlob ) { BOOL fResult; HCRYPTMSG hMsg = NULL; DWORD cbEncodedBlob; LPCSTR pszInnerContentOID; STREAM_OUTPUT_INFO OutputInfo; memset(&OutputInfo, 0, sizeof(OutputInfo)); CMSG_STREAM_INFO StreamInfo; memset(&StreamInfo, 0, sizeof(StreamInfo)); StreamInfo.pfnStreamOutput = StreamOutputCallback; StreamInfo.pvArg = (void *) &OutputInfo; if (dwFlags & SCA_INDEFINITE_STREAM_FLAG) StreamInfo.cbContent = CMSG_INDEFINITE_LENGTH; else { DWORD c; DWORD cbTotal = 0; DWORD *pcb; for (c = cToBeEncoded, pcb = rgcbToBeEncoded; c > 0; c--, pcb++) cbTotal += *pcb; StreamInfo.cbContent = cbTotal; } dwFlags &= ~(SCA_STREAM_ENABLE_FLAG | SCA_INDEFINITE_STREAM_FLAG); // Get input length and default return length to 0 if (pbEncodedBlob == NULL) cbEncodedBlob = 0; else cbEncodedBlob = *pcbEncodedBlob; *pcbEncodedBlob = 0; if (dwInnerContentType) pszInnerContentOID = MsgTypeToOID(dwInnerContentType); else pszInnerContentOID = NULL; { if (fBareContent) dwFlags |= CMSG_BARE_CONTENT_FLAG; if (NULL == (hMsg = CryptMsgOpenToEncode( dwMsgEncodingType, dwFlags, dwMsgType, pvMsgEncodeInfo, (LPSTR) pszInnerContentOID, &StreamInfo ))) goto OpenToEncodeError; if (0 == cToBeEncoded) { if (!CryptMsgUpdate( hMsg, NULL, // pbData 0, // cbData TRUE // fFinal )) goto UpdateError; } else { DWORD c; DWORD *pcb; const BYTE **ppb; for (c = cToBeEncoded, pcb = rgcbToBeEncoded, ppb = rgpbToBeEncoded; c > 0; c--, pcb++, ppb++) { BYTE *pbAlloc = NULL; const BYTE *pb = *ppb; if (NULL == pb) { pbAlloc = (BYTE *) SCAAlloc(*pcb); pb = pbAlloc; } fResult = CryptMsgUpdate( hMsg, pb, *pcb, c == 1 // fFinal ); if (pbAlloc) SCAFree(pbAlloc); if (!fResult) goto UpdateError; } } if (1 != OutputInfo.cFinal) goto BadStreamFinalCountError; *pcbEncodedBlob = OutputInfo.cbData; if (pbEncodedBlob) { if (cbEncodedBlob < OutputInfo.cbData) { SetLastError((DWORD) ERROR_MORE_DATA); goto ErrorReturn; // no trace } if (OutputInfo.cbData > 0) memcpy(pbEncodedBlob, OutputInfo.pbData, OutputInfo.cbData); } } fResult = TRUE; CommonReturn: SCAFree(OutputInfo.pbData); if (hMsg) CryptMsgClose(hMsg); // for success, preserves LastError return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(OpenToEncodeError) TRACE_ERROR(UpdateError) SET_ERROR(BadStreamFinalCountError, E_UNEXPECTED) } //+------------------------------------------------------------------------- // Decodes the message types: // CMSG_SIGNED // CMSG_ENVELOPED // CMSG_SIGNED_AND_ENVELOPED // CMSG_HASHED // // Uses streaming. //-------------------------------------------------------------------------- static BOOL StreamDecodeMsg( IN DWORD dwMsgTypeFlags, IN OPTIONAL PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, IN OPTIONAL PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, IN DWORD dwSignerIndex, IN const BYTE *pbEncodedBlob, IN DWORD cbEncodedBlob, IN DWORD cToBeEncoded, IN OPTIONAL const BYTE *rgpbToBeEncoded[], IN OPTIONAL DWORD rgcbToBeEncoded[], IN DWORD dwPrevInnerContentType, OUT OPTIONAL DWORD *pdwMsgType, OUT OPTIONAL DWORD *pdwInnerContentType, OUT OPTIONAL BYTE *pbDecoded, IN OUT OPTIONAL DWORD *pcbDecoded, OUT OPTIONAL PCCERT_CONTEXT *ppXchgCert, OUT OPTIONAL PCCERT_CONTEXT *ppSignerCert ) { BOOL fResult; HCRYPTMSG hMsg = NULL; DWORD cbData; DWORD dwMsgType; DWORD dwFlags; HCRYPTPROV hCryptProv; DWORD dwMsgEncodingType; DWORD cbDecoded; STREAM_OUTPUT_INFO OutputInfo; memset(&OutputInfo, 0, sizeof(OutputInfo)); CMSG_STREAM_INFO StreamInfo; memset(&StreamInfo, 0, sizeof(StreamInfo)); StreamInfo.pfnStreamOutput = StreamOutputCallback; StreamInfo.pvArg = (void *) &OutputInfo; StreamInfo.cbContent = CMSG_INDEFINITE_LENGTH; // Get input length and default return length to 0 cbDecoded = 0; if (pcbDecoded) { if (pbDecoded) cbDecoded = *pcbDecoded; *pcbDecoded = 0; } // Default optional return values to 0 if (pdwMsgType) *pdwMsgType = 0; if (pdwInnerContentType) *pdwInnerContentType = 0; if (ppXchgCert) *ppXchgCert = NULL; if (ppSignerCert) *ppSignerCert = NULL; if (pDecryptPara) { assert(pDecryptPara->cbSize >= STRUCT_CBSIZE(CRYPT_DECRYPT_MESSAGE_PARA, rghCertStore)); if (pDecryptPara->cbSize < STRUCT_CBSIZE(CRYPT_DECRYPT_MESSAGE_PARA, rghCertStore)) goto InvalidArg; } if (pVerifyPara) { assert(pVerifyPara->cbSize == sizeof(CRYPT_VERIFY_MESSAGE_PARA)); if (pVerifyPara->cbSize != sizeof(CRYPT_VERIFY_MESSAGE_PARA)) goto InvalidArg; hCryptProv = pVerifyPara->hCryptProv; dwMsgEncodingType = pVerifyPara->dwMsgAndCertEncodingType; } else { hCryptProv = 0; if (NULL == pDecryptPara) goto InvalidArg; dwMsgEncodingType = pDecryptPara->dwMsgAndCertEncodingType; } dwMsgEncodingType &= ~SCA_STREAM_ENABLE_FLAG; if (cToBeEncoded) dwFlags = CMSG_DETACHED_FLAG; else dwFlags = 0; if (dwPrevInnerContentType) { dwMsgType = dwPrevInnerContentType; if (CMSG_DATA == dwMsgType) dwMsgType = 0; } else dwMsgType = 0; if (NULL == (hMsg = CryptMsgOpenToDecode( dwMsgEncodingType, dwFlags, dwMsgType, hCryptProv, NULL, // pRecipientInfo &StreamInfo ))) goto OpenToDecodeError; { #if 1 DWORD cbDelta = cbEncodedBlob / 10; #else DWORD cbDelta = 1; #endif DWORD cbRemain = cbEncodedBlob; const BYTE *pb = pbEncodedBlob; do { DWORD cb; if (cbRemain > cbDelta) cb = cbDelta; else cb = cbRemain; if (!CryptMsgUpdate( hMsg, pb, cb, cbRemain == cb // fFinal )) goto UpdateError; pb += cb; cbRemain -= cb; } while (0 != cbRemain); } if (cToBeEncoded) { // Detached signature DWORD c; DWORD *pcb; const BYTE **ppb; for (c = cToBeEncoded, pcb = rgcbToBeEncoded, ppb = rgpbToBeEncoded; c > 0; c--, pcb++, ppb++) { if (!CryptMsgUpdate( hMsg, *ppb, *pcb, c == 1 // fFinal )) goto UpdateError; } } cbData = sizeof(dwMsgType); dwMsgType = 0; if (!CryptMsgGetParam( hMsg, CMSG_TYPE_PARAM, 0, // dwIndex &dwMsgType, &cbData )) goto GetTypeError; if (pdwMsgType) *pdwMsgType = dwMsgType; if (0 == ((1 << dwMsgType) & dwMsgTypeFlags)) goto UnexpectedMsgTypeError; if (pdwInnerContentType) { char szInnerContentType[128]; cbData = sizeof(szInnerContentType); if (!CryptMsgGetParam( hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, // dwIndex szInnerContentType, &cbData )) goto GetInnerContentTypeError; *pdwInnerContentType = OIDToMsgType(szInnerContentType); } if (pcbDecoded && 0 == cbDecoded && NULL == ppXchgCert && NULL == ppSignerCert && dwMsgType != CMSG_ENVELOPED) ; // Length only else { if (dwMsgType == CMSG_ENVELOPED || dwMsgType == CMSG_SIGNED_AND_ENVELOPED) { if (pDecryptPara == NULL) goto InvalidArg; if (!GetXchgCertAndDecrypt( pDecryptPara, hMsg, ppXchgCert )) goto GetXchgCertAndDecryptError; } if (dwMsgType == CMSG_SIGNED || dwMsgType == CMSG_SIGNED_AND_ENVELOPED) { if (pVerifyPara == NULL) goto InvalidArg; if (!GetSignerCertAndVerify( pVerifyPara, dwSignerIndex, hMsg, ppSignerCert )) goto GetSignerCertAndVerifyError; } } if (1 != OutputInfo.cFinal) goto BadStreamFinalCountError; if (pcbDecoded) { *pcbDecoded = OutputInfo.cbData; if (pbDecoded) { if (cbDecoded < OutputInfo.cbData) { SetLastError((DWORD) ERROR_MORE_DATA); goto ErrorReturn; // no trace } if (OutputInfo.cbData > 0) memcpy(pbDecoded, OutputInfo.pbData, OutputInfo.cbData); } } fResult = TRUE; CommonReturn: SCAFree(OutputInfo.pbData); if (hMsg) CryptMsgClose(hMsg); // for success, preserves LastError return fResult; ErrorReturn: if (ppXchgCert && *ppXchgCert) { CertFreeCertificateContext(*ppXchgCert); *ppXchgCert = NULL; } if (ppSignerCert && *ppSignerCert) { CertFreeCertificateContext(*ppSignerCert); *ppSignerCert = NULL; } fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(OpenToDecodeError) TRACE_ERROR(UpdateError) TRACE_ERROR(GetTypeError) TRACE_ERROR(GetInnerContentTypeError) SET_ERROR(UnexpectedMsgTypeError, CRYPT_E_UNEXPECTED_MSG_TYPE) TRACE_ERROR(GetXchgCertAndDecryptError) TRACE_ERROR(GetSignerCertAndVerifyError) SET_ERROR(BadStreamFinalCountError, E_UNEXPECTED) } #endif // ENABLE_SCA_STREAM_TEST //+------------------------------------------------------------------------- // Decodes the HASHED message type // // For detached hash (cToBeHashed != 0), then, pcbDecoded == NULL. //-------------------------------------------------------------------------- static BOOL DecodeHashMsg( IN PCRYPT_HASH_MESSAGE_PARA pHashPara, IN const BYTE *pbEncodedBlob, IN DWORD cbEncodedBlob, IN DWORD cToBeHashed, IN OPTIONAL const BYTE *rgpbToBeHashed[], IN OPTIONAL DWORD rgcbToBeHashed[], OUT OPTIONAL BYTE *pbDecoded, IN OUT OPTIONAL DWORD *pcbDecoded, OUT OPTIONAL BYTE *pbComputedHash, IN OUT OPTIONAL DWORD *pcbComputedHash ) { BOOL fResult; HCRYPTMSG hMsg = NULL; DWORD cbData; DWORD dwMsgType; DWORD dwFlags; HCRYPTPROV hCryptProv; DWORD dwMsgEncodingType; DWORD cbDecoded; DWORD cbComputedHash; // Get input lengths and default return lengths to 0 cbDecoded = 0; if (pcbDecoded) { if (pbDecoded) cbDecoded = *pcbDecoded; *pcbDecoded = 0; } cbComputedHash = 0; if (pcbComputedHash) { if (pbComputedHash) cbComputedHash = *pcbComputedHash; *pcbComputedHash = 0; } assert(pHashPara->cbSize == sizeof(CRYPT_HASH_MESSAGE_PARA)); if (pHashPara->cbSize != sizeof(CRYPT_HASH_MESSAGE_PARA)) goto InvalidArg; hCryptProv = pHashPara->hCryptProv; dwMsgEncodingType = pHashPara->dwMsgEncodingType; if (cToBeHashed) dwFlags = CMSG_DETACHED_FLAG; else if (0 == cbDecoded && NULL == pcbComputedHash) dwFlags = CMSG_LENGTH_ONLY_FLAG; else dwFlags = 0; if (NULL == (hMsg = CryptMsgOpenToDecode( dwMsgEncodingType, dwFlags, 0, // dwMsgType hCryptProv, NULL, // pRecipientInfo NULL // pStreamInfo ))) goto OpenToDecodeError; if (!CryptMsgUpdate( hMsg, pbEncodedBlob, cbEncodedBlob, TRUE // fFinal )) goto UpdateError; if (cToBeHashed) { // Detached signature or hash DWORD c = 0; DWORD *pcb; const BYTE **ppb; for (c = cToBeHashed, pcb = rgcbToBeHashed, ppb = rgpbToBeHashed; c > 0; c--, pcb++, ppb++) { if (!CryptMsgUpdate( hMsg, *ppb, *pcb, c == 1 // fFinal )) goto UpdateError; } } cbData = sizeof(dwMsgType); dwMsgType = 0; if (!CryptMsgGetParam( hMsg, CMSG_TYPE_PARAM, 0, // dwIndex &dwMsgType, &cbData )) goto GetTypeError; if (dwMsgType != CMSG_HASHED) goto UnexpectedMsgTypeError; if (0 == (dwFlags & CMSG_LENGTH_ONLY_FLAG)) { if (!CryptMsgControl( hMsg, 0, // dwFlags CMSG_CTRL_VERIFY_HASH, NULL // pvCtrlPara )) goto ControlError; } fResult = TRUE; if (pcbDecoded) { fResult = CryptMsgGetParam( hMsg, CMSG_CONTENT_PARAM, 0, // dwIndex pbDecoded, &cbDecoded ); *pcbDecoded = cbDecoded; } if (pcbComputedHash) { DWORD dwErr = 0; BOOL fResult2; if (!fResult) dwErr = GetLastError(); fResult2 = CryptMsgGetParam( hMsg, CMSG_COMPUTED_HASH_PARAM, 0, // dwIndex pbComputedHash, &cbComputedHash ); *pcbComputedHash = cbComputedHash; if (!fResult2) fResult = FALSE; else if (!fResult) SetLastError(dwErr); } if (!fResult) goto ErrorReturn; // NO_TRACE CommonReturn: if (hMsg) CryptMsgClose(hMsg); // for success, preserves LastError return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(OpenToDecodeError) TRACE_ERROR(UpdateError) TRACE_ERROR(GetTypeError) SET_ERROR(UnexpectedMsgTypeError, CRYPT_E_UNEXPECTED_MSG_TYPE) TRACE_ERROR(ControlError) } //+------------------------------------------------------------------------- // Get certificate for and verify the message's signer. //-------------------------------------------------------------------------- static BOOL GetSignerCertAndVerify( IN PCRYPT_VERIFY_MESSAGE_PARA pVerifyPara, IN DWORD dwSignerIndex, IN HCRYPTMSG hMsg, OUT OPTIONAL PCCERT_CONTEXT *ppSignerCert ) { BOOL fResult; BOOL fNoSigner = FALSE; PCERT_INFO pSignerId = NULL; PCCERT_CONTEXT pSignerCert = NULL; HCERTSTORE hMsgCertStore = NULL; DWORD dwLastError = 0; { // First, get count of signers in the message and verify the // dwSignerIndex DWORD cSigner = 0; DWORD cbData = sizeof(cSigner); if (!CryptMsgGetParam( hMsg, CMSG_SIGNER_COUNT_PARAM, 0, // dwIndex &cSigner, &cbData )) goto ErrorReturn; if (cSigner <= dwSignerIndex) fNoSigner = TRUE; } if (!fNoSigner) { // Allocate and get the CERT_INFO containing the SignerId // (Issuer and SerialNumber) if (NULL == (pSignerId = (PCERT_INFO) AllocAndMsgGetParam( hMsg, CMSG_SIGNER_CERT_INFO_PARAM, dwSignerIndex ))) goto ErrorReturn; } // Open a cert store initialized with certs and CRLs from the message hMsgCertStore = CertOpenStore( CERT_STORE_PROV_MSG, #ifdef ENABLE_SCA_STREAM_TEST pVerifyPara->dwMsgAndCertEncodingType &= ~SCA_STREAM_ENABLE_FLAG, #else pVerifyPara->dwMsgAndCertEncodingType, #endif pVerifyPara->hCryptProv, CERT_STORE_NO_CRYPT_RELEASE_FLAG, hMsg // pvPara ); if (hMsgCertStore == NULL) goto ErrorReturn; if (pVerifyPara->pfnGetSignerCertificate) pSignerCert = pVerifyPara->pfnGetSignerCertificate( pVerifyPara->pvGetArg, #ifdef ENABLE_SCA_STREAM_TEST pVerifyPara->dwMsgAndCertEncodingType &= ~SCA_STREAM_ENABLE_FLAG, #else pVerifyPara->dwMsgAndCertEncodingType, #endif pSignerId, hMsgCertStore ); else pSignerCert = NullGetSignerCertificate( NULL, #ifdef ENABLE_SCA_STREAM_TEST pVerifyPara->dwMsgAndCertEncodingType &= ~SCA_STREAM_ENABLE_FLAG, #else pVerifyPara->dwMsgAndCertEncodingType, #endif pSignerId, hMsgCertStore ); if (fNoSigner) goto NoSigner; if (pSignerCert == NULL) goto ErrorReturn; #ifdef CMS_PKCS7 { CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA CtrlPara; memset(&CtrlPara, 0, sizeof(CtrlPara)); CtrlPara.cbSize = sizeof(CtrlPara); // CtrlPara.hCryptProv = CtrlPara.dwSignerIndex = dwSignerIndex; CtrlPara.dwSignerType = CMSG_VERIFY_SIGNER_CERT; CtrlPara.pvSigner = (void *) pSignerCert; if (!CryptMsgControl( hMsg, 0, // dwFlags CMSG_CTRL_VERIFY_SIGNATURE_EX, &CtrlPara )) { if (CRYPT_E_MISSING_PUBKEY_PARA != GetLastError()) goto ErrorReturn; else { PCCERT_CHAIN_CONTEXT pChainContext; CERT_CHAIN_PARA ChainPara; // Build a chain. Hopefully, the signer inherit's its public key // parameters from up the chain memset(&ChainPara, 0, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara); if (CertGetCertificateChain( NULL, // hChainEngine pSignerCert, NULL, // pTime hMsgCertStore, &ChainPara, CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL, NULL, // pvReserved &pChainContext )) CertFreeCertificateChain(pChainContext); // Try again. Hopefully the above chain building updated the // signer's context property with the missing public key // parameters if (!CryptMsgControl( hMsg, 0, // dwFlags CMSG_CTRL_VERIFY_SIGNATURE_EX, &CtrlPara)) goto ErrorReturn; } } } #else if (!CryptMsgControl( hMsg, 0, // dwFlags CMSG_CTRL_VERIFY_SIGNATURE, pSignerCert->pCertInfo )) goto ErrorReturn; #endif // CMS_PKCS7 if (ppSignerCert) *ppSignerCert = pSignerCert; else CertFreeCertificateContext(pSignerCert); fResult = TRUE; goto CommonReturn; NoSigner: SetLastError((DWORD) CRYPT_E_NO_SIGNER); ErrorReturn: if (pSignerCert) CertFreeCertificateContext(pSignerCert); if (ppSignerCert) *ppSignerCert = NULL; fResult = FALSE; dwLastError = GetLastError(); CommonReturn: if (pSignerId) SCAFree(pSignerId); if (hMsgCertStore) CertCloseStore(hMsgCertStore, 0); if (dwLastError) SetLastError(dwLastError); return fResult; } #ifdef CMS_PKCS7 static BOOL GetXchgCertAndDecrypt( IN PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, IN HCRYPTMSG hMsg, OUT OPTIONAL PCCERT_CONTEXT *ppXchgCert ) { BOOL fResult; PCMSG_CMS_RECIPIENT_INFO pRecipientInfo = NULL; PCCERT_CONTEXT pXchgCert = NULL; DWORD cRecipient; DWORD cbData; DWORD dwRecipientIdx; // Get # of CMS recipients in the message. cbData = sizeof(cRecipient); cRecipient = 0; fResult = CryptMsgGetParam( hMsg, CMSG_CMS_RECIPIENT_COUNT_PARAM, 0, // dwIndex &cRecipient, &cbData ); if (!fResult) goto ErrorReturn; if (cRecipient == 0) { SetLastError((DWORD) CRYPT_E_RECIPIENT_NOT_FOUND); goto ErrorReturn; } // Loop through the recipients in the message until we find a // recipient cert in one of the stores with either the // CERT_KEY_CONTEXT_PROP_ID or CERT_KEY_PROV_INFO_PROP_ID. for (dwRecipientIdx = 0; dwRecipientIdx < cRecipient; dwRecipientIdx++) { DWORD dwRecipientChoice; PCMSG_KEY_TRANS_RECIPIENT_INFO pKeyTrans = NULL; PCMSG_KEY_AGREE_RECIPIENT_INFO pKeyAgree = NULL; DWORD cRecipientEncryptedKeys; PCMSG_RECIPIENT_ENCRYPTED_KEY_INFO *ppRecipientEncryptedKey = NULL; DWORD dwRecipientEncryptedKeyIndex; pRecipientInfo = (PCMSG_CMS_RECIPIENT_INFO) AllocAndMsgGetParam( hMsg, CMSG_CMS_RECIPIENT_INFO_PARAM, dwRecipientIdx ); if (pRecipientInfo == NULL) goto ErrorReturn; dwRecipientChoice = pRecipientInfo->dwRecipientChoice; switch (dwRecipientChoice) { case CMSG_KEY_TRANS_RECIPIENT: pKeyTrans = pRecipientInfo->pKeyTrans; cRecipientEncryptedKeys = 1; break; case CMSG_KEY_AGREE_RECIPIENT: pKeyAgree = pRecipientInfo->pKeyAgree; if (CMSG_KEY_AGREE_ORIGINATOR_PUBLIC_KEY != pKeyAgree->dwOriginatorChoice) { SCAFree(pRecipientInfo); pRecipientInfo = NULL; continue; } cRecipientEncryptedKeys = pKeyAgree->cRecipientEncryptedKeys; ppRecipientEncryptedKey = pKeyAgree->rgpRecipientEncryptedKeys; break; default: SCAFree(pRecipientInfo); pRecipientInfo = NULL; continue; } for (dwRecipientEncryptedKeyIndex = 0; dwRecipientEncryptedKeyIndex < cRecipientEncryptedKeys; dwRecipientEncryptedKeyIndex++) { PCERT_ID pRecipientId; DWORD dwStoreIdx; if (CMSG_KEY_TRANS_RECIPIENT == dwRecipientChoice) pRecipientId = &pKeyTrans->RecipientId; else { pRecipientId = &ppRecipientEncryptedKey[ dwRecipientEncryptedKeyIndex]->RecipientId; } for (dwStoreIdx = 0; dwStoreIdx < pDecryptPara->cCertStore; dwStoreIdx++) { pXchgCert = CertFindCertificateInStore( pDecryptPara->rghCertStore[dwStoreIdx], pDecryptPara->dwMsgAndCertEncodingType, 0, // dwFindFlags CERT_FIND_CERT_ID, pRecipientId, NULL // pPrevCertContext ); if (pXchgCert) { HCRYPTPROV hCryptProv; DWORD dwKeySpec; BOOL fDidCryptAcquire; DWORD dwAcquireFlags; dwAcquireFlags = CRYPT_ACQUIRE_USE_PROV_INFO_FLAG; if (pDecryptPara->cbSize >= STRUCT_CBSIZE(CRYPT_DECRYPT_MESSAGE_PARA, dwFlags) && (pDecryptPara->dwFlags & CRYPT_MESSAGE_SILENT_KEYSET_FLAG)) dwAcquireFlags |= CRYPT_ACQUIRE_SILENT_FLAG; fResult = CryptAcquireCertificatePrivateKey( pXchgCert, dwAcquireFlags, NULL, // pvReserved &hCryptProv, &dwKeySpec, &fDidCryptAcquire ); if (fResult) { if (CMSG_KEY_TRANS_RECIPIENT == dwRecipientChoice) { CMSG_CTRL_KEY_TRANS_DECRYPT_PARA Para; memset(&Para, 0, sizeof(Para)); Para.cbSize = sizeof(Para); Para.hCryptProv = hCryptProv; Para.dwKeySpec = dwKeySpec; Para.pKeyTrans = pKeyTrans; Para.dwRecipientIndex = dwRecipientIdx; fResult = CryptMsgControl( hMsg, 0, // dwFlags CMSG_CTRL_KEY_TRANS_DECRYPT, &Para ); } else { CMSG_CTRL_KEY_AGREE_DECRYPT_PARA Para; memset(&Para, 0, sizeof(Para)); Para.cbSize = sizeof(Para); Para.hCryptProv = hCryptProv; Para.dwKeySpec = dwKeySpec; Para.pKeyAgree = pKeyAgree; Para.dwRecipientIndex = dwRecipientIdx; Para.dwRecipientEncryptedKeyIndex = dwRecipientEncryptedKeyIndex; Para.OriginatorPublicKey = pKeyAgree->OriginatorPublicKeyInfo.PublicKey; fResult = CryptMsgControl( hMsg, 0, // dwFlags CMSG_CTRL_KEY_AGREE_DECRYPT, &Para ); } if (fDidCryptAcquire) { DWORD dwErr = GetLastError(); CryptReleaseContext(hCryptProv, 0); SetLastError(dwErr); } if (fResult) { if (ppXchgCert) *ppXchgCert = pXchgCert; else CertFreeCertificateContext(pXchgCert); goto CommonReturn; } else goto ErrorReturn; } CertFreeCertificateContext(pXchgCert); pXchgCert = NULL; } } } SCAFree(pRecipientInfo); pRecipientInfo = NULL; } SetLastError((DWORD) CRYPT_E_NO_DECRYPT_CERT); ErrorReturn: if (pXchgCert) CertFreeCertificateContext(pXchgCert); if (ppXchgCert) *ppXchgCert = NULL; fResult = FALSE; CommonReturn: if (pRecipientInfo) SCAFree(pRecipientInfo); return fResult; } #else //+------------------------------------------------------------------------- // Get a certificate with a key provider property for one of the message's // recipients and use to decrypt the message. //-------------------------------------------------------------------------- static BOOL GetXchgCertAndDecrypt( IN PCRYPT_DECRYPT_MESSAGE_PARA pDecryptPara, IN HCRYPTMSG hMsg, OUT OPTIONAL PCCERT_CONTEXT *ppXchgCert ) { BOOL fResult; PCERT_INFO pRecipientId = NULL; PCCERT_CONTEXT pXchgCert = NULL; DWORD cRecipient; DWORD cbData; DWORD dwRecipientIdx; DWORD dwStoreIdx; // Get # of recipients in the message. cbData = sizeof(cRecipient); cRecipient = 0; fResult = CryptMsgGetParam( hMsg, CMSG_RECIPIENT_COUNT_PARAM, 0, // dwIndex &cRecipient, &cbData ); if (!fResult) goto ErrorReturn; if (cRecipient == 0) { SetLastError((DWORD) CRYPT_E_RECIPIENT_NOT_FOUND); goto ErrorReturn; } // Loop through the recipients in the message until we find a // recipient cert in one of the stores with either the // CERT_KEY_CONTEXT_PROP_ID or CERT_KEY_PROV_INFO_PROP_ID. for (dwRecipientIdx = 0; dwRecipientIdx < cRecipient; dwRecipientIdx++) { // Allocate and get the CERT_INFO containing the RecipientId // (Issuer and SerialNumber) pRecipientId = (PCERT_INFO) AllocAndMsgGetParam( hMsg, CMSG_RECIPIENT_INFO_PARAM, dwRecipientIdx ); if (pRecipientId == NULL) goto ErrorReturn; for (dwStoreIdx = 0; dwStoreIdx < pDecryptPara->cCertStore; dwStoreIdx++) { pXchgCert = CertGetSubjectCertificateFromStore( pDecryptPara->rghCertStore[dwStoreIdx], pDecryptPara->dwMsgAndCertEncodingType, pRecipientId ); if (pXchgCert) { CMSG_CTRL_DECRYPT_PARA Para; BOOL fDidCryptAcquire; Para.cbSize = sizeof(CMSG_CTRL_DECRYPT_PARA); fResult = CryptAcquireCertificatePrivateKey( pXchgCert, CRYPT_ACQUIRE_USE_PROV_INFO_FLAG, NULL, // pvReserved &Para.hCryptProv, &Para.dwKeySpec, &fDidCryptAcquire ); if (fResult) { Para.dwRecipientIndex = dwRecipientIdx; fResult = CryptMsgControl( hMsg, 0, // dwFlags CMSG_CTRL_DECRYPT, &Para ); if (fDidCryptAcquire) { DWORD dwErr = GetLastError(); CryptReleaseContext(Para.hCryptProv, 0); SetLastError(dwErr); } if (fResult) { if (ppXchgCert) *ppXchgCert = pXchgCert; else CertFreeCertificateContext(pXchgCert); goto CommonReturn; } else goto ErrorReturn; } CertFreeCertificateContext(pXchgCert); pXchgCert = NULL; } } SCAFree(pRecipientId); pRecipientId = NULL; } SetLastError((DWORD) CRYPT_E_NO_DECRYPT_CERT); ErrorReturn: if (pXchgCert) CertFreeCertificateContext(pXchgCert); if (ppXchgCert) *ppXchgCert = NULL; fResult = FALSE; CommonReturn: if (pRecipientId) SCAFree(pRecipientId); return fResult; } #endif // CMS_PKCS7 //+------------------------------------------------------------------------- // Allocate and get the CMSG_SIGNER_CERT_INFO_PARAM or CMSG_RECIPIENT_INFO_PARAM // from the message //-------------------------------------------------------------------------- static void * AllocAndMsgGetParam( IN HCRYPTMSG hMsg, IN DWORD dwParamType, IN DWORD dwIndex ) { BOOL fResult; void *pvData; DWORD cbData; // First get the length of the CertId's CERT_INFO cbData = 0; CryptMsgGetParam( hMsg, dwParamType, dwIndex, NULL, // pvData &cbData ); if (cbData == 0) return NULL; pvData = SCAAlloc(cbData); if (pvData == NULL) return NULL; fResult = CryptMsgGetParam( hMsg, dwParamType, dwIndex, pvData, &cbData ); if (fResult) return pvData; else { SCAFree(pvData); return NULL; } }