|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: usecert.cpp
//
// Contents: cert store and file operations
//
//--------------------------------------------------------------------------
#include "pch.cpp"
#pragma hdrstop
#include <wincrypt.h>
#include "initcert.h"
#include "cscsp.h"
#include "cspenum.h"
#include "wizpage.h"
#include "usecert.h"
#define __dwFILE__ __dwFILE_OCMSETUP_USECERT_CPP__
typedef struct _EXISTING_CA_IDINFO { LPSTR pszObjId; WCHAR **ppwszIdInfo; } EXISTING_CA_IDINFO;
/*
EXISTING_CA_IDINFO g_ExistingCAIdInfo[] = { {szOID_COMMON_NAME, NULL}, {szOID_ORGANIZATION_NAME, NULL}, {szOID_ORGANIZATIONAL_UNIT_NAME, NULL}, {szOID_LOCALITY_NAME, NULL}, {szOID_STATE_OR_PROVINCE_NAME, NULL}, {szOID_RSA_emailAddr, NULL}, {szOID_COUNTRY_NAME, NULL}, {NULL, NULL}, }; */
HRESULT myMakeExprValidity( IN FILETIME const *pft, OUT LONG *plDayCount) { HRESULT hr; FILETIME ft; LONGLONG llDelta;
*plDayCount = 0;
// get current time
GetSystemTimeAsFileTime(&ft);
llDelta = mySubtractFileTimes(pft, &ft); llDelta /= 1000 * 1000 * 10; llDelta += 12 * 60 * 60; // half day more to avoid truncation
llDelta /= 24 * 60 * 60;
*plDayCount = (LONG) llDelta; if (0 > *plDayCount) { *plDayCount = 0; } hr = S_OK;
//error:
return hr; }
//--------------------------------------------------------------------
// returns true if the CA type is root and the cert is self-signed,
// or the CA type is subordinate and the cert is no self-signed
HRESULT IsCertSelfSignedForCAType( IN CASERVERSETUPINFO * pServer, IN CERT_CONTEXT const * pccCert, OUT BOOL * pbOK) { CSASSERT(NULL!=pccCert); CSASSERT(NULL!=pServer); CSASSERT(NULL!=pbOK);
HRESULT hr; DWORD dwVerificationFlags; BOOL bRetVal;
// See if this cert is self-signed or not.
// First, we flag what we want to check: "Use the public
// key in the issuer's certificate to verify the signature on
// the subject certificate."
// We use the same certificate as issuer and subject
dwVerificationFlags=CERT_STORE_SIGNATURE_FLAG; // perform the checks
bRetVal=CertVerifySubjectCertificateContext( pccCert, pccCert, // issuer same as subject
&dwVerificationFlags); if (FALSE==bRetVal) { hr=myHLastError(); _JumpError(hr, error, "CertVerifySubjectCertificateContext"); } // Every check that passed had its flag zeroed. See if our check passed
if (CERT_STORE_SIGNATURE_FLAG&dwVerificationFlags){ // This cert is not self-signed.
if (IsRootCA(pServer->CAType)) { // A root CA cert must be self-signed.
*pbOK=FALSE; } else { // A subordinate CA cert must not be self-signed.
*pbOK=TRUE; } } else { // This cert is self-signed.
if (IsSubordinateCA(pServer->CAType)) { // A subordinate CA cert must not be self-signed.
*pbOK=FALSE; } else { // A root CA cert must be self-signed.
*pbOK=TRUE; } }
hr=S_OK;
error: return hr; }
//--------------------------------------------------------------------
// find a certificate's hash algorithm in a CSP's list of hash algorithms
HRESULT FindHashAlgorithm( IN CERT_CONTEXT const * pccCert, IN CSP_INFO * pCSPInfo, OUT CSP_HASH ** ppHash) { CSASSERT(NULL!=pccCert); CSASSERT(NULL!=pCSPInfo); CSASSERT(NULL!=ppHash);
HRESULT hr; CSP_HASH * phTravel; const CRYPT_OID_INFO * pOIDInfo;
// Initialize out param
*ppHash=NULL;
// get the AlgID from the hash algorithm OID
// (returned pointer must not be freed)
pOIDInfo=CryptFindOIDInfo( CRYPT_OID_INFO_OID_KEY, pccCert->pCertInfo->SignatureAlgorithm.pszObjId, CRYPT_SIGN_ALG_OID_GROUP_ID ); if (NULL==pOIDInfo) { // function is not doc'd to set GetLastError()
hr=CRYPT_E_NOT_FOUND; _JumpError(hr, error, "Signature algorithm not found"); }
// find the hash algorithm in the list of hash algorithms the CSP supports
for (phTravel=pCSPInfo->pHashList; NULL!=phTravel; phTravel=phTravel->next) { if (pOIDInfo->Algid==phTravel->idAlg) { *ppHash=phTravel; break; } } if (NULL==phTravel) { hr=CRYPT_E_NOT_FOUND; _JumpError(hr, error, "CSP does not support hash algorithm"); }
hr=S_OK;
error: return hr; }
/*
HRESULT HookExistingIdInfoData( CASERVERSETUPINFO *pServer) { HRESULT hr; int i = 0;
while (NULL != g_ExistingCAIdInfo[i].pszObjId) { if (0 == strcmp(szOID_COMMON_NAME, g_ExistingCAIdInfo[i].pszObjId)) { g_ExistingCAIdInfo[i].ppwszIdInfo = &pServer->pwszCACommonName; } else if (0 == strcmp(szOID_ORGANIZATION_NAME, g_ExistingCAIdInfo[i].pszObjId)) { // dead
} else if (0 == strcmp(szOID_ORGANIZATIONAL_UNIT_NAME, g_ExistingCAIdInfo[i].pszObjId)) { // dead
} else if (0 == strcmp(szOID_LOCALITY_NAME, g_ExistingCAIdInfo[i].pszObjId)) { // dead
} else if (0 == strcmp(szOID_STATE_OR_PROVINCE_NAME, g_ExistingCAIdInfo[i].pszObjId)) { // dead
} else if (0 == strcmp(szOID_COUNTRY_NAME, g_ExistingCAIdInfo[i].pszObjId)) { // dead
} else if (0 == strcmp(szOID_RSA_emailAddr, g_ExistingCAIdInfo[i].pszObjId)) { // dead
} else { hr = E_INVALIDARG; _JumpError(hr, error, "unsupported name"); }
++i; }
hr = S_OK; error: return hr; } */
HRESULT DetermineExistingCAIdInfo( IN OUT CASERVERSETUPINFO *pServer, OPTIONAL IN CERT_CONTEXT const *pUpgradeCert) { CERT_NAME_INFO *pCertNameInfo = NULL; DWORD cbCertNameInfo; WCHAR const *pwszNameProp; HRESULT hr = E_FAIL; CERT_CONTEXT const *pCert = pServer->pccExistingCert;
CSASSERT(NULL!=pServer->pccExistingCert || NULL != pUpgradeCert);
if (NULL == pUpgradeCert) { myMakeExprValidity( &pServer->pccExistingCert->pCertInfo->NotAfter, &pServer->lExistingValidity);
pServer->NotBefore = pServer->pccExistingCert->pCertInfo->NotBefore; pServer->NotAfter = pServer->pccExistingCert->pCertInfo->NotAfter; } if (NULL != pUpgradeCert) { pCert = pUpgradeCert; }
if (!myDecodeName(X509_ASN_ENCODING, X509_UNICODE_NAME, pCert->pCertInfo->Subject.pbData, pCert->pCertInfo->Subject.cbData, CERTLIB_USE_LOCALALLOC, &pCertNameInfo, &cbCertNameInfo)) { hr = myHLastError(); _JumpError(hr, error, "myDecodeName"); }
/*
// fill a data structure for existing key id info
hr = HookExistingIdInfoData(pServer); _JumpIfError(hr, error, "HookExistingIdInfoData");
// load names from cert to the the data structure
i = 0;
while (NULL != g_ExistingCAIdInfo[i].pszObjId) { if (S_OK == myGetCertNameProperty( pCertNameInfo, g_ExistingCAIdInfo[i].pszObjId, &pwszNameProp)) { pwszExisting = (WCHAR*)LocalAlloc(LPTR, (wcslen(pwszNameProp) + 1) * sizeof(WCHAR)); _JumpIfOutOfMemory(hr, error, pwszExisting);
// get name
wcscpy(pwszExisting, pwszNameProp);
// make sure free old
if (NULL != *(g_ExistingCAIdInfo[i].ppwszIdInfo)) { LocalFree(*(g_ExistingCAIdInfo[i].ppwszIdInfo)); } *(g_ExistingCAIdInfo[i].ppwszIdInfo) = pwszExisting; } ++i; } */ hr = myGetCertNameProperty( FALSE, pCertNameInfo, szOID_COMMON_NAME, &pwszNameProp); if (hr == S_OK) { if(pServer->pwszCACommonName) { LocalFree(pServer->pwszCACommonName); pServer->pwszCACommonName = NULL; } // Common name exists, copy it out
hr = myDupString(pwszNameProp, &(pServer->pwszCACommonName)); _JumpIfError(hr, error, "myDupString"); }
if(pServer->pwszFullCADN) { LocalFree(pServer->pwszFullCADN); pServer->pwszFullCADN = NULL; }
// now get everything else
hr = myCertNameToStr( X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &pCert->pCertInfo->Subject, CERT_X500_NAME_STR | CERT_NAME_STR_COMMA_FLAG | CERT_NAME_STR_REVERSE_FLAG, &pServer->pwszFullCADN); _JumpIfError(hr, error, "myCertNameToStr");
// No need for a DN suffix, the full DN is already in the cert
if(pServer->pwszDNSuffix) { LocalFree(pServer->pwszDNSuffix); pServer->pwszDNSuffix = NULL; }
hr = myDupString(L"", &(pServer->pwszDNSuffix)); _JumpIfError(hr, error, "myDupString"); hr = S_OK;
error: if (NULL != pCertNameInfo) { LocalFree(pCertNameInfo); } return hr; }
//--------------------------------------------------------------------
// find a cert that matches the currently selected CSP and key container name
// returns CRYPT_E_NOT_FOUND if no certificate. Caller MUST free the returned
// context.
// Note: IT IS VERY IMPORTANT that pfx import maintains all the
// invariants about CSP, key container, hash, cert validity, etc.
// that the rest of the UI (including this function) maintains.
HRESULT FindCertificateByKey( IN CASERVERSETUPINFO * pServer, OUT CERT_CONTEXT const ** ppccCertificateCtx) { CSASSERT(NULL!=pServer); CSASSERT(NULL!=ppccCertificateCtx);
HRESULT hr; DWORD dwPublicKeySize; BOOL bRetVal; DWORD dwVerificationFlags; CERT_CONTEXT const *pccFound = NULL;
// variables that must be cleaned up
HCRYPTPROV hProv=NULL; CERT_PUBLIC_KEY_INFO * pcpkiKeyInfo=NULL; CERT_CONTEXT const * pccCurrentCert=NULL;
// initialize out param
*ppccCertificateCtx=NULL;
// open certificate store if it is not already open
if (NULL==pServer->hMyStore) { pServer->hMyStore=CertOpenStore( CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING, NULL, // hProv
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_ENUM_ARCHIVED_FLAG, wszMY_CERTSTORE); if (NULL==pServer->hMyStore) { hr=myHLastError(); _JumpError(hr, error, "CertOpenStore"); } }
//
// Get public key blob from key container
// Note: This may fail if key is not
// AT_SIGNATURE, but we will never actually use the key in
// this case anyway so it's ok to not find any certs
//
DBGPRINT(( DBG_SS_CERTOCM, "FindCertificateByKey: key=%ws\n", pServer->pwszKeyContainerName));
// first, open the key container
bRetVal=myCertSrvCryptAcquireContext( &hProv, pServer->pwszKeyContainerName, pServer->pCSPInfo->pwszProvName, pServer->pCSPInfo->dwProvType, CRYPT_SILENT, // we should never have to ask anything to get this info
pServer->pCSPInfo->fMachineKeyset); if (FALSE==bRetVal) { hr=myHLastError(); _JumpError(hr, error, "myCertSrvCryptAcquireContext"); }
// get the size of the blob
bRetVal=CryptExportPublicKeyInfo( hProv, AT_SIGNATURE, X509_ASN_ENCODING, NULL, //determine size
&dwPublicKeySize); if (FALSE==bRetVal) { hr=myHLastError(); _JumpError(hr, error, "CryptExportPublicKeyInfo (get data size)"); }
// allocate the blob
pcpkiKeyInfo=(CERT_PUBLIC_KEY_INFO *)LocalAlloc(LMEM_FIXED, dwPublicKeySize); _JumpIfOutOfMemory(hr, error, pcpkiKeyInfo);
// get the public key info blob
bRetVal=CryptExportPublicKeyInfo( hProv, AT_SIGNATURE, X509_ASN_ENCODING, pcpkiKeyInfo, &dwPublicKeySize); if (FALSE==bRetVal) { hr=myHLastError(); _JumpError(hr, error, "CryptExportPublicKeyInfo (get data)"); }
//
// Find a certificate that has a matching key, has not expired,
// and is self-signed or not self-signed depending upon
// the CA type we are trying to install
//
for (;;) { // find the next cert that has this public key
// Note: the function will free the previously
// used context when we pass it back
pccCurrentCert=CertFindCertificateInStore( pServer->hMyStore, X509_ASN_ENCODING, 0, // flags
CERT_FIND_PUBLIC_KEY, pcpkiKeyInfo, pccCurrentCert);
// exit the loop when we can find no more matching certs
if (NULL == pccCurrentCert) { hr = myHLastError(); if (NULL != pccFound) { break; } _JumpError(hr, error, "CertFindCertificateInStore"); }
// check to make sure that the cert has not expired
// first, we flag what we want to check
dwVerificationFlags = CERT_STORE_TIME_VALIDITY_FLAG;
// perform the checks
bRetVal=CertVerifySubjectCertificateContext( pccCurrentCert, NULL, // issuer; not needed
&dwVerificationFlags); if (FALSE==bRetVal) { _PrintError(myHLastError(), "CertVerifySubjectCertificateContext"); // this should not fail, but maybe we got a bad cert. Keep looking.
continue; } // Every check that passed had its flag zeroed. See if our check passed
if (CERT_STORE_TIME_VALIDITY_FLAG&dwVerificationFlags){ // This cert is expired and we can't use it. Keep looking.
continue; }
// verify to make sure no cert in chain is revoked, but don't kill
// yourself if can't connect
// allow untrusted cert if installing a root
hr = myVerifyCertContext( pccCurrentCert, CA_VERIFY_FLAGS_IGNORE_OFFLINE | (IsRootCA(pServer->CAType)? CA_VERIFY_FLAGS_ALLOW_UNTRUSTED_ROOT : 0), 0, NULL, HCCE_LOCAL_MACHINE, NULL, NULL); if (S_OK != hr) { // At least one cert is revoked in chain
_PrintError(hr, "myVerifyCertContext"); continue; }
// See if this cert appropriately is self-signed or not.
// A root CA cert must be self-signed, while
// a subordinate CA cert must not be self-signed.
hr=IsCertSelfSignedForCAType(pServer, pccCurrentCert, &bRetVal); if (FAILED(hr)) { // this should not fail, but maybe we got a bad cert. Keep looking.
_PrintError(hr, "IsCertSelfSignedForCAType"); continue; } if (FALSE==bRetVal) { // this cert is not appropriate for this CA type
_PrintError(S_FALSE, "bad CA Type"); continue; }
// If we got here, the cert we have is a good one.
// If we already found a good cert and this one expires later,
// toss the old one and save this one.
if (NULL != pccFound) { if (0 > CompareFileTime( &pccCurrentCert->pCertInfo->NotAfter, &pccFound->pCertInfo->NotAfter)) { continue; // old one is newer -- keep it
} CertFreeCertificateContext(pccFound); pccFound = NULL; } pccFound = CertDuplicateCertificateContext(pccCurrentCert); if (NULL == pccFound) { hr = myHLastError(); _JumpError(hr, error, "CertDuplicateCertificateContext"); }
} // <- End certificate finding loop
CSASSERT(NULL != pccFound); *ppccCertificateCtx = pccFound; pccFound = NULL; hr = S_OK;
error: if (NULL!=hProv) { CryptReleaseContext(hProv, 0); } if (NULL!=pcpkiKeyInfo) { LocalFree(pcpkiKeyInfo); } if (NULL != pccFound) { CertFreeCertificateContext(pccFound); } if (NULL!=pccCurrentCert) { CertFreeCertificateContext(pccCurrentCert); } if (S_OK!=hr && CRYPT_E_NOT_FOUND!=hr) { _PrintError(hr, "Ignoring error in FindCertificateByKey, returning CRYPT_E_NOT_FOUND") hr=CRYPT_E_NOT_FOUND; }
return hr; }
//--------------------------------------------------------------------
// Set which existing certificate we want to use
HRESULT SetExistingCertToUse( IN CASERVERSETUPINFO * pServer, IN CERT_CONTEXT const * pccCertCtx) { CSASSERT(NULL!=pServer); CSASSERT(NULL!=pccCertCtx);
HRESULT hr; CSP_HASH * pHash;
// to use an existing cert, we must use an existing key
CSASSERT(NULL!=pServer->pwszKeyContainerName);
// find the hash algorithm that matches this cert, and use it if possible
// otherwise, stick with what we are currently using.
hr=FindHashAlgorithm(pccCertCtx, pServer->pCSPInfo, &pHash); if (S_OK==hr) { pServer->pHashInfo = pHash; }
hr = myGetNameId(pccCertCtx, &pServer->dwCertNameId); _PrintIfError(hr, "myGetNameId");
if (MAXDWORD == pServer->dwCertNameId) { pServer->dwCertNameId = 0; }
ClearExistingCertToUse(pServer); pServer->pccExistingCert=pccCertCtx;
// We could assume that everything will work, but it doesn't take long to check
//pServer->fValidatedHashAndKey=TRUE;
hr=S_OK;
//error:
return hr; }
//--------------------------------------------------------------------
// stop using an existing certificate
void ClearExistingCertToUse( IN CASERVERSETUPINFO * pServer) { CSASSERT(NULL!=pServer);
if (NULL!=pServer->pccExistingCert) { CertFreeCertificateContext(pServer->pccExistingCert); pServer->pccExistingCert=NULL; } }
|