1827 lines
61 KiB
C++
1827 lines
61 KiB
C++
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|