Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3664 lines
120 KiB

//+-------------------------------------------------------------------------
//
// 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 <dbgdef.h>
// #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;
}
}