|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1999
//
// File: signhlp.cpp
//
// Contents: Digital Signing Helper APIs
//
// History: June-25-1997 Xiaohs Created
//----------------------------------------------------------------------------
#include "global.hxx"
//+-------------------------------------------------------------------------
// Local function for SpcGetCertFromKey
//
// Signer cert flags. Used to determine the "strength" of the signer cert.
//
// The following must be ordered as follows. ie, END_ENTITY_FLAG is most
// important and needs to be the largest number.
//--------------------------------------------------------------------------
#define SIGNER_CERT_NOT_SELF_SIGNED_FLAG 0x00000001
#define SIGNER_CERT_NOT_GLUE_FLAG 0x00000002
#define SIGNER_CERT_NOT_CA_FLAG 0x00000004
#define SIGNER_CERT_END_ENTITY_FLAG 0x00000008
#define SIGNER_CERT_ALL_FLAGS 0x0000000F
//--------------------------------------------------------------------------
//
// Copy all the certs from store name to hDescStore
//
//--------------------------------------------------------------------------
HRESULT MoveStoreName(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, HCERTSTORE hDescStore, DWORD dwStoreName, DWORD dwStoreFlag) { HCERTSTORE hTmpStore=NULL; HRESULT hr; WCHAR wszStoreName[40];
//load the name of the store
if(0==LoadStringU(hInstance, dwStoreName, wszStoreName, 40)) { hr=SignError(); goto CLEANUP; }
//open a system cert store
if (NULL == (hTmpStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, dwCertEncodingType, hCryptProv, dwStoreFlag, wszStoreName ))) { hr=SignError(); goto CLEANUP; }
hr=MoveStore(hDescStore, hTmpStore);
CLEANUP: if(hTmpStore) CertCloseStore(hTmpStore,0);
return hr;
}
//--------------------------------------------------------------------------
//
// Copy all the certs from hSrcStore to hDescStore
//
//--------------------------------------------------------------------------
HRESULT MoveStore(HCERTSTORE hDescStore, HCERTSTORE hSrcStore) { PCCERT_CONTEXT pCertContext=NULL; PCCERT_CONTEXT pPreContext=NULL; HRESULT hr=S_OK;
while(pCertContext=CertEnumCertificatesInStore(hSrcStore, pPreContext)) { if(!(CertAddCertificateContextToStore(hDescStore, pCertContext,CERT_STORE_ADD_USE_EXISTING, NULL))) { hr=SignError(); goto CLEANUP; }
pPreContext=pCertContext; pCertContext=NULL; }
hr=S_OK;
CLEANUP: if(pCertContext) CertFreeCertificateContext(pCertContext);
return hr; }
//--------------------------------------------------------------------------
//
// Build up the certificate chain. Put the whole chain to the store
//
//
//--------------------------------------------------------------------------
HRESULT BuildCertChain(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, HCERTSTORE hStore, HCERTSTORE hOptionalStore, PCCERT_CONTEXT pSigningCert, DWORD dwCertPolicy) {
DWORD i=0; PCCERT_CHAIN_CONTEXT pCertChainContext = NULL; CERT_CHAIN_PARA CertChainPara; HRESULT hr=E_FAIL; //we regard the chain is good unless there are some cryptographic errors.
//all error code regarding trusted root and CTLs are machine dependent, therefore
//they are ignored. We do not consider revocation.
DWORD dwChainError=CERT_TRUST_IS_NOT_TIME_VALID | CERT_TRUST_IS_NOT_SIGNATURE_VALID;
memset(&CertChainPara, 0, sizeof(CertChainPara)); CertChainPara.cbSize = sizeof(CertChainPara);
if (!CertGetCertificateChain( HCCE_CURRENT_USER, pSigningCert, NULL, hOptionalStore, &CertChainPara, 0, NULL, &pCertChainContext)) { hr=SignError(); goto CLEANUP; } //
// make sure there is at least 1 simple chain
//
if (pCertChainContext->cChain == 0) { hr=SignError(); goto CLEANUP; }
// make sure that we have a good chain
if(dwChainError & (pCertChainContext->rgpChain[0]->TrustStatus.dwErrorStatus)) { hr=CERT_E_CHAINING; goto CLEANUP; }
i = 0;
while (i < pCertChainContext->rgpChain[0]->cElement) { //
// if we are supposed to skip the root cert,
// and we are on the root cert, then continue
//
if(dwCertPolicy & SIGNER_CERT_POLICY_CHAIN_NO_ROOT || dwCertPolicy & SIGNER_CERT_POLICY_SPC) { if ((pCertChainContext->rgpChain[0]->rgpElement[i]->TrustStatus.dwInfoStatus & CERT_TRUST_IS_SELF_SIGNED)) { i++; continue; } }
CertAddCertificateContextToStore( hStore, pCertChainContext->rgpChain[0]->rgpElement[i]->pCertContext, CERT_STORE_ADD_REPLACE_EXISTING, NULL);
i++; } hr=S_OK;
CLEANUP:
if (pCertChainContext != NULL) { CertFreeCertificateChain(pCertChainContext); }
return hr;
}
//--------------------------------------------------------------------------
//
// Make sure the two certificates are the same
//
//
//--------------------------------------------------------------------------
BOOL SameCert(PCCERT_CONTEXT pCertOne, PCCERT_CONTEXT pCertTwo) { if(!pCertOne || !pCertTwo) return FALSE;
if(pCertOne->cbCertEncoded != pCertTwo->cbCertEncoded) return FALSE;
if(0 == memcmp(pCertOne->pbCertEncoded, pCertTwo->pbCertEncoded, pCertTwo->cbCertEncoded)) return TRUE;
return FALSE; }
//The following cert chain building code is obsolete. The new cert chain
//building API should be used
//--------------------------------------------------------------------------
//
// Build up the certificate chain. Put the whole chain to the store
//
//
//--------------------------------------------------------------------------
/*HRESULT BuildCertChain(HCRYPTPROV hCryptProv,
DWORD dwCertEncodingType, HCERTSTORE hStore, HCERTSTORE hOptionalStore, PCCERT_CONTEXT pSigningCert, DWORD dwCertPolicy) { HRESULT hr=E_FAIL; HCERTSTORE hSpcStore=NULL; PCCERT_CONTEXT pSubCertContext=NULL; PCCERT_CONTEXT pIssuerCertContext=NULL; PCCERT_CONTEXT pFindCertContext=NULL; LPWSTR rgwszStoreName[4] ={L"MY", L"ROOT", L"CA",L"SPC"}; DWORD dwStoreOpenFlag=0; HCERTSTORE rghStore[5]={NULL, NULL, NULL, NULL,NULL}; DWORD dwStoreCount=0; DWORD dwStoreIndex=0; FILETIME fileTime; DWORD dwConfidence=0; DWORD dwError=0; BYTE *pbHash=NULL; DWORD cbHash = 0; CRYPT_HASH_BLOB Blob;
//open a spc cert store
dwStoreCount=sizeof(rgwszStoreName)/sizeof(rgwszStoreName[0]); GetSystemTimeAsFileTime(&fileTime);
//open the spc store
if (NULL == (hSpcStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, dwCertEncodingType, hCryptProv, CERT_STORE_NO_CRYPT_RELEASE_FLAG|CERT_SYSTEM_STORE_CURRENT_USER, L"SPC" ))) { hr=SignError(); goto CLEANUP; }
//open SPC, my, CA, root store
for(dwStoreIndex=0; dwStoreIndex<dwStoreCount; dwStoreIndex++) { //open the store
dwStoreOpenFlag= CERT_STORE_NO_CRYPT_RELEASE_FLAG|CERT_SYSTEM_STORE_CURRENT_USER;
if (NULL == (rghStore[dwStoreIndex] = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, dwCertEncodingType, hCryptProv, dwStoreOpenFlag, rgwszStoreName[dwStoreIndex] ))) { hr=SignError(); goto CLEANUP; } }
//copy all the certs in hOptionalStore if present
if(hOptionalStore) { rghStore[dwStoreCount]=hOptionalStore; dwStoreCount++; }
//now, build the chain
pSubCertContext=CertDuplicateCertificateContext(pSigningCert);
//loop until break
while(1==1) { //find the issuer of the certificate
if(!(pIssuerCertContext=TrustFindIssuerCertificate( pSubCertContext, dwCertEncodingType, dwStoreCount, rghStore, &fileTime, &dwConfidence, &dwError, 0)))
{ //fail if we can not find one
hr=CERT_E_CHAINING; goto CLEANUP; }
//now, make sure the confidence level is hign enough
if(dwConfidence < (CERT_CONFIDENCE_SIG+CERT_CONFIDENCE_TIME+CERT_CONFIDENCE_TIMENEST)) { hr=CERT_E_CHAINING; goto CLEANUP; } //check to see if the cert is the root cert
if(TrustIsCertificateSelfSigned(pIssuerCertContext, pIssuerCertContext->dwCertEncodingType, 0)) { if(dwCertPolicy & SIGNER_CERT_POLICY_CHAIN_NO_ROOT) break; else { //add the root and we are done
if(!CertAddCertificateContextToStore(hStore,pIssuerCertContext, CERT_STORE_ADD_USE_EXISTING, NULL)) { hr=CERT_E_CHAINING; goto CLEANUP; }
break; } } else { //add the certificate context to the store
if(!CertAddCertificateContextToStore(hStore,pIssuerCertContext, CERT_STORE_ADD_USE_EXISTING, NULL )) { hr=CERT_E_CHAINING; goto CLEANUP; } }
//check if the certificate is from the spc store
if(dwCertPolicy & SIGNER_CERT_POLICY_SPC) {
//get the SHA1 hash of the certificate
if(!CertGetCertificateContextProperty( pIssuerCertContext, CERT_SHA1_HASH_PROP_ID, NULL, &cbHash )) { hr=SignError(); goto CLEANUP; }
pbHash=(BYTE *)malloc(cbHash); if(!pbHash) { hr=E_OUTOFMEMORY; goto CLEANUP; } if(!CertGetCertificateContextProperty( pIssuerCertContext, CERT_SHA1_HASH_PROP_ID, pbHash, &cbHash )) { hr=SignError(); goto CLEANUP; }
//find the ceritificate in the store
Blob.cbData=cbHash; Blob.pbData=pbHash;
pFindCertContext=CertFindCertificateInStore( hSpcStore, dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &Blob, NULL);
//if the certificate is from the SPC store, we are done
if(pFindCertContext) break; }
//free the subject context
if(pSubCertContext) CertFreeCertificateContext(pSubCertContext);
pSubCertContext=pIssuerCertContext;
pIssuerCertContext=NULL;
}
hr=S_OK;
CLEANUP: if(pIssuerCertContext) CertFreeCertificateContext(pIssuerCertContext);
if(pSubCertContext) CertFreeCertificateContext(pSubCertContext);
if(pFindCertContext) CertFreeCertificateContext(pFindCertContext);
//close all of the stores
for(dwStoreIndex=0; dwStoreIndex < (hOptionalStore ? dwStoreCount-1 : dwStoreCount); dwStoreIndex++) { if(rghStore[dwStoreIndex]) CertCloseStore(rghStore[dwStoreIndex], 0); }
if(hSpcStore) CertCloseStore(hSpcStore,0);
if(pbHash) free(pbHash); return hr; } */
//+-------------------------------------------------------------------------
// Build the SPC certificate store from the SPC file and the certificate chain
//--------------------------------------------------------------------------
HRESULT BuildStoreFromSpcChain(HCRYPTPROV hPvkProv, DWORD dwKeySpec, HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, SIGNER_SPC_CHAIN_INFO *pSpcChainInfo, HCERTSTORE *phSpcStore, PCCERT_CONTEXT *ppSignCert ) { HCERTSTORE hMemoryStore=NULL; HRESULT hr=S_OK; PCCERT_CONTEXT pCertContext=NULL; PCCERT_CONTEXT pPreContext=NULL;
if(!pSpcChainInfo || !phSpcStore || !ppSignCert) return E_INVALIDARG;
//init
*phSpcStore=NULL;
//open a memory store
if (NULL == (hMemoryStore = CertOpenStore( CERT_STORE_PROV_FILENAME_W, dwCertEncodingType, hCryptProv, CERT_STORE_NO_CRYPT_RELEASE_FLAG, pSpcChainInfo->pwszSpcFile))) { hr=SignError(); goto CLEANUP; }
//get the signing certificate
if(S_OK != SpcGetCertFromKey( dwCertEncodingType, hMemoryStore, hPvkProv, dwKeySpec, ppSignCert)) { hr=CRYPT_E_NO_MATCH; goto CLEANUP; }
//add all the certs in optional certStore
if(pSpcChainInfo->dwCertPolicy & SIGNER_CERT_POLICY_STORE) { if(!(pSpcChainInfo->hCertStore)) { hr=CERT_E_CHAINING; goto CLEANUP; }
//enumerate all the certs in store and add them
while(pCertContext=CertEnumCertificatesInStore(pSpcChainInfo->hCertStore, pPreContext)) { if(!CertAddCertificateContextToStore(hMemoryStore, pCertContext, CERT_STORE_ADD_USE_EXISTING, NULL)) { hr=SignError(); goto CLEANUP; }
pPreContext=pCertContext; }
hr=S_OK; }
//see if the certs if self-signed
/* if(TrustIsCertificateSelfSigned(*ppSignCert,
(*ppSignCert)->dwCertEncodingType, 0)) { //no need to build the certificate chain anymore
hr=S_OK; goto CLEANUP; } */
//build up the cert chain as requested
if(pSpcChainInfo->dwCertPolicy & SIGNER_CERT_POLICY_CHAIN || pSpcChainInfo->dwCertPolicy & SIGNER_CERT_POLICY_CHAIN_NO_ROOT || pSpcChainInfo->dwCertPolicy & SIGNER_CERT_POLICY_SPC ) { //include everthing in the chain
hr=BuildCertChain(hCryptProv, dwCertEncodingType, hMemoryStore, hMemoryStore, *ppSignCert, pSpcChainInfo->dwCertPolicy); }
CLEANUP:
if(pCertContext) CertFreeCertificateContext(pCertContext);
if(hr==S_OK) { *phSpcStore=hMemoryStore; } else { if(hMemoryStore) CertCloseStore(hMemoryStore, 0);
if(*ppSignCert) { CertFreeCertificateContext(*ppSignCert); *ppSignCert=NULL; } }
return hr; }
//+-------------------------------------------------------------------------
// Build the spc certificate store from cert chain
//--------------------------------------------------------------------------
HRESULT BuildStoreFromStore(HCRYPTPROV hPvkProv, DWORD dwKeySpec, HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, SIGNER_CERT_STORE_INFO *pCertStoreInfo, HCERTSTORE *phSpcStore, PCCERT_CONTEXT *ppSignCert ) { HCERTSTORE hMemoryStore=NULL; HRESULT hr=S_OK; PCCERT_CONTEXT pCertContext=NULL; PCCERT_CONTEXT pPreContext=NULL;
if(!pCertStoreInfo || !phSpcStore || !ppSignCert) return E_INVALIDARG;
//init
*phSpcStore=NULL;
//open a memory store
if (NULL == (hMemoryStore = CertOpenStore( CERT_STORE_PROV_MEMORY, dwCertEncodingType, hCryptProv, CERT_STORE_NO_CRYPT_RELEASE_FLAG, NULL ))) { hr=SignError(); goto CLEANUP; }
//add the signing cert to the store
if(!CertAddCertificateContextToStore(hMemoryStore, pCertStoreInfo->pSigningCert, CERT_STORE_ADD_USE_EXISTING , NULL)) { hr=SignError(); goto CLEANUP; }
//get the signing certificate based on the private key
if(S_OK != SpcGetCertFromKey( dwCertEncodingType, hMemoryStore, hPvkProv, dwKeySpec, ppSignCert)) { hr=CRYPT_E_NO_MATCH; goto CLEANUP; }
//add all the certs in optional certStore
if(pCertStoreInfo->dwCertPolicy & SIGNER_CERT_POLICY_STORE) { if(!(pCertStoreInfo->hCertStore)) { hr=CERT_E_CHAINING; goto CLEANUP; }
//enumerate all the certs in store and add them
while(pCertContext=CertEnumCertificatesInStore(pCertStoreInfo->hCertStore, pPreContext)) { if(!CertAddCertificateContextToStore(hMemoryStore, pCertContext, CERT_STORE_ADD_USE_EXISTING, NULL)) { hr=SignError(); goto CLEANUP; }
pPreContext=pCertContext; }
hr=S_OK; }
//see if the certs if self-signed
/* if(TrustIsCertificateSelfSigned(pCertStoreInfo->pSigningCert,
pCertStoreInfo->pSigningCert->dwCertEncodingType, 0)) { //no need to build the certificate chain anymore
*ppSignCert=CertDuplicateCertificateContext(pCertStoreInfo->pSigningCert); hr=S_OK; goto CLEANUP; }*/
//build up the cert chain as requested
if(pCertStoreInfo->dwCertPolicy & SIGNER_CERT_POLICY_CHAIN || pCertStoreInfo->dwCertPolicy & SIGNER_CERT_POLICY_CHAIN_NO_ROOT || pCertStoreInfo->dwCertPolicy & SIGNER_CERT_POLICY_SPC ) { //include everthing in the chain
hr=BuildCertChain(hCryptProv, dwCertEncodingType, hMemoryStore, NULL, pCertStoreInfo->pSigningCert, pCertStoreInfo->dwCertPolicy); }
if(S_OK != hr) goto CLEANUP;
hr=S_OK;
CLEANUP:
if(pCertContext) CertFreeCertificateContext(pCertContext);
if(hr==S_OK) { *phSpcStore=hMemoryStore; } else { if(hMemoryStore) CertCloseStore(hMemoryStore, 0);
if(*ppSignCert) { CertFreeCertificateContext(*ppSignCert); *ppSignCert=NULL; }
}
return hr; }
//+-------------------------------------------------------------------------
// Build the spc certificate store from a spc file
//--------------------------------------------------------------------------
HRESULT BuildStoreFromSpcFile(HCRYPTPROV hPvkProv, DWORD dwKeySpec, HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, LPCWSTR pwszSpcFile, HCERTSTORE *phSpcStore, PCCERT_CONTEXT *ppSignCert) { if(!phSpcStore || !pwszSpcFile || !ppSignCert) return E_INVALIDARG;
*phSpcStore=NULL;
// Open up the spc store
*phSpcStore= CertOpenStore(CERT_STORE_PROV_FILENAME_W, dwCertEncodingType, hCryptProv, CERT_STORE_NO_CRYPT_RELEASE_FLAG, pwszSpcFile); if(!(*phSpcStore)) return SignError();
//get the signing certificate
if(S_OK != SpcGetCertFromKey(dwCertEncodingType, *phSpcStore, hPvkProv, dwKeySpec, ppSignCert)) { CertCloseStore(*phSpcStore, 0); *phSpcStore=NULL;
return CRYPT_E_NO_MATCH; }
return S_OK;
}
//+-------------------------------------------------------------------------
// Build the spc certificate store from either a spc file or the
// cert chain
//--------------------------------------------------------------------------
HRESULT BuildCertStore(HCRYPTPROV hPvkProv, DWORD dwKeySpec, HCRYPTPROV hCryptProv, DWORD dwCertEncodingType, SIGNER_CERT *pSignerCert, HCERTSTORE *phSpcStore, PCCERT_CONTEXT *ppSigningCert) { HRESULT hr;
if(!pSignerCert || !phSpcStore || !ppSigningCert) return E_INVALIDARG;
//init
*phSpcStore=NULL;
if(pSignerCert->dwCertChoice==SIGNER_CERT_SPC_FILE) { hr=BuildStoreFromSpcFile(hPvkProv, dwKeySpec, hCryptProv, dwCertEncodingType, pSignerCert->pwszSpcFile, phSpcStore, ppSigningCert); } else { if(pSignerCert->dwCertChoice==SIGNER_CERT_STORE) {
hr=BuildStoreFromStore(hPvkProv, dwKeySpec, hCryptProv, dwCertEncodingType, (pSignerCert->pCertStoreInfo), phSpcStore, ppSigningCert); } else hr=BuildStoreFromSpcChain(hPvkProv, dwKeySpec, hCryptProv, dwCertEncodingType, (pSignerCert->pSpcChainInfo), phSpcStore, ppSigningCert); }
#if (0) //DSIE: Bug 284639, the fix is to also preserve 0x80070002 since we
// really don't know what the impact will be for existing apps,
// if we preserve all error codes.
if(hr!=S_OK && hr!=CRYPT_E_NO_MATCH) hr=CERT_E_CHAINING; #else
if(hr!=S_OK && hr!=CRYPT_E_NO_MATCH && hr!=0x80070002) hr=CERT_E_CHAINING; #endif
return hr;
}
//-----------------------------------------------------------------------------
//
// Parse the private key information from a pCertContext's property
// CERT_PVK_FILE_PROP_ID
//
//----------------------------------------------------------------------------
BOOL GetProviderInfoFromCert(PCCERT_CONTEXT pCertContext, CRYPT_KEY_PROV_INFO *pKeyProvInfo) {
BOOL fResult=FALSE; BYTE *pbData=NULL; BYTE *pbToFree=NULL; DWORD cbData=0;
//init
if(!pCertContext || !pKeyProvInfo) return FALSE;
memset(pKeyProvInfo, 0, sizeof(CRYPT_KEY_PROV_INFO));
//get the property
if(!CertGetCertificateContextProperty(pCertContext, CERT_PVK_FILE_PROP_ID, NULL, &cbData)) return FALSE;
pbData=(BYTE *)malloc(cbData);
if(!pbData) return FALSE;
if(!CertGetCertificateContextProperty(pCertContext, CERT_PVK_FILE_PROP_ID, pbData, &cbData)) goto CLEANUP;
//get the information from the property
pbToFree=pbData;
//get the private key information
cbData=sizeof(WCHAR)*(wcslen((LPWSTR)pbData)+1);
pKeyProvInfo->pwszContainerName=(LPWSTR)malloc(cbData);
if(!(pKeyProvInfo->pwszContainerName)) goto CLEANUP;
wcscpy(pKeyProvInfo->pwszContainerName,(LPWSTR)pbData); //get the key spec
pbData = pbData + cbData;
cbData=sizeof(WCHAR)*(wcslen((LPWSTR)pbData)+1);
pKeyProvInfo->dwKeySpec=_wtol((LPWSTR)pbData);
//get the provider type
pbData = pbData + cbData;
cbData=sizeof(WCHAR)*(wcslen((LPWSTR)pbData)+1);
pKeyProvInfo->dwProvType=_wtol((LPWSTR)pbData);
//get the provider name
pbData = pbData + cbData;
if(*((LPWSTR)pbData)!=L'\0') { cbData=sizeof(WCHAR)*(wcslen((LPWSTR)pbData)+1); pKeyProvInfo->pwszProvName=(LPWSTR)malloc(cbData);
if(NULL == pKeyProvInfo->pwszProvName) goto CLEANUP;
wcscpy(pKeyProvInfo->pwszProvName, (LPWSTR)pbData); }
fResult=TRUE;
CLEANUP:
if(pbToFree) free(pbToFree);
if(FALSE==fResult) { if(pKeyProvInfo->pwszContainerName) free( pKeyProvInfo->pwszContainerName);
if(pKeyProvInfo->pwszProvName) free( pKeyProvInfo->pwszProvName);
//memset the output to 0
memset(pKeyProvInfo, 0, sizeof(CRYPT_KEY_PROV_INFO));
}
return fResult; }
//+-------------------------------------------------------------------------
// Get hCryptProv handle and key spec for the certificate
//--------------------------------------------------------------------------
BOOL WINAPI GetCryptProvFromCert( HWND hwnd, PCCERT_CONTEXT pCert, HCRYPTPROV *phCryptProv, DWORD *pdwKeySpec, BOOL *pfDidCryptAcquire, LPWSTR *ppwszTmpContainer, LPWSTR *ppwszProviderName, DWORD *pdwProviderType ) { BOOL fResult=FALSE; WCHAR wszPublisher[45]; CRYPT_KEY_PROV_INFO keyProvInfo; HRESULT hr;
memset(&keyProvInfo, 0, sizeof(CRYPT_KEY_PROV_INFO));
*ppwszTmpContainer=NULL; *phCryptProv=NULL; *pfDidCryptAcquire=FALSE; *ppwszProviderName=NULL; *pdwKeySpec=0;
//first, try to get from the key container
if(CryptProvFromCert(hwnd, pCert, phCryptProv, pdwKeySpec, pfDidCryptAcquire)) return TRUE;
//load from the resource of string L"publisher"
if(0==LoadStringU(hInstance, IDS_Publisher, wszPublisher, 40)) goto CLEANUP;
//Get provider information from the property
if(!GetProviderInfoFromCert(pCert, &keyProvInfo)) { SetLastError((DWORD) CRYPT_E_NO_KEY_PROPERTY); goto CLEANUP; }
//acquire context based on the private key file. A temporary
//key container will be created, along with information
//about the provider name and provider type, which are needed
//to destroy the key container
if(S_OK!=(hr=PvkGetCryptProv( hwnd, wszPublisher, keyProvInfo.pwszProvName, keyProvInfo.dwProvType, keyProvInfo.pwszContainerName, NULL, &(keyProvInfo.dwKeySpec), ppwszTmpContainer, phCryptProv))) { *phCryptProv=NULL; *ppwszTmpContainer=NULL; SetLastError((DWORD)hr); goto CLEANUP; }
//copy the provder name
if(keyProvInfo.pwszProvName) { *ppwszProviderName=(LPWSTR)malloc( sizeof(WCHAR)*(wcslen(keyProvInfo.pwszProvName)+1));
if((*ppwszProviderName)==NULL) { SetLastError(E_OUTOFMEMORY);
//free the hCrytProv
PvkPrivateKeyReleaseContext( *phCryptProv, keyProvInfo.pwszProvName, keyProvInfo.dwProvType, *ppwszTmpContainer);
*phCryptProv=NULL; *ppwszTmpContainer=NULL;
goto CLEANUP; }
wcscpy(*ppwszProviderName, keyProvInfo.pwszProvName); }
//copy the provider type
*pdwProviderType=keyProvInfo.dwProvType;
//copy the key spec
*pdwKeySpec=keyProvInfo.dwKeySpec; *pfDidCryptAcquire=TRUE;
fResult=TRUE;
CLEANUP:
if(keyProvInfo.pwszProvName) free(keyProvInfo.pwszProvName);
if(keyProvInfo.pwszContainerName) free(keyProvInfo.pwszContainerName);
return fResult;
}
//+-------------------------------------------------------------------------
// Free hCryptProv handle and key spec for the certificate
//--------------------------------------------------------------------------
void WINAPI FreeCryptProvFromCert(BOOL fAcquired, HCRYPTPROV hProv, LPWSTR pwszCapiProvider, DWORD dwProviderType, LPWSTR pwszTmpContainer) { if(fAcquired) { if (pwszTmpContainer) { // Delete the temporary container for the private key from
// the provider
PvkPrivateKeyReleaseContext(hProv, pwszCapiProvider, dwProviderType, pwszTmpContainer);
if(pwszCapiProvider) free(pwszCapiProvider); } else { if (hProv) CryptReleaseContext(hProv, 0); } } }
//+-------------------------------------------------------------------------
//
//This is a subst of GetCryptProvFromCert. This function does not consider
//the private key file property of the certificate
//+-------------------------------------------------------------------------
BOOL WINAPI CryptProvFromCert( HWND hwnd, PCCERT_CONTEXT pCert, HCRYPTPROV *phCryptProv, DWORD *pdwKeySpec, BOOL *pfDidCryptAcquire ) { return CryptAcquireCertificatePrivateKey( pCert, 0, //we do not do the compare. It will be done later.
NULL, phCryptProv, pdwKeySpec, pfDidCryptAcquire);
/*BOOL fResult;
BOOL fDidCryptAcquire = FALSE; CERT_KEY_CONTEXT KeyContext; memset(&KeyContext, 0, sizeof(KeyContext)); PCRYPT_KEY_PROV_INFO pKeyProvInfo = NULL; DWORD cbData; DWORD dwIdx;
// Get either the CERT_KEY_CONTEXT_PROP_ID or
// CERT_KEY_PROV_INFO_PROP_ID, or
// CERT_PVK_FILE_PROP_ID for the Cert.
cbData = sizeof(KeyContext); CertGetCertificateContextProperty( pCert, CERT_KEY_CONTEXT_PROP_ID, &KeyContext, &cbData );
if (KeyContext.hCryptProv == 0) { cbData = 0; CertGetCertificateContextProperty( pCert, CERT_KEY_PROV_INFO_PROP_ID, NULL, &cbData ); if (cbData == 0) { SetLastError((DWORD) CRYPT_E_NO_KEY_PROPERTY); goto ErrorReturn; } else { pKeyProvInfo = (PCRYPT_KEY_PROV_INFO) malloc(cbData); if (pKeyProvInfo == NULL) goto ErrorReturn; fResult = CertGetCertificateContextProperty( pCert, CERT_KEY_PROV_INFO_PROP_ID, pKeyProvInfo, &cbData ); if (!fResult) goto ErrorReturn;
if (PROV_RSA_FULL == pKeyProvInfo->dwProvType && (NULL == pKeyProvInfo->pwszProvName || L'\0' == *pKeyProvInfo->pwszProvName)) fResult = CryptAcquireContextU( &KeyContext.hCryptProv, pKeyProvInfo->pwszContainerName, MS_ENHANCED_PROV_W, PROV_RSA_FULL, pKeyProvInfo->dwFlags & ~CERT_SET_KEY_CONTEXT_PROP_ID ); else fResult = FALSE; if (!fResult) fResult = CryptAcquireContextU( &KeyContext.hCryptProv, pKeyProvInfo->pwszContainerName, pKeyProvInfo->pwszProvName, pKeyProvInfo->dwProvType, pKeyProvInfo->dwFlags & ~CERT_SET_KEY_CONTEXT_PROP_ID ); if (!fResult) goto ErrorReturn; fDidCryptAcquire = TRUE; for (dwIdx = 0; dwIdx < pKeyProvInfo->cProvParam; dwIdx++) { PCRYPT_KEY_PROV_PARAM pKeyProvParam = &pKeyProvInfo->rgProvParam[dwIdx]; fResult = CryptSetProvParam( KeyContext.hCryptProv, pKeyProvParam->dwParam, pKeyProvParam->pbData, pKeyProvParam->dwFlags ); if (!fResult) goto ErrorReturn; } KeyContext.dwKeySpec = pKeyProvInfo->dwKeySpec; if (pKeyProvInfo->dwFlags & CERT_SET_KEY_CONTEXT_PROP_ID) { // Set the certificate's property so we only need to do the
// acquire once
KeyContext.cbSize = sizeof(KeyContext); fResult = CertSetCertificateContextProperty( pCert, CERT_KEY_CONTEXT_PROP_ID, 0, // dwFlags
(void *) &KeyContext ); if (!fResult) goto ErrorReturn; fDidCryptAcquire = FALSE; } } }
fResult = TRUE; goto CommonReturn;
ErrorReturn: if (fDidCryptAcquire) { DWORD dwErr = GetLastError(); CryptReleaseContext(KeyContext.hCryptProv, 0); SetLastError(dwErr);
fDidCryptAcquire = FALSE; } KeyContext.hCryptProv = 0; fResult = FALSE; CommonReturn: if (pKeyProvInfo) free(pKeyProvInfo); *phCryptProv = KeyContext.hCryptProv; *pdwKeySpec = KeyContext.dwKeySpec; *pfDidCryptAcquire = fDidCryptAcquire; return fResult;*/ }
//+-----------------------------------------------------------------------
// Check the SIGNER_SUBJECT_INFO
//
//+-----------------------------------------------------------------------
BOOL CheckSigncodeSubjectInfo( PSIGNER_SUBJECT_INFO pSubjectInfo) { if(!pSubjectInfo) return FALSE;
//check pSubjectInfo
if(pSubjectInfo->cbSize < sizeof(SIGNER_SUBJECT_INFO)) return FALSE;
if(NULL==(pSubjectInfo->pdwIndex)) return FALSE;
//currently, we only allow index of 0
if(0!= (*(pSubjectInfo->pdwIndex))) return FALSE;
if((pSubjectInfo->dwSubjectChoice!=SIGNER_SUBJECT_FILE)&& (pSubjectInfo->dwSubjectChoice!=SIGNER_SUBJECT_BLOB)) return FALSE;
if(pSubjectInfo->dwSubjectChoice==SIGNER_SUBJECT_FILE) { if((pSubjectInfo->pSignerFileInfo)==NULL) return FALSE; //check SIGNER_FILE_INFO
if(pSubjectInfo->pSignerFileInfo->cbSize < sizeof(SIGNER_FILE_INFO)) return FALSE;
if((pSubjectInfo->pSignerFileInfo->pwszFileName)==NULL) return FALSE; } else { if((pSubjectInfo->pSignerBlobInfo)==NULL) return FALSE;
//check SIGNER_BLOB_INFO
if(pSubjectInfo->pSignerBlobInfo->cbSize < sizeof(SIGNER_BLOB_INFO)) return FALSE;
if(NULL==(pSubjectInfo->pSignerBlobInfo->pGuidSubject)) return FALSE;
if(0==(pSubjectInfo->pSignerBlobInfo->cbBlob)) return FALSE;
if(NULL==(pSubjectInfo->pSignerBlobInfo->pbBlob)) return FALSE; }
return TRUE; }
//+-----------------------------------------------------------------------
// Check the input parameters of Signcode. Make sure they are valid.
//
//+-----------------------------------------------------------------------
BOOL CheckSigncodeParam( PSIGNER_SUBJECT_INFO pSubjectInfo, PSIGNER_CERT pSignerCert, PSIGNER_SIGNATURE_INFO pSignatureInfo, PSIGNER_PROVIDER_INFO pProviderInfo) { //except for pPvkInfo and pProviderInfo, the rest are required.
if(!pSubjectInfo ||!pSignerCert || !pSignatureInfo) return FALSE;
//check pSubjectInfo
if(FALSE==CheckSigncodeSubjectInfo(pSubjectInfo)) return FALSE;
//check pSignatureInfo
if(pSignatureInfo->cbSize < sizeof(SIGNER_SIGNATURE_INFO)) return FALSE;
//check the attributes in pSignatureInfo
if(pSignatureInfo->dwAttrChoice == SIGNER_AUTHCODE_ATTR) { if((pSignatureInfo->pAttrAuthcode)==NULL) return FALSE;
//check pSignatureInfo->pAttrAuthcode
if(pSignatureInfo->pAttrAuthcode->cbSize < sizeof(SIGNER_ATTR_AUTHCODE)) return FALSE; } else { if(pSignatureInfo->dwAttrChoice !=SIGNER_NO_ATTR) return FALSE; }
//check provider info
if(pProviderInfo) { if(pProviderInfo->cbSize < sizeof(SIGNER_PROVIDER_INFO)) return FALSE;
//dwPvkType has to be valid
if((pProviderInfo->dwPvkChoice!=PVK_TYPE_FILE_NAME) && (pProviderInfo->dwPvkChoice!=PVK_TYPE_KEYCONTAINER) ) return FALSE;
if(pProviderInfo->dwPvkChoice==PVK_TYPE_FILE_NAME) { if(!(pProviderInfo->pwszPvkFileName)) return FALSE; } else { if(!(pProviderInfo->pwszKeyContainer)) return FALSE; }
}
//check pSignerCert
if(pSignerCert->cbSize < sizeof(SIGNER_CERT)) return FALSE;
//check the dwCertChoice
if((pSignerCert->dwCertChoice!= SIGNER_CERT_SPC_FILE) && ((pSignerCert->dwCertChoice!= SIGNER_CERT_STORE)) && (pSignerCert->dwCertChoice!= SIGNER_CERT_SPC_CHAIN) ) return FALSE;
//check the spc file situation
if(pSignerCert->dwCertChoice == SIGNER_CERT_SPC_FILE) { if(pSignerCert->pwszSpcFile==NULL) return FALSE; }
//check the cert store situation
if(pSignerCert->dwCertChoice==SIGNER_CERT_STORE) { //pCertStoreInfo has to be set
if((pSignerCert->pCertStoreInfo)==NULL) return FALSE;
if((pSignerCert->pCertStoreInfo)->cbSize < sizeof(SIGNER_CERT_STORE_INFO)) return FALSE;
//pSigngingCert has to be set
if((pSignerCert->pCertStoreInfo)->pSigningCert == NULL ) return FALSE; }
//check the SPC chain situation
if(pSignerCert->dwCertChoice==SIGNER_CERT_SPC_CHAIN) { //pCertStoreInfo has to be set
if((pSignerCert->pSpcChainInfo)==NULL) return FALSE;
if((pSignerCert->pSpcChainInfo)->cbSize != sizeof(SIGNER_SPC_CHAIN_INFO)) return FALSE;
//pSigngingCert has to be set
if((pSignerCert->pSpcChainInfo)->pwszSpcFile == NULL ) return FALSE; } //end of the checking
return TRUE;
}
//-------------------------------------------------------------------------
//
// GetSubjectTypeFlags:
// Check the BASIC_CONSTRAINTS extension from the certificate
// to see if the certificate is a CA or end entity certs
//
//-------------------------------------------------------------------------
static DWORD GetSubjectTypeFlags(IN DWORD dwCertEncodingType, IN PCCERT_CONTEXT pCert) { HRESULT hr = S_OK; DWORD grfSubjectType = 0; PCERT_EXTENSION pExt; PCERT_BASIC_CONSTRAINTS_INFO pInfo = NULL; DWORD cbInfo; PKITRY { if ((pExt = CertFindExtension(szOID_BASIC_CONSTRAINTS, pCert->pCertInfo->cExtension, pCert->pCertInfo->rgExtension)) == NULL) PKITHROW(CRYPT_E_NO_MATCH); cbInfo = 0; CryptDecodeObject(dwCertEncodingType, X509_BASIC_CONSTRAINTS, pExt->Value.pbData, pExt->Value.cbData, 0, // dwFlags
NULL, // pInfo
&cbInfo); if (cbInfo == 0) PKITHROW(CRYPT_E_NO_MATCH); pInfo = (PCERT_BASIC_CONSTRAINTS_INFO) malloc(cbInfo); if(!pInfo) PKITHROW(E_OUTOFMEMORY); if (!CryptDecodeObject(dwCertEncodingType, X509_BASIC_CONSTRAINTS, pExt->Value.pbData, pExt->Value.cbData, 0, // dwFlags
pInfo, &cbInfo)) PKITHROW(SignError()); if (pInfo->SubjectType.cbData > 0) { BYTE bSubjectType = *pInfo->SubjectType.pbData; if (bSubjectType & CERT_END_ENTITY_SUBJECT_FLAG) grfSubjectType |= SIGNER_CERT_END_ENTITY_FLAG; if (0 == (bSubjectType & CERT_CA_SUBJECT_FLAG)) grfSubjectType |= SIGNER_CERT_NOT_CA_FLAG; } } PKICATCH(err) { hr = err.pkiError; } PKIEND;
if (pInfo) free(pInfo); return grfSubjectType; }
//-------------------------------------------------------------------------
//
// WSZtoSZ:
// Convert a wchar string to a multi-byte string.
//
//-------------------------------------------------------------------------
HRESULT WSZtoSZ(LPWSTR wsz, LPSTR *psz) {
DWORD cbSize=0;
*psz=NULL;
if(!wsz) return S_OK;
cbSize=WideCharToMultiByte(0,0,wsz,-1, NULL,0,0,0);
if(cbSize==0) return SignError();
*psz=(LPSTR)malloc(cbSize);
if(*psz==NULL) return E_OUTOFMEMORY;
if(WideCharToMultiByte(0,0,wsz,-1, *psz,cbSize,0,0)) { return S_OK; } else { free(*psz); return SignError(); } }
//-------------------------------------------------------------------------
//
// BytesToBase64:
// convert bytes to base64 bstr
//
//-------------------------------------------------------------------------
HRESULT BytesToBase64(BYTE *pb, DWORD cb, CHAR **pszEncode, DWORD *pdwEncode) { DWORD dwErr; DWORD cch; CHAR *psz=NULL;
*pszEncode=NULL; *pdwEncode=0;
if (cb == 0) { return S_OK; }
cch = 0; if (!CryptBinaryToStringA( pb, cb, CRYPT_STRING_BASE64, NULL, &cch )) return HRESULT_FROM_WIN32(GetLastError()); if (NULL == (psz=(CHAR *)malloc(cch * sizeof(char)))) return E_OUTOFMEMORY;
if (!CryptBinaryToStringA( pb, cb, CRYPT_STRING_BASE64, psz, &cch )) { free(psz); return HRESULT_FROM_WIN32(GetLastError()); } else { *pszEncode=psz; *pdwEncode=cch + 1; //plus 1 to include NULL
return S_OK; } }
//-------------------------------------------------------------------------
//
// BytesToBase64:
// conver base64 bstr to bytes
//
//-------------------------------------------------------------------------
HRESULT Base64ToBytes(CHAR *pEncode, DWORD cbEncode, BYTE **ppb, DWORD *pcb) { DWORD dwErr; BYTE *pb; DWORD cb;
*ppb = NULL; *pcb = 0;
cb = 0; if (!CryptStringToBinaryA( pEncode, cbEncode, CRYPT_STRING_ANY, NULL, &cb, NULL, NULL)) return HRESULT_FROM_WIN32(GetLastError()); if (cb == 0) return S_OK;
if (NULL == (pb = (BYTE *) malloc(cb))) return E_OUTOFMEMORY;
if (!CryptStringToBinaryA( pEncode, cbEncode, CRYPT_STRING_ANY, pb, &cb, NULL, NULL )) { free(pb); return HRESULT_FROM_WIN32(GetLastError()); } else { *ppb = pb; *pcb = cb; return S_OK; }
}
//+-------------------------------------------------------------------------
// Find the the cert from the hprov
// Parameter Returns:
// pReturnCert - context of the cert found (must pass in cert context);
// Returns:
// S_OK - everything worked
// E_OUTOFMEMORY - memory failure
// E_INVALIDARG - no pReturnCert supplied
// CRYPT_E_NO_MATCH - could not locate certificate in store
//
//+-------------------------------------------------------------------------
HRESULT SpcGetCertFromKey(IN DWORD dwCertEncodingType, IN HCERTSTORE hStore, IN HCRYPTPROV hProv, IN DWORD dwKeySpec, OUT PCCERT_CONTEXT* pReturnCert) { PCERT_PUBLIC_KEY_INFO psPubKeyInfo = NULL; DWORD dwPubKeyInfo; PCCERT_CONTEXT pCert = NULL; PCCERT_CONTEXT pEnumCert = NULL; DWORD grfCert = 0; HRESULT hr = S_OK;
PKITRY { if(!pReturnCert) PKITHROW(E_INVALIDARG);
// Get public key to compare certificates with
dwPubKeyInfo = 0; CryptExportPublicKeyInfo(hProv, dwKeySpec, dwCertEncodingType, NULL, // psPubKeyInfo
&dwPubKeyInfo); if (dwPubKeyInfo == 0) PKITHROW(SignError()); psPubKeyInfo = (PCERT_PUBLIC_KEY_INFO) malloc(dwPubKeyInfo); if(!psPubKeyInfo) PKITHROW(E_OUTOFMEMORY); if (!CryptExportPublicKeyInfo(hProv, dwKeySpec, dwCertEncodingType, psPubKeyInfo, &dwPubKeyInfo)) PKITHROW(SignError()); // Find the "strongest" cert with a matching public key
while (TRUE) { pEnumCert = CertEnumCertificatesInStore(hStore, pEnumCert); if (pEnumCert) { if (CertComparePublicKeyInfo(pEnumCert->dwCertEncodingType, &pEnumCert->pCertInfo->SubjectPublicKeyInfo, psPubKeyInfo)) { // END_ENTITY, NOT_CA
DWORD grfEnumCert = GetSubjectTypeFlags(pEnumCert->dwCertEncodingType, pEnumCert); if (S_OK != SignIsGlueCert(pEnumCert)) grfEnumCert |= SIGNER_CERT_NOT_GLUE_FLAG; if (!CertCompareCertificateName(pEnumCert->dwCertEncodingType, &pEnumCert->pCertInfo->Issuer, &pEnumCert->pCertInfo->Subject)) grfEnumCert |= SIGNER_CERT_NOT_SELF_SIGNED_FLAG; if (grfEnumCert >= grfCert) { // Found a signer cert with a stronger match
if (pCert) CertFreeCertificateContext(pCert); grfCert = grfEnumCert; if (grfCert == SIGNER_CERT_ALL_FLAGS) { pCert = pEnumCert; break; } else // Not a perfect match. Check for a better signer cert.
pCert = CertDuplicateCertificateContext(pEnumCert); } } } else break; } if (pCert == NULL) PKITHROW(CRYPT_E_NO_MATCH); if (!CertSetCertificateContextProperty(pCert, CERT_KEY_PROV_HANDLE_PROP_ID, CERT_STORE_NO_CRYPT_RELEASE_FLAG, (void *) hProv)) PKITHROW(SignError()); } PKICATCH(err) { hr = err.pkiError; if (pCert) { CertFreeCertificateContext(pCert); pCert = NULL; } } PKIEND; *pReturnCert = pCert; if (psPubKeyInfo) free(psPubKeyInfo); return hr; }
///-------------------------------------------------------------------------
// Authenticode routines (not necessary for all implementations)
//+-------------------------------------------------------------------------
//If all of the following three conditions are true, we should not put
// commercial or individual authenticated attributes into signer info
//
//1. the enhanced key usage extension of the signer's certificate has no code signing usage (szOID_PKIX_KP_CODE_SIGNING)
//2. basic constraints extension of the signer's cert is missing, or it is neither commercial nor individual
//3. user did not specify -individual or -commercial in signcode.exe.
//--------------------------------------------------------------------------
BOOL NeedStatementTypeAttr(IN PCCERT_CONTEXT pSignerCert, IN BOOL fCommercial, IN BOOL fIndividual) { BOOL fNeedStatementTypeAttr=FALSE; PCERT_EXTENSION pEKUExt=NULL; PCERT_EXTENSION pRestrictionExt=NULL; DWORD cPolicyId=0; PCERT_POLICY_ID pPolicyId=NULL;
BOOL fPolicyCommercial = FALSE; BOOL fPolicyIndividual = FALSE; BOOL fCodeSiginigEKU=FALSE; PCERT_KEY_USAGE_RESTRICTION_INFO pInfo = NULL; DWORD cbInfo=0;
PCERT_ENHKEY_USAGE pEKUInfo=NULL; DWORD dwIndex=0;
if(!pSignerCert) return FALSE;
//check for condition # 3
if(fCommercial || fIndividual) return TRUE;
//now we know user did not specify -individual or -commerical options
//if the cert has enhanced key usage extension
pEKUExt = CertFindExtension(szOID_ENHANCED_KEY_USAGE, pSignerCert->pCertInfo->cExtension, pSignerCert->pCertInfo->rgExtension);
pRestrictionExt = CertFindExtension(szOID_KEY_USAGE_RESTRICTION, pSignerCert->pCertInfo->cExtension, pSignerCert->pCertInfo->rgExtension);
if((!pEKUExt) && (!pRestrictionExt)) return FALSE;
if(pEKUExt) { cbInfo=0;
if(CryptDecodeObject(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, pEKUExt->Value.pbData, pEKUExt->Value.cbData, 0, // dwFlags
NULL, // pInfo
&cbInfo) && (cbInfo != 0)) { pEKUInfo = (PCERT_ENHKEY_USAGE) malloc(cbInfo); if(pEKUInfo) { if(CryptDecodeObject(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, pEKUExt->Value.pbData, pEKUExt->Value.cbData, 0, // dwFlags
pEKUInfo, // pInfo
&cbInfo) && (cbInfo != 0)) { for(dwIndex=0; dwIndex < pEKUInfo->cUsageIdentifier; dwIndex++) { if(0==strcmp(szOID_PKIX_KP_CODE_SIGNING, pEKUInfo->rgpszUsageIdentifier[dwIndex])) fCodeSiginigEKU=TRUE;
if(0==strcmp(SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID, pEKUInfo->rgpszUsageIdentifier[dwIndex])) fPolicyCommercial=TRUE;
if(0==strcmp(SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID, pEKUInfo->rgpszUsageIdentifier[dwIndex])) fPolicyIndividual=TRUE;
} }
}
}
}
if(pRestrictionExt) { cbInfo = 0; if(CryptDecodeObject(X509_ASN_ENCODING, X509_KEY_USAGE_RESTRICTION, pRestrictionExt->Value.pbData, pRestrictionExt->Value.cbData, 0, // dwFlags
NULL, // pInfo
&cbInfo) && (cbInfo != 0)) { pInfo = (PCERT_KEY_USAGE_RESTRICTION_INFO) malloc(cbInfo); if(pInfo) { if (CryptDecodeObject(X509_ASN_ENCODING, X509_KEY_USAGE_RESTRICTION, pRestrictionExt->Value.pbData, pRestrictionExt->Value.cbData, 0, // dwFlags
pInfo, &cbInfo)) { if (pInfo->cCertPolicyId) { cPolicyId = pInfo->cCertPolicyId; pPolicyId = pInfo->rgCertPolicyId;
for ( ; cPolicyId > 0; cPolicyId--, pPolicyId++) { DWORD cElementId = pPolicyId->cCertPolicyElementId; LPSTR *ppszElementId = pPolicyId->rgpszCertPolicyElementId;
for ( ; cElementId > 0; cElementId--, ppszElementId++) { if (strcmp(*ppszElementId, SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID) == 0) { fPolicyCommercial = TRUE; }
if (strcmp(*ppszElementId, SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID) == 0) fPolicyIndividual = TRUE; } } } }
}
} }
//free the memory
if(pInfo) free(pInfo);
if(pEKUInfo) free(pEKUInfo);
//if any of the value is true in the properties,
//we need to add the statement type attribute
if( fPolicyCommercial || fPolicyIndividual || fCodeSiginigEKU) return TRUE;
return FALSE; }
//+-------------------------------------------------------------------------
//
// The function decides whether to sign the certificate as a commerical,
// or individual. The default is the certificate's highest capability. If fCommercial
// is set and the cert can not signly commercially, an error is returned.
// Same for fIndividual.
//
//--------------------------------------------------------------------------
HRESULT CheckCommercial(PCCERT_CONTEXT pSignerCert, BOOL fCommercial, BOOL fIndividual, BOOL *pfCommercial) { HRESULT hr = S_OK; BOOL fPolicyCommercial = FALSE; BOOL fPolicyIndividual = FALSE; PCERT_EXTENSION pExt; PCERT_KEY_USAGE_RESTRICTION_INFO pInfo = NULL; DWORD cbInfo;
PCERT_EXTENSION pEKUExt=NULL; PCERT_ENHKEY_USAGE pUsage=NULL; DWORD cCount=0;
if(!pfCommercial) return E_INVALIDARG;
//init
*pfCommercial=FALSE;
//fCommercial and fIndividual can not be set at the same time
if(fCommercial && fIndividual) return E_INVALIDARG;
PKITRY {
//first look into the cert extension szOID_KEY_USAGE_RESTRICTION
pExt = CertFindExtension(szOID_KEY_USAGE_RESTRICTION, pSignerCert->pCertInfo->cExtension, pSignerCert->pCertInfo->rgExtension); if(pExt) { cbInfo = 0; CryptDecodeObject(X509_ASN_ENCODING, X509_KEY_USAGE_RESTRICTION, pExt->Value.pbData, pExt->Value.cbData, 0, // dwFlags
NULL, // pInfo
&cbInfo); if (cbInfo == 0) PKITHROW(SignError()); pInfo = (PCERT_KEY_USAGE_RESTRICTION_INFO) malloc(cbInfo); if(!pInfo) PKITHROW(E_OUTOFMEMORY); if (!CryptDecodeObject(X509_ASN_ENCODING, X509_KEY_USAGE_RESTRICTION, pExt->Value.pbData, pExt->Value.cbData, 0, // dwFlags
pInfo, &cbInfo)) PKITHROW(SignError()); if (pInfo->cCertPolicyId) { DWORD cPolicyId; PCERT_POLICY_ID pPolicyId; cPolicyId = pInfo->cCertPolicyId; pPolicyId = pInfo->rgCertPolicyId; for ( ; cPolicyId > 0; cPolicyId--, pPolicyId++) { DWORD cElementId = pPolicyId->cCertPolicyElementId; LPSTR *ppszElementId = pPolicyId->rgpszCertPolicyElementId; for ( ; cElementId > 0; cElementId--, ppszElementId++) { if (strcmp(*ppszElementId, SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID) == 0) { fPolicyCommercial = TRUE; } if (strcmp(*ppszElementId, SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID) == 0) fPolicyIndividual = TRUE; } } } //end of pInfo->cCertPolicyId
} //end of pExt
//now
} PKICATCH(err) { hr = err.pkiError; } PKIEND;
if (pInfo) { free(pInfo); pInfo=NULL; }
if(hr!=S_OK) return hr;
//if either of the policy is set, we check for the EKU extension
if((!fPolicyCommercial) && (!fPolicyIndividual)) { pExt = CertFindExtension(szOID_ENHANCED_KEY_USAGE, pSignerCert->pCertInfo->cExtension, pSignerCert->pCertInfo->rgExtension);
if(pExt) { cbInfo = 0;
if(CryptDecodeObject(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, pExt->Value.pbData, pExt->Value.cbData, 0, NULL, &cbInfo) && (cbInfo != 0)) { pUsage = (PCERT_ENHKEY_USAGE) malloc(cbInfo);
if(pUsage) { if (CryptDecodeObject(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, pExt->Value.pbData, pExt->Value.cbData, 0, // dwFlags
pUsage, &cbInfo)) { for(cCount=0; cCount< pUsage->cUsageIdentifier; cCount++) { if (strcmp((pUsage->rgpszUsageIdentifier)[cCount], SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID) == 0) { fPolicyCommercial = TRUE; }
if (strcmp((pUsage->rgpszUsageIdentifier)[cCount], SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID) == 0) { fPolicyIndividual = TRUE; } } }
} } } } if(pUsage) { free(pUsage); pUsage=NULL; }
//if either of the policy is set, we use individual
if(!fPolicyCommercial && !fPolicyIndividual) fPolicyIndividual=TRUE;
//default
if((!fCommercial) && (!fIndividual)) { if(fPolicyCommercial) *pfCommercial=TRUE; else *pfCommercial=FALSE;
return S_OK; }
if(fCommercial && (!fIndividual)) { if(fPolicyCommercial) { *pfCommercial=TRUE; return S_OK; } else return TYPE_E_TYPEMISMATCH; }
//the following is fIndividual and !fCommercial
if(fPolicyIndividual) { *pfCommercial=FALSE; return S_OK; } else return TYPE_E_TYPEMISMATCH; }
//+-------------------------------------------------------------------------
// Encode the StatementType authenticated attribute value
//--------------------------------------------------------------------------
HRESULT CreateStatementType(IN BOOL fCommercial, OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded) { HRESULT hr = S_OK; PBYTE pbEncoded = NULL; DWORD cbEncoded; LPSTR pszIndividual = SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID; LPSTR pszCommercial = SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID; SPC_STATEMENT_TYPE StatementType;
StatementType.cKeyPurposeId = 1; if (fCommercial) StatementType.rgpszKeyPurposeId = &pszCommercial; else StatementType.rgpszKeyPurposeId = &pszIndividual;
PKITRY {
cbEncoded = 0; CryptEncodeObject(X509_ASN_ENCODING, SPC_STATEMENT_TYPE_STRUCT, &StatementType, NULL, // pbEncoded
&cbEncoded); if (cbEncoded == 0) PKITHROW(SignError()); pbEncoded = (BYTE *) malloc(cbEncoded); if (pbEncoded == NULL) PKITHROW(E_OUTOFMEMORY); if (!CryptEncodeObject(X509_ASN_ENCODING, SPC_STATEMENT_TYPE_STRUCT, &StatementType, pbEncoded, &cbEncoded)) PKITHROW(SignError()); } PKICATCH(err) { if (pbEncoded) { free(pbEncoded); pbEncoded = NULL; } cbEncoded = 0; hr = err.pkiError; } PKIEND;
*ppbEncoded = pbEncoded; *pcbEncoded = cbEncoded; return hr; }
//+-------------------------------------------------------------------------
// Encode the SpOpusInfo authenticated attribute value
//--------------------------------------------------------------------------
HRESULT CreateOpusInfo(IN LPCWSTR pwszOpusName, IN LPCWSTR pwszOpusInfo, OUT BYTE **ppbEncoded, IN OUT DWORD *pcbEncoded) { HRESULT hr = S_OK; BYTE *pbEncoded = NULL; DWORD cbEncoded; SPC_LINK MoreInfo; SPC_SP_OPUS_INFO sSpcOpusInfo; ZeroMemory(&sSpcOpusInfo, sizeof(SPC_SP_OPUS_INFO)); sSpcOpusInfo.pwszProgramName = (LPWSTR) pwszOpusName;
if (pwszOpusInfo) { MoreInfo.dwLinkChoice = SPC_URL_LINK_CHOICE;
//
// To be backwards compatible with IE 3.0 WinVerifyTrust the
// following is set to an even length to inhibit the possibility
// of an 0x7f length in the encoded ASN.
// In IE 3.0 an 0x81 is erroneously prepended before a
// 0x7f length when the OPUS info is re-encoded before hashing. Making
// the length of pwszUrl even precludes this from happening.
//
// Note, the pwszUrl is first converted to multibyte before being
// encoded. Its the multibyte length that must have an even length.
int cchMultiByte; cchMultiByte = WideCharToMultiByte(CP_ACP, 0, // dwFlags
pwszOpusInfo, -1, // cchWideChar, -1 => null terminated
NULL, // lpMultiByteStr
0, // cchMultiByte
NULL, // lpDefaultChar
NULL // lpfUsedDefaultChar
); // cchMultiByte includes the null terminator
if (cchMultiByte > 1 && ((cchMultiByte - 1) & 1)) { // Odd length. Add extra space to end.
int Len = wcslen(pwszOpusInfo); MoreInfo.pwszUrl = (LPWSTR) _alloca((Len + 2) * sizeof(WCHAR)); wcscpy(MoreInfo.pwszUrl, pwszOpusInfo); wcscpy(MoreInfo.pwszUrl + Len, L" "); } else MoreInfo.pwszUrl = (LPWSTR) pwszOpusInfo; sSpcOpusInfo.pMoreInfo = &MoreInfo; } PKITRY { cbEncoded = 0; CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, SPC_SP_OPUS_INFO_STRUCT, &sSpcOpusInfo, NULL, // pbEncoded
&cbEncoded); if (cbEncoded == 0) PKITHROW(SignError()); pbEncoded = (BYTE *) malloc(cbEncoded); if (pbEncoded == NULL) PKITHROW(E_OUTOFMEMORY); if (!CryptEncodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, SPC_SP_OPUS_INFO_STRUCT, &sSpcOpusInfo, pbEncoded, &cbEncoded)) PKITHROW(SignError()); } PKICATCH(err) { if (pbEncoded) { free(pbEncoded); pbEncoded = NULL; } cbEncoded = 0; hr = err.pkiError; } PKIEND;
*ppbEncoded = pbEncoded; *pcbEncoded = cbEncoded; return hr; }
//+-------------------------------------------------------------------------
// Checks if the certificate a glue certificate
// in IE30
// Returns: S_OK - Is a glue certificate
// S_FALSE - Not a certificate
// CRYPT_E_OSS_ERROR + Oss error - Encode or Decode error.
//+-------------------------------------------------------------------------
HRESULT SignIsGlueCert(IN PCCERT_CONTEXT pCert) { HRESULT hr = S_OK; PCERT_NAME_BLOB pName = &pCert->pCertInfo->Subject; PCERT_NAME_INFO pNameInfo = NULL; DWORD cbNameInfo;
PKITRY { cbNameInfo = 0; CryptDecodeObject(X509_ASN_ENCODING, X509_NAME, pName->pbData, pName->cbData, 0, // dwFlags
NULL, // pNameInfo
&cbNameInfo); if (cbNameInfo == 0) PKITHROW(SignError()); pNameInfo = (PCERT_NAME_INFO) malloc(cbNameInfo); if(!pNameInfo) return E_OUTOFMEMORY; if (!CryptDecodeObject(X509_ASN_ENCODING, X509_NAME, pName->pbData, pName->cbData, 0, // dwFlags
pNameInfo, &cbNameInfo)) PKITHROW(SignError());
if(!CertFindRDNAttr(SPC_GLUE_RDN_OBJID, pNameInfo) != NULL) hr = S_FALSE; } PKICATCH (err) { hr = err.pkiError; } PKIEND;
if (pNameInfo) free(pNameInfo);
return hr; }
//+-------------------------------------------------------------------------
// Skip over the identifier and length octets in an ASN encoded blob.
// Returns the number of bytes skipped.
//
// For an invalid identifier or length octet returns 0.
//--------------------------------------------------------------------------
static DWORD SkipOverIdentifierAndLengthOctets( IN const BYTE *pbDER, IN DWORD cbDER ) { #define TAG_MASK 0x1f
DWORD cb; DWORD cbLength; const BYTE *pb = pbDER;
// Need minimum of 2 bytes
if (cbDER < 2) return 0;
// Skip over the identifier octet(s)
if (TAG_MASK == (*pb++ & TAG_MASK)) { // high-tag-number form
for (cb=2; *pb++ & 0x80; cb++) { if (cb >= cbDER) return 0; } } else // low-tag-number form
cb = 1;
// need at least one more byte for length
if (cb >= cbDER) return 0;
if (0x80 == *pb) // Indefinite
cb++; else if ((cbLength = *pb) & 0x80) { cbLength &= ~0x80; // low 7 bits have number of bytes
cb += cbLength + 1; if (cb > cbDER) return 0; } else cb++;
return cb; }
//--------------------------------------------------------------------------
//
// Skip over the tag and length
//----------------------------------------------------------------------------
BOOL WINAPI SignNoContentWrap(IN const BYTE *pbDER, IN DWORD cbDER) { DWORD cb;
cb = SkipOverIdentifierAndLengthOctets(pbDER, cbDER); if (cb > 0 && cb < cbDER && pbDER[cb] == 0x02) return TRUE; else return FALSE; }
#define SH1_HASH_LENGTH 20
//+-----------------------------------------------------------------------
// Make sure that the certificate is valid for timestamp
//------------------------------------------------------------------------
/*BOOL ValidTimestampCert(PCCERT_CONTEXT pCertContext)
{ BOOL fValid=FALSE; DWORD cbSize=0; PCERT_ENHKEY_USAGE pCertEKU=NULL; BYTE *pbaSignersThumbPrint=NULL; DWORD dwIndex=0;
static BYTE baVerisignTimeStampThumbPrint[SH1_HASH_LENGTH] = { 0x38, 0x73, 0xB6, 0x99, 0xF3, 0x5B, 0x9C, 0xCC, 0x36, 0x62, 0xB6, 0x48, 0x3A, 0x96, 0xBD, 0x6E, 0xEC, 0x97, 0xCF, 0xB7 };
cbSize = 0;
if (!(CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, NULL, &cbSize))) goto CLEANUP;
pbaSignersThumbPrint=(BYTE *)malloc(cbSize); if(!pbaSignersThumbPrint) goto CLEANUP;
if (!(CertGetCertificateContextProperty(pCertContext, CERT_SHA1_HASH_PROP_ID, pbaSignersThumbPrint, &cbSize))) goto CLEANUP;
//
// 1st, check to see if it's Verisign's first timestamp certificate
if(cbSize!=sizeof(baVerisignTimeStampThumbPrint)/sizeof(baVerisignTimeStampThumbPrint[0])) goto CLEANUP;
if (memcmp(pbaSignersThumbPrint, baVerisignTimeStampThumbPrint, cbSize) == 0) { fValid=TRUE; goto CLEANUP; }
//
// see if the certificate has the proper enhanced key usage OID
//
cbSize = 0;
if(!CertGetEnhancedKeyUsage(pCertContext, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, NULL, &cbSize) || (cbSize==0)) goto CLEANUP;
pCertEKU = (PCERT_ENHKEY_USAGE)malloc(cbSize); if(!pCertEKU) goto CLEANUP;
if (!(CertGetEnhancedKeyUsage(pCertContext, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, pCertEKU, &cbSize))) goto CLEANUP;
for (dwIndex = 0; dwIndex < pCertEKU->cUsageIdentifier; dwIndex++) { if (strcmp(pCertEKU->rgpszUsageIdentifier[dwIndex], szOID_KP_TIME_STAMP_SIGNING) == 0) { fValid=TRUE; break; }
if (strcmp(pCertEKU->rgpszUsageIdentifier[dwIndex], szOID_PKIX_KP_TIMESTAMP_SIGNING) == 0) { fValid=TRUE; break; }
}
CLEANUP:
if(pbaSignersThumbPrint) free(pbaSignersThumbPrint);
if(pCertEKU) free(pCertEKU);
return fValid; } */
//-------------------------------------------------------------------------
//
// Call GetLastError and convert the return code to HRESULT
//--------------------------------------------------------------------------
HRESULT WINAPI SignError () { DWORD dw = GetLastError (); HRESULT hr; if ( dw <= (DWORD) 0xFFFF ) hr = HRESULT_FROM_WIN32 ( dw ); else hr = dw; if ( ! FAILED ( hr ) ) { // somebody failed a call without properly setting an error condition
hr = E_UNEXPECTED; } return hr; }
|