windows-nt/Source/XPSP1/NT/ds/security/services/ca/certlib/crypt.cpp
2020-09-26 16:20:57 +08:00

3120 lines
66 KiB
C++

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: crypt.cpp
//
// Contents: Cert Server wrapper routines
//
// History: 17-Oct-96 vich created
//
//---------------------------------------------------------------------------
#include <pch.cpp>
#pragma hdrstop
#include <stdlib.h>
#include "csdisp.h"
#include "cscsp.h"
#include "csprop.h"
#include "csber.h"
HRESULT
myGenerateKeys(
IN WCHAR const *pwszContainer,
OPTIONAL IN WCHAR const *pwszProvName,
IN BOOL fMachineKeySet,
IN DWORD dwKeySpec,
IN DWORD dwProvType,
IN DWORD dwKeySize,
OUT HCRYPTPROV *phProv)
{
HRESULT hr;
HCRYPTKEY hKey = NULL;
DWORD dwFlags = 0;
*phProv = NULL;
if (fMachineKeySet)
{
dwFlags |= CRYPT_MACHINE_KEYSET;
}
// see if the container already exists
if (CryptAcquireContext(
phProv,
pwszContainer,
pwszProvName,
dwProvType,
dwFlags))
{
if (NULL != *phProv)
{
CryptReleaseContext(*phProv, 0);
*phProv = NULL;
}
// container exists -- remove old keys and generate new ones.
if (!CryptAcquireContext(
phProv,
pwszContainer,
pwszProvName,
dwProvType,
CRYPT_DELETEKEYSET | dwFlags))
{
hr = myHLastError();
_JumpError(hr, error, "CryptAcquireContextEx");
}
}
// create new container
if (!CryptAcquireContext(
phProv,
pwszContainer,
pwszProvName,
dwProvType,
CRYPT_NEWKEYSET | dwFlags)) // force new container
{
hr = myHLastError();
_JumpError(hr, error, "CryptAcquireContextEx");
}
// create keys
if (!CryptGenKey(*phProv, dwKeySpec, dwKeySize << 16, &hKey))
{
hr = myHLastError();
_JumpError(hr, error, "CryptGenKey");
}
hr = S_OK;
error:
if (NULL != hKey)
{
CryptDestroyKey(hKey);
}
return(hr);
}
HRESULT
myCryptExportPrivateKey(
IN HCRYPTKEY hKey,
OUT BYTE **ppbKey,
OUT DWORD *pcbKey)
{
HRESULT hr;
BYTE *pbKey = NULL;
*ppbKey = NULL;
// export the key set to a CAPI blob
if (!CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, NULL, pcbKey))
{
hr = myHLastError();
_JumpError2(hr, error, "CryptExportKey", NTE_BAD_KEY_STATE);
}
pbKey = (BYTE *) LocalAlloc(LMEM_FIXED, *pcbKey);
if (NULL == pbKey)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
if (!CryptExportKey(hKey, NULL, PRIVATEKEYBLOB, 0, pbKey, pcbKey))
{
hr = myHLastError();
_JumpError(hr, error, "CryptExportKey");
}
*ppbKey = pbKey;
pbKey = NULL;
hr = S_OK;
error:
if (NULL != pbKey)
{
LocalFree(pbKey);
}
return(hr);
}
HRESULT
myVerifyPublicKey(
IN OPTIONAL CERT_CONTEXT const *pCert,
IN BOOL fV1Cert,
IN OPTIONAL CRYPT_KEY_PROV_INFO const *pKeyProvInfo,
IN OPTIONAL CERT_PUBLIC_KEY_INFO const *pPublicKeyInfo,
OPTIONAL OUT BOOL *pfMatchingKey)
{
HRESULT hr;
HCRYPTPROV hProv = NULL;
DWORD dwKeySpec;
DWORD cb;
CERT_PUBLIC_KEY_INFO *pPublicKeyInfoExported = NULL;
if (NULL != pfMatchingKey)
{
*pfMatchingKey = FALSE;
}
if (NULL == pCert ||
!CryptAcquireCertificatePrivateKey(
pCert,
0, // dwFlags
NULL, // pvReserved
&hProv,
&dwKeySpec,
NULL)) // pfCallerFreeProv
{
if (NULL != pKeyProvInfo)
{
// ok, try passed kpi
if (!myCertSrvCryptAcquireContext(
&hProv,
pKeyProvInfo->pwszContainerName,
pKeyProvInfo->pwszProvName,
pKeyProvInfo->dwProvType,
~CRYPT_MACHINE_KEYSET & pKeyProvInfo->dwFlags,
(CRYPT_MACHINE_KEYSET & pKeyProvInfo->dwFlags)? TRUE : FALSE))
{
hr = myHLastError();
_JumpErrorStr(
hr,
error,
"myCertSrvCryptAcquireContextEx",
pKeyProvInfo->pwszContainerName);
}
dwKeySpec = pKeyProvInfo->dwKeySpec;
}
else if (NULL != pCert)
{
hr = myHLastError();
_JumpError(hr, error, "CryptAcquireCertificatePrivateKey");
}
else
{
hr = E_POINTER;
_JumpError(hr, error, "No cert & no KeyProvInfo");
}
}
if (!myCryptExportPublicKeyInfo(
hProv,
dwKeySpec,
CERTLIB_USE_LOCALALLOC,
&pPublicKeyInfoExported,
&cb))
{
hr = myHLastError();
_JumpError(hr, error, "myCryptExportPublicKeyInfo");
}
if (NULL == pPublicKeyInfo)
{
if (NULL == pCert)
{
hr = E_POINTER;
_JumpError(hr, error, "No cert & no SubjectPublicKeyInfo");
}
pPublicKeyInfo = &pCert->pCertInfo->SubjectPublicKeyInfo;
}
if (!myCertComparePublicKeyInfo(
X509_ASN_ENCODING,
fV1Cert,
pPublicKeyInfoExported,
pPublicKeyInfo))
{
// by design, (my)CertComparePublicKeyInfo doesn't set last error!
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpError(hr, error, "myCertComparePublicKeyInfo");
}
if (NULL != pfMatchingKey)
{
*pfMatchingKey = TRUE;
}
hr = S_OK;
error:
if (NULL != pPublicKeyInfoExported)
{
LocalFree(pPublicKeyInfoExported);
}
if (NULL != hProv)
{
CryptReleaseContext(hProv, 0);
}
return(hr);
}
DWORD
GetLengthIndex(
IN BYTE bBERTag,
IN DWORD iStart,
IN BYTE const *pb,
IN DWORD cb)
{
DWORD Index = MAXDWORD;
pb += iStart;
if (iStart + 4 < cb && bBERTag == pb[0])
{
// make sure there's room to increment the length in place.
if (0x7f > pb[1])
{
Index = iStart + 1;
}
else if (0x81 == pb[1] && 0xff > pb[2])
{
Index = iStart + 2;
}
}
return(Index);
}
// by design, (my)CertComparePublicKeyInfo doesn't set last error!
BOOL
myCertComparePublicKeyInfo(
IN DWORD dwCertEncodingType,
IN BOOL fV1Cert,
IN CERT_PUBLIC_KEY_INFO const *pPublicKey1,
IN CERT_PUBLIC_KEY_INFO const *pPublicKey2)
{
BOOL fMatch = FALSE;
BYTE *pbKeyNew = NULL;
CERT_PUBLIC_KEY_INFO PublicKey;
if (CertComparePublicKeyInfo(
X509_ASN_ENCODING,
const_cast<CERT_PUBLIC_KEY_INFO *>(pPublicKey1),
const_cast<CERT_PUBLIC_KEY_INFO *>(pPublicKey2)))
{
fMatch = TRUE;
goto error;
}
// If this is a V1 X509 cert with the sign bit set in the first public key
// byte -- without a leading zero pad byte, and there's a wasted byte at
// the end of the public key, insert the zero byte and bump up the lengths.
PublicKey = *pPublicKey2;
if (fV1Cert && BER_SEQUENCE == PublicKey.PublicKey.pbData[0])
{
DWORD iLenSequence;
DWORD iLenModulus;
BYTE *pbKey = PublicKey.PublicKey.pbData;
DWORD cbKey = PublicKey.PublicKey.cbData;
iLenSequence = GetLengthIndex(BER_SEQUENCE, 0, pbKey, cbKey);
if (MAXDWORD == iLenSequence)
{
goto error;
}
iLenModulus = GetLengthIndex(
BER_INTEGER,
iLenSequence + 1,
pbKey,
cbKey);
if (MAXDWORD == iLenSequence)
{
goto error;
}
if (0x80 & pbKey[iLenModulus + 1] &&
(DWORD) (iLenSequence + 1 + pbKey[iLenSequence]) < cbKey)
{
pbKeyNew = (BYTE *) LocalAlloc(LMEM_FIXED, cbKey);
if (NULL == pbKeyNew)
{
_JumpError(E_OUTOFMEMORY, error, "LocalAlloc");
}
CopyMemory(pbKeyNew, pbKey, iLenModulus + 1);
pbKeyNew[iLenSequence]++;
pbKeyNew[iLenModulus]++;
pbKeyNew[iLenModulus + 1] = 0;
CopyMemory(
&pbKeyNew[iLenModulus + 2],
&pbKey[iLenModulus + 1],
cbKey - (iLenModulus + 2));
PublicKey.PublicKey.pbData = pbKeyNew;
if (CertComparePublicKeyInfo(
X509_ASN_ENCODING,
const_cast<CERT_PUBLIC_KEY_INFO *>(pPublicKey1),
&PublicKey))
{
fMatch = TRUE;
}
}
}
error:
if (NULL != pbKeyNew)
{
LocalFree(pbKeyNew);
}
return(fMatch);
}
BOOL
myCryptSignMessage(
IN CRYPT_SIGN_MESSAGE_PARA const *pcsmp,
IN BYTE const *pbToBeSigned,
IN DWORD cbToBeSigned,
IN CERTLIB_ALLOCATOR allocType,
OUT BYTE **ppbSignedBlob,
OUT DWORD *pcbSignedBlob)
{
BOOL b;
*ppbSignedBlob = NULL;
*pcbSignedBlob = 0;
while (TRUE)
{
b = CryptSignMessage(
const_cast<CRYPT_SIGN_MESSAGE_PARA *>(pcsmp),
TRUE, // fDetachedSignature
1, // cToBeSigned
&pbToBeSigned, // rgpbToBeSigned
&cbToBeSigned, // rgcbToBeSigned
*ppbSignedBlob,
pcbSignedBlob);
if (b && 0 == *pcbSignedBlob)
{
SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
b = FALSE;
}
if (!b)
{
if (NULL != *ppbSignedBlob)
{
myFree(*ppbSignedBlob, allocType);
*ppbSignedBlob = NULL;
}
break;
}
if (NULL != *ppbSignedBlob)
{
break;
}
*ppbSignedBlob = (BYTE *) myAlloc(*pcbSignedBlob, allocType);
if (NULL == *ppbSignedBlob)
{
b = FALSE;
break;
}
}
return(b);
}
BOOL
myEncodeCert(
IN DWORD dwEncodingType,
IN CERT_SIGNED_CONTENT_INFO const *pInfo,
IN CERTLIB_ALLOCATOR allocType,
OUT BYTE **ppbEncoded,
OUT DWORD *pcbEncoded)
{
return(myEncodeObject(
dwEncodingType,
X509_CERT,
pInfo,
0,
allocType,
ppbEncoded,
pcbEncoded));
}
BOOL
myEncodeName(
IN DWORD dwEncodingType,
IN CERT_NAME_INFO const *pInfo,
IN DWORD dwFlags,
IN CERTLIB_ALLOCATOR allocType,
OUT BYTE **ppbEncoded,
OUT DWORD *pcbEncoded)
{
return(myEncodeObject(
dwEncodingType,
X509_UNICODE_NAME,
pInfo,
dwFlags,
allocType,
ppbEncoded,
pcbEncoded));
}
BOOL
myEncodeKeyAttributes(
IN DWORD dwEncodingType,
IN CERT_KEY_ATTRIBUTES_INFO const *pInfo,
IN CERTLIB_ALLOCATOR allocType,
OUT BYTE **ppbEncoded,
OUT DWORD *pcbEncoded)
{
return(myEncodeObject(
dwEncodingType,
X509_KEY_ATTRIBUTES,
pInfo,
0,
allocType,
ppbEncoded,
pcbEncoded));
}
BOOL
myEncodeKeyUsage(
IN DWORD dwEncodingType,
IN CRYPT_BIT_BLOB const *pInfo,
IN CERTLIB_ALLOCATOR allocType,
OUT BYTE **ppbEncoded,
OUT DWORD *pcbEncoded)
{
return(myEncodeObject(
dwEncodingType,
X509_KEY_USAGE,
pInfo,
0,
allocType,
ppbEncoded,
pcbEncoded));
}
BOOL
myEncodeKeyAuthority2(
IN DWORD dwEncodingType,
IN CERT_AUTHORITY_KEY_ID2_INFO const *pInfo,
IN CERTLIB_ALLOCATOR allocType,
OUT BYTE **ppbEncoded,
OUT DWORD *pcbEncoded)
{
return(myEncodeObject(
dwEncodingType,
X509_AUTHORITY_KEY_ID2,
pInfo,
0,
allocType,
ppbEncoded,
pcbEncoded));
}
BOOL
myEncodeToBeSigned(
DWORD dwEncodingType,
CERT_INFO const *pInfo,
IN CERTLIB_ALLOCATOR allocType,
BYTE **ppbEncoded,
DWORD *pcbEncoded)
{
return(myEncodeObject(
dwEncodingType,
X509_CERT_TO_BE_SIGNED,
pInfo,
0,
allocType,
ppbEncoded,
pcbEncoded));
}
BOOL
myDecodeName(
IN DWORD dwEncodingType,
IN LPCSTR lpszStructType,
IN BYTE const *pbEncoded,
IN DWORD cbEncoded,
IN CERTLIB_ALLOCATOR allocType,
OUT CERT_NAME_INFO **ppNameInfo,
OUT DWORD *pcbNameInfo)
{
return(myDecodeObject(
dwEncodingType,
lpszStructType,
pbEncoded,
cbEncoded,
allocType,
(VOID **) ppNameInfo,
pcbNameInfo));
}
BOOL
myDecodeKeyAuthority(
IN DWORD dwEncodingType,
IN BYTE const *pbEncoded,
IN DWORD cbEncoded,
IN CERTLIB_ALLOCATOR allocType,
OUT CERT_AUTHORITY_KEY_ID_INFO const **ppInfo,
OUT DWORD *pcbInfo)
{
return(myDecodeObject(
dwEncodingType,
X509_AUTHORITY_KEY_ID,
pbEncoded,
cbEncoded,
allocType,
(VOID **) ppInfo,
pcbInfo));
}
BOOL
myDecodeKeyAuthority2(
IN DWORD dwEncodingType,
IN BYTE const *pbEncoded,
IN DWORD cbEncoded,
IN CERTLIB_ALLOCATOR allocType,
OUT CERT_AUTHORITY_KEY_ID2_INFO const **ppInfo,
OUT DWORD *pcbInfo)
{
return(myDecodeObject(
dwEncodingType,
X509_AUTHORITY_KEY_ID2,
pbEncoded,
cbEncoded,
allocType,
(VOID **) ppInfo,
pcbInfo));
}
BOOL
myDecodeExtensions(
IN DWORD dwEncodingType,
IN BYTE const *pbEncoded,
IN DWORD cbEncoded,
IN CERTLIB_ALLOCATOR allocType,
OUT CERT_EXTENSIONS **ppInfo,
OUT DWORD *pcbInfo)
{
return(myDecodeObject(
dwEncodingType,
X509_NAME,
pbEncoded,
cbEncoded,
allocType,
(VOID **) ppInfo,
pcbInfo));
}
BOOL
myDecodeKeyGenRequest(
IN BYTE const *pbRequest,
IN DWORD cbRequest,
IN CERTLIB_ALLOCATOR allocType,
OUT CERT_KEYGEN_REQUEST_INFO **ppKeyGenRequest,
OUT DWORD *pcbKeyGenRequest)
{
return(myDecodeObject(
X509_ASN_ENCODING,
X509_KEYGEN_REQUEST_TO_BE_SIGNED,
pbRequest,
cbRequest,
allocType,
(VOID **) ppKeyGenRequest,
pcbKeyGenRequest));
}
HRESULT
myDecodeCSPProviderAttribute(
IN BYTE const *pbCSPEncoded,
IN DWORD cbCSPEncoded,
OUT CRYPT_CSP_PROVIDER **ppccp)
{
HRESULT hr;
CRYPT_SEQUENCE_OF_ANY *pCSPProviderSeq = NULL;
CERT_NAME_VALUE *pCSPProviderName = NULL;
DWORD cb;
CRYPT_CSP_PROVIDER *pccp;
DWORD dwKeySpec;
CERT_NAME_VALUE *pName = NULL;
CRYPT_BIT_BLOB *pBlob = NULL;
BYTE *pb;
*ppccp = NULL;
if (!myDecodeObject(
X509_ASN_ENCODING,
X509_SEQUENCE_OF_ANY,
pbCSPEncoded,
cbCSPEncoded,
CERTLIB_USE_LOCALALLOC,
(VOID **) &pCSPProviderSeq,
&cb))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeObject");
}
if (3 > pCSPProviderSeq->cValue)
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
_JumpError(hr, error, "Sequence count < 3");
}
dwKeySpec = 0;
if (NULL != pCSPProviderSeq->rgValue[0].pbData &&
0 != pCSPProviderSeq->rgValue[0].cbData)
{
cb = sizeof(dwKeySpec);
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_INTEGER,
pCSPProviderSeq->rgValue[0].pbData,
pCSPProviderSeq->rgValue[0].cbData,
0,
&dwKeySpec,
&cb))
{
hr = myHLastError();
_JumpError(hr, error, "CryptDecodeObject");
}
}
if (NULL != pCSPProviderSeq->rgValue[1].pbData &&
0 != pCSPProviderSeq->rgValue[1].cbData)
{
if (!myDecodeObject(
X509_ASN_ENCODING,
X509_UNICODE_ANY_STRING,
pCSPProviderSeq->rgValue[1].pbData,
pCSPProviderSeq->rgValue[1].cbData,
CERTLIB_USE_LOCALALLOC,
(VOID **) &pName,
&cb))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeObject");
}
}
if (NULL != pCSPProviderSeq->rgValue[2].pbData &&
0 != pCSPProviderSeq->rgValue[2].cbData)
{
if (!myDecodeObject(
X509_ASN_ENCODING,
X509_BITS,
pCSPProviderSeq->rgValue[2].pbData,
pCSPProviderSeq->rgValue[2].cbData,
CERTLIB_USE_LOCALALLOC,
(VOID **) &pBlob,
&cb))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeObject");
}
}
cb = sizeof(*pccp);
if (NULL != pName && NULL != pName->Value.pbData)
{
cb += POINTERROUND((wcslen((WCHAR const *) pName->Value.pbData) + 1) *
sizeof(WCHAR));
}
if (NULL != pBlob && NULL != pBlob->pbData)
{
cb += POINTERROUND(pBlob->cbData);
}
pccp = (CRYPT_CSP_PROVIDER *) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, cb);
if (NULL == pccp)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
*ppccp = pccp;
pb = (BYTE *) (pccp + 1);
pccp->dwKeySpec = dwKeySpec;
if (NULL != pName->Value.pbData)
{
pccp->pwszProviderName = (WCHAR *) pb;
wcscpy(pccp->pwszProviderName, (WCHAR const *) pName->Value.pbData);
pb += POINTERROUND((wcslen((WCHAR const *) pName->Value.pbData) + 1) *
sizeof(WCHAR));
}
if (NULL != pBlob && NULL != pBlob->pbData)
{
pccp->Signature.pbData = pb;
pccp->Signature.cbData = pBlob->cbData;
pccp->Signature.cUnusedBits = pBlob->cUnusedBits;
CopyMemory(pccp->Signature.pbData, pBlob->pbData, pBlob->cbData);
}
hr = S_OK;
error:
if (NULL != pCSPProviderSeq)
{
LocalFree(pCSPProviderSeq);
}
if (NULL != pName)
{
LocalFree(pName);
}
if (NULL != pBlob)
{
LocalFree(pBlob);
}
return(hr);
}
BOOL
myCertGetCertificateContextProperty(
IN CERT_CONTEXT const *pCertContext,
IN DWORD dwPropId,
IN CERTLIB_ALLOCATOR allocType,
OUT VOID **ppvData,
OUT DWORD *pcbData)
{
BOOL b;
*ppvData = NULL;
*pcbData = 0;
while (TRUE)
{
b = CertGetCertificateContextProperty(
pCertContext,
dwPropId,
*ppvData,
pcbData);
if (b && 0 == *pcbData)
{
SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
b = FALSE;
}
if (!b)
{
if (NULL != *ppvData)
{
myFree(*ppvData, allocType);
*ppvData = NULL;
}
break;
}
if (NULL != *ppvData)
{
break;
}
*ppvData = (VOID *) myAlloc(*pcbData, allocType);
if (NULL == *ppvData)
{
b = FALSE;
break;
}
}
return(b);
}
HRESULT
myCertGetKeyProviderInfo(
IN CERT_CONTEXT const *pCert,
OUT CRYPT_KEY_PROV_INFO **ppkpi)
{
HRESULT hr;
DWORD cb;
*ppkpi = NULL;
if (!CertGetCertificateContextProperty(
pCert,
CERT_KEY_PROV_INFO_PROP_ID,
NULL,
&cb))
{
hr = myHLastError();
_JumpError2(
hr,
error,
"CertGetCertificateContextProperty",
CRYPT_E_NOT_FOUND);
}
*ppkpi = (CRYPT_KEY_PROV_INFO *) LocalAlloc(LMEM_FIXED, cb);
if (NULL == *ppkpi)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
if (!CertGetCertificateContextProperty(
pCert,
CERT_KEY_PROV_INFO_PROP_ID,
*ppkpi,
&cb))
{
hr = myHLastError();
_JumpError(hr, error, "CertGetCertificateContextProperty");
}
hr = S_OK;
error:
return(hr);
}
HRESULT
myCryptExportKey(
IN HCRYPTKEY hKey,
IN HCRYPTKEY hKeyExp,
IN DWORD dwBlobType,
IN DWORD dwFlags,
OUT BYTE **ppbKey,
OUT DWORD *pcbKey)
{
HRESULT hr;
if (!CryptExportKey(hKey, hKeyExp, dwBlobType, dwFlags, NULL, pcbKey))
{
hr = myHLastError();
_JumpError(hr, error, "CryptExportKey");
}
*ppbKey = (BYTE *) LocalAlloc(LMEM_FIXED, *pcbKey);
if (NULL == *ppbKey)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
if (!CryptExportKey(hKey, hKeyExp, dwBlobType, dwFlags, *ppbKey, pcbKey))
{
hr = myHLastError();
LocalFree(*ppbKey);
*ppbKey = NULL;
_JumpError(hr, error, "CryptExportKey");
}
hr = S_OK;
error:
return(hr);
}
HRESULT
myCryptEncrypt(
IN HCRYPTKEY hKey,
IN BYTE const *pbIn,
IN DWORD cbIn,
OUT BYTE **ppbEncrypted,
OUT DWORD *pcbEncrypted)
{
HRESULT hr;
BYTE *pbEncrypted = NULL;
DWORD cbEncrypted;
DWORD cbAlloc;
BOOL fRetried = FALSE;
cbAlloc = cbIn + 64; // may be enough to prevent overflow
while (TRUE)
{
cbEncrypted = cbIn;
pbEncrypted = (BYTE *) LocalAlloc(LMEM_FIXED, cbAlloc);
if (NULL == pbEncrypted)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
CopyMemory(pbEncrypted, pbIn, cbIn);
if (!CryptEncrypt(
hKey,
NULL, // hHash
TRUE, // Final
0, // dwFlags
pbEncrypted, // pbData
&cbEncrypted, // pdwDataLen
cbAlloc)) // dwBufLen
{
hr = myHLastError();
if (!fRetried && HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr)
{
LocalFree(pbEncrypted);
pbEncrypted = NULL;
DBGPRINT((
DBG_SS_CERTLIB,
"CryptEncrypt(MORE DATA): %u -> %u\n",
cbAlloc,
cbEncrypted));
cbAlloc = cbEncrypted;
fRetried = TRUE;
continue;
}
_JumpError(hr, error, "CryptEncrypt");
}
break;
}
*ppbEncrypted = pbEncrypted;
*pcbEncrypted = cbEncrypted;
pbEncrypted = NULL;
hr = S_OK;
error:
if (NULL != pbEncrypted)
{
LocalFree(pbEncrypted);
}
return(hr);
}
HRESULT
myCryptDecrypt(
IN HCRYPTKEY hKey,
IN BYTE const *pbIn,
IN DWORD cbIn,
OUT BYTE **ppbDecrypted,
OUT DWORD *pcbDecrypted)
{
HRESULT hr;
BYTE *pbDecrypted = NULL;
DWORD cbDecrypted;
// init
*ppbDecrypted = NULL;
*pcbDecrypted = 0;
cbDecrypted = cbIn;
pbDecrypted = (BYTE *) LocalAlloc(LMEM_FIXED, cbIn);
if (NULL == pbDecrypted)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
CopyMemory(pbDecrypted, pbIn, cbIn);
if (!CryptDecrypt(
hKey,
NULL, // hHash
TRUE, // Final
0, // dwFlags
pbDecrypted, // pbData
&cbDecrypted)) // pdwDataLen
{
hr = myHLastError();
_JumpError(hr, error, "CryptDecrypt");
}
*ppbDecrypted = pbDecrypted;
*pcbDecrypted = cbDecrypted;
pbDecrypted = NULL;
hr = S_OK;
error:
if (NULL != pbDecrypted)
{
LocalFree(pbDecrypted);
}
return(hr);
}
HRESULT
myCryptEncryptMessage(
IN ALG_ID algId,
IN DWORD cCertRecipient,
IN CERT_CONTEXT const **rgCertRecipient,
IN BYTE const *pbIn,
IN DWORD cbIn,
IN OPTIONAL HCRYPTPROV hCryptProv,
OUT BYTE **ppbEncrypted,
OUT DWORD *pcbEncrypted)
{
HRESULT hr;
CRYPT_ENCRYPT_MESSAGE_PARA cemp;
CRYPT_OID_INFO const *pOidInfo;
CERT_CONTEXT const *pcc = NULL;
// Convert to an Object Id
pOidInfo = CryptFindOIDInfo(
CRYPT_OID_INFO_ALGID_KEY,
&algId,
CRYPT_ENCRYPT_ALG_OID_GROUP_ID);
if (NULL == pOidInfo)
{
// function is not doc'd to set GetLastError()
hr = CRYPT_E_NOT_FOUND;
DBGPRINT((DBG_SS_ERROR, "algId = %x\n", algId));
_JumpError(hr, error, "CryptFindOIDInfo");
}
// Encrypt the data with the public key
ZeroMemory(&cemp, sizeof(cemp));
cemp.cbSize = sizeof(cemp);
cemp.dwMsgEncodingType = PKCS_7_ASN_ENCODING | CRYPT_ASN_ENCODING;
cemp.ContentEncryptionAlgorithm.pszObjId = const_cast<char *>(pOidInfo->pszOID);
cemp.hCryptProv = hCryptProv;
*pcbEncrypted = 0;
while (TRUE)
{
if (!CryptEncryptMessage(
&cemp,
cCertRecipient, // cRecipientCert
rgCertRecipient, // rgpRecipientCert IN
pbIn, // pbToBeEncrypted
cbIn, // cbToBeEncrypted
*ppbEncrypted, // pbEncryptedBlob
pcbEncrypted)) // pcbEncryptedBlob
{
hr = myHLastError();
if (NULL != *ppbEncrypted)
{
LocalFree(*ppbEncrypted);
*ppbEncrypted = NULL;
}
_JumpError(hr, error, "CryptEncryptMessage");
}
if (NULL != *ppbEncrypted)
{
break;
}
*ppbEncrypted = (BYTE *) LocalAlloc(LMEM_FIXED, *pcbEncrypted);
if (NULL == *ppbEncrypted)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
}
hr = S_OK;
error:
CSASSERT(S_OK == hr || FAILED(hr));
return(hr);
}
HRESULT
myCryptDecryptMessage(
IN HCERTSTORE hStore,
IN BYTE const *pbEncrypted,
IN DWORD cbEncrypted,
IN CERTLIB_ALLOCATOR allocType,
OUT BYTE **ppbDecrypted,
OUT DWORD *pcbDecrypted)
{
HRESULT hr;
CRYPT_DECRYPT_MESSAGE_PARA cdmp;
ZeroMemory(&cdmp, sizeof(cdmp));
cdmp.cbSize = sizeof(cdmp);
cdmp.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
cdmp.cCertStore = 1;
cdmp.rghCertStore = &hStore;
*ppbDecrypted = NULL;
*pcbDecrypted = 0;
while (TRUE)
{
if (!CryptDecryptMessage(
&cdmp,
pbEncrypted,
cbEncrypted,
*ppbDecrypted,
pcbDecrypted,
NULL)) // ppXchgCert
{
hr = myHLastError();
if (NULL != *ppbDecrypted)
{
myFree(*ppbDecrypted, allocType);
*ppbDecrypted = NULL;
}
_JumpError(hr, error, "CryptDecryptMessage");
}
if (NULL != *ppbDecrypted)
{
break;
}
*ppbDecrypted = (BYTE *) myAlloc(*pcbDecrypted, allocType);
if (NULL == *ppbDecrypted)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "myAlloc");
}
}
hr = S_OK;
error:
return(hr);
}
HRESULT
myGetInnerPKCS10(
IN HCRYPTMSG hMsg,
IN char const *pszInnerContentObjId,
OUT CERT_REQUEST_INFO **ppRequest)
{
HRESULT hr;
BYTE *pbContent = NULL;
DWORD cbContent;
CMC_DATA_INFO *pcmcData = NULL;
DWORD cbcmcData;
CMC_TAGGED_CERT_REQUEST const *pTaggedCertRequest;
DWORD cbRequest;
*ppRequest = NULL;
#define szOID_CT_PKI_DATA_OLDRFC "1.3.6.1.5.5.7.5.2" // BUGBUG: temporary!!!
if (0 != strcmp(pszInnerContentObjId, szOID_CT_PKI_DATA) &&
0 != strcmp(pszInnerContentObjId, szOID_CT_PKI_DATA_OLDRFC))
{
hr = CRYPT_E_INVALID_MSG_TYPE;
_JumpError(hr, error, "Not a CMC request");
}
// Get the request content, then search for the PKCS10's public key.
hr = myCryptMsgGetParam(
hMsg,
CMSG_CONTENT_PARAM,
0,
CERTLIB_USE_LOCALALLOC,
(VOID **) &pbContent,
&cbContent);
_JumpIfError(hr, error, "myCryptMsgGetParam");
if (!myDecodeObject(
X509_ASN_ENCODING,
CMC_DATA,
pbContent,
cbContent,
CERTLIB_USE_LOCALALLOC,
(VOID **) &pcmcData,
&cbcmcData))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeObject");
}
if (1 != pcmcData->cTaggedRequest ||
CMC_TAGGED_CERT_REQUEST_CHOICE !=
pcmcData->rgTaggedRequest[0].dwTaggedRequestChoice)
{
hr = CRYPT_E_INVALID_MSG_TYPE;
_JumpError(hr, error, "Must be 1 PKCS10");
}
pTaggedCertRequest = pcmcData->rgTaggedRequest[0].pTaggedCertRequest;
if (!myDecodeObject(
X509_ASN_ENCODING,
X509_CERT_REQUEST_TO_BE_SIGNED,
pTaggedCertRequest->SignedCertRequest.pbData,
pTaggedCertRequest->SignedCertRequest.cbData,
CERTLIB_USE_LOCALALLOC,
(VOID **) ppRequest,
&cbRequest))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeObject");
}
hr = S_OK;
error:
if (NULL != pbContent)
{
LocalFree(pbContent);
}
if (NULL != pcmcData)
{
LocalFree(pcmcData);
}
return(hr);
}
HRESULT
myPKCSEncodeString(
IN WCHAR const *pwsz,
OUT BYTE **ppbOut,
OUT DWORD *pcbOut)
{
HRESULT hr = S_OK;
CERT_NAME_VALUE cnv;
// encode the string as an IA5 string
cnv.dwValueType = CERT_RDN_IA5_STRING;
cnv.Value.pbData = (BYTE *) pwsz;
cnv.Value.cbData = 0; // Use L'\0' termination for the length
if (!myEncodeObject(
X509_ASN_ENCODING,
X509_UNICODE_NAME_VALUE,
&cnv,
0,
CERTLIB_USE_LOCALALLOC,
ppbOut,
pcbOut))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
error:
return(hr);
}
HRESULT
myPKCSDecodeString(
IN BYTE const *pbIn,
IN DWORD cbIn,
OUT WCHAR **ppwszOut)
{
HRESULT hr = S_OK;
CERT_NAME_VALUE *pcnv = NULL;
DWORD cbOut;
*ppwszOut = NULL;
// decode the string from an IA5 string
if (!myDecodeObject(
X509_ASN_ENCODING,
X509_UNICODE_NAME_VALUE,
pbIn,
cbIn,
CERTLIB_USE_LOCALALLOC,
(VOID **) &pcnv,
&cbOut))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeObject");
}
if (CERT_RDN_IA5_STRING != pcnv->dwValueType)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "Not an IA5 string");
}
cbOut = (wcslen((WCHAR const *) pcnv->Value.pbData) + 1) * sizeof(WCHAR);
*ppwszOut = (WCHAR *) LocalAlloc(LMEM_FIXED, cbOut);
if (NULL == *ppwszOut)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
CopyMemory(*ppwszOut, pcnv->Value.pbData, cbOut);
error:
if (NULL != pcnv)
{
LocalFree(pcnv);
}
return(hr);
}
HRESULT
myPKCSEncodeLong(
IN LONG Value,
OUT BYTE **ppbOut,
OUT DWORD *pcbOut)
{
HRESULT hr = S_OK;
// encode the long value
if (!myEncodeObject(
X509_ASN_ENCODING,
X509_INTEGER,
&Value,
0,
CERTLIB_USE_LOCALALLOC,
ppbOut,
pcbOut))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
error:
return(hr);
}
HRESULT
myPKCSDecodeLong(
IN BYTE const *pbIn,
IN DWORD cbIn,
OUT LONG **ppValue)
{
HRESULT hr = S_OK;
DWORD cbOut;
// encode the long value
if (!myDecodeObject(
X509_ASN_ENCODING,
X509_INTEGER,
pbIn,
cbIn,
CERTLIB_USE_LOCALALLOC,
(VOID **) ppValue,
&cbOut))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeObject");
}
CSASSERT(sizeof(**ppValue) == cbOut);
error:
return(hr);
}
HRESULT
myPKCSEncodeDate(
IN FILETIME const *pft,
OUT BYTE **ppbOut,
OUT DWORD *pcbOut)
{
HRESULT hr = S_OK;
// encode the time value
if (!myEncodeObject(
X509_ASN_ENCODING,
X509_CHOICE_OF_TIME,
pft,
0,
CERTLIB_USE_LOCALALLOC,
ppbOut,
pcbOut))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
error:
return(hr);
}
HRESULT
myPKCSDecodeDate(
IN BYTE const *pbIn,
IN DWORD cbIn,
OUT FILETIME **ppftOut)
{
HRESULT hr = S_OK;
DWORD cbOut;
// encode the time value
if (!myDecodeObject(
X509_ASN_ENCODING,
X509_CHOICE_OF_TIME,
pbIn,
cbIn,
CERTLIB_USE_LOCALALLOC,
(VOID **) ppftOut,
&cbOut))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeObject");
}
CSASSERT(sizeof(**ppftOut) == cbOut);
error:
return(hr);
}
HRESULT
myEncodeExtension(
IN DWORD Flags,
IN BYTE const *pbIn,
IN DWORD cbIn,
OUT BYTE **ppbOut,
OUT DWORD *pcbOut)
{
HRESULT hr = E_INVALIDARG;
// everyone assumes pbIn != NULL
if (NULL == pbIn || 0 == cbIn)
{
_JumpError(hr, error, "NULL param");
}
switch (PROPTYPE_MASK & Flags)
{
case PROPTYPE_STRING:
if (0 == (PROPMARSHAL_LOCALSTRING & Flags) &&
sizeof(WCHAR) <= cbIn)
{
cbIn -= sizeof(WCHAR);
}
if (wcslen((WCHAR const *) pbIn) * sizeof(WCHAR) != cbIn)
{
_JumpError(hr, error, "bad string len");
}
hr = myPKCSEncodeString((WCHAR const *) pbIn, ppbOut, pcbOut);
_JumpIfError(hr, error, "myPKCSEncodeString");
break;
case PROPTYPE_LONG:
CSASSERT(sizeof(DWORD) == cbIn);
hr = myPKCSEncodeLong(*(DWORD const *) pbIn, ppbOut, pcbOut);
_JumpIfError(hr, error, "myPKCSEncodeLong");
break;
case PROPTYPE_DATE:
CSASSERT(sizeof(FILETIME) == cbIn);
hr = myPKCSEncodeDate((FILETIME const *) pbIn, ppbOut, pcbOut);
_JumpIfError(hr, error, "myPKCSEncodeDate");
break;
default:
_JumpError(hr, error, "variant type/value");
}
hr = S_OK;
error:
return(hr);
}
HRESULT
myDecodeExtension(
IN DWORD Flags,
IN BYTE const *pbIn,
IN DWORD cbIn,
OUT BYTE **ppbOut,
OUT DWORD *pcbOut)
{
HRESULT hr;
switch (PROPTYPE_MASK & Flags)
{
case PROPTYPE_STRING:
hr = myPKCSDecodeString(pbIn, cbIn, (WCHAR **) ppbOut);
_JumpIfError(hr, error, "myPKCSDecodeString");
*pcbOut = wcslen((WCHAR const *) *ppbOut) * sizeof(WCHAR);
break;
case PROPTYPE_LONG:
hr = myPKCSDecodeLong(pbIn, cbIn, (LONG **) ppbOut);
_JumpIfError(hr, error, "myPKCSDecodeLong");
*pcbOut = sizeof(LONG);
break;
case PROPTYPE_DATE:
hr = myPKCSDecodeDate(pbIn, cbIn, (FILETIME **) ppbOut);
_JumpIfError(hr, error, "myPKCSDecodeDate");
*pcbOut = sizeof(FILETIME);
break;
default:
hr = E_INVALIDARG;
_JumpError(hr, error, "Flags: Invalid type");
}
error:
return(hr);
}
// szOID_ENROLLMENT_NAME_VALUE_PAIR
BOOL
myDecodeNameValuePair(
IN DWORD dwEncodingType,
IN BYTE const *pbEncoded,
IN DWORD cbEncoded,
IN CERTLIB_ALLOCATOR allocType,
OUT CRYPT_ENROLLMENT_NAME_VALUE_PAIR **ppInfo,
OUT DWORD *pcbInfo)
{
return(myDecodeObject(
X509_ASN_ENCODING,
szOID_ENROLLMENT_NAME_VALUE_PAIR,
pbEncoded,
cbEncoded,
allocType,
(VOID **) ppInfo,
pcbInfo));
}
//+-------------------------------------------------------------------------
// myVerifyObjIdA - verify the passed pszObjId is valid as per X.208
//
// Encode and Decode the Object Id and make sure it survives the round trip.
// The first number must be 0, 1 or 2.
// Enforce all characters are digits and dots.
// Enforce that no dot starts or ends the Object Id, and disallow double dots.
// Enforce there is at least one dot separator.
// If the first number is 0 or 1, the second number must be between 0 & 39.
// If the first number is 2, the second number can be any value.
//--------------------------------------------------------------------------
HRESULT
myVerifyObjIdA(
IN CHAR const *pszObjId)
{
HRESULT hr;
BYTE *pbEncoded = NULL;
DWORD cbEncoded;
CRYPT_ATTRIBUTE ainfo;
CRYPT_ATTRIBUTE *painfo = NULL;
DWORD cbainfo;
char const *psz;
int i;
BOOL fNoisy = FALSE;
hr = E_INVALIDARG;
for (psz = pszObjId; '\0' != *psz; psz++)
{
// must be a digit or a dot separator
if (!isdigit(*psz))
{
if ('.' != *psz)
{
_JumpError2(hr, error, "bad ObjId: bad char", hr);
}
// can't have dot at start, double dots or dot at end
if (psz == pszObjId || '.' == psz[1] || '\0' == psz[1])
{
_JumpError2(hr, error, "bad ObjId: dot location", hr);
}
}
}
psz = strchr(pszObjId, '.');
if (NULL == psz)
{
_JumpError2(hr, error, "bad ObjId: must have at least one dot", hr);
}
i = atoi(pszObjId);
switch (i)
{
case 0:
case 1:
i = atoi(++psz);
if (0 > i || 39 < i)
{
_JumpError(hr, error, "bad ObjId: 0. or 1. must be followed by 0..39");
}
break;
case 2:
break;
default:
fNoisy = TRUE;
_JumpError(hr, error, "bad ObjId: must start with 0, 1 or 2");
}
fNoisy = TRUE;
ainfo.pszObjId = const_cast<char *>(pszObjId);
ainfo.cValue = 0;
ainfo.rgValue = NULL;
if (!myEncodeObject(
X509_ASN_ENCODING,
PKCS_ATTRIBUTE,
&ainfo,
0,
CERTLIB_USE_LOCALALLOC,
&pbEncoded,
&cbEncoded))
{
hr = myHLastError();
_JumpError(hr, error, "myEncodeObject");
}
if (!myDecodeObject(
X509_ASN_ENCODING,
PKCS_ATTRIBUTE,
pbEncoded,
cbEncoded,
CERTLIB_USE_LOCALALLOC,
(VOID **) &painfo,
&cbainfo))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeObject");
}
if (0 != strcmp(pszObjId, painfo->pszObjId))
{
hr = E_INVALIDARG;
_JumpError(hr, error, "bad ObjId: decode mismatch");
}
hr = S_OK;
error:
if (S_OK != hr)
{
DBGPRINT((
fNoisy? DBG_SS_CERTLIB : DBG_SS_CERTLIBI,
"myVerifyObjIdA(%hs): %x\n",
pszObjId,
hr));
}
if (NULL != pbEncoded)
{
LocalFree(pbEncoded);
}
if (NULL != painfo)
{
LocalFree(painfo);
}
return(hr);
}
HRESULT
myVerifyObjId(
IN WCHAR const *pwszObjId)
{
HRESULT hr;
CHAR *pszObjId = NULL;
if (!ConvertWszToSz(&pszObjId, pwszObjId, -1))
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "ConvertWszToSz");
}
hr = myVerifyObjIdA(pszObjId);
_JumpIfErrorStr2(hr, error, "myVerifyObjIdA", pwszObjId, E_INVALIDARG);
error:
if (NULL != pszObjId)
{
LocalFree(pszObjId);
}
return(hr);
}
// The returned pszObjId is a constant that must not be freed. CryptFindOIDInfo
// has a static internal database that is valid until crypt32.dll is unloaded.
#define GON_GROUP 0x00000001
#define GON_GENERIC 0x00000002
typedef struct _OIDNAME
{
char const *pszObjId;
WCHAR const *pwszDisplayName;
} OIDNAME;
#if DBG
#define wszCERTLIB L"(certlib)"
#else
#define wszCERTLIB L""
#endif
OIDNAME s_aOIDName[] = {
{ szOID_CT_PKI_DATA, L"CMC Data" wszCERTLIB, },
{ szOID_CT_PKI_RESPONSE, L"CMC Response" wszCERTLIB, },
{ szOID_CMC, L"Unsigned CMC Request" wszCERTLIB, },
{ szOID_CMC_TRANSACTION_ID, L"Transaction Id" wszCERTLIB, },
{ szOID_CMC_SENDER_NONCE, L"Sender Nonce" wszCERTLIB, },
{ szOID_CMC_RECIPIENT_NONCE, L"Recipient Nonce" wszCERTLIB, },
{ szOID_CMC_REG_INFO, L"Reg Info" wszCERTLIB, },
{ szOID_CMC_GET_CERT, L"Get Certificate" wszCERTLIB, },
{ szOID_CMC_GET_CRL, L"Get CRL" wszCERTLIB, },
{ szOID_CMC_REVOKE_REQUEST, L"Revoke Request" wszCERTLIB, },
{ szOID_CMC_QUERY_PENDING, L"Query Pending" wszCERTLIB, },
{ szOID_CMC_ID_CONFIRM_CERT_ACCEPTANCE, L"Confirm Certificate Acceptance" wszCERTLIB, },
{ szOID_CMC_STATUS_INFO, L"Unsigned CMC Response" wszCERTLIB, },
{ szOID_CMC_ADD_EXTENSIONS, L"CMC Extensions" wszCERTLIB, },
{ szOID_CMC_ADD_ATTRIBUTES, L"CMC Attributes" wszCERTLIB, },
{ szOID_VERISIGN_ONSITE_JURISDICTION_HASH, L"Jurisdiction Hash" wszCERTLIB, },
{ szOID_PKCS_7_DATA, L"PKCS 7 Data" wszCERTLIB, },
{ szOID_ARCHIVED_KEY_ATTR, L"Archived Key" wszCERTLIB, },
{ szOID_CTL, L"Certifcate Trust List" wszCERTLIB, },
{ szOID_ARCHIVED_KEY_CERT_HASH, L"Archived Key Certificate Hash" wszCERTLIB, },
{ szOID_ROOT_LIST_SIGNER, L"Root List Signer" wszCERTLIB, },
{ szOID_PRIVATEKEY_USAGE_PERIOD, L"Private Key Usage Period" wszCERTLIB, },
{ szOID_REQUEST_CLIENT_INFO, L"Client Information" wszCERTLIB, },
};
WCHAR const *
myGetOIDNameA(
IN char const *pszObjId)
{
CRYPT_OID_INFO const *pInfo = NULL;
WCHAR const *pwszName = L"";
DWORD Flags = GON_GROUP | GON_GENERIC;
if ('+' == *pszObjId)
{
Flags = GON_GROUP; // Group lookup only
pszObjId++;
}
else
if ('-' == *pszObjId)
{
Flags = GON_GENERIC; // Generic lookup only
pszObjId++;
}
// First try looking up the ObjectId as an Extension or Attribute, because
// we get a better Display Name, especially for Subject RDNs: CN, L, etc.
// If that fails, look it up withoput restricting the group.
if (GON_GROUP & Flags)
{
pInfo = CryptFindOIDInfo(
CRYPT_OID_INFO_OID_KEY,
(VOID *) pszObjId,
CRYPT_EXT_OR_ATTR_OID_GROUP_ID);
}
if ((GON_GENERIC & Flags) &&
(NULL == pInfo ||
NULL == pInfo->pwszName ||
L'\0' == pInfo->pwszName[0]))
{
pInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, (VOID *) pszObjId, 0);
}
if (NULL != pInfo && NULL != pInfo->pwszName && L'\0' != pInfo->pwszName[0])
{
pwszName = pInfo->pwszName;
}
else
{
OIDNAME const *pOIDName;
for (pOIDName = s_aOIDName;
pOIDName < &s_aOIDName[ARRAYSIZE(s_aOIDName)];
pOIDName++)
{
if (0 == strcmp(pOIDName->pszObjId, pszObjId))
{
pwszName = pOIDName->pwszDisplayName;
break;
}
}
}
return(pwszName);
}
WCHAR const *
myGetOIDName(
IN WCHAR const *pwszObjId)
{
char *pszObjId = NULL;
WCHAR const *pwszName = L"";
if (!ConvertWszToSz(&pszObjId, pwszObjId, -1))
{
_JumpError(E_OUTOFMEMORY, error, "ConvertWszToSz");
}
pwszName = myGetOIDNameA(pszObjId);
error:
if (NULL != pszObjId)
{
LocalFree(pszObjId);
}
return(pwszName);
}
typedef struct _DUMPFLAGS
{
DWORD Mask;
DWORD Value;
WCHAR const *pwszDescription;
} DUMPFLAGS;
#define _DFBIT(def) { (def), (def), L#def }
#define _DFBIT2(mask, def) { (mask), (def), L#def }
DUMPFLAGS g_adfErrorStatus[] =
{
_DFBIT(CERT_TRUST_IS_NOT_TIME_VALID),
_DFBIT(CERT_TRUST_IS_NOT_TIME_NESTED),
_DFBIT(CERT_TRUST_IS_REVOKED),
_DFBIT(CERT_TRUST_IS_NOT_SIGNATURE_VALID),
_DFBIT(CERT_TRUST_IS_NOT_VALID_FOR_USAGE),
_DFBIT(CERT_TRUST_IS_UNTRUSTED_ROOT),
_DFBIT(CERT_TRUST_REVOCATION_STATUS_UNKNOWN),
_DFBIT(CERT_TRUST_IS_CYCLIC),
_DFBIT(CERT_TRUST_INVALID_EXTENSION),
_DFBIT(CERT_TRUST_INVALID_POLICY_CONSTRAINTS),
_DFBIT(CERT_TRUST_INVALID_BASIC_CONSTRAINTS),
_DFBIT(CERT_TRUST_INVALID_NAME_CONSTRAINTS),
_DFBIT(CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT),
_DFBIT(CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT),
_DFBIT(CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT),
_DFBIT(CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT),
_DFBIT(CERT_TRUST_IS_OFFLINE_REVOCATION),
_DFBIT(CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY),
_DFBIT(CERT_TRUST_IS_PARTIAL_CHAIN),
_DFBIT(CERT_TRUST_CTL_IS_NOT_TIME_VALID),
_DFBIT(CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID),
_DFBIT(CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE),
{ 0, 0, NULL }
};
DUMPFLAGS g_adfInfoStatus[] =
{
_DFBIT(CERT_TRUST_HAS_EXACT_MATCH_ISSUER),
_DFBIT(CERT_TRUST_HAS_KEY_MATCH_ISSUER),
_DFBIT(CERT_TRUST_HAS_NAME_MATCH_ISSUER),
_DFBIT(CERT_TRUST_IS_SELF_SIGNED),
_DFBIT(CERT_TRUST_HAS_PREFERRED_ISSUER),
_DFBIT(CERT_TRUST_HAS_ISSUANCE_CHAIN_POLICY),
_DFBIT(CERT_TRUST_HAS_VALID_NAME_CONSTRAINTS),
_DFBIT(CERT_TRUST_IS_COMPLEX_CHAIN),
{ 0, 0, NULL }
};
VOID
DumpFlags(
IN DWORD Flags,
IN WCHAR const *pwsz,
IN DUMPFLAGS const *pdf)
{
for ( ; NULL != pdf->pwszDescription; pdf++)
{
if ((Flags & pdf->Mask) == pdf->Value)
{
CONSOLEPRINT2((
MAXDWORD,
"%ws = %ws\n",
pwsz,
pdf->pwszDescription));
}
}
}
VOID
DumpUsage(
IN WCHAR const *pwsz,
OPTIONAL IN CERT_ENHKEY_USAGE const *pUsage)
{
DWORD i;
if (NULL != pUsage)
{
for (i = 0; i < pUsage->cUsageIdentifier; i++)
{
CONSOLEPRINT4((
MAXDWORD,
"%ws[%u] = %hs %ws\n",
pwsz,
i,
pUsage->rgpszUsageIdentifier[i],
myGetOIDNameA(pUsage->rgpszUsageIdentifier[i])));
}
}
}
HRESULT
WriteBlob(
IN FILE *pf,
IN BYTE const *pb,
IN DWORD cb,
IN DWORD Flags)
{
HRESULT hr;
char *pszBase64 = NULL;
hr = myCryptBinaryToStringA(
pb,
cb,
Flags | CRYPT_STRING_NOCR,
&pszBase64);
_JumpIfError(hr, error, "myCryptBinaryToStringA");
fputs(pszBase64, pf);
fflush(pf);
if (ferror(pf))
{
hr = HRESULT_FROM_WIN32(ERROR_DISK_FULL);
_JumpError(hr, error, "fputs");
}
hr = S_OK;
error:
if (NULL != pszBase64)
{
LocalFree(pszBase64);
}
return(hr);
}
VOID
WriteChain(
IN CERT_SIMPLE_CHAIN const *pChain,
IN DWORD SaveIndex,
IN DWORD ChainIndex)
{
HRESULT hr;
char szPath[MAX_PATH];
DWORD i;
FILE *pf = NULL;
if (0 == GetEnvironmentVariableA("temp", szPath, ARRAYSIZE(szPath)))
{
strcpy(szPath, "\\");
}
i = strlen(szPath);
if (0 == i || '\\' != szPath[i - 1])
{
szPath[i++] = '\\';
}
sprintf(&szPath[i], "Chain%d_%d.txt", SaveIndex, ChainIndex);
pf = fopen(szPath, "w");
if (NULL == pf)
{
hr = errno;
_JumpError(hr, error, "fopen");
}
for (i = 0; i < pChain->cElement; i++)
{
CERT_CHAIN_ELEMENT const *pElement = pChain->rgpElement[i];
CERT_REVOCATION_INFO *pRevocationInfo;
if (0 < i)
{
fputs("\n", pf);
}
fprintf(pf, "Certificate %d:\n", i);
hr = WriteBlob(
pf,
pElement->pCertContext->pbCertEncoded,
pElement->pCertContext->cbCertEncoded,
CRYPT_STRING_BASE64HEADER);
_JumpIfError(hr, error, "WriteBlob");
pRevocationInfo = pElement->pRevocationInfo;
if (NULL != pRevocationInfo &&
CCSIZEOF_STRUCT(CERT_REVOCATION_INFO, pCrlInfo) <=
pRevocationInfo->cbSize &&
NULL != pRevocationInfo->pCrlInfo)
{
CERT_REVOCATION_CRL_INFO *pCrlInfo;
pCrlInfo = pRevocationInfo->pCrlInfo;
if (NULL != pCrlInfo)
{
if (NULL != pCrlInfo->pBaseCrlContext)
{
fprintf(pf, "\nCRL %d:\n", i);
hr = WriteBlob(
pf,
pCrlInfo->pBaseCrlContext->pbCrlEncoded,
pCrlInfo->pBaseCrlContext->cbCrlEncoded,
CRYPT_STRING_BASE64X509CRLHEADER);
_JumpIfError(hr, error, "WriteBlob");
}
if (NULL != pCrlInfo->pDeltaCrlContext)
{
fprintf(pf, "\nDelta CRL %d:\n", i);
hr = WriteBlob(
pf,
pCrlInfo->pDeltaCrlContext->pbCrlEncoded,
pCrlInfo->pDeltaCrlContext->cbCrlEncoded,
CRYPT_STRING_BASE64X509CRLHEADER);
_JumpIfError(hr, error, "WriteBlob");
}
}
}
}
error:
if (NULL != pf)
{
fclose(pf);
}
}
VOID
DumpChain(
IN HRESULT hrVerify,
IN DWORD dwFlags,
IN CERT_CONTEXT const *pCert,
OPTIONAL IN WCHAR const *pwszMissingIssuer,
IN CERT_CHAIN_CONTEXT const *pChainContext)
{
HRESULT hr;
DWORD i;
DWORD j;
static BOOL s_fEnvChecked = FALSE;
static BOOL s_fDumpEnabled = FALSE;
static DWORD s_SaveCount = 0;
static DWORD s_SaveIndex;
BOOL fDump;
BOOL fSave;
if (!s_fEnvChecked)
{
WCHAR wszBuf[20];
if (0 != GetEnvironmentVariable(
L"CertSrv_Chain",
wszBuf,
ARRAYSIZE(wszBuf)))
{
s_fDumpEnabled = TRUE;
s_SaveCount = _wtoi(wszBuf);
s_SaveIndex = s_SaveCount;
}
s_fEnvChecked = TRUE;
}
fSave = 0 != s_SaveCount || (CA_VERIFY_FLAGS_SAVE_CHAIN & dwFlags);
fDump = s_fDumpEnabled ||
S_OK != hrVerify ||
(CA_VERIFY_FLAGS_DUMP_CHAIN & dwFlags);
#if DBG_CERTSRV
if (DbgIsSSActive(DBG_SS_CERTLIBI))
{
fDump = TRUE;
}
#endif
if (!fSave && !fDump)
{
return;
}
if (0 != s_SaveCount)
{
if (++s_SaveIndex >= s_SaveCount)
{
s_SaveIndex = 0;
}
}
if (fDump)
{
DBGPRINT((MAXDWORD, "-------- Chain Start --------\n"));
DumpFlags(
pChainContext->TrustStatus.dwInfoStatus,
L"ChainContext.dwInfoStatus",
g_adfInfoStatus);
DumpFlags(
pChainContext->TrustStatus.dwErrorStatus,
L"ChainContext.dwErrorStatus",
g_adfErrorStatus);
}
for (i = 0; i < pChainContext->cChain; i++)
{
if (fSave)
{
WriteChain(pChainContext->rgpChain[i], s_SaveIndex, i);
}
if (fDump)
{
DumpFlags(
pChainContext->rgpChain[i]->TrustStatus.dwInfoStatus,
L"SimpleChain.dwInfoStatus",
g_adfInfoStatus);
DumpFlags(
pChainContext->rgpChain[i]->TrustStatus.dwErrorStatus,
L"SimpleChain.dwErrorStatus",
g_adfErrorStatus);
}
for (j = 0; j < pChainContext->rgpChain[i]->cElement; j++)
{
CERT_CHAIN_ELEMENT const *pElement;
pElement = pChainContext->rgpChain[i]->rgpElement[j];
if (fDump ||
S_OK != hrVerify ||
0 != pElement->TrustStatus.dwErrorStatus)
{
WCHAR *pwsz;
CERT_REVOCATION_INFO *pRevocationInfo;
CONSOLEPRINT4((
MAXDWORD,
"CertContext[%u][%u]: dwInfoStatus=%x dwErrorStatus=%x\n",
i,
j,
pElement->TrustStatus.dwInfoStatus,
pElement->TrustStatus.dwErrorStatus));
pwsz = NULL;
hr = myCertNameToStr(
X509_ASN_ENCODING,
&pElement->pCertContext->pCertInfo->Issuer,
CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
&pwsz);
_PrintIfError(hr, "myCertNameToStr");
if (NULL != pwsz)
{
CONSOLEPRINT1((MAXDWORD, " Issuer: %ws\n", pwsz));
LocalFree(pwsz);
}
pwsz = NULL;
hr = myCertNameToStr(
X509_ASN_ENCODING,
&pElement->pCertContext->pCertInfo->Subject,
CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
&pwsz);
_PrintIfError(hr, "myCertNameToStr");
if (NULL != pwsz)
{
CONSOLEPRINT1((MAXDWORD, " Subject: %ws\n", pwsz));
LocalFree(pwsz);
}
DumpFlags(
pElement->TrustStatus.dwInfoStatus,
L" CertContext.dwInfoStatus",
g_adfInfoStatus);
DumpFlags(
pElement->TrustStatus.dwErrorStatus,
L" CertContext.dwErrorStatus",
g_adfErrorStatus);
pRevocationInfo = pElement->pRevocationInfo;
if (NULL != pRevocationInfo &&
CCSIZEOF_STRUCT(CERT_REVOCATION_INFO, pCrlInfo) <=
pRevocationInfo->cbSize &&
NULL != pRevocationInfo->pCrlInfo)
{
CERT_REVOCATION_CRL_INFO *pCrlInfo;
pCrlInfo = pRevocationInfo->pCrlInfo;
if (NULL != pCrlInfo)
{
if (NULL != pCrlInfo->pBaseCrlContext)
{
CONSOLEPRINT1((MAXDWORD, " CRL %d:\n", i));
}
if (NULL != pCrlInfo->pDeltaCrlContext)
{
CONSOLEPRINT1((MAXDWORD, " Delta CRL %d:\n", i));
}
}
}
if (FIELD_OFFSET(CERT_CHAIN_ELEMENT, pIssuanceUsage) <
pElement->cbSize)
{
DumpUsage(L" Issuance", pElement->pIssuanceUsage);
}
if (FIELD_OFFSET(CERT_CHAIN_ELEMENT, pApplicationUsage) <
pElement->cbSize)
{
DumpUsage(L" Application", pElement->pApplicationUsage);
}
if (FIELD_OFFSET(CERT_CHAIN_ELEMENT, pwszExtendedErrorInfo) <
pElement->cbSize &&
NULL != pElement->pwszExtendedErrorInfo)
{
CONSOLEPRINT1((
MAXDWORD,
" %ws",
pElement->pwszExtendedErrorInfo));
}
}
}
}
if (fDump)
{
if (S_OK != hrVerify)
{
WCHAR *pwszCertSubject;
WCHAR const *pwszErr = myGetErrorMessageText(hrVerify, TRUE);
if (NULL != pwszMissingIssuer)
{
CONSOLEPRINT1((
MAXDWORD,
"Missing Issuer: %ws\n",
pwszMissingIssuer));
}
hr = myCertNameToStr(
X509_ASN_ENCODING,
&pCert->pCertInfo->Subject,
CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
&pwszCertSubject);
_PrintIfError(hr, "myCertNameToStr");
if (S_OK == hr)
{
CONSOLEPRINT1((MAXDWORD, "Subject: %ws\n", pwszCertSubject));
LocalFree(pwszCertSubject);
}
if (NULL != pwszErr)
{
CONSOLEPRINT1((MAXDWORD, "%ws\n", pwszErr));
LocalFree(const_cast<WCHAR *>(pwszErr));
}
}
DBGPRINT((MAXDWORD, "-------- Chain End --------\n"));
}
}
HRESULT
SavePolicies(
OPTIONAL IN CERT_ENHKEY_USAGE const *pUsage,
OUT WCHAR **ppwszzPolicies)
{
HRESULT hr;
DWORD i;
DWORD cwc;
char const *psz;
WCHAR *pwsz;
if (NULL != pUsage &&
0 != pUsage->cUsageIdentifier &&
NULL != pUsage->rgpszUsageIdentifier)
{
cwc = 1;
for (i = 0; i < pUsage->cUsageIdentifier; i++)
{
cwc += strlen(pUsage->rgpszUsageIdentifier[i]) + 1;
}
pwsz = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR));
if (NULL == pwsz)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
*ppwszzPolicies = pwsz;
for (i = 0; i < pUsage->cUsageIdentifier; i++)
{
psz = pUsage->rgpszUsageIdentifier[i];
while (*pwsz++ = *psz++)
;
}
*pwsz++ = L'\0';
CSASSERT(pwsz == &(*ppwszzPolicies)[cwc]);
}
hr = S_OK;
error:
return(hr);
}
HRESULT
myVerifyCertContextEx(
IN CERT_CONTEXT const *pCert,
IN DWORD dwFlags,
IN DWORD cUsageOids,
OPTIONAL IN CHAR const * const *apszUsageOids,
OPTIONAL IN HCERTCHAINENGINE hChainEngine,
OPTIONAL IN FILETIME const *pft,
OPTIONAL IN HCERTSTORE hAdditionalStore,
OPTIONAL OUT WCHAR **ppwszMissingIssuer,
OPTIONAL OUT WCHAR **ppwszzIssuancePolicies,
OPTIONAL OUT WCHAR **ppwszzApplicationPolicies)
{
HRESULT hr;
CERT_CHAIN_PARA ChainParams;
CERT_CHAIN_POLICY_PARA ChainPolicy;
CERT_CHAIN_POLICY_STATUS PolicyStatus;
CERT_CHAIN_CONTEXT const *pChainContext = NULL;
LPCSTR pszChainPolicyFlags;
WCHAR *pwszMissingIssuer = NULL;
CERT_CHAIN_ELEMENT const *pElement;
if (NULL != ppwszMissingIssuer)
{
*ppwszMissingIssuer = NULL;
}
if (NULL != ppwszzIssuancePolicies)
{
*ppwszzIssuancePolicies = NULL;
}
if (NULL != ppwszzApplicationPolicies)
{
*ppwszzApplicationPolicies = NULL;
}
ZeroMemory(&ChainParams, sizeof(ChainParams));
ChainParams.cbSize = sizeof(ChainParams);
ChainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
//ChainParams.RequestedUsage.Usage.cUsageIdentifier = 0;
//ChainParams.RequestedUsage.Usage.rgpszUsageIdentifier = NULL;
ChainParams.RequestedUsage.Usage.cUsageIdentifier = cUsageOids;
ChainParams.RequestedUsage.Usage.rgpszUsageIdentifier = (char **) apszUsageOids;
DBGPRINT((DBG_SS_CERTLIBI, "Calling CertGetCertificateChain...\n"));
// Get the chain and verify the cert:
if (!CertGetCertificateChain(
hChainEngine, // hChainEngine
pCert, // pCertContext
const_cast<FILETIME *>(pft), // pTime
hAdditionalStore, // hAdditionalStore
&ChainParams, // pChainPara
(dwFlags & CA_VERIFY_FLAGS_NO_REVOCATION)?
0 : CERT_CHAIN_REVOCATION_CHECK_CHAIN,
NULL, // pvReserved
&pChainContext)) // ppChainContext
{
hr = myHLastError();
_JumpError(hr, error, "CertGetCertificateChain");
}
DBGPRINT((DBG_SS_CERTLIBI, "CertGetCertificateChain done\n"));
ZeroMemory(&ChainPolicy, sizeof(ChainPolicy));
ChainPolicy.cbSize = sizeof(ChainPolicy);
ChainPolicy.dwFlags = CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG;
//ChainPolicy.pvExtraPolicyPara = NULL;
ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
PolicyStatus.cbSize = sizeof(PolicyStatus);
//PolicyStatus.dwError = 0;
PolicyStatus.lChainIndex = -1;
PolicyStatus.lElementIndex = -1;
//PolicyStatus.pvExtraPolicyStatus = NULL;
// use NTAuth policy if (usage oids being added) & (caller asks us to check(EntCA))
pszChainPolicyFlags =
(NULL != apszUsageOids && (CA_VERIFY_FLAGS_NT_AUTH & dwFlags))?
CERT_CHAIN_POLICY_NT_AUTH : CERT_CHAIN_POLICY_BASE;
if (!CertVerifyCertificateChainPolicy(
pszChainPolicyFlags,
pChainContext,
&ChainPolicy,
&PolicyStatus))
{
hr = myHLastError();
_JumpError(hr, error, "CertVerifyCertificateChainPolicy");
}
hr = myHError(PolicyStatus.dwError);
if ((CA_VERIFY_FLAGS_IGNORE_OFFLINE | CA_VERIFY_FLAGS_NO_REVOCATION) & dwFlags)
{
if (CRYPT_E_NO_REVOCATION_CHECK == hr ||
CRYPT_E_REVOCATION_OFFLINE == hr)
{
hr = S_OK;
}
}
if (CA_VERIFY_FLAGS_ALLOW_UNTRUSTED_ROOT & dwFlags)
{
if (CERT_E_UNTRUSTEDROOT == hr)
{
hr = S_OK;
}
}
if (S_OK != hr &&
0 < pChainContext->cChain &&
0 < pChainContext->rgpChain[0]->cElement)
{
pElement = pChainContext->rgpChain[0]->rgpElement[
pChainContext->rgpChain[0]->cElement - 1];
if (0 == (CERT_TRUST_IS_SELF_SIGNED & pElement->TrustStatus.dwInfoStatus))
{
HRESULT hr2;
hr2 = myCertNameToStr(
X509_ASN_ENCODING,
&pElement->pCertContext->pCertInfo->Issuer,
CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
&pwszMissingIssuer);
_PrintIfError(hr2, "myCertNameToStr");
}
}
DumpChain(hr, dwFlags, pCert, pwszMissingIssuer, pChainContext);
if (NULL != ppwszMissingIssuer)
{
*ppwszMissingIssuer = pwszMissingIssuer;
pwszMissingIssuer = NULL;
}
_JumpIfError(hr, error, "PolicyStatus.dwError");
pElement = pChainContext->rgpChain[0]->rgpElement[0];
if (NULL != ppwszzIssuancePolicies &&
FIELD_OFFSET(CERT_CHAIN_ELEMENT, pIssuanceUsage) < pElement->cbSize)
{
hr = SavePolicies(
pElement->pIssuanceUsage,
ppwszzIssuancePolicies);
_JumpIfError(hr, error, "SavePolicies");
}
if (NULL != ppwszzApplicationPolicies &&
FIELD_OFFSET(CERT_CHAIN_ELEMENT, pApplicationUsage) < pElement->cbSize)
{
hr = SavePolicies(
pElement->pApplicationUsage,
ppwszzApplicationPolicies);
_JumpIfError(hr, error, "SavePolicies");
}
error:
if (S_OK != hr)
{
if (NULL != ppwszzIssuancePolicies && NULL != *ppwszzIssuancePolicies)
{
LocalFree(*ppwszzIssuancePolicies);
*ppwszzIssuancePolicies = NULL;
}
}
if (NULL != pwszMissingIssuer)
{
LocalFree(pwszMissingIssuer);
}
if (NULL != pChainContext)
{
CertFreeCertificateChain(pChainContext);
}
return(hr);
}
HRESULT
myVerifyCertContext(
IN CERT_CONTEXT const *pCert,
IN DWORD dwFlags,
IN DWORD cUsageOids,
OPTIONAL IN CHAR const * const *apszUsageOids,
OPTIONAL IN HCERTCHAINENGINE hChainEngine,
OPTIONAL IN HCERTSTORE hAdditionalStore,
OPTIONAL OUT WCHAR **ppwszMissingIssuer)
{
HRESULT hr;
hr = myVerifyCertContextEx(
pCert,
dwFlags,
cUsageOids,
apszUsageOids,
hChainEngine,
NULL, // pft
hAdditionalStore,
ppwszMissingIssuer,
NULL, // ppwszzIssuancePolicies
NULL); // ppwszzApplicationPolicies
_JumpIfError2(hr, error, "myVerifyCertContextEx", hr);
error:
return(hr);
}
HRESULT
myIsFirstSigner(
IN CERT_NAME_BLOB const *pNameBlob,
OUT BOOL *pfFirst)
{
HRESULT hr;
CERT_NAME_INFO *pNameInfo = NULL;
DWORD cbNameInfo;
DWORD i;
*pfFirst = FALSE;
if (!myDecodeName(
X509_ASN_ENCODING,
X509_UNICODE_NAME,
pNameBlob->pbData,
pNameBlob->cbData,
CERTLIB_USE_LOCALALLOC,
&pNameInfo,
&cbNameInfo))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeName");
}
for (i = 0; i < pNameInfo->cRDN; i++)
{
CERT_RDN const *prdn;
DWORD j;
prdn = &pNameInfo->rgRDN[i];
for (j = 0; j < prdn->cRDNAttr; j++)
{
if (0 == strcmp(
prdn->rgRDNAttr[j].pszObjId,
szOID_RDN_DUMMY_SIGNER))
{
*pfFirst = TRUE;
i = pNameInfo->cRDN; // terminate outer loop
break;
}
}
}
hr = S_OK;
error:
if (NULL != pNameInfo)
{
LocalFree(pNameInfo);
}
return(hr);
}
HCERTSTORE
myPFXImportCertStore(
IN CRYPT_DATA_BLOB *ppfx,
OPTIONAL IN WCHAR const *pwszPassword,
IN DWORD dwFlags)
{
HCERTSTORE hStore;
HRESULT hr;
if (NULL == pwszPassword)
{
pwszPassword = L""; // Try empty password first, then NULL
}
while (TRUE)
{
hStore = PFXImportCertStore(ppfx, pwszPassword, dwFlags);
if (NULL == hStore)
{
hr = myHLastError();
if (HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD) != hr ||
NULL == pwszPassword ||
L'\0' != *pwszPassword)
{
_JumpError2(
hr,
error,
"PFXImportCertStore",
HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD));
}
pwszPassword = NULL; // empty password failed; try NULL
continue;
}
break;
}
error:
return(hStore);
}
// No longer support versions before IE3.02 - Auth2 update, advisory only
HRESULT
CertCheck7f(
IN CERT_CONTEXT const *pcc)
{
HRESULT hr;
DWORD State;
DWORD Index1;
DWORD Index2;
DWORD cwcField;
WCHAR wszField[128];
DWORD cwcObjectId;
WCHAR wszObjectId[128];
WCHAR const *pwszObjectIdDescription = NULL;
cwcField = sizeof(wszField)/sizeof(wszField[0]);
cwcObjectId = sizeof(wszObjectId)/sizeof(wszObjectId[0]);
hr = myCheck7f(
pcc->pbCertEncoded,
pcc->cbCertEncoded,
FALSE,
&State,
&Index1,
&Index2,
&cwcField,
wszField,
&cwcObjectId,
wszObjectId,
&pwszObjectIdDescription); // Static: do not free!
_JumpIfError(hr, error, "myCheck7f");
if (CHECK7F_NONE != State)
{
hr = CERTSRV_E_ENCODING_LENGTH;
#if DBG_CERTSRV
WCHAR wszBuf[2048];
wcscpy(wszBuf, wszField);
if (0 != Index1)
{
wsprintf(
&wszBuf[wcslen(wszBuf)],
0 != Index2? L"[%u,%u]" : L"[%u]",
Index1 - 1,
Index2 - 1);
}
if (0 != cwcObjectId)
{
wcscat(wszBuf, L" ObjectId=");
wcscat(wszBuf, wszObjectId);
}
if (NULL != pwszObjectIdDescription)
{
wcscat(wszBuf, L" (");
wcscat(wszBuf, pwszObjectIdDescription);
wcscat(wszBuf, L")");
}
DBGPRINT((DBG_SS_CERTLIB, "CertCheck7f: %ws, hr=%x\n", wszBuf, hr));
#endif // DBG_CERTSRV
}
error:
return(hr);
}
HRESULT
myAddCertToStore(
IN HCERTSTORE hStore,
IN CERT_CONTEXT const *pCertContext,
OPTIONAL IN CRYPT_KEY_PROV_INFO const *pkpi,
OPTIONAL OUT CERT_CONTEXT const **ppCert)
{
HRESULT hr;
CERT_CONTEXT const *pcc = NULL;
if (NULL != ppCert)
{
*ppCert = NULL;
}
// for root cert, if it shows related private key, it will
// pfx import failure for other applications
// Add as encoded blob to avoid all properties, key prov info, etc.
if (!CertAddEncodedCertificateToStore(
hStore,
X509_ASN_ENCODING,
pCertContext->pbCertEncoded,
pCertContext->cbCertEncoded,
NULL != pkpi?
CERT_STORE_ADD_REPLACE_EXISTING :
CERT_STORE_ADD_USE_EXISTING,
&pcc)) // ppCertContext
{
hr = myHLastError();
_JumpError(hr, error, "CertAddEncodedCertificateToStore");
}
if (NULL != pkpi)
{
if (!CertSetCertificateContextProperty(
pcc,
CERT_KEY_PROV_INFO_PROP_ID,
0,
pkpi))
{
hr = myHLastError();
_JumpError(hr, error, "CertSetCertificateContextProperty");
}
}
if (NULL != ppCert)
{
*ppCert = pcc;
pcc = NULL;
}
hr = S_OK;
error:
if (NULL != pcc)
{
CertFreeCertificateContext(pcc);
}
return(hr);
}
HRESULT
mySaveChainAndKeys(
IN CERT_SIMPLE_CHAIN const *pSimpleChain,
IN WCHAR const *pwszStore,
IN DWORD dwStoreFlags,
IN CRYPT_KEY_PROV_INFO const *pkpi,
OPTIONAL IN CERT_CONTEXT const **ppCert)
{
HRESULT hr;
HCERTSTORE hRootStore = NULL;
HCERTSTORE hCAStore = NULL;
HCERTSTORE hMyStore = NULL;
DWORD i;
if (NULL != ppCert)
{
*ppCert = NULL;
}
hRootStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM_REGISTRY_W,
X509_ASN_ENCODING,
NULL, // hProv
dwStoreFlags,
wszROOT_CERTSTORE);
if (NULL == hRootStore)
{
hr = myHLastError();
_JumpErrorStr(hr, error, "CertOpenStore", wszROOT_CERTSTORE);
}
hCAStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM_REGISTRY_W,
X509_ASN_ENCODING,
NULL, // hProv
dwStoreFlags,
wszCA_CERTSTORE);
if (NULL == hCAStore)
{
hr = myHLastError();
_JumpErrorStr(hr, error, "CertOpenStore", wszCA_CERTSTORE);
}
hMyStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM_REGISTRY_W,
X509_ASN_ENCODING,
NULL, // hProv
dwStoreFlags,
pwszStore);
if (NULL == hMyStore)
{
hr = myHLastError();
_JumpErrorStr(hr, error, "CertOpenStore", pwszStore);
}
for (i = 0; i < pSimpleChain->cElement; i++)
{
CERT_CONTEXT const *pcc = pSimpleChain->rgpElement[i]->pCertContext;
HCERTSTORE hStore;
// CertCheck7f(pcc);
// if leaf CA cert, add to MY store
if (0 == i)
{
PCCERT_CONTEXT pFoundCC = CertFindCertificateInStore(
hMyStore,
X509_ASN_ENCODING,
0,
CERT_FIND_EXISTING,
pcc,
NULL);
if(!pFoundCC)
{
hr = myAddCertToStore(hMyStore, pcc, pkpi, ppCert);
_JumpIfError(hr, error, "myAddCertToStore");
}
else
{
if(ppCert)
{
*ppCert = pFoundCC;
}
else
{
CertFreeCertificateContext(pFoundCC);
}
}
}
// if root cert, add to ROOT store (without key); else add to CA store
hStore = hCAStore;
if (CERT_TRUST_IS_SELF_SIGNED &
pSimpleChain->rgpElement[i]->TrustStatus.dwInfoStatus)
{
hStore = hRootStore;
}
hr = myAddCertToStore(hStore, pcc, NULL, NULL);
_JumpIfError(hr, error, "myAddCertToStore");
}
hr = S_OK;
error:
if (NULL != hRootStore)
{
CertCloseStore(hRootStore, CERT_CLOSE_STORE_CHECK_FLAG);
}
if (NULL != hCAStore)
{
CertCloseStore(hCAStore, CERT_CLOSE_STORE_CHECK_FLAG);
}
if (NULL != hMyStore)
{
CertCloseStore(hMyStore, CERT_CLOSE_STORE_CHECK_FLAG);
}
return(hr);
}
HRESULT
myGetNameId(
IN CERT_CONTEXT const *pCACert,
OUT DWORD *pdwNameId)
{
HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
CERT_EXTENSION const *pExt;
DWORD NameId;
DWORD cb;
*pdwNameId = MAXDWORD;
pExt = CertFindExtension(
szOID_CERTSRV_CA_VERSION,
pCACert->pCertInfo->cExtension,
pCACert->pCertInfo->rgExtension);
if (NULL == pExt)
{
// This API doesn't set LastError
_JumpError(hr, error, "CertFindExtension(CA Version)");
}
cb = sizeof(NameId);
NameId = 0;
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_INTEGER,
pExt->Value.pbData,
pExt->Value.cbData,
0,
&NameId,
&cb))
{
hr = myHLastError();
_JumpError(hr, error, "CryptDecodeObject");
}
*pdwNameId = NameId;
hr = S_OK;
error:
return(hr);
}
HRESULT
myCertGetNameString(
IN CERT_CONTEXT const *pcc,
IN DWORD dwType,
OUT WCHAR **ppwszSimpleName)
{
HRESULT hr;
WCHAR *pwsz = NULL;
DWORD cwc;
*ppwszSimpleName = NULL;
cwc = 0;
while (TRUE)
{
cwc = CertGetNameString(
pcc,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0, // dwFlags
NULL, // pvTypePara
pwsz,
cwc);
if (1 >= cwc)
{
hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
_JumpError(hr, error, "CertGetNameString");
}
if (NULL != pwsz)
{
break;
}
pwsz = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR));
if (NULL == pwsz)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
}
*ppwszSimpleName = pwsz;
pwsz = NULL;
hr = S_OK;
error:
if (NULL != pwsz)
{
LocalFree(pwsz);
}
return(hr);
}
HRESULT
myCertStrToName(
IN DWORD dwCertEncodingType,
IN LPCWSTR pszX500,
IN DWORD dwStrType,
IN OPTIONAL void *pvReserved,
OUT BYTE **ppbEncoded,
OUT DWORD *pcbEncoded,
OUT OPTIONAL LPCWSTR *ppszError)
{
HRESULT hr;
*ppbEncoded = NULL;
*pcbEncoded = 0;
while (TRUE)
{
if (!CertStrToName(
dwCertEncodingType,
pszX500,
dwStrType,
pvReserved,
*ppbEncoded,
pcbEncoded,
ppszError))
{
hr = myHLastError();
if (NULL != *ppbEncoded)
{
LocalFree(*ppbEncoded);
*ppbEncoded = NULL;
}
_JumpError(hr, error, "CertStrToName");
}
if (NULL != *ppbEncoded)
{
break;
}
*ppbEncoded = (BYTE *) LocalAlloc(LMEM_FIXED, *pcbEncoded);
if (NULL == *ppbEncoded)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
}
hr = S_OK;
error:
return(hr);
}
HRESULT
myCertNameToStr(
IN DWORD dwCertEncodingType,
IN CERT_NAME_BLOB const *pName,
IN DWORD dwStrType,
OUT WCHAR **ppwszName)
{
HRESULT hr;
DWORD cwc = 0;
WCHAR *pwszName = NULL;
while (TRUE)
{
cwc = CertNameToStr(
dwCertEncodingType,
const_cast<CERT_NAME_BLOB *>(pName),
dwStrType,
pwszName,
cwc);
if (1 > cwc)
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
_JumpError(hr, error, "CertNameToStr");
}
if (NULL != pwszName)
{
break;
}
pwszName = (WCHAR *) LocalAlloc(
LMEM_FIXED,
(cwc + 1) * sizeof(WCHAR));
if (NULL == pwszName)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
}
*ppwszName = pwszName;
pwszName = NULL;
hr = S_OK;
error:
if (NULL != pwszName)
{
LocalFree(pwszName);
}
return(hr);
}
HRESULT
myVerifyKRACertContext(
IN CERT_CONTEXT const *pCert,
IN DWORD dwFlags)
{
HRESULT hr;
WCHAR *pwszzAppPolicies = NULL;
WCHAR *pwszCrt;
hr = myVerifyCertContextEx(
pCert,
dwFlags,
0, // cUsageOids
NULL, // apszUsageOids
HCCE_LOCAL_MACHINE, // hChainEngine
NULL, // pft
NULL, // hAdditionalStore
NULL, // ppwszMissingIssuer
NULL, // ppwszzIssuancePolicies
&pwszzAppPolicies);
_JumpIfError(hr, error, "myVerifyCertContextEx");
hr = CERT_E_WRONG_USAGE;
for(pwszCrt = pwszzAppPolicies;
pwszCrt && L'\0' != *pwszCrt;
pwszCrt = pwszCrt + wcslen(pwszzAppPolicies) + 1)
{
if(0==wcscmp(TEXT(szOID_KP_KEY_RECOVERY_AGENT), pwszCrt))
{
hr = S_OK;
break;
}
}
_JumpIfError(hr, error, "myVerifyKRACertContext");
error:
if(pwszzAppPolicies)
{
LocalFree(pwszzAppPolicies);
}
return(hr);
}