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
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;
|
|
}
|
|
}
|