//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: msghlpr.cpp // // Contents: Cryptographic Message Helper APIs // // APIs: CryptMsgGetAndVerifySigner // CryptMsgSignCTL // CryptMsgEncodeAndSignCTL // // History: 02-May-97 philh created //-------------------------------------------------------------------------- #include "global.hxx" #include "pkialloc.h" void *ICM_AllocAndGetMsgParam( IN HCRYPTMSG hMsg, IN DWORD dwParamType, IN DWORD dwIndex ) { void *pvData; DWORD cbData; if (!CryptMsgGetParam( hMsg, dwParamType, dwIndex, NULL, // pvData &cbData) || 0 == cbData) goto GetParamError; if (NULL == (pvData = ICM_Alloc(cbData))) goto OutOfMemory; if (!CryptMsgGetParam( hMsg, dwParamType, dwIndex, pvData, &cbData)) { ICM_Free(pvData); goto GetParamError; } CommonReturn: return pvData; ErrorReturn: pvData = NULL; goto CommonReturn; TRACE_ERROR(OutOfMemory) TRACE_ERROR(GetParamError) } #ifdef CMS_PKCS7 BOOL ICM_GetAndVerifySigner( IN HCRYPTMSG hCryptMsg, IN DWORD dwSignerIndex, IN DWORD cSignerStore, IN OPTIONAL HCERTSTORE *rghSignerStore, IN DWORD dwFlags, OUT OPTIONAL PCCERT_CONTEXT *ppSigner ) { BOOL fResult; PCERT_INFO pSignerId = NULL; PCCERT_CONTEXT pSigner = NULL; DWORD dwCertEncodingType; DWORD dwVerifyErr = 0; HCERTSTORE hCollection = NULL; if (NULL == (pSignerId = (PCERT_INFO) ICM_AllocAndGetMsgParam( hCryptMsg, CMSG_SIGNER_CERT_INFO_PARAM, dwSignerIndex ))) goto GetSignerError; // If no CertEncodingType, then, use the MsgEncodingType dwCertEncodingType = ((PCRYPT_MSG_INFO) hCryptMsg)->dwEncodingType; if (0 == (dwCertEncodingType & CERT_ENCODING_TYPE_MASK)) dwCertEncodingType = (dwCertEncodingType >> 16) & CERT_ENCODING_TYPE_MASK; if (NULL == (hCollection = CertOpenStore( CERT_STORE_PROV_COLLECTION, 0, // dwEncodingType 0, // hCryptProv 0, // dwFlags NULL // pvPara ))) goto OpenCollectionStoreError; if (0 == (dwFlags & CMSG_TRUSTED_SIGNER_FLAG)) { HCERTSTORE hMsgCertStore; // Open a cert store initialized with certs from the message // and add to collection if (hMsgCertStore = CertOpenStore( CERT_STORE_PROV_MSG, dwCertEncodingType, 0, // hCryptProv 0, // dwFlags hCryptMsg // pvPara )) { CertAddStoreToCollection( hCollection, hMsgCertStore, CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0 // dwPriority ); CertCloseStore(hMsgCertStore, 0); } } // Add all the signer stores to the collection for ( ; cSignerStore > 0; cSignerStore--, rghSignerStore++) { HCERTSTORE hSignerStore = *rghSignerStore; if (NULL == hSignerStore) continue; CertAddStoreToCollection( hCollection, hSignerStore, CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0 // dwPriority ); } if (pSigner = CertGetSubjectCertificateFromStore(hCollection, dwCertEncodingType, pSignerId)) { CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA CtrlPara; if (dwFlags & CMSG_SIGNER_ONLY_FLAG) goto SuccessReturn; memset(&CtrlPara, 0, sizeof(CtrlPara)); CtrlPara.cbSize = sizeof(CtrlPara); // CtrlPara.hCryptProv = CtrlPara.dwSignerIndex = dwSignerIndex; CtrlPara.dwSignerType = CMSG_VERIFY_SIGNER_CERT; CtrlPara.pvSigner = (void *) pSigner; if (CryptMsgControl( hCryptMsg, 0, // dwFlags CMSG_CTRL_VERIFY_SIGNATURE_EX, &CtrlPara)) goto SuccessReturn; else { dwVerifyErr = GetLastError(); if (CRYPT_E_MISSING_PUBKEY_PARA == dwVerifyErr) { 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 pSigner, NULL, // pTime hCollection, &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( hCryptMsg, 0, // dwFlags CMSG_CTRL_VERIFY_SIGNATURE_EX, &CtrlPara)) goto SuccessReturn; } } CertFreeCertificateContext(pSigner); pSigner = NULL; } if (dwVerifyErr) goto VerifySignatureError; else goto NoSignerError; SuccessReturn: fResult = TRUE; CommonReturn: if (hCollection) CertCloseStore(hCollection, 0); ICM_Free(pSignerId); if (ppSigner) *ppSigner = pSigner; else if (pSigner) CertFreeCertificateContext(pSigner); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(OpenCollectionStoreError) TRACE_ERROR(GetSignerError) SET_ERROR(NoSignerError, CRYPT_E_NO_TRUSTED_SIGNER) SET_ERROR_VAR(VerifySignatureError, dwVerifyErr) } #else BOOL ICM_GetAndVerifySigner( IN HCRYPTMSG hCryptMsg, IN DWORD dwSignerIndex, IN DWORD cSignerStore, IN OPTIONAL HCERTSTORE *rghSignerStore, IN DWORD dwFlags, OUT OPTIONAL PCCERT_CONTEXT *ppSigner ) { BOOL fResult; PCERT_INFO pSignerId = NULL; PCCERT_CONTEXT pSigner = NULL; DWORD dwCertEncodingType; DWORD dwVerifyErr = 0; if (NULL == (pSignerId = (PCERT_INFO) ICM_AllocAndGetMsgParam( hCryptMsg, CMSG_SIGNER_CERT_INFO_PARAM, dwSignerIndex ))) goto GetSignerError; // If no CertEncodingType, then, use the MsgEncodingType dwCertEncodingType = ((PCRYPT_MSG_INFO) hCryptMsg)->dwEncodingType; if (0 == (dwCertEncodingType & CERT_ENCODING_TYPE_MASK)) dwCertEncodingType = (dwCertEncodingType >> 16) & CERT_ENCODING_TYPE_MASK; if (0 == (dwFlags & CMSG_TRUSTED_SIGNER_FLAG)) { HCERTSTORE hMsgCertStore; // Open a cert store initialized with certs from the message if (hMsgCertStore = CertOpenStore( CERT_STORE_PROV_MSG, dwCertEncodingType, 0, // hCryptProv 0, // dwFlags hCryptMsg // pvPara )) { pSigner = CertGetSubjectCertificateFromStore(hMsgCertStore, dwCertEncodingType, pSignerId); CertCloseStore(hMsgCertStore, 0); } if (pSigner) { if (dwFlags & CMSG_SIGNER_ONLY_FLAG) goto SuccessReturn; if (CryptMsgControl( hCryptMsg, 0, // dwFlags CMSG_CTRL_VERIFY_SIGNATURE, pSigner->pCertInfo)) goto SuccessReturn; else dwVerifyErr = GetLastError(); CertFreeCertificateContext(pSigner); pSigner = NULL; } } for ( ; cSignerStore > 0; cSignerStore--, rghSignerStore++) { HCERTSTORE hSignerStore = *rghSignerStore; if (NULL == hSignerStore) continue; if (pSigner = CertGetSubjectCertificateFromStore(hSignerStore, dwCertEncodingType, pSignerId)) { if (dwFlags & CMSG_SIGNER_ONLY_FLAG) goto SuccessReturn; if (CryptMsgControl( hCryptMsg, 0, // dwFlags CMSG_CTRL_VERIFY_SIGNATURE, pSigner->pCertInfo)) goto SuccessReturn; else dwVerifyErr = GetLastError(); CertFreeCertificateContext(pSigner); pSigner = NULL; } } if (dwVerifyErr) goto VerifySignatureError; else goto NoSignerError; SuccessReturn: fResult = TRUE; CommonReturn: ICM_Free(pSignerId); if (ppSigner) *ppSigner = pSigner; else if (pSigner) CertFreeCertificateContext(pSigner); return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(GetSignerError) SET_ERROR(NoSignerError, CRYPT_E_NO_TRUSTED_SIGNER) SET_ERROR_VAR(VerifySignatureError, dwVerifyErr) } #endif // CMS_PKCS7 //+------------------------------------------------------------------------- // Get and verify the signer of a cryptographic message. // // If CMSG_TRUSTED_SIGNER_FLAG is set, then, treat the Signer stores as being // trusted and only search them to find the certificate corresponding to the // signer's issuer and serial number. Otherwise, the SignerStores are // optionally provided to supplement the message's store of certificates. // If a signer certificate is found, its public key is used to verify // the message signature. The CMSG_SIGNER_ONLY_FLAG can be set to // return the signer without doing the signature verify. // // If CMSG_USE_SIGNER_INDEX_FLAG is set, then, only get the signer specified // by *pdwSignerIndex. Otherwise, iterate through all the signers // until a signer verifies or no more signers. // // For a verified signature, *ppSigner is updated with certificate context // of the signer and *pdwSignerIndex is updated with the index of the signer. // ppSigner and/or pdwSignerIndex can be NULL, indicating the caller isn't // interested in getting the CertContext and/or index of the signer. //-------------------------------------------------------------------------- BOOL WINAPI CryptMsgGetAndVerifySigner( IN HCRYPTMSG hCryptMsg, IN DWORD cSignerStore, IN OPTIONAL HCERTSTORE *rghSignerStore, IN DWORD dwFlags, OUT OPTIONAL PCCERT_CONTEXT *ppSigner, IN OUT OPTIONAL DWORD *pdwSignerIndex ) { BOOL fResult = FALSE; DWORD dwSignerCount; DWORD dwSignerIndex; if (dwFlags & CMSG_USE_SIGNER_INDEX_FLAG) { dwSignerCount = 1; dwSignerIndex = *pdwSignerIndex; } else { DWORD cbData; dwSignerIndex = 0; if (pdwSignerIndex) *pdwSignerIndex = 0; cbData = sizeof(dwSignerCount); if (!CryptMsgGetParam( hCryptMsg, CMSG_SIGNER_COUNT_PARAM, 0, // dwIndex &dwSignerCount, &cbData) || 0 == dwSignerCount) goto NoSignerError; } // Minimum of one iteration for ( ; dwSignerCount > 0; dwSignerCount--, dwSignerIndex++) { if (fResult = ICM_GetAndVerifySigner( hCryptMsg, dwSignerIndex, cSignerStore, rghSignerStore, dwFlags, ppSigner)) { if (pdwSignerIndex && 0 == (dwFlags & CMSG_USE_SIGNER_INDEX_FLAG)) *pdwSignerIndex = dwSignerIndex; break; } } CommonReturn: return fResult; ErrorReturn: if (ppSigner) *ppSigner = NULL; fResult = FALSE; goto CommonReturn; SET_ERROR(NoSignerError, CRYPT_E_NO_TRUSTED_SIGNER) } //+------------------------------------------------------------------------- // Sign an encoded CTL. //-------------------------------------------------------------------------- BOOL WINAPI CryptMsgSignCTL( IN DWORD dwMsgEncodingType, IN BYTE *pbCtlContent, IN DWORD cbCtlContent, IN PCMSG_SIGNED_ENCODE_INFO pSignInfo, IN DWORD dwFlags, OUT BYTE *pbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; HCRYPTMSG hMsg = NULL; DWORD dwMsgFlags; #ifdef CMS_PKCS7 if (dwFlags & CMSG_CMS_ENCAPSULATED_CTL_FLAG) dwMsgFlags = CMSG_CMS_ENCAPSULATED_CONTENT_FLAG; else dwMsgFlags = 0; #else dwMsgFlags = 0; #endif // CMS_PKCS7 if (NULL == pbEncoded) { if (0 == (*pcbEncoded = CryptMsgCalculateEncodedLength( dwMsgEncodingType, dwMsgFlags, CMSG_SIGNED, pSignInfo, szOID_CTL, cbCtlContent))) goto CalculateEncodedLengthError; fResult = TRUE; } else { if (NULL == (hMsg = CryptMsgOpenToEncode( dwMsgEncodingType, dwMsgFlags, CMSG_SIGNED, pSignInfo, szOID_CTL, NULL // pStreamInfo ))) goto OpenToEncodeError; if (!CryptMsgUpdate( hMsg, pbCtlContent, cbCtlContent, TRUE // fFinal )) goto UpdateError; fResult = CryptMsgGetParam( hMsg, CMSG_CONTENT_PARAM, 0, // dwIndex pbEncoded, pcbEncoded); } CommonReturn: if (hMsg) CryptMsgClose(hMsg); return fResult; ErrorReturn: *pcbEncoded = 0; fResult = FALSE; goto CommonReturn; TRACE_ERROR(CalculateEncodedLengthError) TRACE_ERROR(OpenToEncodeError) TRACE_ERROR(UpdateError) } //+------------------------------------------------------------------------- // Encode the CTL and create a signed message containing the encoded CTL. //-------------------------------------------------------------------------- BOOL WINAPI CryptMsgEncodeAndSignCTL( IN DWORD dwMsgEncodingType, IN PCTL_INFO pCtlInfo, IN PCMSG_SIGNED_ENCODE_INFO pSignInfo, IN DWORD dwFlags, OUT BYTE *pbEncoded, IN OUT DWORD *pcbEncoded ) { BOOL fResult; BYTE *pbContent = NULL; DWORD cbContent; DWORD dwEncodingType; LPCSTR lpszStructType; DWORD dwEncodeFlags; dwEncodingType = (dwMsgEncodingType >> 16) & CERT_ENCODING_TYPE_MASK; assert(dwEncodingType != 0); if (0 == dwEncodingType) goto InvalidArg; dwEncodeFlags = CRYPT_ENCODE_ALLOC_FLAG; if (dwFlags & CMSG_ENCODE_SORTED_CTL_FLAG) { lpszStructType = PKCS_SORTED_CTL; if (dwFlags & CMSG_ENCODE_HASHED_SUBJECT_IDENTIFIER_FLAG) dwEncodeFlags |= CRYPT_SORTED_CTL_ENCODE_HASHED_SUBJECT_IDENTIFIER_FLAG; } else { lpszStructType = PKCS_CTL; } if (!CryptEncodeObjectEx( dwEncodingType, lpszStructType, pCtlInfo, dwEncodeFlags, &PkiEncodePara, (void *) &pbContent, &cbContent )) goto EncodeError; fResult = CryptMsgSignCTL( dwMsgEncodingType, pbContent, cbContent, pSignInfo, dwFlags, pbEncoded, pcbEncoded ); CommonReturn: PkiFree(pbContent); return fResult; ErrorReturn: *pcbEncoded = 0; fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidArg, E_INVALIDARG) TRACE_ERROR(EncodeError) }