Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1826 lines
61 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1995 - 1999
//
// File: pfxhelp.cpp
//
// Contents: Support functions for PFX
//
// Functions: CertExportSafeContents
// CertImportSafeContents
//
// History: 23-Feb-96 philh created
//--------------------------------------------------------------------------
#include "global.hxx"
#include <dbgdef.h>
#include "pfxhelp.h"
#include "pfxpkcs.h"
#include "pfxcmn.h"
#include "pfxcrypt.h"
// All the *pvInfo extra stuff needs to be aligned
#define INFO_LEN_ALIGN(Len) ((Len + 7) & ~7)
// remove when this is defined in wincrypt.h
#ifndef PP_KEYSET_TYPE
#define PP_KEYSET_TYPE 27
#endif
#define DISALLOWED_FLAG_MASK ~(CRYPT_EXPORTABLE | CRYPT_DELETEKEYSET)
//+-------------------------------------------------------------------------
// PFX helpe allocation and free functions
//--------------------------------------------------------------------------
static void *PFXHelpAlloc(
IN size_t cbBytes
)
{
void *pv;
pv = malloc(cbBytes);
if (pv == NULL)
SetLastError((DWORD) E_OUTOFMEMORY);
return pv;
}
static void *PFXHelpRealloc(
IN void *pvOrg,
IN size_t cbBytes
)
{
void *pv;
if (NULL == (pv = pvOrg ? realloc(pvOrg, cbBytes) : malloc(cbBytes)))
SetLastError((DWORD) E_OUTOFMEMORY);
return pv;
}
static void PFXHelpFree(
IN void *pv
)
{
if (pv)
free(pv);
}
// this function will search an a SAFE_CONTENTS to see if any of the SAFE_BAGS have the
// same private key as the one passed to the function. if it finds a matching private
// key it will return a pointer the encoded keyID and return TRUE, it will return FALSE
// otherwise. NOTE that if it returns a pointer to the encoded blob that the caller
// is responsible for copying the data and must not free what is returned
static BOOL WINAPI PrivateKeyAlreadyExists(
BYTE *pPrivateKey,
DWORD cbPrivateKey,
SAFE_CONTENTS *pSafeContents,
PCRYPT_DER_BLOB pEncodedKeyID
)
{
BOOL bKeyFound = FALSE;
DWORD i = 0;
if (pSafeContents == NULL) {
goto CommonReturn;
}
while ((!bKeyFound) && (i < pSafeContents->cSafeBags))
{
if ( ((strcmp(pSafeContents->pSafeBags[i].pszBagTypeOID, szOID_PKCS_12_KEY_BAG) == 0) ||
(strcmp(pSafeContents->pSafeBags[i].pszBagTypeOID, szOID_PKCS_12_SHROUDEDKEY_BAG) == 0)) &&
(cbPrivateKey == pSafeContents->pSafeBags[i].BagContents.cbData) &&
(memcmp(pPrivateKey, pSafeContents->pSafeBags[i].BagContents.pbData, cbPrivateKey) == 0))
{
pEncodedKeyID->pbData = pSafeContents->pSafeBags[i].Attributes.rgAttr[0].rgValue[0].pbData;
pEncodedKeyID->cbData = pSafeContents->pSafeBags[i].Attributes.rgAttr[0].rgValue[0].cbData;
bKeyFound = TRUE;
}
else {
i++;
}
}
CommonReturn:
return bKeyFound;
}
// this function will walk through a SAFE_CONTENTS structure and free all the space
// associated with it
static BOOL WINAPI FreeSafeContents(
SAFE_CONTENTS *pSafeContents
)
{
DWORD i,j,k;
// loop for each SAFE_BAG
for (i=0; i<pSafeContents->cSafeBags; i++) {
if (pSafeContents->pSafeBags[i].BagContents.pbData)
PFXHelpFree(pSafeContents->pSafeBags[i].BagContents.pbData);
// loop for each attribute
for (j=0; j<pSafeContents->pSafeBags[i].Attributes.cAttr; j++) {
// l0op for each value
for (k=0; k<pSafeContents->pSafeBags[i].Attributes.rgAttr[j].cValue; k++) {
if (pSafeContents->pSafeBags[i].Attributes.rgAttr[j].rgValue[k].pbData)
PFXHelpFree(pSafeContents->pSafeBags[i].Attributes.rgAttr[j].rgValue[k].pbData);
}
// free the value struct array
if (pSafeContents->pSafeBags[i].Attributes.rgAttr[j].rgValue)
PFXHelpFree(pSafeContents->pSafeBags[i].Attributes.rgAttr[j].rgValue);
}
// free the attribute struct array
if (pSafeContents->pSafeBags[i].Attributes.rgAttr)
PFXHelpFree(pSafeContents->pSafeBags[i].Attributes.rgAttr);
}
// finally, free the safe bag array
if (pSafeContents->pSafeBags != NULL)
{
PFXHelpFree(pSafeContents->pSafeBags);
}
return TRUE;
}
#define SZ_NO_PROVIDER_NAME_KEY L"Software\\Microsoft\\Windows\\CurrentVersion\\PFX"
#define SZ_NO_PROVIDER_NAME_VALUE L"NoProviderName"
BOOL
NoProviderNameRegValueSet()
{
HKEY hKey = NULL;
BOOL fRet = FALSE;
DWORD dwData;
DWORD dwDataSize = sizeof(dwData);
if (ERROR_SUCCESS != RegOpenKeyExU(
HKEY_CURRENT_USER,
SZ_NO_PROVIDER_NAME_KEY,
0,
KEY_EXECUTE,
&hKey))
{
goto Return;;
}
if (ERROR_SUCCESS == RegQueryValueExU(
hKey,
SZ_NO_PROVIDER_NAME_VALUE,
NULL,
NULL,
(LPBYTE) &dwData,
&dwDataSize))
{
fRet = (BOOL) dwData;
}
Return:
if (hKey != NULL)
RegCloseKey(hKey);
return fRet;
}
//+-------------------------------------------------------------------------
// hCertStore - handle to the cert store that contains the certs whose
// corresponding private keys are to be exported
// pSafeContents - pointer to a buffer to receive the SAFE_CONTENTS structure
// and supporting data
// pcbSafeContents - (in) specifies the length, in bytes, of the pSafeContents
// buffer. (out) gets filled in with the number of bytes
// used by the operation. If this is set to 0, the
// required length of pSafeContents is filled in, and
// pSafeContents is ignored.
// dwFlags - the current available flags are:
// EXPORT_PRIVATE_KEYS
// if this flag is set then the private keys are exported as well
// as the certificates
// REPORT_NO_PRIVATE_KEY
// if this flag is set and a certificate is encountered that has no
// no associated private key, the function will return immediately
// with ppCertContext filled in with a pointer to the cert context
// in question. the caller is responsible for freeing the cert
// context which is passed back.
// REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY
// if this flag is set and a certificate is encountered that has a
// non-exportable private key, the function will return immediately
// with ppCertContext filled in with a pointer to the cert context
// in question. the caller is responsible for freeing the cert
// context which is passed back.
// ppCertContext - a pointer to a pointer to a cert context. this is used
// if REPORT_NO_PRIVATE_KEY or REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY
// flags are set. the caller is responsible for freeing the
// cert context.
// pvAuxInfo - reserved for future use, must be set to NULL
//+-------------------------------------------------------------------------
BOOL WINAPI CertExportSafeContents(
HCERTSTORE hCertStore, // in
SAFE_CONTENTS *pSafeContents, // out
DWORD *pcbSafeContents, // in, out
EXPORT_SAFE_CALLBACK_STRUCT *ExportSafeCallbackStruct, // in
DWORD dwFlags, // in
PCCERT_CONTEXT *ppCertContext, // out
void *pvAuxInfo // in
)
{
BOOL fResult = TRUE;
PCCERT_CONTEXT pCertContext = NULL;
DWORD dwKeySpec;
DWORD dwBytesRequired = sizeof(SAFE_CONTENTS);
SAFE_CONTENTS localSafeContents;
BYTE *pCurrentBufferLocation = NULL;
DWORD dwIDs = 1;
DWORD i,j,k;
// all these variables are used in the while loop that enumerates through
// the cert contexts
CRYPT_KEY_PROV_INFO *pCryptKeyProvInfo = NULL;
DWORD cbCryptKeyProvInfo = 0;
HCRYPTPROV hCryptProv = NULL;
BYTE *pPrivateKey = NULL;
DWORD cbPrivateKey = 0;
void *pTempMemBlock = NULL;
SAFE_BAG *pCurrentSafeBag = NULL;
DWORD dwKeyID = 0;
CRYPT_ATTR_BLOB keyID;
CRYPT_DER_BLOB EncodedKeyID;
CERT_NAME_VALUE wideFriendlyName;
BYTE *pFriendlyName = NULL;
DWORD cbFriendlyName = 0;
DWORD dwFriendlyNameAttributeIndex = 0;
BOOL fAddProviderName;
LPWSTR pwszProviderName = NULL;
DWORD cbProviderName = 0;
localSafeContents.cSafeBags = 0;
localSafeContents.pSafeBags = NULL;
// validate input parameters
if ((pcbSafeContents == NULL) ||
(pvAuxInfo != NULL ||
((*pcbSafeContents != 0) && (pSafeContents == NULL)))) {
SetLastError((DWORD)ERROR_INVALID_PARAMETER);
goto ErrorReturn;
}
if ((dwFlags & REPORT_NO_PRIVATE_KEY) || (dwFlags & REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY)) {
if (ppCertContext == NULL) {
SetLastError((DWORD)ERROR_INVALID_PARAMETER);
goto ErrorReturn;
}
*ppCertContext = NULL;
}
fAddProviderName = !NoProviderNameRegValueSet();
// loop for each certificate context in the store and export the cert and
// corresponding private key if one exists
while (NULL != (pCertContext = CertEnumCertificatesInStore(hCertStore, pCertContext))) {
// initialize all loop variables
if (pCryptKeyProvInfo)
PFXHelpFree(pCryptKeyProvInfo);
pCryptKeyProvInfo = NULL;
cbCryptKeyProvInfo = 0;
if (hCryptProv)
CryptReleaseContext(hCryptProv, 0);
hCryptProv = NULL;
if (pPrivateKey)
PFXHelpFree(pPrivateKey);
pPrivateKey = NULL;
cbPrivateKey = 0;
pTempMemBlock = NULL;
pCurrentSafeBag = NULL;
// keyID is the CRYPT_ATTR_BLOB that is always used to encode the key id
// for certs and private keys. dwKeyID is the only thing that will need
// to be set properly before calling CryptEncodeObject with keyID.
keyID.pbData = (BYTE *) &dwKeyID;
keyID.cbData = sizeof(DWORD);
// initialize EncodedKeyID so when exporting the cert it can check to see if this
// has been set
EncodedKeyID.pbData = NULL;
EncodedKeyID.cbData = 0;
// if the EXPORT_PRIVATE_KEYS flag is set then
// try to export the private key which corresponds to this certificate before
// exporting the certificate so we know how to set the key ID on the certificate
if (EXPORT_PRIVATE_KEYS & dwFlags)
// get the provider info so we can export the private key
if (CertGetCertificateContextProperty(
pCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
NULL,
&cbCryptKeyProvInfo
)) {
if (NULL == (pCryptKeyProvInfo = (CRYPT_KEY_PROV_INFO *)
PFXHelpAlloc(cbCryptKeyProvInfo))) {
goto ErrorReturn;
}
if (CertGetCertificateContextProperty(
pCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
pCryptKeyProvInfo,
&cbCryptKeyProvInfo
)) {
// acquire the HCRYPTPROV so we can export the private key in that puppy
if (!CryptAcquireContextU(
&hCryptProv,
pCryptKeyProvInfo->pwszContainerName,
pCryptKeyProvInfo->pwszProvName,
pCryptKeyProvInfo->dwProvType,
pCryptKeyProvInfo->dwFlags & (DISALLOWED_FLAG_MASK)) ) {
goto ErrorReturn;
}
CRYPT_PKCS8_EXPORT_PARAMS sExportParams = { hCryptProv,
pCryptKeyProvInfo->dwKeySpec,
pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId,
//szOID_RSA_RSA, // FIX -what do I do here??, possibly look at the algorithm in the cert
(ExportSafeCallbackStruct) ? ExportSafeCallbackStruct->pEncryptPrivateKeyFunc : NULL,
(ExportSafeCallbackStruct) ? ExportSafeCallbackStruct->pVoidEncryptFunc : NULL};
// do the actual export of the private key
if (CryptExportPKCS8Ex(
&sExportParams,
PFX_MODE,
NULL,
NULL,
&cbPrivateKey
)) {
if (NULL == (pPrivateKey = (BYTE *) PFXHelpAlloc(cbPrivateKey))) {
goto ErrorReturn;
}
if (CryptExportPKCS8Ex(
&sExportParams,
(dwFlags & GIVE_ME_DATA) ? PFX_MODE | GIVE_ME_DATA : PFX_MODE,
NULL,
pPrivateKey,
&cbPrivateKey
)) {
// search the array of key bags to see if the private key is already there
// and take action accordingly. if the private key already exists, the
// EncodedKeyID contains the encoded keyID attribute for exporting the
// certificate so we don't need to do anything
if (!PrivateKeyAlreadyExists(
pPrivateKey,
cbPrivateKey,
&localSafeContents,
&EncodedKeyID
)) {
// extend the length of the SAFE_BAGs array by one
if (NULL == (pTempMemBlock = PFXHelpRealloc(
localSafeContents.pSafeBags,
sizeof(SAFE_BAG) *
++localSafeContents.cSafeBags))) {
goto ErrorReturn;
}
localSafeContents.pSafeBags = (SAFE_BAG *) pTempMemBlock;
pCurrentSafeBag =
&localSafeContents.pSafeBags[localSafeContents.cSafeBags - 1];
ZeroMemory(pCurrentSafeBag, sizeof(SAFE_BAG));
dwBytesRequired += sizeof(SAFE_BAG);
// set up the OID information for the bag type
pCurrentSafeBag->pszBagTypeOID = (ExportSafeCallbackStruct->pEncryptPrivateKeyFunc) ? szOID_PKCS_12_SHROUDEDKEY_BAG : szOID_PKCS_12_KEY_BAG;
dwBytesRequired += INFO_LEN_ALIGN(strlen(pCurrentSafeBag->pszBagTypeOID) + 1);
// copy the pointer to the private key into the new safe bag
// and NULL out the pPrivateKey pointer so the memory does not get freed
pCurrentSafeBag->BagContents.pbData = pPrivateKey;
pCurrentSafeBag->BagContents.cbData = cbPrivateKey;
dwBytesRequired += INFO_LEN_ALIGN(cbPrivateKey);
pPrivateKey = NULL;
cbPrivateKey = 0;
// set up the attributes array for the SAFE_BAG
// FIX - for right now just do the
// szOID_PKCS_12_LOCAL_KEY_ID,
// szOID_PKCS_12_FRIENDLY_NAME_ATTR,
// and szOID_PKCS_12_KEY_PROVIDER_NAME_ATTR. (if the NoProviderName reg value not set)
// optional szOID_LOCAL_MACHINE_KEYSET if needed
pCurrentSafeBag->Attributes.cAttr = fAddProviderName ? 3 : 2;
if (pCryptKeyProvInfo->dwFlags & CRYPT_MACHINE_KEYSET)
pCurrentSafeBag->Attributes.cAttr++;
if (NULL == (pCurrentSafeBag->Attributes.rgAttr = (CRYPT_ATTRIBUTE *)
PFXHelpAlloc(sizeof(CRYPT_ATTRIBUTE) * pCurrentSafeBag->Attributes.cAttr))) {
goto ErrorReturn;
}
ZeroMemory(pCurrentSafeBag->Attributes.rgAttr, sizeof(CRYPT_ATTRIBUTE) * pCurrentSafeBag->Attributes.cAttr);
dwBytesRequired += sizeof(CRYPT_ATTRIBUTE) * pCurrentSafeBag->Attributes.cAttr;
// allocate space and do setup based on whether the szOID_LOCAL_MACHINE_KEYSET
// attribute is needed
if (pCryptKeyProvInfo->dwFlags & CRYPT_MACHINE_KEYSET)
{
// since there is nothing to do for the szOID_LOCAL_MACHINE_KEYSET
// besides just setting the OID do it here and put it in the last
// attribute
pCurrentSafeBag->Attributes.rgAttr[pCurrentSafeBag->Attributes.cAttr-1].pszObjId =
szOID_LOCAL_MACHINE_KEYSET;
dwBytesRequired += INFO_LEN_ALIGN(strlen(szOID_LOCAL_MACHINE_KEYSET) + 1);
pCurrentSafeBag->Attributes.rgAttr[pCurrentSafeBag->Attributes.cAttr-1].rgValue = NULL;
pCurrentSafeBag->Attributes.rgAttr[pCurrentSafeBag->Attributes.cAttr-1].cValue = 0;
}
// set the OID in the szOID_PKCS_12_LOCAL_KEY_ID attribute
pCurrentSafeBag->Attributes.rgAttr[0].pszObjId =
szOID_PKCS_12_LOCAL_KEY_ID;
dwBytesRequired += INFO_LEN_ALIGN(strlen(szOID_PKCS_12_LOCAL_KEY_ID) + 1);
// allocate space for the single value inside the attribute
if (NULL == (pCurrentSafeBag->Attributes.rgAttr[0].rgValue =
(CRYPT_ATTR_BLOB *) PFXHelpAlloc(sizeof(CRYPT_ATTR_BLOB)))) {
goto ErrorReturn;
}
ZeroMemory(pCurrentSafeBag->Attributes.rgAttr[0].rgValue, sizeof(CRYPT_ATTR_BLOB));
dwBytesRequired += sizeof(CRYPT_ATTR_BLOB);
pCurrentSafeBag->Attributes.rgAttr[0].cValue = 1;
// set the key ID to the appropriate key ID
dwKeyID = dwIDs++;
// encode the keyID
pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].pbData = NULL;
pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].cbData = 0;
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_OCTET_STRING,
&keyID,
NULL,
&pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].cbData)) {
goto ErrorReturn;
}
if (NULL == (pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].pbData =
(BYTE *) PFXHelpAlloc(pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].cbData))) {
goto ErrorReturn;
}
dwBytesRequired += INFO_LEN_ALIGN(pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].cbData);
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_OCTET_STRING,
&keyID,
pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].pbData,
&pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].cbData)) {
goto ErrorReturn;
}
// set the fields in EncodedKeyID so that when the cert is exported
// it can just copy the already encoded keyID to it's attributes
EncodedKeyID.pbData = pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].pbData;
EncodedKeyID.cbData = pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].cbData;
// Friendly Name
// set the OID in the szOID_PKCS_12_FRIENDLY_NAME_ATTR attribute
pCurrentSafeBag->Attributes.rgAttr[1].pszObjId =
szOID_PKCS_12_FRIENDLY_NAME_ATTR;
dwBytesRequired += INFO_LEN_ALIGN(strlen(szOID_PKCS_12_FRIENDLY_NAME_ATTR) + 1);
// allocate space for the single value inside the attribute
if (NULL == (pCurrentSafeBag->Attributes.rgAttr[1].rgValue =
(CRYPT_ATTR_BLOB *) PFXHelpAlloc(sizeof(CRYPT_ATTR_BLOB)))) {
goto ErrorReturn;
}
ZeroMemory(pCurrentSafeBag->Attributes.rgAttr[1].rgValue, sizeof(CRYPT_ATTR_BLOB));
dwBytesRequired += sizeof(CRYPT_ATTR_BLOB);
pCurrentSafeBag->Attributes.rgAttr[1].cValue = 1;
// encode the provider name so it can be used on import
wideFriendlyName.dwValueType = CERT_RDN_BMP_STRING;
wideFriendlyName.Value.pbData = (BYTE *) pCryptKeyProvInfo->pwszContainerName;
wideFriendlyName.Value.cbData = 0;
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_UNICODE_ANY_STRING,
(void *)&wideFriendlyName,
NULL,
&pCurrentSafeBag->Attributes.rgAttr[1].rgValue[0].cbData)) {
goto ErrorReturn;
}
if (NULL == (pCurrentSafeBag->Attributes.rgAttr[1].rgValue[0].pbData =
(BYTE *) PFXHelpAlloc(pCurrentSafeBag->Attributes.rgAttr[1].rgValue[0].cbData))) {
goto ErrorReturn;
}
dwBytesRequired += INFO_LEN_ALIGN(pCurrentSafeBag->Attributes.rgAttr[1].rgValue[0].cbData);
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_UNICODE_ANY_STRING,
(void *)&wideFriendlyName,
pCurrentSafeBag->Attributes.rgAttr[1].rgValue[0].pbData,
&pCurrentSafeBag->Attributes.rgAttr[1].rgValue[0].cbData)) {
goto ErrorReturn;
}
// Provider Name
if (fAddProviderName)
{
// set the OID in the szOID_PKCS_12_KEY_PROVIDER_NAME_ATTR attribute
pCurrentSafeBag->Attributes.rgAttr[2].pszObjId =
szOID_PKCS_12_KEY_PROVIDER_NAME_ATTR;
dwBytesRequired += INFO_LEN_ALIGN(strlen(szOID_PKCS_12_KEY_PROVIDER_NAME_ATTR) + 1);
// allocate space for the single value inside the attribute
if (NULL == (pCurrentSafeBag->Attributes.rgAttr[2].rgValue =
(CRYPT_ATTR_BLOB *) PFXHelpAlloc(sizeof(CRYPT_ATTR_BLOB)))) {
goto ErrorReturn;
}
ZeroMemory(pCurrentSafeBag->Attributes.rgAttr[2].rgValue, sizeof(CRYPT_ATTR_BLOB));
dwBytesRequired += sizeof(CRYPT_ATTR_BLOB);
pCurrentSafeBag->Attributes.rgAttr[2].cValue = 1;
// encode the provider name so it can be used on import
//
// if the provider name is NULL or the empty string, then use
// the default provider name for the provider type
//
wideFriendlyName.dwValueType = CERT_RDN_BMP_STRING;
wideFriendlyName.Value.cbData = 0;
if ((pCryptKeyProvInfo->pwszProvName == NULL) ||
(wcscmp(pCryptKeyProvInfo->pwszProvName, L"") == 0))
{
if (!CryptGetDefaultProviderW(
pCryptKeyProvInfo->dwProvType,
NULL,
(pCryptKeyProvInfo->dwFlags & CRYPT_MACHINE_KEYSET) ?
CRYPT_MACHINE_DEFAULT : CRYPT_USER_DEFAULT,
NULL,
&cbProviderName))
{
goto ErrorReturn;
}
if (NULL == (pwszProviderName = (LPWSTR) PFXHelpAlloc(cbProviderName)))
{
goto ErrorReturn;
}
if (!CryptGetDefaultProviderW(
pCryptKeyProvInfo->dwProvType,
NULL,
(pCryptKeyProvInfo->dwFlags & CRYPT_MACHINE_KEYSET) ?
CRYPT_MACHINE_DEFAULT : CRYPT_USER_DEFAULT,
pwszProviderName,
&cbProviderName))
{
goto ErrorReturn;
}
wideFriendlyName.Value.pbData = (BYTE *) pwszProviderName;
}
else
{
wideFriendlyName.Value.pbData = (BYTE *) pCryptKeyProvInfo->pwszProvName;
}
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_UNICODE_ANY_STRING,
(void *)&wideFriendlyName,
NULL,
&pCurrentSafeBag->Attributes.rgAttr[2].rgValue[0].cbData)) {
goto ErrorReturn;
}
if (NULL == (pCurrentSafeBag->Attributes.rgAttr[2].rgValue[0].pbData =
(BYTE *) PFXHelpAlloc(pCurrentSafeBag->Attributes.rgAttr[2].rgValue[0].cbData))) {
goto ErrorReturn;
}
dwBytesRequired += INFO_LEN_ALIGN(pCurrentSafeBag->Attributes.rgAttr[2].rgValue[0].cbData);
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_UNICODE_ANY_STRING,
(void *)&wideFriendlyName,
pCurrentSafeBag->Attributes.rgAttr[2].rgValue[0].pbData,
&pCurrentSafeBag->Attributes.rgAttr[2].rgValue[0].cbData)) {
goto ErrorReturn;
}
}
}
} // if (CryptExportPKCS8Ex())
else {
// check to see if it is a non-exportable key error or no key error
if (GetLastError() == NTE_BAD_KEY ||
GetLastError() == NTE_BAD_KEY_STATE) {
// the user has specified whether this is a fatal error or not
if (dwFlags & REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY) {
*ppCertContext = pCertContext;
pCertContext = NULL;
goto ErrorReturn;
}
}
else if (GetLastError() == NTE_NO_KEY) {
// the user has specified whether this is a fatal error or not
if (dwFlags & REPORT_NO_PRIVATE_KEY) {
*ppCertContext = pCertContext;
pCertContext = NULL;
goto ErrorReturn;
}
}
else {
// it isn't a non-exportable key error or no key error, so it is bad... bad...
goto ErrorReturn;
}
}
} // if (CryptExportPKCS8Ex())
else {
// check to see if it is a non-exportable key error or no key error
if (GetLastError() == NTE_BAD_KEY ||
GetLastError() == NTE_BAD_KEY_STATE) {
// the user has specified whether this is a fatal error or not
if (dwFlags & REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY) {
*ppCertContext = pCertContext;
pCertContext = NULL;
goto ErrorReturn;
}
}
else if (GetLastError() == NTE_NO_KEY) {
// the user has specified whether this is a fatal error or not
if (dwFlags & REPORT_NO_PRIVATE_KEY) {
*ppCertContext = pCertContext;
pCertContext = NULL;
goto ErrorReturn;
}
}
else {
// it was not a non-exportable error,so go directly to ErrorReturn
goto ErrorReturn;
}
}
} // if (CertGetCertificateContextProperty())
else {
// if CertGetCertificateContextProperty failed then there is no corresponding
// private key, the user has indicated via dwFlags whether this is fatal or not,
// if it is fatal then return an error, otherwise just loop and get the next cert
if (dwFlags & REPORT_NO_PRIVATE_KEY) {
*ppCertContext = pCertContext;
pCertContext = NULL;
goto ErrorReturn;
}
}
} // if (CertGetCertificateContextProperty())
else {
// if CertGetCertificateContextProperty failed then there is no corresponding
// private key, the user has indicated via dwFlags whether this is fatal or not,
// if it is fatal then return an error, otherwise just continue and export the cert
if (dwFlags & REPORT_NO_PRIVATE_KEY) {
*ppCertContext = pCertContext;
pCertContext = NULL;
goto ErrorReturn;
}
}
// now export the current cert!!
// extend the length of the SAFE_BAGs array by one
if (NULL == (pTempMemBlock = PFXHelpRealloc(
localSafeContents.pSafeBags,
sizeof(SAFE_BAG) * ++localSafeContents.cSafeBags))) {
goto ErrorReturn;
}
localSafeContents.pSafeBags = (SAFE_BAG *) pTempMemBlock;
pCurrentSafeBag = &localSafeContents.pSafeBags[localSafeContents.cSafeBags - 1];
ZeroMemory(pCurrentSafeBag, sizeof(SAFE_BAG));
dwBytesRequired += sizeof(SAFE_BAG);
// set up the OID information for the bag type
pCurrentSafeBag->pszBagTypeOID = szOID_PKCS_12_CERT_BAG;
dwBytesRequired += INFO_LEN_ALIGN(strlen(szOID_PKCS_12_CERT_BAG) + 1);
// take the encoded cert and turn it into an encoded CertBag and place in the
// BagContents
pCurrentSafeBag->BagContents.cbData = 0;
if (!MakeEncodedCertBag(
pCertContext->pbCertEncoded,
pCertContext->cbCertEncoded,
NULL,
&(pCurrentSafeBag->BagContents.cbData))) {
goto ErrorReturn;
}
if (NULL == (pCurrentSafeBag->BagContents.pbData =
(BYTE *) PFXHelpAlloc(pCurrentSafeBag->BagContents.cbData))) {
goto ErrorReturn;
}
if (!MakeEncodedCertBag(
pCertContext->pbCertEncoded,
pCertContext->cbCertEncoded,
pCurrentSafeBag->BagContents.pbData,
&(pCurrentSafeBag->BagContents.cbData))) {
goto ErrorReturn;
}
dwBytesRequired += INFO_LEN_ALIGN(pCurrentSafeBag->BagContents.cbData);
// check to see how many attributes there will be, the possibilities right now
// are FREINDLY_NAME and LOCAL_KEY_ID
// try to get the friendly name property from the cert context
if (!CertGetCertificateContextProperty(
pCertContext,
CERT_FRIENDLY_NAME_PROP_ID,
NULL,
&cbFriendlyName)) {
// just set this to insure that it is 0 if we don't have a friendly name
cbFriendlyName = 0;
}
// allocate space for the attributes array in the safe bag accordingly
// if EncodedKeyID.pbData != NULL means there is a corresponding private
// key, so the LOCAL_KEY_ID attribute needs to be set
if ((cbFriendlyName != 0) && (EncodedKeyID.pbData != NULL)) {
if (NULL == (pCurrentSafeBag->Attributes.rgAttr =
(CRYPT_ATTRIBUTE *) PFXHelpAlloc(sizeof(CRYPT_ATTRIBUTE) * 2))) {
goto ErrorReturn;
}
ZeroMemory(pCurrentSafeBag->Attributes.rgAttr, sizeof(CRYPT_ATTRIBUTE) * 2);
dwBytesRequired += sizeof(CRYPT_ATTRIBUTE) * 2;
pCurrentSafeBag->Attributes.cAttr = 2;
}
else if ((cbFriendlyName != 0) || (EncodedKeyID.pbData != NULL)) {
if (NULL == (pCurrentSafeBag->Attributes.rgAttr =
(CRYPT_ATTRIBUTE *) PFXHelpAlloc(sizeof(CRYPT_ATTRIBUTE)))) {
goto ErrorReturn;
}
ZeroMemory(pCurrentSafeBag->Attributes.rgAttr, sizeof(CRYPT_ATTRIBUTE));
dwBytesRequired += sizeof(CRYPT_ATTRIBUTE);
pCurrentSafeBag->Attributes.cAttr = 1;
}
else {
pCurrentSafeBag->Attributes.rgAttr = NULL;
pCurrentSafeBag->Attributes.cAttr = 0;
}
// check to see if the cert has a corresponding private key, if so then set
// up the first attribute to point to it.... if there is a private key then
// LOCAL_KEY_ID will always be the 0th element in the attribute array
if (EncodedKeyID.pbData != NULL) {
// set the OID in the single attribute
pCurrentSafeBag->Attributes.rgAttr[0].pszObjId = szOID_PKCS_12_LOCAL_KEY_ID;
dwBytesRequired += INFO_LEN_ALIGN(strlen(szOID_PKCS_12_LOCAL_KEY_ID) + 1);
// allocate space for the single value inside the single attribute
if (NULL == (pCurrentSafeBag->Attributes.rgAttr[0].rgValue =
(CRYPT_ATTR_BLOB *) PFXHelpAlloc(sizeof(CRYPT_ATTR_BLOB)))) {
goto ErrorReturn;
}
ZeroMemory(pCurrentSafeBag->Attributes.rgAttr[0].rgValue, sizeof(CRYPT_ATTR_BLOB));
dwBytesRequired += sizeof(CRYPT_ATTR_BLOB);
pCurrentSafeBag->Attributes.rgAttr[0].cValue = 1;
// copy the encoded keyID that was set up during export of private key
if (NULL == (pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].pbData =
(BYTE *) PFXHelpAlloc(EncodedKeyID.cbData))) {
goto ErrorReturn;
}
pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].cbData = EncodedKeyID.cbData;
dwBytesRequired += INFO_LEN_ALIGN(EncodedKeyID.cbData);
memcpy(
pCurrentSafeBag->Attributes.rgAttr[0].rgValue[0].pbData,
EncodedKeyID.pbData,
EncodedKeyID.cbData);
} // if (EncodedKeyID.pbData != NULL)
// check to see if this cert has a friendly name property, if so,
// get it and put it in an attribute
if (cbFriendlyName != 0) {
if ((pFriendlyName = (BYTE *) PFXHelpAlloc(cbFriendlyName)) != NULL) {
if (CertGetCertificateContextProperty(
pCertContext,
CERT_FRIENDLY_NAME_PROP_ID,
pFriendlyName,
&cbFriendlyName)) {
// set the index of the attribute which will hold the FRIENDLY_NAME,
// if there is a LOCAL_KEY_ID attribute then the index will be 1,
// if there isn't then the index will be 0
if (EncodedKeyID.pbData != NULL) {
dwFriendlyNameAttributeIndex = 1;
}
else {
dwFriendlyNameAttributeIndex = 0;
}
// set the OID in the szOID_PKCS_12_FRIENDLY_NAME_ATTR attribute
pCurrentSafeBag->Attributes.rgAttr[dwFriendlyNameAttributeIndex].pszObjId =
szOID_PKCS_12_FRIENDLY_NAME_ATTR;
dwBytesRequired += INFO_LEN_ALIGN(strlen(szOID_PKCS_12_FRIENDLY_NAME_ATTR) + 1);
// allocate space for the single value inside the attribute
if (NULL == (pCurrentSafeBag->Attributes.rgAttr[dwFriendlyNameAttributeIndex].rgValue =
(CRYPT_ATTR_BLOB *) PFXHelpAlloc(sizeof(CRYPT_ATTR_BLOB)))) {
goto ErrorReturn;
}
ZeroMemory(pCurrentSafeBag->Attributes.rgAttr[dwFriendlyNameAttributeIndex].rgValue, sizeof(CRYPT_ATTR_BLOB));
dwBytesRequired += sizeof(CRYPT_ATTR_BLOB);
pCurrentSafeBag->Attributes.rgAttr[dwFriendlyNameAttributeIndex].cValue = 1;
// encode the friendly name, reuse the containerName variable because its there
wideFriendlyName.dwValueType = CERT_RDN_BMP_STRING;
wideFriendlyName.Value.pbData = pFriendlyName;
wideFriendlyName.Value.cbData = cbFriendlyName;
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_UNICODE_ANY_STRING,
(void *)&wideFriendlyName,
NULL,
&pCurrentSafeBag->Attributes.rgAttr[dwFriendlyNameAttributeIndex].rgValue[0].cbData)) {
goto ErrorReturn;
}
if (NULL == (pCurrentSafeBag->Attributes.rgAttr[dwFriendlyNameAttributeIndex].rgValue[0].pbData =
(BYTE *) PFXHelpAlloc(pCurrentSafeBag->Attributes.rgAttr[dwFriendlyNameAttributeIndex].rgValue[0].cbData))) {
goto ErrorReturn;
}
dwBytesRequired += INFO_LEN_ALIGN(pCurrentSafeBag->Attributes.rgAttr[dwFriendlyNameAttributeIndex].rgValue[0].cbData);
if (!CryptEncodeObject(
X509_ASN_ENCODING,
X509_UNICODE_ANY_STRING,
(void *)&wideFriendlyName,
pCurrentSafeBag->Attributes.rgAttr[dwFriendlyNameAttributeIndex].rgValue[0].pbData,
&pCurrentSafeBag->Attributes.rgAttr[dwFriendlyNameAttributeIndex].rgValue[0].cbData)) {
goto ErrorReturn;
}
} // if (CertGetCertificateContextProperty(CERT_FRIENDLY_NAME_PROP_ID))
} // if (PFXHelpAlloc())
} // if (CertGetCertificateContextProperty(CERT_FRIENDLY_NAME_PROP_ID))
} // while (NULL != (pCertContext = CertEnumCertificatesInStore(hCertStore, pCertContext)))
// check to see if the caller passed in a buffer with encough enough space
if (0 == *pcbSafeContents) {
*pcbSafeContents = dwBytesRequired;
goto CommonReturn;
}
else if (*pcbSafeContents < dwBytesRequired) {
*pcbSafeContents = dwBytesRequired;
SetLastError((DWORD) ERROR_MORE_DATA);
goto ErrorReturn;
}
// copy the contents into the callers buffer
// initialize the SAFE_CONTENTS structure that is at the head of the buffer
ZeroMemory(pSafeContents, dwBytesRequired);
pCurrentBufferLocation = ((BYTE *) pSafeContents) + sizeof(SAFE_CONTENTS);
// initialize the callers SAFE_CONTENTS
pSafeContents->cSafeBags = localSafeContents.cSafeBags;
if (0 == localSafeContents.cSafeBags) {
pSafeContents->pSafeBags = NULL;
}
else {
pSafeContents->pSafeBags = (SAFE_BAG *) pCurrentBufferLocation;
}
pCurrentBufferLocation += localSafeContents.cSafeBags * sizeof(SAFE_BAG);
// copy each safe bag in the array
for (i=0; i<localSafeContents.cSafeBags; i++) {
// copy the bag type
pSafeContents->pSafeBags[i].pszBagTypeOID = (LPSTR) pCurrentBufferLocation;
strcpy(pSafeContents->pSafeBags[i].pszBagTypeOID, localSafeContents.pSafeBags[i].pszBagTypeOID);
pCurrentBufferLocation += INFO_LEN_ALIGN(strlen(pSafeContents->pSafeBags[i].pszBagTypeOID) + 1);
// copy the bag contents
pSafeContents->pSafeBags[i].BagContents.cbData = localSafeContents.pSafeBags[i].BagContents.cbData;
pSafeContents->pSafeBags[i].BagContents.pbData = pCurrentBufferLocation;
memcpy(
pSafeContents->pSafeBags[i].BagContents.pbData,
localSafeContents.pSafeBags[i].BagContents.pbData,
pSafeContents->pSafeBags[i].BagContents.cbData);
pCurrentBufferLocation += INFO_LEN_ALIGN(pSafeContents->pSafeBags[i].BagContents.cbData);
// copy the attributes
if (localSafeContents.pSafeBags[i].Attributes.cAttr > 0)
{
pSafeContents->pSafeBags[i].Attributes.cAttr = localSafeContents.pSafeBags[i].Attributes.cAttr;
pSafeContents->pSafeBags[i].Attributes.rgAttr = (PCRYPT_ATTRIBUTE) pCurrentBufferLocation;
pCurrentBufferLocation += pSafeContents->pSafeBags[i].Attributes.cAttr * sizeof(CRYPT_ATTRIBUTE);
for (j=0; j<pSafeContents->pSafeBags[i].Attributes.cAttr; j++) {
// copy the OID of the attribute
pSafeContents->pSafeBags[i].Attributes.rgAttr[j].pszObjId =
(LPSTR) pCurrentBufferLocation;
strcpy(
pSafeContents->pSafeBags[i].Attributes.rgAttr[j].pszObjId,
localSafeContents.pSafeBags[i].Attributes.rgAttr[j].pszObjId);
pCurrentBufferLocation +=
INFO_LEN_ALIGN(strlen(pSafeContents->pSafeBags[i].Attributes.rgAttr[j].pszObjId) + 1);
// copy value count
pSafeContents->pSafeBags[i].Attributes.rgAttr[j].cValue =
localSafeContents.pSafeBags[i].Attributes.rgAttr[j].cValue;
// copy the values
if (pSafeContents->pSafeBags[i].Attributes.rgAttr[j].cValue > 0) {
// setup the array of values
pSafeContents->pSafeBags[i].Attributes.rgAttr[j].rgValue =
(PCRYPT_ATTR_BLOB) pCurrentBufferLocation;
pCurrentBufferLocation +=
pSafeContents->pSafeBags[i].Attributes.rgAttr[j].cValue * sizeof(CRYPT_ATTR_BLOB);
// loop once for each value in the array
for (k=0; k<pSafeContents->pSafeBags[i].Attributes.rgAttr[j].cValue; k++) {
pSafeContents->pSafeBags[i].Attributes.rgAttr[j].rgValue[k].cbData =
localSafeContents.pSafeBags[i].Attributes.rgAttr[j].rgValue[k].cbData;
pSafeContents->pSafeBags[i].Attributes.rgAttr[j].rgValue[k].pbData =
pCurrentBufferLocation;
memcpy(
pSafeContents->pSafeBags[i].Attributes.rgAttr[j].rgValue[k].pbData,
localSafeContents.pSafeBags[i].Attributes.rgAttr[j].rgValue[k].pbData,
pSafeContents->pSafeBags[i].Attributes.rgAttr[j].rgValue[k].cbData);
pCurrentBufferLocation +=
INFO_LEN_ALIGN(pSafeContents->pSafeBags[i].Attributes.rgAttr[j].rgValue[k].cbData);
}
}
else {
pSafeContents->pSafeBags[i].Attributes.rgAttr[j].rgValue = NULL;
}
}
}
else {
pSafeContents->pSafeBags[i].Attributes.cAttr = 0;
pSafeContents->pSafeBags[i].Attributes.rgAttr = NULL;
}
}
goto CommonReturn;
ErrorReturn:
fResult = FALSE;
CommonReturn:
FreeSafeContents(&localSafeContents);
if (pCertContext)
CertFreeCertificateContext(pCertContext);
if (pCryptKeyProvInfo)
PFXHelpFree(pCryptKeyProvInfo);
if (pPrivateKey)
PFXHelpFree(pPrivateKey);
if (pFriendlyName)
PFXHelpFree(pFriendlyName);
if (pwszProviderName)
PFXHelpFree(pwszProviderName);
if (hCryptProv)
{
HRESULT hr = GetLastError();
CryptReleaseContext(hCryptProv, 0);
SetLastError(hr);
}
return fResult;
}
static DWORD ResolveKeySpec(
CRYPT_PRIVATE_KEY_INFO *pPrivateKeyInfo)
{
DWORD i = 0;
DWORD dwKeySpec;
DWORD cbAttribute = 0;
CRYPT_BIT_BLOB *pAttribute = NULL;
PCRYPT_ATTRIBUTES pCryptAttributes = pPrivateKeyInfo->pAttributes;
// set the default keyspec
if ((0 == strcmp(pPrivateKeyInfo->Algorithm.pszObjId, szOID_RSA_RSA)) ||
(0 == strcmp(pPrivateKeyInfo->Algorithm.pszObjId, szOID_ANSI_X942_DH)))
{
dwKeySpec = AT_KEYEXCHANGE;
}
else
{
dwKeySpec = AT_SIGNATURE;
}
if (pCryptAttributes != NULL)
while (i < pCryptAttributes->cAttr) {
if (lstrcmp(pCryptAttributes->rgAttr[i].pszObjId, szOID_KEY_USAGE) == 0) {
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_BITS,
pCryptAttributes->rgAttr[i].rgValue->pbData,
pCryptAttributes->rgAttr[i].rgValue->cbData,
0,
NULL,
&cbAttribute
)) {
i++;
continue;
}
if (NULL == (pAttribute = (CRYPT_BIT_BLOB *) PFXHelpAlloc(cbAttribute))) {
i++;
continue;
}
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_BITS,
pCryptAttributes->rgAttr[i].rgValue->pbData,
pCryptAttributes->rgAttr[i].rgValue->cbData,
0,
pAttribute,
&cbAttribute
)) {
i++;
PFXHelpFree(pAttribute);
continue;
}
if ((pAttribute->pbData[0] & CERT_KEY_ENCIPHERMENT_KEY_USAGE) ||
(pAttribute->pbData[0] & CERT_DATA_ENCIPHERMENT_KEY_USAGE)) {
dwKeySpec = AT_KEYEXCHANGE;
goto CommonReturn;
}
else if ((pAttribute->pbData[0] & CERT_DIGITAL_SIGNATURE_KEY_USAGE) ||
(pAttribute->pbData[0] & CERT_KEY_CERT_SIGN_KEY_USAGE) ||
(pAttribute->pbData[0] & CERT_CRL_SIGN_KEY_USAGE)) {
dwKeySpec = AT_SIGNATURE;
goto CommonReturn;
}
} // if (lstrcmp(pCryptAttributes->rgAttr[i].pszObjId, szOID_KEY_USAGE) == 0)
i++;
} // while (i < pCryptAttributes->cAttr)
//ErrorReturn:
CommonReturn:
if (pAttribute)
PFXHelpFree(pAttribute);
return dwKeySpec;
}
typedef struct _HCRYPT_QUERY_FUNC_STATE {
DWORD dwSafeBagIndex;
PHCRYPTPROV_QUERY_FUNC phCryptQueryFunc;
LPVOID pVoid;
DWORD dwKeySpec;
DWORD dwPFXImportFlags;
} HCRYPT_QUERY_FUNC_STATE, *PHCRYPT_QUERY_FUNC_STATE;
// this is the callback handler for resolving what HCRYPTPROV should
// be used to import the key to, it is handed in to the ImportPKCS8
// call, and will be called from that context.
// this callback will just turn around and call the callback provided
// when CertImportSafeContents was called.
static BOOL CALLBACK ResolvehCryptFunc(
CRYPT_PRIVATE_KEY_INFO *pPrivateKeyInfo,
HCRYPTPROV *phCryptProv,
LPVOID pVoidResolveFunc)
{
HCRYPT_QUERY_FUNC_STATE *pState = (HCRYPT_QUERY_FUNC_STATE *) pVoidResolveFunc;
// set the dwKeySpec field in the HCRYPT_QUERY_FUNC_STATE structure
// so that the CertImportSafeContents function can use it
pState->dwKeySpec = ResolveKeySpec(pPrivateKeyInfo);
return (pState->phCryptQueryFunc(
pPrivateKeyInfo,
pState->dwSafeBagIndex,
phCryptProv,
pState->pVoid,
pState->dwPFXImportFlags));
}
// this function will seach through two arrays of attributes and find the KeyID
// attributes and see if they match
static BOOL WINAPI KeyIDsMatch(
CRYPT_ATTRIBUTES *pAttr1,
CRYPT_ATTRIBUTES *pAttr2
)
{
BOOL bMatch = FALSE;
BOOL bFound = FALSE;
DWORD i = 0;
DWORD j = 0;
CRYPT_ATTR_BLOB *pDecodedAttr1 = NULL;
DWORD cbDecodedAttr1 = 0;
CRYPT_ATTR_BLOB *pDecodedAttr2 = NULL;
DWORD cbDecodedAttr2 = 0;
// search the first attribute array for a key id
while ((i<pAttr1->cAttr) && (!bFound)) {
if ((strcmp(pAttr1->rgAttr[i].pszObjId, szOID_PKCS_12_LOCAL_KEY_ID) == 0) &&
(pAttr1->rgAttr[i].cValue != 0)){
bFound = TRUE;
}
else {
i++;
}
}
// check to see if a key id was found
if (!bFound)
goto CommonReturn;
// search the second attribute array for a key id
bFound = FALSE;
while ((j<pAttr2->cAttr) && (!bFound)) {
if ((strcmp(pAttr2->rgAttr[j].pszObjId, szOID_PKCS_12_LOCAL_KEY_ID) == 0) &&
(pAttr2->rgAttr[j].cValue != 0)) {
bFound = TRUE;
}
else {
j++;
}
}
// check to see if a key id was found
if (!bFound)
goto CommonReturn;
// decode the values
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_OCTET_STRING,
pAttr1->rgAttr[i].rgValue[0].pbData,
pAttr1->rgAttr[i].rgValue[0].cbData,
0,
NULL,
&cbDecodedAttr1
)) {
goto ErrorReturn;
}
if (NULL == (pDecodedAttr1 = (CRYPT_ATTR_BLOB *) PFXHelpAlloc(cbDecodedAttr1))) {
goto ErrorReturn;
}
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_OCTET_STRING,
pAttr1->rgAttr[i].rgValue[0].pbData,
pAttr1->rgAttr[i].rgValue[0].cbData,
0,
pDecodedAttr1,
&cbDecodedAttr1
)) {
goto ErrorReturn;
}
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_OCTET_STRING,
pAttr2->rgAttr[j].rgValue[0].pbData,
pAttr2->rgAttr[j].rgValue[0].cbData,
0,
NULL,
&cbDecodedAttr2
)) {
goto ErrorReturn;
}
if (NULL == (pDecodedAttr2 = (CRYPT_ATTR_BLOB *) PFXHelpAlloc(cbDecodedAttr1))) {
goto ErrorReturn;
}
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_OCTET_STRING,
pAttr2->rgAttr[j].rgValue[0].pbData,
pAttr2->rgAttr[j].rgValue[0].cbData,
0,
pDecodedAttr2,
&cbDecodedAttr2
)) {
goto ErrorReturn;
}
if ((pDecodedAttr1->cbData == pDecodedAttr2->cbData) &&
(memcmp(pDecodedAttr1->pbData, pDecodedAttr2->pbData, pDecodedAttr1->cbData) == 0)) {
bMatch = TRUE;
}
goto CommonReturn;
ErrorReturn:
bMatch = FALSE;
CommonReturn:
if (pDecodedAttr1)
PFXHelpFree(pDecodedAttr1);
if (pDecodedAttr2)
PFXHelpFree(pDecodedAttr2);
return bMatch;
}
// this function will search the attributes array and try to find a
// FRIENDLY_NAME attribute, if it does it will add it as a property
// to the given cert context
static
BOOL
WINAPI
AddFriendlyNameProperty(
PCCERT_CONTEXT pCertContext,
CRYPT_ATTRIBUTES *pAttr
)
{
BOOL fReturn = TRUE;
BOOL bFound = FALSE;
DWORD i = 0;
CERT_NAME_VALUE *pFriendlyName = NULL;
DWORD cbDecodedFriendlyName = 0;
CRYPT_DATA_BLOB friendlyNameDataBlob;
// search the attribute array for a FRIENDLY_NAME
while ((i<pAttr->cAttr) && (!bFound)) {
if ((strcmp(pAttr->rgAttr[i].pszObjId, szOID_PKCS_12_FRIENDLY_NAME_ATTR) == 0) &&
(pAttr->rgAttr[i].cValue != 0)){
bFound = TRUE;
// try to decode the FRIENDLY_NAME
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_UNICODE_ANY_STRING,
pAttr->rgAttr[i].rgValue[0].pbData,
pAttr->rgAttr[i].rgValue[0].cbData,
0,
NULL,
&cbDecodedFriendlyName
)) {
goto ErrorReturn;
}
if (NULL == (pFriendlyName = (CERT_NAME_VALUE *) PFXHelpAlloc(cbDecodedFriendlyName))) {
goto ErrorReturn;
}
if (!CryptDecodeObject(
X509_ASN_ENCODING,
X509_UNICODE_ANY_STRING,
pAttr->rgAttr[i].rgValue[0].pbData,
pAttr->rgAttr[i].rgValue[0].cbData,
0,
pFriendlyName,
&cbDecodedFriendlyName
)) {
goto ErrorReturn;
}
friendlyNameDataBlob.pbData = pFriendlyName->Value.pbData;
friendlyNameDataBlob.cbData =
(wcslen((LPWSTR)friendlyNameDataBlob.pbData) + 1) * sizeof(WCHAR);
if (!CertSetCertificateContextProperty(
pCertContext,
CERT_FRIENDLY_NAME_PROP_ID,
0,
&friendlyNameDataBlob)) {
goto ErrorReturn;
}
}
else {
i++;
}
}
goto CommonReturn;
ErrorReturn:
fReturn = FALSE;
CommonReturn:
if (pFriendlyName)
PFXHelpFree(pFriendlyName);
return fReturn;
}
static BOOL GetProvType(HCRYPTPROV hCryptProv, DWORD *pdwProvType)
{
BOOL fRet = TRUE;
HCRYPTKEY hCryptKey = NULL;
PUBLICKEYSTRUC *pKeyBlob = NULL;
DWORD cbKeyBlob = 0;
*pdwProvType = 0;
// get a handle to the keyset to export
if (!CryptGetUserKey(
hCryptProv,
AT_KEYEXCHANGE,
&hCryptKey))
if (!CryptGetUserKey(
hCryptProv,
AT_SIGNATURE,
&hCryptKey))
goto ErrorReturn;
// export the key set to a CAPI blob
if (!CryptExportKey(
hCryptKey,
0,
PUBLICKEYBLOB,
0,
NULL,
&cbKeyBlob))
goto ErrorReturn;
if (NULL == (pKeyBlob = (PUBLICKEYSTRUC *) SSAlloc(cbKeyBlob)))
goto ErrorReturn;
if (!CryptExportKey(
hCryptKey,
0,
PUBLICKEYBLOB,
0,
(BYTE *)pKeyBlob,
&cbKeyBlob))
goto ErrorReturn;
switch (pKeyBlob->aiKeyAlg)
{
case CALG_DSS_SIGN:
*pdwProvType = PROV_DSS_DH;
break;
case CALG_RSA_SIGN:
*pdwProvType = PROV_RSA_SIG;
break;
case CALG_RSA_KEYX:
*pdwProvType = PROV_RSA_FULL;
break;
default:
goto ErrorReturn;
}
goto CommonReturn;
ErrorReturn:
fRet = FALSE;
CommonReturn:
if (hCryptKey)
{
DWORD dwErr = GetLastError();
CryptDestroyKey(hCryptKey);
SetLastError(dwErr);
}
if (pKeyBlob)
SSFree(pKeyBlob);
return (fRet);
}
//+-------------------------------------------------------------------------
// hCertStore - handle of the cert store to import the safe contents to
// SafeContents - pointer to the safe contents to import to the store
// dwCertAddDisposition - used when importing certificate to the store.
// for a full explanation of the possible values
// and their meanings see documentation for
// CertAddEncodedCertificateToStore
// ImportSafeCallbackStruct - structure that contains pointers to functions
// which are callled to get a HCRYPTPROV for import
// and to decrypt the key if a EncryptPrivateKeyInfo
// is encountered during import
// dwFlags - The available flags are:
// CRYPT_EXPORTABLE
// this flag is used when importing private keys, for a full
// explanation please see the documentation for CryptImportKey.
// CRYPT_USER_PROTECTED
// this flag is used when importing private keys, for a full
// explanation please see the documentation for CryptImportKey.
// CRYPT_MACHINE_KEYSET
// this flag is used when calling CryptAcquireContext.
// pvAuxInfo - reserved for future use, must be set to NULL
//+-------------------------------------------------------------------------
BOOL WINAPI CertImportSafeContents(
HCERTSTORE hCertStore, // in
SAFE_CONTENTS *pSafeContents, // in
DWORD dwCertAddDisposition, // in
IMPORT_SAFE_CALLBACK_STRUCT *ImportSafeCallbackStruct, // in
DWORD dwFlags, // in
void *pvAuxInfo // in
)
{
BOOL fResult = TRUE;
DWORD i,j;
PCCERT_CONTEXT pCertContext = NULL;
BOOL *pAlreadyInserted = NULL;
HCRYPT_QUERY_FUNC_STATE stateStruct;
CRYPT_PKCS8_IMPORT_PARAMS PrivateKeyBlobAndParams;
HCRYPTPROV hCryptProv = NULL;
CRYPT_KEY_PROV_INFO cryptKeyProvInfo;
LPSTR pszContainerName = NULL;
DWORD cbContainerName = 0;
LPSTR pszProviderName = NULL;
DWORD cbProviderName = 0;
DWORD dwProvType;
DWORD cbProvType = sizeof(DWORD);
DWORD dwNumWideChars = 0;
BYTE *pbEncodedCert = NULL;
DWORD cbEncodedCert = 0;
DWORD dwKeySetType;
DWORD cbKeySetType = sizeof(DWORD);
ZeroMemory(&cryptKeyProvInfo, sizeof(CRYPT_KEY_PROV_INFO));
// validate parameters
if (pvAuxInfo != NULL) {
SetLastError((DWORD)ERROR_INVALID_PARAMETER);
goto ErrorReturn;
}
// set up the pAlreadyInserted array so that it has an entry for each safe
// bag and all entries are set to false. this is used so that the certificates
// can be imported at the same time their corresponding private keys are imported
if (NULL == (pAlreadyInserted = (BOOL *) PFXHelpAlloc(sizeof(BOOL) * pSafeContents->cSafeBags))) {
goto ErrorReturn;
}
else {
for (i=0; i<pSafeContents->cSafeBags; i++) {
pAlreadyInserted[i] = FALSE;
}
}
// loop for each safe bag and import it if it is a private key
for (i=0; i<pSafeContents->cSafeBags; i++) {
// check to see if it is a cert or a key
if ((strcmp(pSafeContents->pSafeBags[i].pszBagTypeOID, szOID_PKCS_12_KEY_BAG) == 0) ||
(strcmp(pSafeContents->pSafeBags[i].pszBagTypeOID, szOID_PKCS_12_SHROUDEDKEY_BAG) == 0)) {
// set up the stateStruct so when the hCryptQueryFunc is called when can make
// our callback
stateStruct.dwSafeBagIndex = i;
stateStruct.phCryptQueryFunc = ImportSafeCallbackStruct->phCryptProvQueryFunc;
stateStruct.pVoid = ImportSafeCallbackStruct->pVoidhCryptProvQuery;
stateStruct.dwPFXImportFlags = dwFlags;
// import the private key
PrivateKeyBlobAndParams.PrivateKey.pbData = pSafeContents->pSafeBags[i].BagContents.pbData;
PrivateKeyBlobAndParams.PrivateKey.cbData = pSafeContents->pSafeBags[i].BagContents.cbData;
PrivateKeyBlobAndParams.pResolvehCryptProvFunc = ResolvehCryptFunc;
PrivateKeyBlobAndParams.pVoidResolveFunc = (LPVOID) &stateStruct;
PrivateKeyBlobAndParams.pDecryptPrivateKeyFunc = ImportSafeCallbackStruct->pDecryptPrivateKeyFunc;
PrivateKeyBlobAndParams.pVoidDecryptFunc = ImportSafeCallbackStruct->pVoidDecryptFunc;
if (!CryptImportPKCS8(
PrivateKeyBlobAndParams,
dwFlags,
&hCryptProv,
NULL)) {
goto ErrorReturn;
}
pAlreadyInserted[i] = TRUE;
// now look at each safe bag and see if it contains a cert with a KeyID that
// matches the private key that we just imported
for (j=0; j<pSafeContents->cSafeBags; j++) {
if ((strcmp(pSafeContents->pSafeBags[j].pszBagTypeOID, szOID_PKCS_12_CERT_BAG) == 0) &&
(!pAlreadyInserted[j]) &&
(KeyIDsMatch(&pSafeContents->pSafeBags[i].Attributes, &pSafeContents->pSafeBags[j].Attributes))){
// extract the encoded cert from an encoded cert bag
pbEncodedCert = NULL;
cbEncodedCert = 0;
if (!GetEncodedCertFromEncodedCertBag(
pSafeContents->pSafeBags[j].BagContents.pbData,
pSafeContents->pSafeBags[j].BagContents.cbData,
NULL,
&cbEncodedCert)) {
goto ErrorReturn;
}
if (NULL == (pbEncodedCert = (BYTE *) PFXHelpAlloc(cbEncodedCert))) {
goto ErrorReturn;
}
if (!GetEncodedCertFromEncodedCertBag(
pSafeContents->pSafeBags[j].BagContents.pbData,
pSafeContents->pSafeBags[j].BagContents.cbData,
pbEncodedCert,
&cbEncodedCert)) {
PFXHelpFree(pbEncodedCert);
goto ErrorReturn;
}
// insert the X509 cert blob into the store
if (!CertAddEncodedCertificateToStore(
hCertStore,
X509_ASN_ENCODING,
pbEncodedCert,
cbEncodedCert,
dwCertAddDisposition,
&pCertContext)) {
PFXHelpFree(pbEncodedCert);
goto ErrorReturn;
}
// we don't need this anymore
PFXHelpFree(pbEncodedCert);
if (!AddFriendlyNameProperty(
pCertContext,
&pSafeContents->pSafeBags[j].Attributes)) {
goto ErrorReturn;
}
// get information needed to set up a connection between the
// certificate and private key
if (!CryptGetProvParam(
hCryptProv,
PP_CONTAINER,
NULL,
&cbContainerName,
0))
goto ErrorReturn;
if (NULL == (pszContainerName =
(LPSTR) PFXHelpAlloc(cbContainerName)))
goto ErrorReturn;
if (!CryptGetProvParam(
hCryptProv,
PP_CONTAINER,
(BYTE *) pszContainerName,
&cbContainerName,
0))
goto ErrorReturn;
if (!CryptGetProvParam(
hCryptProv,
PP_NAME,
NULL,
&cbProviderName,
0))
goto ErrorReturn;
if (NULL == (pszProviderName =
(LPSTR) PFXHelpAlloc(cbProviderName)))
goto ErrorReturn;
if (!CryptGetProvParam(
hCryptProv,
PP_NAME,
(BYTE *) pszProviderName,
&cbProviderName,
0))
goto ErrorReturn;
if (!CryptGetProvParam(
hCryptProv,
PP_PROVTYPE,
(BYTE *) &dwProvType,
&cbProvType,
0)) {
// we couldn't get the information from the provider
// so try to figure it out ourselves
if (!GetProvType(hCryptProv, &dwProvType))
{
goto ErrorReturn;
}
}
// convert strings to wide chars
dwNumWideChars = MultiByteToWideChar(
CP_ACP,
0,
pszContainerName,
-1,
NULL,
0);
if (NULL == (cryptKeyProvInfo.pwszContainerName = (LPWSTR)
PFXHelpAlloc(dwNumWideChars * sizeof(WCHAR)))) {
goto ErrorReturn;
}
if (!MultiByteToWideChar(
CP_ACP,
0,
pszContainerName,
-1,
cryptKeyProvInfo.pwszContainerName,
dwNumWideChars)) {
goto ErrorReturn;
}
dwNumWideChars = MultiByteToWideChar(
CP_ACP,
0,
pszProviderName,
-1,
NULL,
0);
if (NULL == (cryptKeyProvInfo.pwszProvName = (LPWSTR)
PFXHelpAlloc(dwNumWideChars * sizeof(WCHAR)))) {
goto ErrorReturn;
}
if (!MultiByteToWideChar(
CP_ACP,
0,
pszProviderName,
-1,
cryptKeyProvInfo.pwszProvName,
dwNumWideChars)) {
goto ErrorReturn;
}
cryptKeyProvInfo.dwProvType = dwProvType;
if (CryptGetProvParam(
hCryptProv,
PP_KEYSET_TYPE,
(BYTE *) &dwKeySetType,
&cbKeySetType,
0)) {
if (CRYPT_MACHINE_KEYSET == dwKeySetType)
{
cryptKeyProvInfo.dwFlags = CRYPT_MACHINE_KEYSET;
}
}
// the dwKeySpec field was set by the callback generated from the
// CryptImportPKCS8 call. the callback is currently used to because at
// the point the callback is made the private key has been decoded and
// the attributes are available, one of which is the key usage attribute.
// FIX - in the future we should be able to call CryptGetProvParam to get
// the dwKeySpec, right now that is not supported.
cryptKeyProvInfo.dwKeySpec = stateStruct.dwKeySpec;
// set up a property to point to the private key
if (!CertSetCertificateContextProperty(
pCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
0,
(void *) &cryptKeyProvInfo)) {
CertFreeCertificateContext(pCertContext);
goto ErrorReturn;
}
CertFreeCertificateContext(pCertContext);
pAlreadyInserted[j] = TRUE;
}
} // for (j=0; j<pSafeContents->cSafeBags; j++)
} // if (strcmp(pSafeContents->pSafeBags[i].pszBagTypeOID, szOID_PKCS_12_KEY_BAG) == 0)
} // for (i=0; i<pSafeContents->cSafeBags; i++)
// now loop for each safe bag again and import the certificates which didn't have private keys
for (i=0; i<pSafeContents->cSafeBags; i++) {
// if the certificate has not been inserted, then do it
if (!pAlreadyInserted[i]) {
// extract the encoded cert from an encoded cert bag
pbEncodedCert = NULL;
cbEncodedCert = 0;
if (!GetEncodedCertFromEncodedCertBag(
pSafeContents->pSafeBags[i].BagContents.pbData,
pSafeContents->pSafeBags[i].BagContents.cbData,
NULL,
&cbEncodedCert)) {
goto ErrorReturn;
}
if (NULL == (pbEncodedCert = (BYTE *) PFXHelpAlloc(cbEncodedCert))) {
goto ErrorReturn;
}
if (!GetEncodedCertFromEncodedCertBag(
pSafeContents->pSafeBags[i].BagContents.pbData,
pSafeContents->pSafeBags[i].BagContents.cbData,
pbEncodedCert,
&cbEncodedCert)) {
PFXHelpFree(pbEncodedCert);
goto ErrorReturn;
}
if (!CertAddEncodedCertificateToStore(
hCertStore,
X509_ASN_ENCODING,
pbEncodedCert,
cbEncodedCert,
dwCertAddDisposition,
&pCertContext)) {
PFXHelpFree(pbEncodedCert);
goto ErrorReturn;
}
// we don't need this anymore
PFXHelpFree(pbEncodedCert);
if (!AddFriendlyNameProperty(
pCertContext,
&pSafeContents->pSafeBags[i].Attributes)) {
goto ErrorReturn;
}
CertFreeCertificateContext(pCertContext);
}
}
goto CommonReturn;
ErrorReturn:
fResult = FALSE;
CommonReturn:
if (pAlreadyInserted)
PFXHelpFree(pAlreadyInserted);
if (pszContainerName)
PFXHelpFree(pszContainerName);
if (pszProviderName)
PFXHelpFree(pszProviderName);
if (cryptKeyProvInfo.pwszContainerName)
PFXHelpFree(cryptKeyProvInfo.pwszContainerName);
if (cryptKeyProvInfo.pwszProvName)
PFXHelpFree(cryptKeyProvInfo.pwszProvName);
if (hCryptProv)
{
HRESULT hr = GetLastError();
CryptReleaseContext(hCryptProv, 0);
SetLastError(hr);
}
return fResult;
}