4610 lines
100 KiB
C++
4610 lines
100 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1995 - 1999
|
|
//
|
|
// File: store.cpp
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include <pch.cpp>
|
|
|
|
#pragma hdrstop
|
|
|
|
#include <setupapi.h>
|
|
#include "ocmanage.h"
|
|
#include "initcert.h"
|
|
#include "cscsp.h"
|
|
#include "csber.h"
|
|
|
|
|
|
DWORD
|
|
cuGetSystemStoreFlags()
|
|
{
|
|
return(g_fEnterpriseRegistry?
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE :
|
|
(g_fUserRegistry?
|
|
CERT_SYSTEM_STORE_CURRENT_USER :
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE));
|
|
}
|
|
|
|
|
|
// Parse a CertIndex -- any one of the following:
|
|
//
|
|
// Return the following in *piCert, *piCRL and *piCTL. MAXDWORD if not
|
|
// specified
|
|
// Each value must be less than 64k.
|
|
// iCert decimal number
|
|
// iCert.iCRL decimal number, period, decimal number
|
|
// .iCRL period, decimal number
|
|
// ..iCTL period, period, decimal number
|
|
//
|
|
// Return the string in *ppwszCertName, if no Cert, CRL and CTL indexes.
|
|
|
|
HRESULT
|
|
ParseCertCRLIndex(
|
|
IN WCHAR const *pwszCertIndex,
|
|
OUT WCHAR **ppwszCertName,
|
|
OUT DWORD *piCert,
|
|
OUT DWORD *piCRL,
|
|
OUT DWORD *piCTL)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszCopy = NULL;
|
|
|
|
*ppwszCertName = NULL;
|
|
*piCert = MAXDWORD;
|
|
*piCRL = MAXDWORD;
|
|
*piCTL = MAXDWORD;
|
|
if (NULL != pwszCertIndex && 0 != lstrcmp(L"*", pwszCertIndex))
|
|
{
|
|
BOOL fNumericIndex = TRUE;
|
|
WCHAR *pwszCert;
|
|
WCHAR *pwszCRL;
|
|
WCHAR *pwszCTL;
|
|
|
|
if (L' ' == *pwszCertIndex)
|
|
{
|
|
fNumericIndex = FALSE;
|
|
pwszCertIndex++;
|
|
}
|
|
hr = myDupString(pwszCertIndex, &pwszCopy);
|
|
_JumpIfError(hr, error, "myDupString");
|
|
|
|
pwszCert = pwszCopy;
|
|
|
|
if (!iswdigit(*pwszCert) && L'.' != *pwszCert)
|
|
{
|
|
fNumericIndex = FALSE;
|
|
}
|
|
|
|
pwszCRL = NULL;
|
|
pwszCTL = NULL;
|
|
if (fNumericIndex)
|
|
{
|
|
pwszCRL = wcschr(pwszCert, L'.');
|
|
if (NULL != pwszCRL)
|
|
{
|
|
*pwszCRL++ = L'\0';
|
|
pwszCTL = wcschr(pwszCRL, L'.');
|
|
if (NULL != pwszCTL)
|
|
{
|
|
*pwszCTL++ = L'\0';
|
|
if (L'\0' != *pwszCTL)
|
|
{
|
|
hr = cuGetLong(pwszCTL, (LONG *) piCTL);
|
|
if (S_OK != hr || 64*1024 <= *piCTL)
|
|
{
|
|
fNumericIndex = FALSE;
|
|
}
|
|
}
|
|
}
|
|
if (fNumericIndex && L'\0' != *pwszCRL)
|
|
{
|
|
hr = cuGetLong(pwszCRL, (LONG *) piCRL);
|
|
if (S_OK != hr || 64*1024 <= *piCRL)
|
|
{
|
|
fNumericIndex = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (fNumericIndex && L'\0' != *pwszCert)
|
|
{
|
|
hr = cuGetLong(pwszCert, (LONG *) piCert);
|
|
if (S_OK != hr || 64*1024 <= *piCert)
|
|
{
|
|
fNumericIndex = FALSE;
|
|
}
|
|
}
|
|
if (!fNumericIndex)
|
|
{
|
|
hr = myRevertSanitizeName(pwszCertIndex, ppwszCertName);
|
|
_JumpIfError(hr, error, "myRevertSanitizeName");
|
|
|
|
*piCert = MAXDWORD;
|
|
*piCRL = MAXDWORD;
|
|
*piCTL = MAXDWORD;
|
|
}
|
|
}
|
|
if (g_fVerbose)
|
|
{
|
|
wprintf(
|
|
L"pwszCertIndex=%ws, Name=%ws idx=%d.%d.%d\n",
|
|
pwszCertIndex,
|
|
*ppwszCertName,
|
|
*piCert,
|
|
*piCRL,
|
|
*piCTL);
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszCopy)
|
|
{
|
|
LocalFree(pwszCopy);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SavePFXStoreToFile(
|
|
IN HCERTSTORE hStorePFX,
|
|
IN WCHAR const *pwszfnOut,
|
|
IN WCHAR const *pwszPassword,
|
|
IN OUT WCHAR **ppwszPassword)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_DATA_BLOB pfx;
|
|
WCHAR wszPassword[MAX_PATH];
|
|
|
|
pfx.pbData = NULL;
|
|
|
|
if (NULL == *ppwszPassword)
|
|
{
|
|
if (NULL == pwszPassword || 0 == wcscmp(L"*", pwszPassword))
|
|
{
|
|
wprintf(L"Enter new password for output file %ws:\n", pwszfnOut);
|
|
hr = cuGetPassword(TRUE, wszPassword, ARRAYSIZE(wszPassword));
|
|
_JumpIfError(hr, error, "cuGetPassword");
|
|
|
|
pwszPassword = wszPassword;
|
|
}
|
|
hr = myDupString(pwszPassword, ppwszPassword);
|
|
_JumpIfError(hr, error, "myDupString");
|
|
}
|
|
pwszPassword = *ppwszPassword;
|
|
|
|
// GemPlus returns NTE_BAD_TYPE instead of NTE_BAD_KEY, blowing up
|
|
// REPORT_NOT_ABLE* filtering. if they ever get this right, we can pass
|
|
// "[...] : EXPORT_PRIVATE_KEYS"
|
|
|
|
hr = myPFXExportCertStore(
|
|
hStorePFX,
|
|
&pfx,
|
|
pwszPassword,
|
|
EXPORT_PRIVATE_KEYS | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY);
|
|
_JumpIfError(hr, error, "myPFXExportCertStore");
|
|
|
|
hr = EncodeToFileW(
|
|
pwszfnOut,
|
|
pfx.pbData,
|
|
pfx.cbData,
|
|
CRYPT_STRING_BINARY | g_EncodeFlags);
|
|
_JumpIfError(hr, error, "EncodeToFileW");
|
|
|
|
error:
|
|
if (NULL != pfx.pbData)
|
|
{
|
|
LocalFree(pfx.pbData);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SavePFXToFile(
|
|
IN CERT_CONTEXT const *pCert,
|
|
IN WCHAR const *pwszfnOut,
|
|
IN BOOL fFirst,
|
|
IN WCHAR const *pwszPassword,
|
|
IN OUT WCHAR **ppwszPassword)
|
|
{
|
|
HRESULT hr;
|
|
HCERTSTORE hTempMemoryStore = NULL;
|
|
|
|
hTempMemoryStore = CertOpenStore(
|
|
CERT_STORE_PROV_MEMORY,
|
|
X509_ASN_ENCODING,
|
|
NULL,
|
|
CERT_STORE_NO_CRYPT_RELEASE_FLAG |
|
|
CERT_STORE_ENUM_ARCHIVED_FLAG,
|
|
NULL);
|
|
if (NULL == hTempMemoryStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
|
|
// Begin Chain Building
|
|
|
|
hr = myAddChainToMemoryStore(hTempMemoryStore, pCert);
|
|
_JumpIfError(hr, error, "myAddChainToMemoryStore");
|
|
|
|
// End Chain Building
|
|
|
|
hr = SavePFXStoreToFile(
|
|
hTempMemoryStore,
|
|
pwszfnOut,
|
|
pwszPassword,
|
|
ppwszPassword);
|
|
_JumpIfError(hr, error, "SavePFXStoreToFile");
|
|
|
|
error:
|
|
if (NULL != hTempMemoryStore)
|
|
{
|
|
CertCloseStore(hTempMemoryStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SavePVKToFile(
|
|
IN CERT_CONTEXT const *pCert,
|
|
IN WCHAR const *pwszfnOut,
|
|
IN BOOL fFirst)
|
|
{
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
cuDumpCTLProperties(
|
|
IN CTL_CONTEXT const *pCTL)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwPropId;
|
|
BYTE *pb = NULL;
|
|
DWORD cb;
|
|
|
|
dwPropId = 0;
|
|
while (TRUE)
|
|
{
|
|
if (NULL != pb)
|
|
{
|
|
LocalFree(pb);
|
|
pb = NULL;
|
|
}
|
|
dwPropId = CertEnumCTLContextProperties(pCTL, dwPropId);
|
|
if (0 == dwPropId)
|
|
{
|
|
break;
|
|
}
|
|
while (TRUE)
|
|
{
|
|
if (!CertGetCTLContextProperty(pCTL, dwPropId, pb, &cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCTLContextProperty");
|
|
}
|
|
if (NULL != pb)
|
|
{
|
|
break; // memory alloc'd, property fetched
|
|
}
|
|
pb = (BYTE *) LocalAlloc(LMEM_FIXED, cb);
|
|
if (NULL == pb)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
}
|
|
hr = cuDumpFormattedProperty(dwPropId, NULL, pb, cb);
|
|
_PrintIfError(hr, "cuDumpFormattedProperty");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pb)
|
|
{
|
|
LocalFree(pb);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
cuDumpCRLProperties(
|
|
IN CRL_CONTEXT const *pCRL)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwPropId;
|
|
BYTE *pb = NULL;
|
|
DWORD cb;
|
|
|
|
dwPropId = 0;
|
|
while (TRUE)
|
|
{
|
|
if (NULL != pb)
|
|
{
|
|
LocalFree(pb);
|
|
pb = NULL;
|
|
}
|
|
dwPropId = CertEnumCRLContextProperties(pCRL, dwPropId);
|
|
if (0 == dwPropId)
|
|
{
|
|
break;
|
|
}
|
|
while (TRUE)
|
|
{
|
|
if (!CertGetCRLContextProperty(pCRL, dwPropId, pb, &cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCRLContextProperty");
|
|
}
|
|
if (NULL != pb)
|
|
{
|
|
break; // memory alloc'd, property fetched
|
|
}
|
|
pb = (BYTE *) LocalAlloc(LMEM_FIXED, cb);
|
|
if (NULL == pb)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
}
|
|
hr = cuDumpFormattedProperty(dwPropId, NULL, pb, cb);
|
|
_PrintIfError(hr, "cuDumpFormattedProperty");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pb)
|
|
{
|
|
LocalFree(pb);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
cuDumpCertProperties(
|
|
IN CERT_CONTEXT const *pCert)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwPropId;
|
|
BYTE *pb = NULL;
|
|
DWORD cb;
|
|
|
|
dwPropId = 0;
|
|
while (TRUE)
|
|
{
|
|
dwPropId = CertEnumCertificateContextProperties(pCert, dwPropId);
|
|
if (0 == dwPropId)
|
|
{
|
|
break;
|
|
}
|
|
if (NULL != pb)
|
|
{
|
|
LocalFree(pb);
|
|
pb = NULL;
|
|
}
|
|
while (TRUE)
|
|
{
|
|
if (!CertGetCertificateContextProperty(pCert, dwPropId, pb, &cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateContextProperty");
|
|
}
|
|
if (NULL != pb)
|
|
{
|
|
break; // memory alloc'd, property fetched
|
|
}
|
|
pb = (BYTE *) LocalAlloc(LMEM_FIXED, cb);
|
|
if (NULL == pb)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
}
|
|
hr = cuDumpFormattedProperty(dwPropId, NULL, pb, cb);
|
|
_PrintIfError(hr, "cuDumpFormattedProperty");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pb)
|
|
{
|
|
LocalFree(pb);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
EnumCertsInStore(
|
|
IN HCERTSTORE hStore,
|
|
IN DWORD Mode,
|
|
IN DWORD iCertSave,
|
|
OPTIONAL IN WCHAR const *pwszCertName,
|
|
IN DWORD cbHash,
|
|
OPTIONAL IN BYTE *pbHash,
|
|
OPTIONAL IN WCHAR const *pwszfnOut,
|
|
OPTIONAL IN WCHAR const *pwszPasswordArg,
|
|
IN OUT WCHAR **ppwszPassword,
|
|
OUT DWORD *pcCert)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hr2;
|
|
DWORD iCert;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
BSTR strSerialNumber = NULL;
|
|
|
|
*pcCert = 0;
|
|
hr2 = S_OK;
|
|
if (NULL != pwszCertName)
|
|
{
|
|
hr = myMakeSerialBstr(pwszCertName, &strSerialNumber);
|
|
_PrintIfError2(hr, "myMakeSerialBstr", hr);
|
|
}
|
|
for (iCert = 0; ; iCert++)
|
|
{
|
|
DWORD VerifyState;
|
|
BOOL fSigningKey;
|
|
BOOL fMatchingKey;
|
|
|
|
pCert = CertEnumCertificatesInStore(hStore, pCert);
|
|
if (NULL == pCert)
|
|
{
|
|
break;
|
|
}
|
|
if (MAXDWORD == iCertSave || iCert == iCertSave)
|
|
{
|
|
DWORD cb;
|
|
|
|
if (NULL != pwszCertName)
|
|
{
|
|
BOOL fMatch;
|
|
|
|
hr = myCertMatch(
|
|
pCert,
|
|
pwszCertName,
|
|
NULL == strSerialNumber && // fAllowMissingCN
|
|
NULL == pbHash,
|
|
pbHash,
|
|
cbHash,
|
|
strSerialNumber,
|
|
&fMatch);
|
|
_PrintIfError(hr, "myCertMatch");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
if (S_OK != hr || !fMatch)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
if (0 != *pcCert)
|
|
{
|
|
wprintf(wszNewLine);
|
|
}
|
|
|
|
wprintf(
|
|
myLoadResourceString(IDS_FORMAT_DUMP_CERT_INDEX), // "================ Certificate %d ================"
|
|
iCert);
|
|
wprintf(wszNewLine);
|
|
|
|
if (CertGetCertificateContextProperty(
|
|
pCert,
|
|
CERT_ARCHIVED_PROP_ID,
|
|
NULL,
|
|
&cb))
|
|
{
|
|
wprintf(myLoadResourceString(IDS_ARCHIVED)); // "Archived!"
|
|
wprintf(wszNewLine);
|
|
}
|
|
if (iCert == iCertSave &&
|
|
NULL != pwszfnOut &&
|
|
(DVNS_SAVECERT & Mode))
|
|
{
|
|
hr = EncodeToFileW(
|
|
pwszfnOut,
|
|
pCert->pbCertEncoded,
|
|
pCert->cbCertEncoded,
|
|
CRYPT_STRING_BINARY | g_EncodeFlags);
|
|
_PrintIfError(hr, "EncodeToFileW");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
|
|
hr = cuDumpAsnBinary(
|
|
pCert->pbCertEncoded,
|
|
pCert->cbCertEncoded,
|
|
MAXDWORD);
|
|
_PrintIfError(hr, "cuDumpAsnBinary");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
|
|
if (DVNS_REPAIRKPI & Mode)
|
|
{
|
|
if (!CryptFindCertificateKeyProvInfo(
|
|
pCert,
|
|
0, // dwFlags
|
|
NULL)) // pvReserved
|
|
{
|
|
hr = myHLastError();
|
|
_PrintError(hr, "CryptFindCertificateKeyProvInfo");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
|
|
}
|
|
if ((DVNS_DUMPPROPERTIES & Mode) && !g_fQuiet)
|
|
{
|
|
hr = cuDumpCertProperties(pCert);
|
|
_PrintIfError(hr, "cuDumpCertProperties");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
if (DVNS_DUMPKEYS & Mode)
|
|
{
|
|
if (0 == (DVNS_DUMPPROPERTIES & Mode) || g_fQuiet)
|
|
{
|
|
hr = cuDumpCertKeyProviderInfo(
|
|
g_wszPad2,
|
|
pCert,
|
|
NULL,
|
|
NULL);
|
|
_PrintIfError(hr, "cuDumpCertKeyProviderInfo");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
hr = cuDumpPrivateKey(pCert, &fSigningKey, &fMatchingKey);
|
|
if (!IsHrSkipPrivateKey(hr))
|
|
{
|
|
if (S_OK != hr)
|
|
{
|
|
wprintf(myLoadResourceString(
|
|
fSigningKey?
|
|
IDS_SIGNATURE_BAD : // "Signature test FAILED"
|
|
IDS_ENCRYPTION_BAD)); // "Encryption test FAILED"
|
|
wprintf(wszNewLine);
|
|
_PrintError(hr, "cuDumpPrivateKey");
|
|
fMatchingKey = FALSE;
|
|
}
|
|
|
|
if (fMatchingKey)
|
|
{
|
|
wprintf(myLoadResourceString(
|
|
fSigningKey?
|
|
IDS_SIGNATURE_OK : // "Signature test passed"
|
|
IDS_ENCRYPTION_OK)); // "Encryption test passed"
|
|
wprintf(wszNewLine);
|
|
}
|
|
}
|
|
}
|
|
if (DVNS_VERIFYCERT & Mode)
|
|
{
|
|
hr = cuVerifyCertContext(
|
|
pCert,
|
|
(DVNS_CASTORE & Mode)? hStore : NULL,
|
|
NULL,
|
|
0,
|
|
&VerifyState);
|
|
if (S_OK != hr)
|
|
{
|
|
cuPrintError(IDS_ERR_FORMAT_BAD_CERT, hr);
|
|
_PrintError(hr, "cuVerifyCertContext");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr; // Save first error
|
|
}
|
|
}
|
|
else if (0 == (VS_ERRORMASK & VerifyState))
|
|
{
|
|
wprintf(myLoadResourceString(IDS_CERT_VERIFIES)); // "Certificate is valid"
|
|
}
|
|
wprintf(wszNewLine);
|
|
}
|
|
if (DVNS_SAVEPFX & Mode)
|
|
{
|
|
hr = SavePFXToFile(
|
|
pCert,
|
|
pwszfnOut,
|
|
0 == *pcCert,
|
|
pwszPasswordArg,
|
|
ppwszPassword);
|
|
_PrintIfError(hr, "SavePFXToFile");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
if (DVNS_SAVEPVK & Mode)
|
|
{
|
|
hr = SavePVKToFile(pCert, pwszfnOut, 0 == *pcCert);
|
|
_PrintIfError(hr, "SavePVKToFile");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
(*pcCert)++;
|
|
}
|
|
}
|
|
hr = hr2;
|
|
_JumpIfError(hr, error, "EnumCertsInStore");
|
|
|
|
error:
|
|
if (NULL != pCert)
|
|
{
|
|
CertFreeCertificateContext(pCert);
|
|
}
|
|
if (NULL != strSerialNumber)
|
|
{
|
|
SysFreeString(strSerialNumber);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
EnumCRLsInStore(
|
|
IN HCERTSTORE hStore,
|
|
IN DWORD Mode,
|
|
IN DWORD iCRLSave,
|
|
OPTIONAL IN WCHAR const *pwszCertName,
|
|
IN DWORD cbHash,
|
|
OPTIONAL IN BYTE *pbHash,
|
|
OPTIONAL IN WCHAR const *pwszfnOut,
|
|
OUT DWORD *pcCRL)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hr2;
|
|
DWORD iCRL;
|
|
CRL_CONTEXT const *pCRL = NULL;
|
|
|
|
*pcCRL = 0;
|
|
hr2 = S_OK;
|
|
for (iCRL = 0; ; iCRL++)
|
|
{
|
|
pCRL = CertEnumCRLsInStore(hStore, pCRL);
|
|
if (NULL == pCRL)
|
|
{
|
|
break;
|
|
}
|
|
if (MAXDWORD == iCRLSave || iCRL == iCRLSave)
|
|
{
|
|
if (NULL != pwszCertName)
|
|
{
|
|
BOOL fMatch;
|
|
|
|
hr = myCRLMatch(
|
|
pCRL,
|
|
pwszCertName,
|
|
TRUE, // fAllowMissingCN
|
|
pbHash,
|
|
cbHash,
|
|
&fMatch);
|
|
_PrintIfError(hr, "myCRLMatch");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
if (S_OK != hr || !fMatch)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
if (0 != *pcCRL)
|
|
{
|
|
wprintf(wszNewLine);
|
|
}
|
|
|
|
wprintf(
|
|
myLoadResourceString(IDS_FORMAT_DUMP_CRL_INDEX), // "================ CRL %d ================"
|
|
iCRL);
|
|
wprintf(wszNewLine);
|
|
|
|
if (iCRL == iCRLSave &&
|
|
NULL != pwszfnOut &&
|
|
(DVNS_SAVECRL & Mode))
|
|
{
|
|
hr = EncodeToFileW(
|
|
pwszfnOut,
|
|
pCRL->pbCrlEncoded,
|
|
pCRL->cbCrlEncoded,
|
|
CRYPT_STRING_BINARY | g_EncodeFlags);
|
|
_PrintIfError(hr, "EncodeToFileW");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
|
|
hr = cuDumpAsnBinary(
|
|
pCRL->pbCrlEncoded,
|
|
pCRL->cbCrlEncoded,
|
|
MAXDWORD);
|
|
_PrintIfError(hr, "cuDumpAsnBinary");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
|
|
if ((DVNS_DUMPPROPERTIES & Mode) && !g_fQuiet)
|
|
{
|
|
hr = cuDumpCRLProperties(pCRL);
|
|
_PrintIfError(hr, "cuDumpCRLProperties");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
(*pcCRL)++;
|
|
}
|
|
}
|
|
hr = hr2;
|
|
_JumpIfError(hr, error, "EnumCRLsInStore");
|
|
|
|
error:
|
|
if (NULL != pCRL)
|
|
{
|
|
CertFreeCRLContext(pCRL);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
EnumCTLsInStore(
|
|
IN HCERTSTORE hStore,
|
|
IN DWORD Mode,
|
|
IN DWORD iCTLSave,
|
|
OPTIONAL IN WCHAR const *pwszCertName,
|
|
IN DWORD cbHash,
|
|
OPTIONAL IN BYTE *pbHash,
|
|
OPTIONAL IN WCHAR const *pwszfnOut,
|
|
OUT DWORD *pcCTL)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hr2;
|
|
DWORD iCTL;
|
|
CTL_CONTEXT const *pCTL = NULL;
|
|
|
|
*pcCTL = 0;
|
|
hr2 = S_OK;
|
|
for (iCTL = 0; ; iCTL++)
|
|
{
|
|
pCTL = CertEnumCTLsInStore(hStore, pCTL);
|
|
if (NULL == pCTL)
|
|
{
|
|
break;
|
|
}
|
|
if (MAXDWORD == iCTLSave || iCTL == iCTLSave)
|
|
{
|
|
DWORD cb;
|
|
|
|
if (NULL != pwszCertName)
|
|
{
|
|
BOOL fMatch;
|
|
|
|
hr = myCTLMatch(pCTL, pbHash, cbHash, &fMatch);
|
|
_PrintIfError(hr, "myCTLMatch");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
if (S_OK != hr || !fMatch)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
if (0 != *pcCTL)
|
|
{
|
|
wprintf(wszNewLine);
|
|
}
|
|
|
|
wprintf(
|
|
myLoadResourceString(IDS_FORMAT_DUMP_CTL_INDEX), // "================ CTL %d ================"
|
|
iCTL);
|
|
wprintf(wszNewLine);
|
|
|
|
if (CertGetCTLContextProperty(
|
|
pCTL,
|
|
CERT_ARCHIVED_PROP_ID,
|
|
NULL,
|
|
&cb))
|
|
{
|
|
wprintf(myLoadResourceString(IDS_ARCHIVED)); // "Archived!"
|
|
wprintf(wszNewLine);
|
|
}
|
|
if (iCTL == iCTLSave &&
|
|
NULL != pwszfnOut &&
|
|
(DVNS_SAVECTL & Mode))
|
|
{
|
|
hr = EncodeToFileW(
|
|
pwszfnOut,
|
|
pCTL->pbCtlEncoded,
|
|
pCTL->cbCtlEncoded,
|
|
CRYPT_STRING_BINARY | g_EncodeFlags);
|
|
_PrintIfError(hr, "EncodeToFileW");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
|
|
hr = cuDumpAsnBinary(
|
|
pCTL->pbCtlEncoded,
|
|
pCTL->cbCtlEncoded,
|
|
MAXDWORD);
|
|
_PrintIfError(hr, "cuDumpAsnBinary");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
|
|
if ((DVNS_DUMPPROPERTIES & Mode) && !g_fQuiet)
|
|
{
|
|
hr = cuDumpCTLProperties(pCTL);
|
|
_PrintIfError(hr, "cuDumpCTLProperties");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
#if 0
|
|
if (DVNS_VERIFYCERT & Mode)
|
|
{
|
|
hr = cuVerifyCertContext(
|
|
pCTL,
|
|
(DVNS_CASTORE & Mode)? hStore : NULL,
|
|
NULL,
|
|
0,
|
|
&VerifyState);
|
|
if (S_OK != hr)
|
|
{
|
|
cuPrintError(IDS_ERR_FORMAT_BAD_CTL, hr);
|
|
_PrintError(hr, "cuVerifyCertContext");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr; // Save first error
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wprintf(myLoadResourceString(IDS_CTL_VERIFIES)); // "CTL is valid"
|
|
}
|
|
wprintf(wszNewLine);
|
|
}
|
|
#endif
|
|
(*pcCTL)++;
|
|
}
|
|
}
|
|
hr = hr2;
|
|
_JumpIfError(hr, error, "EnumCTLsInStore");
|
|
|
|
error:
|
|
if (NULL != pCTL)
|
|
{
|
|
CertFreeCTLContext(pCTL);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
cuDumpAndVerifyStore(
|
|
IN HCERTSTORE hStore,
|
|
IN DWORD Mode,
|
|
OPTIONAL IN WCHAR const *pwszCertName,
|
|
IN DWORD iCertSave,
|
|
IN DWORD iCRLSave,
|
|
IN DWORD iCTLSave,
|
|
OPTIONAL IN WCHAR const *pwszfnOut,
|
|
OPTIONAL IN WCHAR const *pwszPasswordArg)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hr2;
|
|
BYTE *pbHash = NULL;
|
|
DWORD cbHash;
|
|
BOOL fVerboseOld = g_fVerbose;
|
|
BOOL fQuietOld = g_fQuiet;
|
|
WCHAR *pwszPassword = NULL;
|
|
DWORD cCert = 0;
|
|
DWORD cCRL = 0;
|
|
DWORD cCTL = 0;
|
|
|
|
if (g_fVerbose)
|
|
{
|
|
g_fVerbose--;
|
|
}
|
|
else
|
|
{
|
|
g_fQuiet = TRUE;
|
|
}
|
|
hr2 = S_OK;
|
|
|
|
if (NULL != pwszCertName)
|
|
{
|
|
hr = WszToMultiByteInteger(TRUE, pwszCertName, &cbHash, &pbHash);
|
|
_PrintIfError2(hr, "WszToMultiByteInteger", hr);
|
|
}
|
|
|
|
if (NULL != pwszCertName ||
|
|
MAXDWORD != iCertSave ||
|
|
(MAXDWORD == iCRLSave && MAXDWORD == iCTLSave))
|
|
{
|
|
hr = EnumCertsInStore(
|
|
hStore,
|
|
Mode,
|
|
iCertSave,
|
|
pwszCertName,
|
|
cbHash,
|
|
pbHash,
|
|
pwszfnOut,
|
|
pwszPasswordArg,
|
|
&pwszPassword,
|
|
&cCert);
|
|
_PrintIfError(hr, "EnumCertsInStore");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
|
|
if (NULL != pwszCertName ||
|
|
MAXDWORD != iCRLSave ||
|
|
(MAXDWORD == iCertSave && MAXDWORD == iCTLSave))
|
|
{
|
|
hr = EnumCRLsInStore(
|
|
hStore,
|
|
Mode,
|
|
iCRLSave,
|
|
pwszCertName,
|
|
cbHash,
|
|
pbHash,
|
|
pwszfnOut,
|
|
&cCRL);
|
|
_PrintIfError(hr, "EnumCRLsInStore");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
|
|
if (NULL != pwszCertName ||
|
|
MAXDWORD != iCTLSave ||
|
|
(MAXDWORD == iCertSave && MAXDWORD == iCRLSave))
|
|
{
|
|
hr = EnumCTLsInStore(
|
|
hStore,
|
|
Mode,
|
|
iCTLSave,
|
|
pwszCertName,
|
|
cbHash,
|
|
pbHash,
|
|
pwszfnOut,
|
|
&cCTL);
|
|
_PrintIfError(hr, "EnumCTLsInStore");
|
|
if (S_OK == hr2)
|
|
{
|
|
hr2 = hr;
|
|
}
|
|
}
|
|
|
|
hr = hr2;
|
|
if (S_OK == hr && NULL != pwszCertName && 0 == (cCert + cCRL + cCTL))
|
|
{
|
|
hr = NTE_NOT_FOUND;
|
|
_JumpError(hr, error, "cuDumpAndVerifyStore");
|
|
}
|
|
|
|
error:
|
|
g_fVerbose = fVerboseOld;
|
|
g_fQuiet = fQuietOld;
|
|
if (NULL != pbHash)
|
|
{
|
|
LocalFree(pbHash);
|
|
}
|
|
if (NULL != pwszPassword)
|
|
{
|
|
LocalFree(pwszPassword);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
#define wszLDAPPREFIX L"ldap:///"
|
|
|
|
HRESULT
|
|
cuOpenCertStore(
|
|
IN WCHAR const *pwszStoreName,
|
|
IN OUT DWORD *pMode,
|
|
OUT HCERTSTORE *phStore)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR awc[7];
|
|
LPCSTR pszStoreProvider = CERT_STORE_PROV_SYSTEM_REGISTRY_W;
|
|
WCHAR *pwszStoreAlloc = NULL;
|
|
WCHAR *pwszStoreAlloc2 = NULL;
|
|
|
|
if (NULL == pwszStoreName ||
|
|
0 == wcscmp(L"*", pwszStoreName) ||
|
|
0 == lstrcmpi(wszCA_CERTSTORE, pwszStoreName))
|
|
{
|
|
pwszStoreName = wszCA_CERTSTORE;
|
|
*pMode |= DVNS_CASTORE;
|
|
}
|
|
wcsncpy(awc, pwszStoreName, ARRAYSIZE(awc));
|
|
awc[ARRAYSIZE(awc) - 1] = L'\0';
|
|
if (0 == lstrcmpi(L"ldap:/", awc))
|
|
{
|
|
pszStoreProvider = CERT_STORE_PROV_LDAP_W;
|
|
*pMode |= DVNS_DSSTORE;
|
|
}
|
|
else
|
|
{
|
|
awc[3] = L'\0';
|
|
if (0 == lstrcmpi(L"CN=", awc))
|
|
{
|
|
pszStoreProvider = CERT_STORE_PROV_LDAP_W;
|
|
pwszStoreAlloc = (WCHAR *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(WSZARRAYSIZE(wszLDAPPREFIX) + wcslen(pwszStoreName) + 1) *
|
|
sizeof(WCHAR));
|
|
if (NULL == pwszStoreAlloc)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
wcscpy(pwszStoreAlloc, wszLDAPPREFIX);
|
|
wcscat(pwszStoreAlloc, pwszStoreName);
|
|
pwszStoreName = pwszStoreAlloc;
|
|
*pMode |= DVNS_DSSTORE;
|
|
}
|
|
}
|
|
if (DVNS_DSSTORE & *pMode)
|
|
{
|
|
hr = myInternetUncanonicalizeURL(pwszStoreName, &pwszStoreAlloc2);
|
|
_JumpIfError(hr, error, "myInternetUncanonicalizeURL");
|
|
|
|
pwszStoreName = pwszStoreAlloc2;
|
|
}
|
|
|
|
*phStore = CertOpenStore(
|
|
pszStoreProvider,
|
|
X509_ASN_ENCODING,
|
|
NULL, // hProv
|
|
CERT_STORE_NO_CRYPT_RELEASE_FLAG |
|
|
CERT_STORE_ENUM_ARCHIVED_FLAG |
|
|
(((DVNS_REPAIRKPI | DVNS_WRITESTORE) & *pMode)?
|
|
0 : CERT_STORE_READONLY_FLAG) |
|
|
(g_fForce? 0 : CERT_STORE_OPEN_EXISTING_FLAG) |
|
|
cuGetSystemStoreFlags(),
|
|
pwszStoreName);
|
|
if (NULL == *phStore)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpErrorStr(hr, error, "CertOpenStore", pwszStoreName);
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszStoreAlloc)
|
|
{
|
|
LocalFree(pwszStoreAlloc);
|
|
}
|
|
if (NULL != pwszStoreAlloc2)
|
|
{
|
|
LocalFree(pwszStoreAlloc2);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
DumpAndVerifyNamedStore(
|
|
IN WCHAR const *pwszStoreName,
|
|
IN DWORD Mode,
|
|
OPTIONAL IN WCHAR const *pwszCertName,
|
|
IN DWORD iCertSave,
|
|
IN DWORD iCRLSave,
|
|
IN DWORD iCTLSave,
|
|
OPTIONAL IN WCHAR const *pwszfnOut,
|
|
OPTIONAL IN WCHAR const *pwszPassword)
|
|
{
|
|
HRESULT hr;
|
|
HCERTSTORE hStore = NULL;
|
|
|
|
hr = cuOpenCertStore(pwszStoreName, &Mode, &hStore);
|
|
_JumpIfError(hr, error, "cuOpenCertStore");
|
|
|
|
hr = cuDumpAndVerifyStore(
|
|
hStore,
|
|
Mode,
|
|
pwszCertName,
|
|
iCertSave,
|
|
iCRLSave,
|
|
iCTLSave,
|
|
pwszfnOut,
|
|
pwszPassword);
|
|
_JumpIfError(hr, error, "cuDumpAndVerifyStore");
|
|
|
|
error:
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbViewOrDeleteStore(
|
|
IN WCHAR const *pwszOption,
|
|
OPTIONAL IN WCHAR const *pwszStoreName,
|
|
OPTIONAL IN WCHAR const *pwszCertIndex,
|
|
OPTIONAL IN WCHAR const *pwszfnOut,
|
|
IN WCHAR const *pwszArg4)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszCertName = NULL;
|
|
DWORD Mode;
|
|
DWORD iCert;
|
|
DWORD iCRL;
|
|
DWORD iCTL;
|
|
HCERTSTORE hStore = NULL;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
BOOL fDelete = g_wszViewDelStore == pwszOption;
|
|
WCHAR *pwszSubject = NULL;
|
|
|
|
hr = ParseCertCRLIndex(pwszCertIndex, &pwszCertName, &iCert, &iCRL, &iCTL);
|
|
_JumpIfErrorStr(hr, error, "ParseCertCRLIndex", pwszCertIndex);
|
|
|
|
Mode = DVNS_SAVECERT;
|
|
if (fDelete)
|
|
{
|
|
Mode |= DVNS_WRITESTORE;
|
|
}
|
|
|
|
hr = cuOpenCertStore(pwszStoreName, &Mode, &hStore);
|
|
_JumpIfError(hr, error, "cuOpenCertStore");
|
|
|
|
hr = myGetCertificateFromPicker(
|
|
g_hInstance, // hInstance
|
|
NULL, // hwndParent
|
|
IDS_VIEWSTORE_TITLE, // idTitle
|
|
fDelete? IDS_VIEWSTORE_SUBTITLE_DELETE :
|
|
IDS_VIEWSTORE_SUBTITLE, // idSubTitle
|
|
0, // dwFlags -- CUCS_*
|
|
pwszCertName, // pwszCommonName
|
|
1, // cStore
|
|
&hStore, // rghStore
|
|
0, // cpszObjId
|
|
NULL, // apszObjId
|
|
&pCert); // ppCert
|
|
_JumpIfError(hr, error, "myGetCertificateFromPicker");
|
|
|
|
if (NULL != pCert)
|
|
{
|
|
hr = myCertNameToStr(
|
|
X509_ASN_ENCODING,
|
|
&pCert->pCertInfo->Subject,
|
|
CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
|
|
&pwszSubject);
|
|
_JumpIfError(hr, error, "myCertNameToStr");
|
|
|
|
if (NULL != pwszfnOut)
|
|
{
|
|
hr = EncodeToFileW(
|
|
pwszfnOut,
|
|
pCert->pbCertEncoded,
|
|
pCert->cbCertEncoded,
|
|
CRYPT_STRING_BINARY | g_EncodeFlags);
|
|
_JumpIfError(hr, error, "EncodeToFileW");
|
|
wprintf(
|
|
myLoadResourceString(
|
|
IDS_FORMAT_SAVED_CERT_NAME), // "Saved certificate %ws"
|
|
pwszSubject);
|
|
wprintf(L": %ws\n", pwszfnOut);
|
|
}
|
|
if (fDelete)
|
|
{
|
|
if (!CertDeleteCertificateFromStore(pCert))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertDeleteCertificateFromStore");
|
|
}
|
|
pCert = NULL;
|
|
|
|
wprintf(
|
|
myLoadResourceString(
|
|
IDS_FORMAT_DELETED_CERT_NAME), // "Deleted certificate %ws"
|
|
pwszSubject);
|
|
wprintf(wszNewLine);
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszSubject)
|
|
{
|
|
LocalFree(pwszSubject);
|
|
}
|
|
if (NULL != pwszCertName)
|
|
{
|
|
LocalFree(pwszCertName);
|
|
}
|
|
if (NULL != pCert)
|
|
{
|
|
CertFreeCertificateContext(pCert);
|
|
}
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbStore(
|
|
IN WCHAR const *pwszOption,
|
|
IN WCHAR const *pwszStoreName,
|
|
IN WCHAR const *pwszCertIndex,
|
|
IN WCHAR const *pwszfnOut,
|
|
IN WCHAR const *pwszArg4)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszCertName = NULL;
|
|
DWORD iCert;
|
|
DWORD iCRL;
|
|
DWORD iCTL;
|
|
|
|
hr = ParseCertCRLIndex(pwszCertIndex, &pwszCertName, &iCert, &iCRL, &iCTL);
|
|
_JumpIfErrorStr(hr, error, "ParseCertCRLIndex", pwszCertIndex);
|
|
|
|
hr = DumpAndVerifyNamedStore(
|
|
pwszStoreName,
|
|
DVNS_SAVECERT |
|
|
DVNS_SAVECRL |
|
|
DVNS_SAVECTL |
|
|
DVNS_DUMP |
|
|
DVNS_DUMPKEYS |
|
|
DVNS_DUMPPROPERTIES,
|
|
pwszCertName,
|
|
iCert,
|
|
iCRL,
|
|
iCTL,
|
|
pwszfnOut,
|
|
NULL);
|
|
_JumpIfError(hr, error, "DumpAndVerifyNamedStore");
|
|
|
|
error:
|
|
if (NULL != pwszCertName)
|
|
{
|
|
LocalFree(pwszCertName);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbAddStore(
|
|
IN WCHAR const *pwszOption,
|
|
IN WCHAR const *pwszStoreName,
|
|
IN WCHAR const *pwszfnIn,
|
|
IN WCHAR const *pwszArg3,
|
|
IN WCHAR const *pwszArg4)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszStore = NULL;
|
|
DWORD Mode;
|
|
HCERTSTORE hStore = NULL;
|
|
CERT_CONTEXT const *pCertContext = NULL;
|
|
CRL_CONTEXT const *pCRLContext = NULL;
|
|
BOOL fRoot = FALSE;
|
|
|
|
Mode = DVNS_WRITESTORE; // force open for write
|
|
|
|
hr = cuOpenCertStore(pwszStoreName, &Mode, &hStore);
|
|
if (S_OK != hr)
|
|
{
|
|
wprintf(myLoadResourceString(
|
|
g_fForce?
|
|
IDS_CANNOT_CREATE_STORE : // "Cannot open Cert store."
|
|
IDS_CANNOT_OPEN_STORE)); // "Cannot open existing Cert store. Use -f switch to force Cert store creation."
|
|
wprintf(wszNewLine);
|
|
_JumpErrorStr(hr, error, "cuOpenCertStore", pwszStoreName);
|
|
}
|
|
|
|
// Load and decode certificate
|
|
|
|
hr = cuLoadCert(pwszfnIn, &pCertContext);
|
|
if (S_OK != hr)
|
|
{
|
|
if (CRYPT_E_ASN1_BADTAG != hr)
|
|
{
|
|
_JumpError(hr, error, "cuLoadCert");
|
|
}
|
|
hr = cuLoadCRL(pwszfnIn, &pCRLContext);
|
|
_JumpIfError(hr, error, "cuLoadCRL");
|
|
|
|
if (!CertAddCRLContextToStore(
|
|
hStore,
|
|
pCRLContext,
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
NULL))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertAddCRLContextToStore");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CertCompareCertificateName(
|
|
X509_ASN_ENCODING,
|
|
&pCertContext->pCertInfo->Issuer,
|
|
&pCertContext->pCertInfo->Subject))
|
|
{
|
|
hr = cuVerifySignature(
|
|
pCertContext->pbCertEncoded,
|
|
pCertContext->cbCertEncoded,
|
|
&pCertContext->pCertInfo->SubjectPublicKeyInfo,
|
|
FALSE);
|
|
fRoot = S_OK == hr;
|
|
_PrintIfError(hr, "cuVerifySignature");
|
|
}
|
|
if (0 == lstrcmpi(pwszStoreName, L"root") && !fRoot)
|
|
{
|
|
wprintf(myLoadResourceString(IDS_ROOT_STORE_NEEDS_ROOT_CERT)); // "Cannot add a non-root certificate to the root store"
|
|
wprintf(wszNewLine);
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "Non-root cert");
|
|
}
|
|
|
|
if (!CertAddCertificateContextToStore(
|
|
hStore,
|
|
pCertContext,
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
NULL))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertAddCertificateContextToStore");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
cuUnloadCert(&pCertContext);
|
|
cuUnloadCRL(&pCRLContext);
|
|
if (NULL != pwszStore)
|
|
{
|
|
LocalFree(pwszStore);
|
|
}
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbDelStore(
|
|
IN WCHAR const *pwszOption,
|
|
IN WCHAR const *pwszStoreName,
|
|
IN WCHAR const *pwszCertIndex,
|
|
IN WCHAR const *pwszArg3,
|
|
IN WCHAR const *pwszArg4)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszStore = NULL;
|
|
HCERTSTORE hStore = NULL;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
CRL_CONTEXT const *pCRL = NULL;
|
|
WCHAR *pwszCertName = NULL;
|
|
BYTE *pbHash = NULL;
|
|
DWORD cbHash;
|
|
BSTR strSerialNumber = NULL;
|
|
DWORD Mode;
|
|
DWORD iCert;
|
|
DWORD iCertDel;
|
|
DWORD iCRL;
|
|
DWORD iCRLDel;
|
|
DWORD iCTL;
|
|
DWORD iCTLDel;
|
|
|
|
if (NULL == pwszStoreName || 0 == wcscmp(L"*", pwszStoreName))
|
|
{
|
|
pwszStoreName = wszCA_CERTSTORE;
|
|
}
|
|
|
|
hr = ParseCertCRLIndex(pwszCertIndex, &pwszCertName, &iCertDel, &iCRLDel, &iCTLDel);
|
|
_JumpIfErrorStr(hr, error, "ParseCertCRLIndex", pwszCertIndex);
|
|
|
|
if (MAXDWORD == iCertDel && NULL == pwszCertName && MAXDWORD == iCRLDel)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpErrorStr(hr, error, "incomplete Index arg", pwszCertIndex);
|
|
}
|
|
if (NULL != pwszCertName)
|
|
{
|
|
hr = WszToMultiByteInteger(TRUE, pwszCertName, &cbHash, &pbHash);
|
|
_PrintIfError2(hr, "WszToMultiByteInteger", hr);
|
|
|
|
hr = myMakeSerialBstr(pwszCertName, &strSerialNumber);
|
|
_PrintIfError2(hr, "myMakeSerialBstr", hr);
|
|
}
|
|
|
|
Mode = DVNS_WRITESTORE; // force open for write
|
|
|
|
hr = cuOpenCertStore(pwszStoreName, &Mode, &hStore);
|
|
_JumpIfError(hr, error, "cuOpenCertStore");
|
|
|
|
if (MAXDWORD != iCertDel || NULL != pwszCertName)
|
|
{
|
|
for (iCert = 0; ; iCert++)
|
|
{
|
|
pCert = CertEnumCertificatesInStore(hStore, pCert);
|
|
if (NULL == pCert)
|
|
{
|
|
break;
|
|
}
|
|
if (iCert == iCertDel ||
|
|
(MAXDWORD == iCertDel && NULL != pwszCertName))
|
|
{
|
|
CERT_CONTEXT const *pCertT;
|
|
|
|
if (NULL != pwszCertName)
|
|
{
|
|
BOOL fMatch;
|
|
|
|
hr = myCertMatch(
|
|
pCert,
|
|
pwszCertName,
|
|
FALSE, // fAllowMissingCN
|
|
pbHash,
|
|
cbHash,
|
|
strSerialNumber,
|
|
&fMatch);
|
|
_JumpIfError(hr, error, "myCertMatch");
|
|
|
|
if (!fMatch)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
wprintf(
|
|
myLoadResourceString(IDS_FORMAT_DELETE_CERT_INDEX), // "Deleting Certificate %d"
|
|
iCert);
|
|
wprintf(wszNewLine);
|
|
|
|
pCertT = CertDuplicateCertificateContext(pCert);
|
|
if (!CertDeleteCertificateFromStore(pCertT))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertDeleteCertificateFromStore");
|
|
}
|
|
if (iCert == iCertDel)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (MAXDWORD != iCRLDel)
|
|
{
|
|
for (iCRL = 0; ; iCRL++)
|
|
{
|
|
pCRL = CertEnumCRLsInStore(hStore, pCRL);
|
|
if (NULL == pCRL)
|
|
{
|
|
break;
|
|
}
|
|
if (iCRL == iCRLDel ||
|
|
(MAXDWORD == iCRLDel && NULL != pwszCertName))
|
|
{
|
|
CRL_CONTEXT const *pCRLT;
|
|
|
|
if (NULL != pwszCertName)
|
|
{
|
|
BOOL fMatch;
|
|
|
|
hr = myCRLMatch(
|
|
pCRL,
|
|
pwszCertName,
|
|
FALSE, // fAllowMissingCN
|
|
pbHash,
|
|
cbHash,
|
|
&fMatch);
|
|
_JumpIfError(hr, error, "myCRLMatch");
|
|
|
|
if (!fMatch)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
wprintf(
|
|
myLoadResourceString(IDS_FORMAT_DELETE_CRL_INDEX), // "Deleting CRL %d"
|
|
iCRL);
|
|
wprintf(wszNewLine);
|
|
|
|
pCRLT = CertDuplicateCRLContext(pCRL);
|
|
if (!CertDeleteCRLFromStore(pCRLT))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertDeleteCRLFromStore");
|
|
}
|
|
if (iCRL == iCRLDel)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != strSerialNumber)
|
|
{
|
|
SysFreeString(strSerialNumber);
|
|
}
|
|
if (NULL != pbHash)
|
|
{
|
|
LocalFree(pbHash);
|
|
}
|
|
if (NULL != pwszCertName)
|
|
{
|
|
LocalFree(pwszCertName);
|
|
}
|
|
if (NULL != pwszStore)
|
|
{
|
|
LocalFree(pwszStore);
|
|
}
|
|
if (NULL != pCert)
|
|
{
|
|
CertFreeCertificateContext(pCert);
|
|
}
|
|
if (NULL != pCRL)
|
|
{
|
|
CertFreeCRLContext(pCRL);
|
|
}
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbVerifyStore(
|
|
IN WCHAR const *pwszOption,
|
|
IN WCHAR const *pwszStoreName,
|
|
IN WCHAR const *pwszCertIndex,
|
|
IN WCHAR const *pwszArg3,
|
|
IN WCHAR const *pwszArg4)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszCertName = NULL;
|
|
DWORD iCert;
|
|
DWORD iCRL;
|
|
DWORD iCTL;
|
|
|
|
hr = ParseCertCRLIndex(pwszCertIndex, &pwszCertName, &iCert, &iCRL, &iCTL);
|
|
_JumpIfErrorStr(hr, error, "ParseCertCRLIndex", pwszCertIndex);
|
|
|
|
hr = DumpAndVerifyNamedStore(
|
|
pwszStoreName,
|
|
DVNS_SAVECERT |
|
|
DVNS_SAVECRL |
|
|
DVNS_SAVECTL |
|
|
DVNS_VERIFYCERT |
|
|
DVNS_DUMPKEYS |
|
|
DVNS_DUMPPROPERTIES,
|
|
pwszCertName,
|
|
iCert,
|
|
iCRL,
|
|
iCTL,
|
|
NULL,
|
|
NULL);
|
|
_JumpIfError(hr, error, "DumpAndVerifyNamedStore");
|
|
|
|
error:
|
|
if (NULL != pwszCertName)
|
|
{
|
|
LocalFree(pwszCertName);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbRepairStore(
|
|
IN WCHAR const *pwszOption,
|
|
IN WCHAR const *pwszStoreName,
|
|
IN WCHAR const *pwszCertIndex,
|
|
IN WCHAR const *pwszArg3,
|
|
IN WCHAR const *pwszArg4)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszCertName = NULL;
|
|
DWORD iCert;
|
|
DWORD iCRL;
|
|
DWORD iCTL;
|
|
|
|
hr = ParseCertCRLIndex(pwszCertIndex, &pwszCertName, &iCert, &iCRL, &iCTL);
|
|
_JumpIfErrorStr(hr, error, "ParseCertCRLIndex", pwszCertIndex);
|
|
|
|
hr = DumpAndVerifyNamedStore(
|
|
pwszStoreName,
|
|
DVNS_SAVECERT |
|
|
DVNS_SAVECRL |
|
|
DVNS_SAVECTL |
|
|
DVNS_REPAIRKPI |
|
|
DVNS_DUMPKEYS,
|
|
pwszCertName,
|
|
iCert,
|
|
iCRL,
|
|
iCTL,
|
|
NULL,
|
|
NULL);
|
|
_JumpIfError(hr, error, "DumpAndVerifyNamedStore");
|
|
|
|
error:
|
|
if (NULL != pwszCertName)
|
|
{
|
|
LocalFree(pwszCertName);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbExportPVK(
|
|
IN WCHAR const *pwszOption,
|
|
IN WCHAR const *pwszCertIndex,
|
|
IN WCHAR const *pwszfnPVKBaseName,
|
|
IN WCHAR const *pwszStoreName,
|
|
IN WCHAR const *pwszArg4)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszCertName = NULL;
|
|
DWORD iCert;
|
|
DWORD iCRL;
|
|
DWORD iCTL;
|
|
|
|
hr = ParseCertCRLIndex(pwszCertIndex, &pwszCertName, &iCert, &iCRL, &iCTL);
|
|
_JumpIfErrorStr(hr, error, "ParseCertCRLIndex", pwszCertIndex);
|
|
|
|
hr = DumpAndVerifyNamedStore(
|
|
NULL == pwszStoreName? wszMY_CERTSTORE : pwszStoreName,
|
|
DVNS_SAVEPVK | DVNS_DUMPKEYS,
|
|
pwszCertName,
|
|
iCert,
|
|
iCRL,
|
|
iCTL,
|
|
pwszfnPVKBaseName,
|
|
NULL);
|
|
_JumpIfError(hr, error, "DumpAndVerifyNamedStore");
|
|
|
|
error:
|
|
if (NULL != pwszCertName)
|
|
{
|
|
LocalFree(pwszCertName);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbExportPFX(
|
|
IN WCHAR const *pwszOption,
|
|
IN WCHAR const *pwszCertIndex,
|
|
IN WCHAR const *pwszfnPFX,
|
|
IN WCHAR const *pwszPassword,
|
|
IN WCHAR const *pwszStoreName)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszCertName = NULL;
|
|
DWORD iCert;
|
|
DWORD iCRL;
|
|
DWORD iCTL;
|
|
|
|
hr = ParseCertCRLIndex(pwszCertIndex, &pwszCertName, &iCert, &iCRL, &iCTL);
|
|
_JumpIfErrorStr(hr, error, "ParseCertCRLIndex", pwszCertIndex);
|
|
|
|
hr = DumpAndVerifyNamedStore(
|
|
NULL == pwszStoreName? wszMY_CERTSTORE : pwszStoreName,
|
|
DVNS_SAVEPFX | DVNS_DUMPKEYS,
|
|
pwszCertName,
|
|
iCert,
|
|
iCRL,
|
|
iCTL,
|
|
pwszfnPFX,
|
|
pwszPassword);
|
|
_JumpIfError(hr, error, "DumpAndVerifyNamedStore");
|
|
|
|
error:
|
|
if (NULL != pwszCertName)
|
|
{
|
|
LocalFree(pwszCertName);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
cuImportChainAndKeys(
|
|
IN CERT_CHAIN_CONTEXT const *pChain,
|
|
IN BOOL fUser,
|
|
OPTIONAL IN WCHAR const *pwszStoreName)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_KEY_PROV_INFO *pkpi = NULL;
|
|
WCHAR *pwszSimpleName = NULL;
|
|
WCHAR *pwszRawContainerName = NULL;
|
|
WCHAR *pwszKeyContainerName = NULL;
|
|
CERT_CONTEXT const *pcc;
|
|
DWORD cwc;
|
|
WCHAR *pwsz;
|
|
|
|
if (NULL == pwszStoreName)
|
|
{
|
|
pwszStoreName = wszMY_CERTSTORE;
|
|
}
|
|
pcc = pChain->rgpChain[0]->rgpElement[0]->pCertContext;
|
|
|
|
hr = myCertGetNameString(
|
|
pcc,
|
|
CERT_NAME_SIMPLE_DISPLAY_TYPE,
|
|
&pwszSimpleName);
|
|
_JumpIfError(hr, error, "myCertGetNameString");
|
|
|
|
hr = myCertGetKeyProviderInfo(pcc, &pkpi);
|
|
_JumpIfError(hr, error, "myCertGetKeyProviderInfo");
|
|
|
|
for (pwsz = pkpi->pwszContainerName; L'\0' != *pwsz; pwsz++)
|
|
{
|
|
if (L'A' <= *pwsz && L'F' >= *pwsz)
|
|
{
|
|
*pwsz += L'a' - L'A';
|
|
}
|
|
}
|
|
pwsz = pkpi->pwszContainerName;
|
|
if (wcLBRACE == *pwsz)
|
|
{
|
|
pwsz++;
|
|
}
|
|
|
|
cwc = wcslen(pwszSimpleName) + 1 + wcslen(pwsz);
|
|
pwszRawContainerName = (WCHAR *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(cwc + 1) * sizeof(WCHAR));
|
|
if (NULL == pwszRawContainerName)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
wcscpy(pwszRawContainerName, pwszSimpleName);
|
|
wcscat(pwszRawContainerName, L"-");
|
|
wcscat(pwszRawContainerName, pwsz);
|
|
pwsz = &pwszRawContainerName[wcslen(pwszRawContainerName) - 1];
|
|
if (wcLBRACE == *pkpi->pwszContainerName && wcRBRACE == *pwsz)
|
|
{
|
|
*pwsz = L'\0';
|
|
}
|
|
|
|
hr = mySanitizeName(pwszRawContainerName, &pwszKeyContainerName);
|
|
_JumpIfError(hr, error, "mySanitizeName");
|
|
|
|
wprintf(L"%ws -- %ws\n", pwszSimpleName, pwszKeyContainerName);
|
|
|
|
hr = myCopyKeys(
|
|
pkpi,
|
|
pkpi->pwszContainerName, // pwszOldContainer
|
|
pwszKeyContainerName, // pwszNewContainer
|
|
fUser, // fOldUserKey
|
|
fUser, // fNewUserKey
|
|
g_fForce);
|
|
_JumpIfError(hr, error, "myCopyKeys");
|
|
|
|
pkpi->pwszContainerName = pwszKeyContainerName;
|
|
|
|
hr = mySaveChainAndKeys(
|
|
pChain->rgpChain[0],
|
|
pwszStoreName,
|
|
cuGetSystemStoreFlags(),
|
|
pkpi,
|
|
NULL);
|
|
_JumpIfError(hr, error, "mySaveChainAndKeys");
|
|
|
|
error:
|
|
if (NULL != pkpi)
|
|
{
|
|
LocalFree(pkpi);
|
|
}
|
|
if (NULL != pwszSimpleName)
|
|
{
|
|
LocalFree(pwszSimpleName);
|
|
}
|
|
if (NULL != pwszRawContainerName)
|
|
{
|
|
LocalFree(pwszRawContainerName);
|
|
}
|
|
if (NULL != pwszKeyContainerName)
|
|
{
|
|
LocalFree(pwszKeyContainerName);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbImportPFX(
|
|
IN WCHAR const *pwszOption,
|
|
IN WCHAR const *pwszfnPFX,
|
|
IN WCHAR const *pwszPassword,
|
|
IN WCHAR const *pwszStoreName,
|
|
IN WCHAR const *pwszArg4)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_DATA_BLOB pfx;
|
|
WCHAR wszPassword[MAX_PATH];
|
|
HCERTSTORE hStorePFX = NULL;
|
|
RESTORECHAIN *paRestoreChain = NULL;
|
|
DWORD cRestoreChain;
|
|
DWORD iChain;
|
|
BOOL fUser = !g_fEnterpriseRegistry && g_fUserRegistry;
|
|
|
|
pfx.pbData = NULL;
|
|
|
|
if (NULL == pwszPassword || 0 == wcscmp(L"*", pwszPassword))
|
|
{
|
|
hr = cuGetPassword(FALSE, wszPassword, ARRAYSIZE(wszPassword));
|
|
_JumpIfError(hr, error, "cuGetPassword");
|
|
|
|
pwszPassword = wszPassword;
|
|
}
|
|
hr = DecodeFileW(pwszfnPFX, &pfx.pbData, &pfx.cbData, CRYPT_STRING_ANY);
|
|
_JumpIfError(hr, error, "DecodeFileW");
|
|
|
|
CSASSERT(NULL != pfx.pbData);
|
|
|
|
if (!PFXIsPFXBlob(&pfx))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "PFXIsPFXBlob");
|
|
}
|
|
|
|
hStorePFX = myPFXImportCertStore(
|
|
&pfx,
|
|
pwszPassword,
|
|
CRYPT_EXPORTABLE |
|
|
(fUser? 0 : CRYPT_MACHINE_KEYSET));
|
|
if (NULL == hStorePFX)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myPFXImportCertStore");
|
|
}
|
|
|
|
cRestoreChain = 0;
|
|
hr = myGetChainArrayFromStore(
|
|
hStorePFX,
|
|
FALSE,
|
|
fUser,
|
|
NULL, // ppwszCommonName
|
|
&cRestoreChain,
|
|
NULL);
|
|
_JumpIfError(hr, error, "myGetChainArrayFromStore");
|
|
|
|
if (0 == cRestoreChain)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(CRYPT_E_SELF_SIGNED);
|
|
_JumpError(hr, error, "myGetChainArrayFromStore <no chain>");
|
|
}
|
|
|
|
paRestoreChain = (RESTORECHAIN *) LocalAlloc(
|
|
LMEM_FIXED | LMEM_ZEROINIT,
|
|
cRestoreChain * sizeof(paRestoreChain[0]));
|
|
if (NULL == paRestoreChain)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
hr = myGetChainArrayFromStore(
|
|
hStorePFX,
|
|
FALSE,
|
|
fUser,
|
|
NULL, // ppwszCommonName
|
|
&cRestoreChain,
|
|
paRestoreChain);
|
|
_JumpIfError(hr, error, "myGetChainArrayFromStore");
|
|
|
|
for (iChain = 0; iChain < cRestoreChain; iChain++)
|
|
{
|
|
CERT_CHAIN_CONTEXT const *pChain = paRestoreChain[iChain].pChain;
|
|
CERT_PUBLIC_KEY_INFO *pPublicKeyInfo;
|
|
|
|
if (1 > pChain->cChain)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "No Chain Context");
|
|
}
|
|
hr = cuImportChainAndKeys(pChain, fUser, pwszStoreName);
|
|
_JumpIfError(hr, error, "cuImportChainAndKeys");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != paRestoreChain)
|
|
{
|
|
for (iChain = 0; iChain < cRestoreChain; iChain++)
|
|
{
|
|
if (NULL != paRestoreChain[iChain].pChain)
|
|
{
|
|
CertFreeCertificateChain(paRestoreChain[iChain].pChain);
|
|
}
|
|
}
|
|
LocalFree(paRestoreChain);
|
|
}
|
|
if (NULL != hStorePFX)
|
|
{
|
|
myDeleteGuidKeys(hStorePFX, !fUser);
|
|
CertCloseStore(hStorePFX, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
if (NULL != pfx.pbData)
|
|
{
|
|
LocalFree(pfx.pbData);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
AddStringToList(
|
|
IN WCHAR const *pwszNew,
|
|
IN OUT WCHAR ***papwsz)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszAlloc = NULL;
|
|
WCHAR **ppwsz;
|
|
DWORD i;
|
|
|
|
// Count the strings in the existing list.
|
|
// If the new string matches an existing string, return imemdiately.
|
|
|
|
ppwsz = *papwsz;
|
|
i = 0;
|
|
if (NULL != ppwsz)
|
|
{
|
|
for ( ; NULL != ppwsz[i]; i++)
|
|
{
|
|
if (0 == lstrcmp(pwszNew, ppwsz[i]))
|
|
{
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
hr = myDupString(pwszNew, &pwszAlloc);
|
|
_JumpIfError(hr, error, "myDupString");
|
|
|
|
ppwsz = (WCHAR **) LocalAlloc(LMEM_FIXED, (i + 2) * sizeof(*ppwsz));
|
|
if (NULL == ppwsz)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
// Insert the new string at the head of the list.
|
|
|
|
ppwsz[0] = pwszAlloc;
|
|
pwszAlloc = NULL;
|
|
if (0 != i)
|
|
{
|
|
CopyMemory(&ppwsz[1], *papwsz, i * sizeof(*ppwsz));
|
|
LocalFree(*papwsz);
|
|
}
|
|
ppwsz[i + 1] = NULL;
|
|
*papwsz = ppwsz;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszAlloc)
|
|
{
|
|
LocalFree(pwszAlloc);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
AddPFXToStore(
|
|
IN WCHAR const *pwszfn,
|
|
IN HCERTSTORE hStoreMerge,
|
|
IN OUT WCHAR ***papwszPasswordList)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR const * const *ppwszPasswordList = *papwszPasswordList;
|
|
DWORD i;
|
|
CRYPT_DATA_BLOB pfx;
|
|
HCERTSTORE hStorePFX = NULL;
|
|
WCHAR wszPassword[MAX_PATH];
|
|
WCHAR const *pwszPassword;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
|
|
pfx.pbData = NULL;
|
|
|
|
hr = DecodeFileW(pwszfn, &pfx.pbData, &pfx.cbData, CRYPT_STRING_ANY);
|
|
_JumpIfError(hr, error, "DecodeFileW");
|
|
|
|
if (!PFXIsPFXBlob(&pfx))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "PFXIsPFXBlob");
|
|
}
|
|
|
|
// Try all of the passwords collected so far.
|
|
|
|
if (NULL != ppwszPasswordList)
|
|
{
|
|
for (i = 0; NULL != ppwszPasswordList[i]; i++)
|
|
{
|
|
hStorePFX = myPFXImportCertStore(
|
|
&pfx,
|
|
ppwszPasswordList[i],
|
|
CRYPT_EXPORTABLE);
|
|
if (NULL != hStorePFX)
|
|
{
|
|
break;
|
|
}
|
|
hr = myHLastError();
|
|
_PrintError2(
|
|
hr,
|
|
"myPFXImportCertStore",
|
|
HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD));
|
|
}
|
|
}
|
|
|
|
// Try the unparsed command line password, or collect a new one.
|
|
|
|
pwszPassword = g_pwszPassword;
|
|
while (NULL == hStorePFX)
|
|
{
|
|
if (NULL != pwszPassword)
|
|
{
|
|
hStorePFX = myPFXImportCertStore(
|
|
&pfx,
|
|
pwszPassword,
|
|
CRYPT_EXPORTABLE);
|
|
if (NULL != hStorePFX)
|
|
{
|
|
break;
|
|
}
|
|
hr = myHLastError();
|
|
_PrintError2(
|
|
hr,
|
|
"myPFXImportCertStore",
|
|
HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD));
|
|
}
|
|
|
|
wprintf(L"Enter password for %ws:\n", pwszfn);
|
|
hr = cuGetPassword(FALSE, wszPassword, ARRAYSIZE(wszPassword));
|
|
_JumpIfError(hr, error, "cuGetPassword");
|
|
|
|
hr = AddStringToList(wszPassword, papwszPasswordList);
|
|
_JumpIfError(hr, error, "AddStringToList");
|
|
|
|
pwszPassword = wszPassword;
|
|
}
|
|
CSASSERT(NULL != hStorePFX);
|
|
while (TRUE)
|
|
{
|
|
pCert = CertEnumCertificatesInStore(hStorePFX, pCert);
|
|
if (NULL == pCert)
|
|
{
|
|
break;
|
|
}
|
|
if (!CertAddCertificateContextToStore(
|
|
hStoreMerge,
|
|
pCert,
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
NULL))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertAddCertificateContextToStore");
|
|
}
|
|
if (!CertDeleteCertificateFromStore(pCert))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertDeleteCertificateFromStore");
|
|
}
|
|
pCert = NULL;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pCert)
|
|
{
|
|
CertFreeCertificateContext(pCert);
|
|
}
|
|
if (NULL != hStorePFX)
|
|
{
|
|
myDeleteGuidKeys(hStorePFX, FALSE);
|
|
CertCloseStore(hStorePFX, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
if (NULL != pfx.pbData)
|
|
{
|
|
LocalFree(pfx.pbData);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbMergePFX(
|
|
IN WCHAR const *pwszOption,
|
|
IN WCHAR const *pwszfnPFXInFileList,
|
|
IN WCHAR const *pwszfnPFXOutFile,
|
|
IN WCHAR const *pwszArg3,
|
|
IN WCHAR const *pwszArg4)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR **ppwszfnList = NULL;
|
|
WCHAR **ppwszPasswordList = NULL;
|
|
DWORD i;
|
|
HCERTSTORE hStoreMerge = NULL;
|
|
WCHAR *pwszPasswordAlloc = NULL;
|
|
WCHAR *pwszPasswordOut;
|
|
|
|
hr = cuParseStrings(
|
|
pwszfnPFXInFileList,
|
|
FALSE,
|
|
NULL,
|
|
NULL,
|
|
&ppwszfnList,
|
|
NULL);
|
|
_JumpIfError(hr, error, "cuParseStrings");
|
|
|
|
pwszPasswordOut = NULL;
|
|
if (NULL != g_pwszPassword)
|
|
{
|
|
hr = cuParseStrings(
|
|
g_pwszPassword,
|
|
FALSE,
|
|
NULL,
|
|
NULL,
|
|
&ppwszPasswordList,
|
|
NULL);
|
|
_JumpIfError(hr, error, "cuParseStrings");
|
|
|
|
for (i = 0; NULL != ppwszPasswordList[i]; i++)
|
|
{
|
|
}
|
|
if (i > 1 && 0 != lstrcmp(L"*", ppwszPasswordList[i - 1]))
|
|
{
|
|
pwszPasswordOut = ppwszPasswordList[i - 1];
|
|
}
|
|
}
|
|
hStoreMerge = CertOpenStore(
|
|
CERT_STORE_PROV_MEMORY,
|
|
X509_ASN_ENCODING,
|
|
NULL,
|
|
CERT_STORE_NO_CRYPT_RELEASE_FLAG |
|
|
CERT_STORE_ENUM_ARCHIVED_FLAG,
|
|
NULL);
|
|
if (NULL == hStoreMerge)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
for (i = 0; NULL != ppwszfnList[i]; i++)
|
|
{
|
|
hr = AddPFXToStore(
|
|
ppwszfnList[i],
|
|
hStoreMerge,
|
|
&ppwszPasswordList);
|
|
_JumpIfError(hr, error, "AddPFXToStore");
|
|
}
|
|
hr = SavePFXStoreToFile(
|
|
hStoreMerge,
|
|
pwszfnPFXOutFile,
|
|
pwszPasswordOut,
|
|
&pwszPasswordAlloc);
|
|
_JumpIfError(hr, error, "SavePFXStoreToFile");
|
|
|
|
error:
|
|
if (NULL != hStoreMerge)
|
|
{
|
|
myDeleteGuidKeys(hStoreMerge, FALSE);
|
|
CertCloseStore(hStoreMerge, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
cuFreeStringArray(ppwszPasswordList);
|
|
cuFreeStringArray(ppwszfnList);
|
|
if (NULL != pwszPasswordAlloc)
|
|
{
|
|
LocalFree(pwszPasswordAlloc);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
GetMarshaledDword(
|
|
IN BOOL fFetchLength,
|
|
IN OUT BYTE const **ppb,
|
|
IN OUT DWORD *pcb,
|
|
OUT DWORD *pdw)
|
|
{
|
|
HRESULT hr = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
|
|
if (sizeof(*pdw) > *pcb)
|
|
{
|
|
_JumpError(hr, error, "input buffer too small");
|
|
}
|
|
*pdw = *(DWORD UNALIGNED *) *ppb;
|
|
*ppb += sizeof(*pdw);
|
|
*pcb -= sizeof(*pdw);
|
|
if (fFetchLength && *pdw > *pcb)
|
|
{
|
|
_JumpError(hr, error, "input buffer too small for length");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
DecodeSequence(
|
|
IN BYTE const *pbSeq,
|
|
IN DWORD cbSeq,
|
|
IN DWORD cSeq,
|
|
OUT CRYPT_SEQUENCE_OF_ANY **ppSeq)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cb;
|
|
DWORD i;
|
|
CRYPT_SEQUENCE_OF_ANY *pSeq = NULL;
|
|
|
|
*ppSeq = NULL;
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_SEQUENCE_OF_ANY,
|
|
pbSeq,
|
|
cbSeq,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pSeq,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
if (cSeq != pSeq->cValue)
|
|
{
|
|
_JumpError(hr, error, "Sequence count");
|
|
}
|
|
for (i = 0; i < cSeq; i++)
|
|
{
|
|
if (NULL == pSeq->rgValue[i].pbData || 0 == pSeq->rgValue[i].cbData)
|
|
{
|
|
_JumpError(hr, error, "Empty Sequence");
|
|
}
|
|
}
|
|
*ppSeq = pSeq;
|
|
pSeq = NULL;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pSeq)
|
|
{
|
|
LocalFree(pSeq);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
#define k_PrivateKeyVersion 0
|
|
|
|
HRESULT
|
|
VerifyKeyVersion(
|
|
IN BYTE const *pbIn,
|
|
IN DWORD cbIn)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwKeyVersion;
|
|
DWORD cb;
|
|
|
|
dwKeyVersion = MAXDWORD;
|
|
cb = sizeof(dwKeyVersion);
|
|
if (!CryptDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_INTEGER,
|
|
pbIn,
|
|
cbIn,
|
|
0,
|
|
&dwKeyVersion,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptDecodeObject");
|
|
}
|
|
if (k_PrivateKeyVersion != dwKeyVersion)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "Public key version");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Inputs a private key in PKCS PrivateKeyInfo format:
|
|
// RSAPrivateKeyInfo ::= SEQUENCE {
|
|
// version Version, -- only 0 supported
|
|
// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
|
|
// privateKey PrivateKey
|
|
// }
|
|
//
|
|
// Version ::= INTEGER
|
|
//
|
|
// PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
|
|
//
|
|
// PrivateKey ::= OCTET STRING -- contains an RSAPrivateKey
|
|
//
|
|
// RSAPrivateKey ::= SEQUENCE {
|
|
// version Version, -- only 0 supported
|
|
// modulus INTEGER, -- n
|
|
// publicExponent INTEGER, -- e
|
|
// privateExponent INTEGER, -- d
|
|
// prime1 INTEGER, -- p
|
|
// prime2 INTEGER, -- q
|
|
// exponent1 INTEGER, -- d mod (p-1)
|
|
// exponent2 INTEGER, -- d mod (q-1)
|
|
// coefficient INTEGER -- (inverse of q) mod p
|
|
// }
|
|
//
|
|
// returns a PRIVATEKEYBLOB
|
|
//--------------------------------------------------------------------------
|
|
|
|
// Indexes into pSeqOuter:
|
|
#define ISO_VERSION 0
|
|
#define ISO_ALG 1
|
|
#define ISO_KEY 2
|
|
#define ISO_MAX 3 // number of elements
|
|
|
|
// Indexes into pSeqAlg:
|
|
#define ISA_OID 0
|
|
#define ISA_PARM 1
|
|
#define ISA_MAX 2 // number of elements
|
|
|
|
// Indexes into pSeqKey:
|
|
#define ISK_VERSION 0
|
|
#define ISK_MODULUS 1 // public key
|
|
#define ISK_PUBEXP 2
|
|
#define ISK_PRIVEXP 3
|
|
#define ISK_PRIME1 4
|
|
#define ISK_PRIME2 5
|
|
#define ISK_EXP1 6
|
|
#define ISK_EXP2 7
|
|
#define ISK_COEFF 8
|
|
#define ISK_MAX 9 // number of elements
|
|
|
|
typedef struct _KEYBLOBMAP
|
|
{
|
|
DWORD dwisk; // index into pSeqKey: ISK_*
|
|
DWORD dwdivisor; // cbitKey/dwDivisor is expected byte count
|
|
} KEYBLOBMAP;
|
|
|
|
// The KEYBLOBMAP array defines the order and expected size of the key element
|
|
// integers as they will appear in the RSA PRIVATEKEYBLOB.
|
|
|
|
KEYBLOBMAP g_akbm[] = {
|
|
{ ISK_MODULUS, 8 }, // public key
|
|
{ ISK_PRIME1, 16 },
|
|
{ ISK_PRIME2, 16 },
|
|
{ ISK_EXP1, 16 },
|
|
{ ISK_EXP2, 16 },
|
|
{ ISK_COEFF, 16 },
|
|
{ ISK_PRIVEXP, 8 },
|
|
};
|
|
|
|
HRESULT
|
|
myDecodeKMSRSAKey(
|
|
IN BYTE const *pbKMSRSAKey,
|
|
IN DWORD cbKMSRSAKey,
|
|
OUT BYTE **ppbKey,
|
|
OUT DWORD *pcbKey)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_SEQUENCE_OF_ANY *pSeqOuter = NULL;
|
|
CRYPT_SEQUENCE_OF_ANY *pSeqAlg = NULL;
|
|
CRYPT_SEQUENCE_OF_ANY *pSeqKey = NULL;
|
|
DWORD cb;
|
|
DWORD i;
|
|
BYTE *pb;
|
|
BYTE *pbKey = NULL;
|
|
DWORD cbKey;
|
|
DWORD cbitKey;
|
|
char *pszObjId = NULL;
|
|
CRYPT_DATA_BLOB *pBlobKey = NULL;
|
|
CRYPT_INTEGER_BLOB *apIntKey[ISK_MAX];
|
|
DWORD dwPubExp;
|
|
|
|
*ppbKey = NULL;
|
|
ZeroMemory(apIntKey, sizeof(apIntKey));
|
|
hr = DecodeSequence(pbKMSRSAKey, cbKMSRSAKey, ISO_MAX, &pSeqOuter);
|
|
_JumpIfError(hr, error, "DecodeSequence");
|
|
|
|
hr = VerifyKeyVersion(
|
|
pSeqOuter->rgValue[ISO_VERSION].pbData,
|
|
pSeqOuter->rgValue[ISO_VERSION].cbData);
|
|
_JumpIfError(hr, error, "VerifyKeyVersion");
|
|
|
|
hr = DecodeSequence(
|
|
pSeqOuter->rgValue[ISO_ALG].pbData,
|
|
pSeqOuter->rgValue[ISO_ALG].cbData,
|
|
ISA_MAX,
|
|
&pSeqAlg);
|
|
_JumpIfError(hr, error, "DecodeSequence");
|
|
|
|
hr = cuDecodeObjId(
|
|
pSeqAlg->rgValue[ISA_OID].pbData,
|
|
pSeqAlg->rgValue[ISA_OID].cbData,
|
|
&pszObjId);
|
|
_JumpIfError(hr, error, "cuDecodeObjId");
|
|
|
|
// key algorithm must be szOID_RSA_RSA
|
|
|
|
if (0 != strcmp(szOID_RSA_RSA, pszObjId))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "Bad key alg ObjId");
|
|
}
|
|
|
|
// key algorithm parms must be NULL (BER_NULL, cb == 0)
|
|
|
|
if (2 != pSeqAlg->rgValue[ISA_PARM].cbData ||
|
|
BER_NULL != pSeqAlg->rgValue[ISA_PARM].pbData[0] ||
|
|
0 != pSeqAlg->rgValue[ISA_PARM].pbData[1])
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "Bad key alg parameters");
|
|
}
|
|
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_OCTET_STRING,
|
|
pSeqOuter->rgValue[ISO_KEY].pbData,
|
|
pSeqOuter->rgValue[ISO_KEY].cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pBlobKey,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
|
|
hr = DecodeSequence(
|
|
pBlobKey->pbData,
|
|
pBlobKey->cbData,
|
|
ARRAYSIZE(apIntKey),
|
|
&pSeqKey);
|
|
_JumpIfError(hr, error, "DecodeSequence");
|
|
|
|
hr = VerifyKeyVersion(
|
|
pSeqKey->rgValue[ISK_VERSION].pbData,
|
|
pSeqKey->rgValue[ISK_VERSION].cbData);
|
|
_JumpIfError(hr, error, "VerifyKeyVersion");
|
|
|
|
cb = sizeof(dwPubExp);
|
|
if (!CryptDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_INTEGER,
|
|
pSeqKey->rgValue[ISK_PUBEXP].pbData,
|
|
pSeqKey->rgValue[ISK_PUBEXP].cbData,
|
|
0,
|
|
&dwPubExp,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptDecodeObject");
|
|
}
|
|
|
|
for (i = 0; i < ARRAYSIZE(apIntKey); i++)
|
|
{
|
|
if (!myDecodeObject(
|
|
X509_ASN_ENCODING,
|
|
X509_MULTI_BYTE_INTEGER,
|
|
pSeqKey->rgValue[i].pbData,
|
|
pSeqKey->rgValue[i].cbData,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &apIntKey[i],
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myDecodeObject");
|
|
}
|
|
}
|
|
cbitKey = apIntKey[ISK_MODULUS]->cbData * 8;
|
|
|
|
#if 0
|
|
for (i = 0; i < ARRAYSIZE(apIntKey); i++)
|
|
{
|
|
wprintf(wszNewLine);
|
|
DumpHex(
|
|
DH_NOTABPREFIX | DH_NOASCIIHEX | 4,
|
|
apIntKey[i]->pbData,
|
|
apIntKey[i]->cbData);
|
|
}
|
|
#endif
|
|
cbKey = sizeof(BLOBHEADER) + sizeof(RSAPUBKEY);
|
|
for (i = 0; i < ARRAYSIZE(g_akbm); i++)
|
|
{
|
|
cbKey += cbitKey / g_akbm[i].dwdivisor;
|
|
}
|
|
pbKey = (BYTE *) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, cbKey);
|
|
if (NULL == pbKey)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
pb = pbKey;
|
|
((BLOBHEADER *) pb)->bType = PRIVATEKEYBLOB;
|
|
((BLOBHEADER *) pb)->bVersion = CUR_BLOB_VERSION;
|
|
((BLOBHEADER *) pb)->aiKeyAlg = CALG_RSA_KEYX;
|
|
|
|
pb += sizeof(BLOBHEADER);
|
|
((RSAPUBKEY *) pb)->magic = 0x32415352; // "RSA2"
|
|
((RSAPUBKEY *) pb)->bitlen = cbitKey;
|
|
((RSAPUBKEY *) pb)->pubexp = dwPubExp;
|
|
|
|
pb += sizeof(RSAPUBKEY);
|
|
for (i = 0; i < ARRAYSIZE(g_akbm); i++)
|
|
{
|
|
CSASSERT(ISK_MAX > g_akbm[i].dwisk);
|
|
cb = cbitKey / g_akbm[i].dwdivisor;
|
|
if (cb != apIntKey[g_akbm[i].dwisk]->cbData)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "Bad key element size");
|
|
}
|
|
CopyMemory(pb, apIntKey[g_akbm[i].dwisk]->pbData, cb);
|
|
pb += cb;
|
|
}
|
|
CSASSERT(pb = &pbKey[cbKey]);
|
|
if (g_fVerbose)
|
|
{
|
|
wprintf(L"%d bit key\n", cbitKey);
|
|
if (1 < g_fVerbose)
|
|
{
|
|
DumpHex(DH_NOTABPREFIX | 4, pbKey, cbKey);
|
|
}
|
|
}
|
|
*pcbKey = cbKey;
|
|
*ppbKey = pbKey;
|
|
pbKey = NULL;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pSeqOuter)
|
|
{
|
|
LocalFree(pSeqOuter);
|
|
}
|
|
if (NULL != pSeqAlg)
|
|
{
|
|
LocalFree(pSeqAlg);
|
|
}
|
|
if (NULL != pSeqKey)
|
|
{
|
|
LocalFree(pSeqKey);
|
|
}
|
|
if (NULL != pszObjId)
|
|
{
|
|
LocalFree(pszObjId);
|
|
}
|
|
if (NULL != pBlobKey)
|
|
{
|
|
LocalFree(pBlobKey);
|
|
}
|
|
for (i = 0; i < ARRAYSIZE(apIntKey); i++)
|
|
{
|
|
if (NULL != apIntKey[i])
|
|
{
|
|
LocalFree(apIntKey[i]);
|
|
}
|
|
}
|
|
if (NULL != pbKey)
|
|
{
|
|
LocalFree(pbKey);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myVerifyKMSKey(
|
|
IN BYTE const *pbCert,
|
|
IN DWORD cbCert,
|
|
IN BYTE const *pbKey,
|
|
IN DWORD cbKey)
|
|
{
|
|
HRESULT hr;
|
|
CERT_CONTEXT const *pCert;
|
|
HCRYPTPROV hProv = NULL;
|
|
HCRYPTKEY hKey = NULL;
|
|
CERT_PUBLIC_KEY_INFO *pPublicKeyInfo = NULL;
|
|
DWORD cb;
|
|
|
|
pCert = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCert);
|
|
if (NULL == pCert)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
|
|
if (!CryptAcquireContext(
|
|
&hProv,
|
|
NULL,
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
CRYPT_VERIFYCONTEXT))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptAcquireContext");
|
|
}
|
|
if (!CryptImportKey(hProv, pbKey, cbKey, NULL, CRYPT_EXPORTABLE, &hKey))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptImportKey");
|
|
}
|
|
if (!myCryptExportPublicKeyInfo(
|
|
hProv,
|
|
AT_KEYEXCHANGE,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
&pPublicKeyInfo,
|
|
&cb))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myCryptExportPublicKeyInfo");
|
|
}
|
|
if (g_fVerbose)
|
|
{
|
|
cuDumpVersion(pCert->pCertInfo->dwVersion + 1);
|
|
if (1 < g_fVerbose)
|
|
{
|
|
cuDumpPublicKey(&pCert->pCertInfo->SubjectPublicKeyInfo);
|
|
cuDumpPublicKey(pPublicKeyInfo);
|
|
}
|
|
}
|
|
|
|
if (!myCertComparePublicKeyInfo(
|
|
X509_ASN_ENCODING,
|
|
CERT_V1 == pCert->pCertInfo->dwVersion,
|
|
pPublicKeyInfo,
|
|
&pCert->pCertInfo->SubjectPublicKeyInfo))
|
|
{
|
|
// by design, (my)CertComparePublicKeyInfo doesn't set last error!
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
wprintf(myLoadResourceString(IDS_ERR_PUBLICKEY_MISMATCH)); // "ERROR: Certificate public key does NOT match stored keyset"
|
|
wprintf(wszNewLine);
|
|
_JumpError(hr, error, "myCertComparePublicKeyInfo");
|
|
}
|
|
hr = myValidateKeyForEncrypting(
|
|
hProv,
|
|
&pCert->pCertInfo->SubjectPublicKeyInfo,
|
|
CALG_RC4);
|
|
if (S_OK != hr)
|
|
{
|
|
wprintf(myLoadResourceString(IDS_ERR_PRIVATEKEY_MISMATCH)); // "ERROR: Certificate public key does NOT match private key"
|
|
wprintf(wszNewLine);
|
|
_JumpError(hr, error, "myValidateKeyForEncrypting");
|
|
}
|
|
if (g_fVerbose)
|
|
{
|
|
wprintf(L"Private key verifies\n");
|
|
}
|
|
|
|
error:
|
|
if (NULL != pCert)
|
|
{
|
|
CertFreeCertificateContext(pCert);
|
|
}
|
|
if (NULL != pPublicKeyInfo)
|
|
{
|
|
LocalFree(pPublicKeyInfo);
|
|
}
|
|
if (NULL != hKey)
|
|
{
|
|
CryptDestroyKey(hKey);
|
|
}
|
|
if (NULL != hProv)
|
|
{
|
|
CryptReleaseContext(hProv, 0);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
cuDumpAsnBinaryQuiet(
|
|
IN BYTE const *pb,
|
|
IN DWORD cb,
|
|
IN DWORD iElement)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fVerboseOld = g_fVerbose;
|
|
BOOL fQuietOld = g_fQuiet;
|
|
|
|
if (g_fVerbose)
|
|
{
|
|
g_fVerbose--;
|
|
}
|
|
else
|
|
{
|
|
g_fQuiet = TRUE;
|
|
}
|
|
hr = cuDumpAsnBinary(pb, cb, iElement);
|
|
_JumpIfError(hr, error, "cuDumpAsnBinary");
|
|
|
|
error:
|
|
g_fVerbose = fVerboseOld;
|
|
g_fQuiet = fQuietOld;
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ReadTaggedBlob(
|
|
IN HANDLE hFile,
|
|
IN DWORD cbRemain,
|
|
OUT TagHeader *pth,
|
|
OUT BYTE **ppb)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cbRead;
|
|
|
|
*ppb = NULL;
|
|
if (!ReadFile(hFile, pth, sizeof(*pth), &cbRead, NULL))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "ReadFile");
|
|
}
|
|
hr = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
if (cbRead != sizeof(*pth))
|
|
{
|
|
DBGPRINT((
|
|
DBG_SS_ERROR,
|
|
"ReadFile read %u bytes, requested %u\n",
|
|
cbRead,
|
|
sizeof(*pth)));
|
|
_JumpError(hr, error, "ReadFile(cbRead)");
|
|
}
|
|
if (cbRead + pth->cbSize > cbRemain)
|
|
{
|
|
DBGPRINT((
|
|
DBG_SS_ERROR,
|
|
"Header size %u bytes, cbRemain %u\n",
|
|
sizeof(*pth) + pth->cbSize,
|
|
cbRemain));
|
|
_JumpError(hr, error, "cbRemain");
|
|
}
|
|
|
|
*ppb = (BYTE *) LocalAlloc(LMEM_FIXED, pth->cbSize);
|
|
if (NULL == *ppb)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
if (!ReadFile(hFile, *ppb, pth->cbSize, &cbRead, NULL))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "ReadFile");
|
|
}
|
|
if (cbRead != pth->cbSize)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
DBGPRINT((
|
|
DBG_SS_ERROR,
|
|
"ReadFile read %u bytes, requested %u\n",
|
|
cbRead,
|
|
pth->cbSize));
|
|
_JumpError(hr, error, "ReadFile(cbRead)");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (S_OK != hr && NULL != *ppb)
|
|
{
|
|
LocalFree(*ppb);
|
|
*ppb = NULL;
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DumpKMSTag(
|
|
IN TagHeader const *pth)
|
|
{
|
|
WCHAR const *pwsz;
|
|
WCHAR awctag[20];
|
|
|
|
pwsz = NULL;
|
|
switch (pth->tag)
|
|
{
|
|
case KMS_LOCKBOX_TAG: pwsz = L"KMS_LOCKBOX_TAG"; break;
|
|
case KMS_SIGNING_CERT_TAG: pwsz = L"KMS_SIGNING_CERT_TAG"; break;
|
|
case KMS_SIGNATURE_TAG: pwsz = L"KMS_SIGNATURE_TAG"; break;
|
|
case KMS_USER_RECORD_TAG: pwsz = L"KMS_USER_RECORD_TAG"; break;
|
|
default:
|
|
swprintf(awctag, L"%u", pth->tag);
|
|
pwsz = awctag;
|
|
break;
|
|
}
|
|
if (1 < g_fVerbose)
|
|
{
|
|
wprintf(
|
|
L"%ws: %x (%u) %ws\n",
|
|
pwsz,
|
|
pth->cbSize,
|
|
pth->cbSize,
|
|
myLoadResourceString(IDS_BYTES)); // "Bytes"
|
|
}
|
|
return(pwsz != awctag); // TRUE if tag is valid
|
|
}
|
|
|
|
|
|
HRESULT
|
|
VerifyKMSExportFile(
|
|
IN HANDLE hFile,
|
|
IN DWORD cbFile,
|
|
OUT CERT_CONTEXT const **ppccSigner)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cbRemain;
|
|
DWORD cbRead;
|
|
TagHeader th;
|
|
BYTE *pb = NULL;
|
|
CERT_CONTEXT const *pccSigner = NULL;
|
|
HCRYPTPROV hProv = NULL;
|
|
HCRYPTHASH hHash = NULL;
|
|
HCRYPTKEY hkeyPub = NULL;
|
|
BOOL fVerified = FALSE;
|
|
WCHAR *pwszSubject = NULL;
|
|
|
|
*ppccSigner = NULL;
|
|
|
|
if (!CryptAcquireContext(
|
|
&hProv,
|
|
NULL, // pszContainer
|
|
NULL, // pszProvider
|
|
PROV_RSA_FULL,
|
|
CRYPT_VERIFYCONTEXT))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptAcquireContext");
|
|
}
|
|
if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptCreateHash");
|
|
}
|
|
|
|
cbRemain = cbFile;
|
|
while (0 < cbRemain)
|
|
{
|
|
fVerified = FALSE;
|
|
CSASSERT(NULL == pb);
|
|
hr = ReadTaggedBlob(hFile, cbRemain, &th, &pb);
|
|
_JumpIfError(hr, error, "ReadTaggedBlob");
|
|
|
|
if (!DumpKMSTag(&th))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "invalid tag");
|
|
}
|
|
|
|
switch (th.tag)
|
|
{
|
|
case KMS_SIGNING_CERT_TAG:
|
|
|
|
if (g_fVerbose || g_fSplitASN)
|
|
{
|
|
hr = cuDumpAsnBinaryQuiet(pb, th.cbSize, MAXDWORD);
|
|
_JumpIfError(hr, error, "cuDumpAsnBinaryQuiet");
|
|
}
|
|
|
|
if (NULL != pccSigner)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
|
|
_JumpError(hr, error, "too many signers");
|
|
}
|
|
pccSigner = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
pb,
|
|
th.cbSize);
|
|
if (NULL == pccSigner)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
hr = myCertNameToStr(
|
|
X509_ASN_ENCODING,
|
|
&pccSigner->pCertInfo->Subject,
|
|
CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG,
|
|
&pwszSubject);
|
|
_PrintIfError(hr, "myCertNameToStr");
|
|
wprintf(L"Processing KMS exports from:");
|
|
wprintf(L"\n %ws\n\n", pwszSubject);
|
|
break;
|
|
|
|
case KMS_SIGNATURE_TAG:
|
|
if (NULL != hkeyPub)
|
|
{
|
|
_JumpError(hr, error, "too many signatures");
|
|
}
|
|
if (NULL == pccSigner)
|
|
{
|
|
hr = TRUST_E_NO_SIGNER_CERT;
|
|
_JumpError(hr, error, "no signer");
|
|
}
|
|
if (!CryptImportPublicKeyInfo(
|
|
hProv,
|
|
X509_ASN_ENCODING,
|
|
&pccSigner->pCertInfo->SubjectPublicKeyInfo,
|
|
&hkeyPub))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptImportPublicKeyInfo");
|
|
}
|
|
if (!CryptVerifySignature(
|
|
hHash,
|
|
pb,
|
|
th.cbSize,
|
|
hkeyPub,
|
|
NULL,
|
|
0))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptVerifySignature");
|
|
}
|
|
fVerified = TRUE;
|
|
wprintf(L"KMS export file signature verifies\n");
|
|
break;
|
|
|
|
default:
|
|
if (!CryptHashData(hHash, (BYTE *) &th, sizeof(th), 0))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptHashData");
|
|
}
|
|
if (!CryptHashData(hHash, pb, th.cbSize, 0))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptHashData");
|
|
}
|
|
break;
|
|
}
|
|
LocalFree(pb);
|
|
pb = NULL;
|
|
CSASSERT(cbRemain >= sizeof(th) + sizeof(th.cbSize));
|
|
cbRemain -= sizeof(th) + th.cbSize;
|
|
}
|
|
if (!fVerified)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "unsigned data");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszSubject)
|
|
{
|
|
LocalFree(pwszSubject);
|
|
}
|
|
if (NULL != pb)
|
|
{
|
|
LocalFree(pb);
|
|
}
|
|
if (NULL != hkeyPub)
|
|
{
|
|
CryptDestroyKey(hkeyPub);
|
|
}
|
|
if (NULL != hHash)
|
|
{
|
|
CryptDestroyHash(hHash);
|
|
}
|
|
if (NULL != hProv)
|
|
{
|
|
CryptReleaseContext(hProv, 0);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
myEncryptPrivateKey(
|
|
IN CERT_CONTEXT const *pccXchg,
|
|
IN BYTE const *pbKey,
|
|
IN DWORD cbKey,
|
|
OUT BYTE **ppbKeyEncrypted,
|
|
OUT DWORD *pcbKeyEncrypted)
|
|
{
|
|
HRESULT hr;
|
|
ALG_ID rgalgId[] = { CALG_3DES, CALG_RC4, CALG_RC2 };
|
|
DWORD i;
|
|
|
|
*ppbKeyEncrypted = NULL;
|
|
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
for (i = 0; i < ARRAYSIZE(rgalgId); i++)
|
|
{
|
|
// encryt into pkcs7
|
|
|
|
hr = myCryptEncryptMessage(
|
|
rgalgId[i],
|
|
1, // cCertRecipient
|
|
&pccXchg, // rgCertRecipient
|
|
pbKey,
|
|
cbKey,
|
|
NULL, // hCryptProv
|
|
ppbKeyEncrypted,
|
|
pcbKeyEncrypted);
|
|
if (S_OK == hr)
|
|
{
|
|
break; // done
|
|
}
|
|
_PrintError2(hr, "myCryptEncryptMessage", hr);
|
|
}
|
|
_JumpIfError(hr, error, "myCryptEncryptMessage");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
#define CB_IV 8
|
|
|
|
typedef struct _KMSSTATS {
|
|
DWORD cRecUser;
|
|
|
|
DWORD cCertWithoutKeys;
|
|
DWORD cCertTotal;
|
|
DWORD cCertNotSaved;
|
|
DWORD cCertAlreadySaved;
|
|
DWORD cCertSaved;
|
|
DWORD cCertSavedForeign;
|
|
|
|
DWORD cKeyTotal;
|
|
DWORD cKeyNotSaved;
|
|
DWORD cKeyAlreadySaved;
|
|
DWORD cKeySaved;
|
|
DWORD cKeySavedOverwrite;
|
|
} KMSSTATS;
|
|
|
|
|
|
HRESULT
|
|
ArchiveCertAndKey(
|
|
IN DISPATCHINTERFACE *pdiAdmin,
|
|
IN CERT_CONTEXT const *pccXchg,
|
|
IN BYTE const *pbCert,
|
|
IN DWORD cbCert,
|
|
IN BYTE const *pbKey,
|
|
IN DWORD cbKey,
|
|
IN OUT KMSSTATS *pkmsStats)
|
|
{
|
|
HRESULT hr;
|
|
LONG RequestId;
|
|
BYTE *pbKeyEncrypted = NULL;
|
|
DWORD cbKeyEncrypted;
|
|
CERT_CONTEXT const *pcc = NULL;
|
|
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
|
|
DWORD cbHash;
|
|
BSTR strHash = NULL;
|
|
BOOL fCertSaved = FALSE;
|
|
DWORD ids;
|
|
|
|
pcc = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCert);
|
|
if (NULL == pcc)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
ids = 0;
|
|
hr = Admin_ImportCertificate(
|
|
pdiAdmin,
|
|
g_pwszConfig,
|
|
(WCHAR const *) pbCert,
|
|
cbCert,
|
|
CR_IN_BINARY,
|
|
&RequestId);
|
|
if (g_fForce &&
|
|
S_OK != hr &&
|
|
HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS) != hr)
|
|
{
|
|
hr = Admin_ImportCertificate(
|
|
pdiAdmin,
|
|
g_pwszConfig,
|
|
(WCHAR const *) pbCert,
|
|
cbCert,
|
|
ICF_ALLOWFOREIGN | CR_IN_BINARY,
|
|
&RequestId);
|
|
if (S_OK == hr)
|
|
{
|
|
pkmsStats->cCertSavedForeign++;
|
|
ids = IDS_IMPORT_CERT_FOREIGN; // "Imported foreign certificate"
|
|
}
|
|
}
|
|
if (HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS) != hr)
|
|
{
|
|
_JumpIfError2(hr, error, "Admin_ImportCertificate", NTE_BAD_SIGNATURE);
|
|
|
|
//wprintf(L"RequestId = %u\n", RequestId);
|
|
pkmsStats->cCertSaved++;
|
|
if (0 == ids)
|
|
{
|
|
ids = IDS_IMPORT_CERT_DOMESTIC; // "Imported certificate"
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RequestId = MAXDWORD;
|
|
pkmsStats->cCertAlreadySaved++;
|
|
ids = IDS_IMPORT_CERT_EXISTS; // "Certificate exists"
|
|
|
|
cbHash = sizeof(abHash);
|
|
if (!CertGetCertificateContextProperty(
|
|
pcc,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
abHash,
|
|
&cbHash))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateContextProperty");
|
|
}
|
|
hr = MultiByteIntegerToBstr(TRUE, cbHash, abHash, &strHash);
|
|
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
|
|
}
|
|
fCertSaved = TRUE;
|
|
if (g_fVerbose)
|
|
{
|
|
wprintf(myLoadResourceString(ids));
|
|
wprintf(wszNewLine);
|
|
}
|
|
else
|
|
{
|
|
wprintf(L".");
|
|
}
|
|
|
|
hr = myEncryptPrivateKey(
|
|
pccXchg,
|
|
pbKey,
|
|
cbKey,
|
|
&pbKeyEncrypted,
|
|
&cbKeyEncrypted);
|
|
_JumpIfError(hr, error, "myEncryptPrivateKey");
|
|
|
|
ids = 0;
|
|
hr = Admin2_ImportKey(
|
|
pdiAdmin,
|
|
g_pwszConfig,
|
|
RequestId,
|
|
strHash,
|
|
CR_IN_BINARY,
|
|
(WCHAR const *) pbKeyEncrypted,
|
|
cbKeyEncrypted);
|
|
if (g_fForce && HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS) == hr)
|
|
{
|
|
hr = Admin2_ImportKey(
|
|
pdiAdmin,
|
|
g_pwszConfig,
|
|
RequestId,
|
|
strHash,
|
|
IKF_OVERWRITE | CR_IN_BINARY,
|
|
(WCHAR const *) pbKeyEncrypted,
|
|
cbKeyEncrypted);
|
|
if (S_OK == hr)
|
|
{
|
|
pkmsStats->cKeySavedOverwrite++;
|
|
ids = IDS_IMPORT_KEY_REPLACED; // "Archived key replaced"
|
|
}
|
|
}
|
|
if (HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS) != hr)
|
|
{
|
|
_JumpIfError(hr, error, "Admin2_ImportKey");
|
|
|
|
pkmsStats->cKeySaved++;
|
|
if (0 == ids)
|
|
{
|
|
ids = IDS_IMPORT_KEY_SAVED; // "Archived key"
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pkmsStats->cKeyAlreadySaved++;
|
|
ids = IDS_IMPORT_KEY_EXISTS; // "Key already archived"
|
|
}
|
|
if (g_fVerbose)
|
|
{
|
|
wprintf(myLoadResourceString(ids));
|
|
wprintf(wszNewLine);
|
|
}
|
|
else
|
|
{
|
|
wprintf(L".");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (S_OK != hr)
|
|
{
|
|
cuPrintErrorMessageText(hr);
|
|
if (!fCertSaved)
|
|
{
|
|
pkmsStats->cCertNotSaved++;
|
|
}
|
|
pkmsStats->cKeyNotSaved++;
|
|
}
|
|
if (NULL != pbKeyEncrypted)
|
|
{
|
|
LocalFree(pbKeyEncrypted);
|
|
}
|
|
if (NULL != strHash)
|
|
{
|
|
SysFreeString(strHash);
|
|
}
|
|
if (NULL != pcc)
|
|
{
|
|
CertFreeCertificateContext(pcc);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ImportOneKMSUser(
|
|
IN DISPATCHINTERFACE *pdiAdmin,
|
|
IN CERT_CONTEXT const *pccXchg,
|
|
IN BYTE const *pbRecUser,
|
|
IN DWORD cbRecUser,
|
|
IN HCRYPTKEY hkeySym,
|
|
IN OUT KMSSTATS *pkmsStats)
|
|
{
|
|
HRESULT hr;
|
|
BYTE const *pbT = pbRecUser;
|
|
WCHAR *pwszUser = NULL;
|
|
DWORD cbT = cbRecUser;
|
|
DWORD cb;
|
|
DWORD dw;
|
|
CLSID clsid;
|
|
WCHAR *pwszGUID = NULL;
|
|
BYTE *pbKeyASN = NULL;
|
|
DWORD cbKeyASN;
|
|
DWORD cbStream;
|
|
|
|
pkmsStats->cRecUser++;
|
|
|
|
// Get the user's directory GUID
|
|
|
|
CopyMemory(&clsid, pbT, sizeof(clsid));
|
|
|
|
hr = myCLSIDToWsz(&clsid, &pwszGUID);
|
|
_JumpIfError(hr, error, "myCLSIDToWsz");
|
|
|
|
pbT += sizeof(GUID);
|
|
cbT -= sizeof(GUID);
|
|
|
|
// Get the user's name length
|
|
|
|
hr = GetMarshaledDword(TRUE, &pbT, &cbT, &cb);
|
|
_JumpIfError(hr, error, "GetMarshaledDword");
|
|
|
|
pwszUser = (WCHAR *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
((cb / sizeof(WCHAR)) + 1) * sizeof(WCHAR));
|
|
if (NULL == pwszUser)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
CopyMemory(pwszUser, pbT, cb);
|
|
pwszUser[cb / sizeof(WCHAR)] = L'\0';
|
|
|
|
if (g_fVerbose)
|
|
{
|
|
wprintf(L"\n----------------------\n");
|
|
}
|
|
if (g_fVerbose)
|
|
{
|
|
wprintf(L"User: %ws -- %ws\n", pwszUser, pwszGUID);
|
|
}
|
|
|
|
pbT += cb;
|
|
cbT -= cb;
|
|
|
|
// for each User cert:
|
|
|
|
while (0 < cbT)
|
|
{
|
|
DWORD CertStatus;
|
|
FILETIME ftRevoke;
|
|
BYTE const *pbCert;
|
|
DWORD cbCert;
|
|
|
|
if (g_fVerbose)
|
|
{
|
|
wprintf(wszNewLine);
|
|
}
|
|
hr = GetMarshaledDword(FALSE, &pbT, &cbT, &CertStatus);
|
|
_JumpIfError(hr, error, "GetMarshaledDword");
|
|
|
|
if (1 < g_fVerbose)
|
|
{
|
|
wprintf(wszNewLine);
|
|
cuRegPrintDwordValue(
|
|
TRUE,
|
|
wszKMSCERTSTATUS,
|
|
wszKMSCERTSTATUS,
|
|
CertStatus);
|
|
}
|
|
|
|
hr = GetMarshaledDword(TRUE, &pbT, &cbT, &cb);
|
|
_JumpIfError(hr, error, "GetMarshaledDword");
|
|
|
|
// Dump one user cert:
|
|
|
|
pbCert = pbT;
|
|
cbCert = cb;
|
|
if (g_fSplitASN)
|
|
{
|
|
wprintf(wszNewLine);
|
|
hr = cuDumpAsnBinaryQuiet(pbCert, cbCert, MAXDWORD);
|
|
_JumpIfError(hr, error, "cuDumpAsnBinaryQuiet");
|
|
}
|
|
|
|
pbT += cb;
|
|
cbT -= cb;
|
|
|
|
// Get the revocation date (KMS export date):
|
|
|
|
hr = GetMarshaledDword(
|
|
FALSE,
|
|
&pbT,
|
|
&cbT,
|
|
&ftRevoke.dwLowDateTime);
|
|
_JumpIfError(hr, error, "GetMarshaledDword");
|
|
|
|
hr = GetMarshaledDword(
|
|
FALSE,
|
|
&pbT,
|
|
&cbT,
|
|
&ftRevoke.dwHighDateTime);
|
|
_JumpIfError(hr, error, "GetMarshaledDword");
|
|
|
|
if (g_fVerbose)
|
|
{
|
|
hr = cuDumpFileTime(IDS_REVOCATIONDATE, NULL, &ftRevoke);
|
|
_PrintIfError(hr, "cuDumpFileTime");
|
|
}
|
|
|
|
// Only encryption certs have archived keys:
|
|
|
|
if (0 == (CERTFLAGS_SEALING & CertStatus))
|
|
{
|
|
pkmsStats->cCertWithoutKeys++;
|
|
if (g_fVerbose)
|
|
{
|
|
wprintf(myLoadResourceString(IDS_IMPORT_CERT_SKIPPED_SIGNING)); // "Ignored signing certificate"
|
|
wprintf(wszNewLine);
|
|
}
|
|
continue;
|
|
}
|
|
pkmsStats->cCertTotal++;
|
|
pkmsStats->cKeyTotal++;
|
|
|
|
// get encrypted private key size
|
|
|
|
hr = GetMarshaledDword(TRUE, &pbT, &cbT, &cb);
|
|
_JumpIfError(hr, error, "GetMarshaledDword");
|
|
|
|
// get 8 byte RC2 IV
|
|
|
|
if (1 < g_fVerbose)
|
|
{
|
|
wprintf(L"IV:\n");
|
|
DumpHex(
|
|
DH_NOADDRESS | DH_NOTABPREFIX | DH_NOASCIIHEX | 4,
|
|
pbT,
|
|
CB_IV);
|
|
}
|
|
|
|
if (NULL != hkeySym)
|
|
{
|
|
// Set IV
|
|
|
|
if (!CryptSetKeyParam(
|
|
hkeySym,
|
|
KP_IV,
|
|
const_cast<BYTE *>(pbT),
|
|
0))
|
|
{
|
|
hr = GetLastError();
|
|
_JumpIfError(hr, error, "CryptSetKeyParam");
|
|
}
|
|
}
|
|
pbT += CB_IV;
|
|
cbT -= CB_IV;
|
|
cb -= CB_IV;
|
|
|
|
if (1 < g_fVerbose)
|
|
{
|
|
wprintf(wszNewLine);
|
|
wprintf(L"Encrypted key:\n");
|
|
DumpHex(0, pbT, cb);
|
|
}
|
|
|
|
// decrypt key using hkeySym
|
|
// in-place decode is Ok because the size of the
|
|
// original data is always less than or equal to that
|
|
// of the encrypted data
|
|
|
|
cbStream = cb; // save off the real stream size first
|
|
if (NULL != hkeySym)
|
|
{
|
|
cbKeyASN = cb;
|
|
pbKeyASN = (BYTE *) LocalAlloc(LMEM_FIXED, cb);
|
|
if (NULL == pbKeyASN)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
CopyMemory(pbKeyASN, pbT, cbKeyASN);
|
|
if (!CryptDecrypt(hkeySym, NULL, TRUE, 0, pbKeyASN, &cb))
|
|
{
|
|
hr = GetLastError();
|
|
_PrintError(hr, "CryptDecrypt");
|
|
}
|
|
else
|
|
{
|
|
BYTE *pbKey;
|
|
DWORD cbKey;
|
|
|
|
if (1 < g_fVerbose)
|
|
{
|
|
wprintf(wszNewLine);
|
|
wprintf(L"Decrypted key:\n");
|
|
DumpHex(0, pbKeyASN, cb);
|
|
}
|
|
|
|
hr = myDecodeKMSRSAKey(pbKeyASN, cbKeyASN, &pbKey, &cbKey);
|
|
_JumpIfError(hr, error, "myDecodeKMSRSAKey");
|
|
|
|
hr = myVerifyKMSKey(pbCert, cbCert, pbKey, cbKey);
|
|
_PrintIfError(hr, "myVerifyKMSKey");
|
|
|
|
hr = ArchiveCertAndKey(
|
|
pdiAdmin,
|
|
pccXchg,
|
|
pbCert,
|
|
cbCert,
|
|
pbKey,
|
|
cbKey,
|
|
pkmsStats);
|
|
_PrintIfError2(hr, "ArchiveCertAndKey", NTE_BAD_SIGNATURE);
|
|
|
|
ZeroMemory(pbKey, cbKey); // Key material
|
|
LocalFree(pbKey);
|
|
}
|
|
ZeroMemory(pbKeyASN, cbKeyASN); // Key material
|
|
LocalFree(pbKeyASN);
|
|
pbKeyASN = NULL;
|
|
}
|
|
|
|
// skip cbStream bytes, not cb
|
|
|
|
pbT += cbStream;
|
|
cbT -= cbStream;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszGUID)
|
|
{
|
|
LocalFree(pwszGUID);
|
|
}
|
|
if (NULL != pbKeyASN)
|
|
{
|
|
ZeroMemory(pbKeyASN, cbKeyASN); // Key material
|
|
LocalFree(pbKeyASN);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
GetCAXchgCert(
|
|
IN DISPATCHINTERFACE *pdiAdmin,
|
|
OUT CERT_CONTEXT const **ppccXchg)
|
|
{
|
|
HRESULT hr;
|
|
BSTR strCert = NULL;
|
|
|
|
*ppccXchg = NULL;
|
|
|
|
hr = Admin2_GetCAProperty(
|
|
pdiAdmin,
|
|
g_pwszConfig,
|
|
CR_PROP_CAXCHGCERT,
|
|
0, // PropIndex
|
|
PROPTYPE_BINARY,
|
|
CR_OUT_BINARY,
|
|
&strCert);
|
|
_JumpIfError(hr, error, "Admin2_GetCAProperty");
|
|
|
|
*ppccXchg = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING,
|
|
(BYTE const *) strCert,
|
|
SysStringByteLen(strCert));
|
|
if (NULL == *ppccXchg)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != strCert)
|
|
{
|
|
SysFreeString(strCert);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
typedef struct _KMSMAP {
|
|
DWORD dwFieldOffset;
|
|
DWORD idMsg;
|
|
} KMSMAP;
|
|
|
|
|
|
KMSMAP g_akmUsers[] = {
|
|
{ FIELD_OFFSET(KMSSTATS, cRecUser), IDS_KMS_USERS, },
|
|
};
|
|
|
|
KMSMAP g_akmCerts[] = {
|
|
{ FIELD_OFFSET(KMSSTATS, cCertWithoutKeys), IDS_KMS_CERTS_SKIPPED, },
|
|
{ FIELD_OFFSET(KMSSTATS, cCertTotal), IDS_KMS_CERTS_TOTAL, },
|
|
{ FIELD_OFFSET(KMSSTATS, cCertSavedForeign), IDS_KMS_CERTS_FOREIGN, },
|
|
{ FIELD_OFFSET(KMSSTATS, cCertAlreadySaved), IDS_KMS_CERTS_ALREADYSAVED, },
|
|
{ FIELD_OFFSET(KMSSTATS, cCertSaved), IDS_KMS_CERTS_SAVED, },
|
|
{ FIELD_OFFSET(KMSSTATS, cCertNotSaved), IDS_KMS_CERTS_NOTSAVED, },
|
|
};
|
|
|
|
KMSMAP g_akmKeys[] = {
|
|
{ FIELD_OFFSET(KMSSTATS, cKeyTotal), IDS_KMS_KEYS_TOTAL, },
|
|
{ FIELD_OFFSET(KMSSTATS, cKeyAlreadySaved), IDS_KMS_KEYS_ALREADYSAVED, },
|
|
{ FIELD_OFFSET(KMSSTATS, cKeySavedOverwrite), IDS_KMS_KEYS_UPDATED, },
|
|
{ FIELD_OFFSET(KMSSTATS, cKeySaved), IDS_KMS_KEYS_SAVED, },
|
|
{ FIELD_OFFSET(KMSSTATS, cKeyNotSaved), IDS_KMS_KEYS_NOTSAVED, },
|
|
};
|
|
|
|
|
|
VOID
|
|
DumpKMSMap(
|
|
IN KMSSTATS const *pkmsStats,
|
|
IN KMSMAP const *pkm,
|
|
IN DWORD ckm)
|
|
{
|
|
DWORD i;
|
|
BOOL fFirst = TRUE;
|
|
DWORD count;
|
|
|
|
for (i = 0; i < ckm; i++)
|
|
{
|
|
count = *(DWORD *) Add2ConstPtr(pkmsStats, pkm[i].dwFieldOffset);
|
|
if (g_fVerbose || 0 != count)
|
|
{
|
|
if (fFirst)
|
|
{
|
|
wprintf(wszNewLine);
|
|
fFirst = FALSE;
|
|
}
|
|
wprintf(myLoadResourceString(pkm[i].idMsg));
|
|
wprintf(L": %u\n", count);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
DumpKMSStats(
|
|
IN KMSSTATS const *pkmsStats)
|
|
{
|
|
DumpKMSMap(pkmsStats, g_akmUsers, ARRAYSIZE(g_akmUsers));
|
|
DumpKMSMap(pkmsStats, g_akmCerts, ARRAYSIZE(g_akmCerts));
|
|
DumpKMSMap(pkmsStats, g_akmKeys, ARRAYSIZE(g_akmKeys));
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ImportKMSExportedUsers(
|
|
IN HANDLE hFile,
|
|
IN DWORD cbFile,
|
|
IN HCRYPTPROV hProvKMS,
|
|
IN HCRYPTKEY hkeyKMS)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cbRemain;
|
|
DWORD cbRead;
|
|
TagHeader th;
|
|
BYTE *pb = NULL;
|
|
HCRYPTKEY hkeySym = NULL;
|
|
KMSSTATS kmsStats;
|
|
DISPATCHINTERFACE diAdmin;
|
|
BOOL fMustRelease = FALSE;
|
|
CERT_CONTEXT const *pccXchg = NULL;
|
|
|
|
ZeroMemory(&kmsStats, sizeof(kmsStats));
|
|
|
|
hr = Admin_Init(g_DispatchFlags, &diAdmin);
|
|
_JumpIfError(hr, error, "Admin_Init");
|
|
|
|
fMustRelease = TRUE;
|
|
|
|
hr = GetCAXchgCert(&diAdmin, &pccXchg);
|
|
_JumpIfError(hr, error, "GetCAXchgCert");
|
|
|
|
cbRemain = cbFile;
|
|
while (0 < cbRemain)
|
|
{
|
|
CSASSERT(NULL == pb);
|
|
hr = ReadTaggedBlob(hFile, cbRemain, &th, &pb);
|
|
_JumpIfError(hr, error, "ReadTaggedBlob");
|
|
|
|
if (!DumpKMSTag(&th))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "invalid tag");
|
|
}
|
|
|
|
switch (th.tag)
|
|
{
|
|
case KMS_LOCKBOX_TAG:
|
|
{
|
|
if (1 < g_fVerbose)
|
|
{
|
|
hr = cuDumpPrivateKeyBlob(pb, th.cbSize, FALSE);
|
|
_PrintIfError(hr, "cuDumpPrivateKeyBlob");
|
|
}
|
|
|
|
// only need one symmetric key per file
|
|
|
|
if (NULL == hkeySym)
|
|
{
|
|
// 0x0000660c ALG_ID CALG_RC2_128
|
|
//
|
|
// CALG_RC2_128:
|
|
// ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_RC2_128
|
|
//
|
|
// CALG_CYLINK_MEK:
|
|
// ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_CYLINK_MEK
|
|
//
|
|
// UGH! Exchange's CALG_RC2_128 #define collides with
|
|
// wincrypt.h's CALG_CYLINK_MEK -- fix it up to be the
|
|
// correct CALG_RC2 algid from wincrypt.h.
|
|
|
|
((PUBLICKEYSTRUC *) pb)->aiKeyAlg = CALG_RC2;
|
|
|
|
// dump the fixed-up blob
|
|
|
|
if (1 < g_fVerbose)
|
|
{
|
|
hr = cuDumpPrivateKeyBlob(pb, th.cbSize, FALSE);
|
|
_PrintIfError(hr, "cuDumpPrivateKeyBlob");
|
|
}
|
|
|
|
// import 128 bit key
|
|
|
|
if (!CryptImportKey(
|
|
hProvKMS,
|
|
pb,
|
|
th.cbSize,
|
|
hkeyKMS,
|
|
0,
|
|
&hkeySym))
|
|
{
|
|
hr = myHLastError();
|
|
_PrintError(hr, "CryptImportKey");
|
|
wprintf(L"Failed to import symmetric key\n");
|
|
}
|
|
else
|
|
{
|
|
// We found the right lockbox. Effective keylen is
|
|
// still 40 bits in our CSP, reset to 128
|
|
|
|
DWORD dwEffectiveKeylen = 128;
|
|
|
|
if (!CryptSetKeyParam(
|
|
hkeySym,
|
|
KP_EFFECTIVE_KEYLEN,
|
|
(BYTE *) &dwEffectiveKeylen,
|
|
0))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptSetKeyParam(KP_EFFECTIVE_KEYLEN)");
|
|
}
|
|
wprintf(L"Lock box opened, symmetric key successfully decrypted\n");
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case KMS_USER_RECORD_TAG:
|
|
hr = ImportOneKMSUser(
|
|
&diAdmin,
|
|
pccXchg,
|
|
pb,
|
|
th.cbSize,
|
|
hkeySym,
|
|
&kmsStats);
|
|
_JumpIfError(hr, error, "ImportOneKMSUser");
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
LocalFree(pb);
|
|
pb = NULL;
|
|
CSASSERT(cbRemain >= sizeof(th) + sizeof(th.cbSize));
|
|
cbRemain -= sizeof(th) + th.cbSize;
|
|
}
|
|
|
|
if (!g_fVerbose)
|
|
{
|
|
wprintf(wszNewLine);
|
|
}
|
|
DumpKMSStats(&kmsStats);
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pccXchg)
|
|
{
|
|
CertFreeCertificateContext(pccXchg);
|
|
}
|
|
if (fMustRelease)
|
|
{
|
|
Admin_Release(&diAdmin);
|
|
}
|
|
if (NULL != hkeySym)
|
|
{
|
|
CryptDestroyKey(hkeySym);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
LoadKMSCert(
|
|
IN WCHAR const *pwszCertId,
|
|
OUT CERT_CONTEXT const **ppccKMS,
|
|
OUT HCRYPTPROV *phProvKMS,
|
|
OUT HCRYPTKEY *phkeyKMS)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_KEY_PROV_INFO *pkpiKMS = NULL;
|
|
DWORD cbkpiKMS;
|
|
BYTE *pbKey = NULL;
|
|
DWORD cbKey;
|
|
HCRYPTKEY hkeyKMSSig = NULL;
|
|
|
|
*ppccKMS = NULL;
|
|
*phProvKMS = NULL;
|
|
*phkeyKMS = NULL;
|
|
|
|
hr = myGetCertificateFromPicker(
|
|
g_hInstance,
|
|
NULL, // hwndParent
|
|
IDS_GETCERT_TITLE, // "Certificate List"
|
|
IDS_GETDECRYPTCERT_SUBTITLE,
|
|
|
|
// dwFlags: HKLM+HKCU My store
|
|
CUCS_MYSTORE |
|
|
CUCS_MACHINESTORE |
|
|
CUCS_USERSTORE |
|
|
CUCS_PRIVATEKEYREQUIRED |
|
|
(g_fCryptSilent? CUCS_SILENT : 0),
|
|
pwszCertId,
|
|
0, // cStore
|
|
NULL, // rghStore
|
|
0, // cpszObjId
|
|
NULL, // apszObjId
|
|
ppccKMS); // ppCert
|
|
_JumpIfError(hr, error, "myGetCertificateFromPicker");
|
|
|
|
if (NULL == *ppccKMS)
|
|
{
|
|
hr = ERROR_CANCELLED;
|
|
_JumpError(hr, error, "myGetCertificateFromPicker");
|
|
}
|
|
|
|
if (!myCertGetCertificateContextProperty(
|
|
*ppccKMS,
|
|
CERT_KEY_PROV_INFO_PROP_ID,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
(VOID **) &pkpiKMS,
|
|
&cbkpiKMS))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myCertGetCertificateContextProperty");
|
|
}
|
|
if (g_fVerbose)
|
|
{
|
|
wprintf(L"CryptAcquireContext(%ws)\n", pkpiKMS->pwszContainerName);
|
|
}
|
|
if (!CryptAcquireContext(
|
|
phProvKMS,
|
|
pkpiKMS->pwszContainerName,
|
|
pkpiKMS->pwszProvName,
|
|
pkpiKMS->dwProvType,
|
|
pkpiKMS->dwFlags))
|
|
{
|
|
hr = myHLastError();
|
|
wprintf(L"CryptAcquireContext() --> %x\n", hr);
|
|
_JumpError(hr, error, "CryptAcquireContext");
|
|
}
|
|
|
|
if (!CryptGetUserKey(*phProvKMS, AT_KEYEXCHANGE, phkeyKMS))
|
|
{
|
|
hr = myHLastError();
|
|
if (hr != NTE_NO_KEY)
|
|
{
|
|
_JumpError(hr, error, "CryptGetUserKey");
|
|
}
|
|
|
|
if (!CryptGetUserKey(*phProvKMS, AT_SIGNATURE, &hkeyKMSSig))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptGetUserKey - sig");
|
|
}
|
|
|
|
// UGH! migrate from AT_SIGNATURE container!
|
|
|
|
cbKey = 0;
|
|
hr = myCryptExportKey(
|
|
hkeyKMSSig, // hKey
|
|
NULL, // hKeyExp
|
|
PRIVATEKEYBLOB, // dwBlobType
|
|
0, // dwFlags
|
|
&pbKey,
|
|
&cbKey);
|
|
_JumpIfError(hr, error, "myCryptExportKey");
|
|
|
|
// UGH! fix up the algid to signature...
|
|
|
|
((PUBLICKEYSTRUC *) pbKey)->aiKeyAlg = CALG_RSA_KEYX;
|
|
|
|
// and re-import it
|
|
|
|
if (!CryptImportKey(
|
|
*phProvKMS,
|
|
pbKey,
|
|
cbKey,
|
|
NULL,
|
|
0,
|
|
phkeyKMS))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptImportKey");
|
|
}
|
|
wprintf(L"Moved AT_SIGNATURE key to AT_KEYEXCHANGE\n");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (S_OK != hr)
|
|
{
|
|
if (NULL != *ppccKMS)
|
|
{
|
|
CertFreeCertificateContext(*ppccKMS);
|
|
*ppccKMS = NULL;
|
|
}
|
|
if (NULL != *phProvKMS)
|
|
{
|
|
CryptReleaseContext(*phProvKMS, 0);
|
|
*phProvKMS = NULL;
|
|
}
|
|
}
|
|
if (NULL != pbKey)
|
|
{
|
|
LocalFree(pbKey);
|
|
}
|
|
if (NULL != pkpiKMS)
|
|
{
|
|
LocalFree(pkpiKMS);
|
|
}
|
|
if (NULL != hkeyKMSSig)
|
|
{
|
|
CryptDestroyKey(hkeyKMSSig);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ImportOnePFXCert(
|
|
IN DISPATCHINTERFACE *pdiAdmin,
|
|
IN CERT_CONTEXT const *pccXchg,
|
|
IN CERT_CONTEXT const *pCert,
|
|
IN OUT KMSSTATS *pkmsStats)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_KEY_PROV_INFO *pkpi = NULL;
|
|
HCRYPTPROV hProv = NULL;
|
|
HCRYPTKEY hKey = NULL;
|
|
BYTE *pbKey = NULL;
|
|
DWORD cbKey;
|
|
|
|
hr = myCertGetKeyProviderInfo(pCert, &pkpi);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "myCertGetKeyProviderInfo");
|
|
pkmsStats->cCertWithoutKeys++;
|
|
hr = S_OK;
|
|
goto error;
|
|
}
|
|
pkmsStats->cCertTotal++;
|
|
if (!CryptAcquireContext(
|
|
&hProv,
|
|
pkpi->pwszContainerName,
|
|
pkpi->pwszProvName,
|
|
pkpi->dwProvType,
|
|
0))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CryptAcquireContext");
|
|
}
|
|
if (!CryptGetUserKey(hProv, pkpi->dwKeySpec, &hKey))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpIfError(hr, error, "CryptGetUserKey");
|
|
}
|
|
hr = myCryptExportPrivateKey(hKey, &pbKey, &cbKey);
|
|
_JumpIfError(hr, error, "myCryptExportPrivateKey");
|
|
|
|
pkmsStats->cKeyTotal++;
|
|
|
|
hr = myVerifyKMSKey(
|
|
pCert->pbCertEncoded,
|
|
pCert->cbCertEncoded,
|
|
pbKey,
|
|
cbKey);
|
|
_JumpIfError(hr, error, "myVerifyKMSKey");
|
|
|
|
hr = ArchiveCertAndKey(
|
|
pdiAdmin,
|
|
pccXchg,
|
|
pCert->pbCertEncoded,
|
|
pCert->cbCertEncoded,
|
|
pbKey,
|
|
cbKey,
|
|
pkmsStats);
|
|
_JumpIfError(hr, error, "ArchiveCertAndKey");
|
|
|
|
error:
|
|
if (NULL != pbKey)
|
|
{
|
|
ZeroMemory(pbKey, cbKey); // Key material
|
|
LocalFree(pbKey);
|
|
}
|
|
if (NULL != hProv)
|
|
{
|
|
CryptReleaseContext(hProv, 0);
|
|
}
|
|
if (NULL != pkpi)
|
|
{
|
|
LocalFree(pkpi);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ImportKMSPFXFile(
|
|
IN WCHAR const *pwszfn)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_DATA_BLOB pfx;
|
|
WCHAR wszPassword[MAX_PATH];
|
|
WCHAR const *pwszPassword;
|
|
CERT_CONTEXT const *pccXchg = NULL;
|
|
HCERTSTORE hStorePFX = NULL;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
DISPATCHINTERFACE diAdmin;
|
|
BOOL fMustRelease = FALSE;
|
|
KMSSTATS kmsStats;
|
|
|
|
ZeroMemory(&kmsStats, sizeof(kmsStats));
|
|
pfx.pbData = NULL;
|
|
|
|
hr = DecodeFileW(pwszfn, &pfx.pbData, &pfx.cbData, CRYPT_STRING_ANY);
|
|
if (S_OK != hr)
|
|
{
|
|
cuPrintError(IDS_ERR_FORMAT_DECODEFILE, hr);
|
|
goto error;
|
|
}
|
|
if (!PFXIsPFXBlob(&pfx))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "not a PFX");
|
|
}
|
|
|
|
pwszPassword = g_pwszPassword;
|
|
if (NULL == pwszPassword)
|
|
{
|
|
hr = cuGetPassword(FALSE, wszPassword, ARRAYSIZE(wszPassword));
|
|
_JumpIfError(hr, error, "cuGetPassword");
|
|
}
|
|
|
|
hStorePFX = myPFXImportCertStore(&pfx, pwszPassword, CRYPT_EXPORTABLE);
|
|
if (NULL == hStorePFX)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myPFXImportCertStore");
|
|
}
|
|
|
|
hr = Admin_Init(g_DispatchFlags, &diAdmin);
|
|
_JumpIfError(hr, error, "Admin_Init");
|
|
|
|
fMustRelease = TRUE;
|
|
|
|
hr = GetCAXchgCert(&diAdmin, &pccXchg);
|
|
_JumpIfError(hr, error, "GetCAXchgCert");
|
|
|
|
while (TRUE)
|
|
{
|
|
pCert = CertEnumCertificatesInStore(hStorePFX, pCert);
|
|
if (NULL == pCert)
|
|
{
|
|
break;
|
|
}
|
|
hr = ImportOnePFXCert(&diAdmin, pccXchg, pCert, &kmsStats);
|
|
_PrintIfError(hr, "ImportOnePFXCert");
|
|
}
|
|
DumpKMSStats(&kmsStats);
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != hStorePFX)
|
|
{
|
|
myDeleteGuidKeys(hStorePFX, FALSE);
|
|
CertCloseStore(hStorePFX, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
}
|
|
if (NULL != pccXchg)
|
|
{
|
|
CertFreeCertificateContext(pccXchg);
|
|
}
|
|
if (fMustRelease)
|
|
{
|
|
Admin_Release(&diAdmin);
|
|
}
|
|
if (NULL != pfx.pbData)
|
|
{
|
|
LocalFree(pfx.pbData);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ImportKMSExportFile(
|
|
IN WCHAR const *pwszfnKMS,
|
|
IN WCHAR const *pwszCertId,
|
|
OUT BOOL *pfBadTag)
|
|
{
|
|
HRESULT hr;
|
|
CERT_CONTEXT const *pccKMS = NULL;
|
|
HCRYPTPROV hProvKMS = NULL;
|
|
HCRYPTKEY hkeyKMS = NULL;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
DWORD cbFile;
|
|
CERT_CONTEXT const *pccSigner = NULL;
|
|
|
|
*pfBadTag = TRUE;
|
|
hFile = CreateFile(
|
|
pwszfnKMS,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL);
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CreateFile");
|
|
}
|
|
|
|
cbFile = GetFileSize(hFile, NULL);
|
|
if (MAXDWORD == cbFile)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "GetFileSize");
|
|
}
|
|
|
|
// verify the KMS data signature
|
|
|
|
hr = VerifyKMSExportFile(hFile, cbFile, &pccSigner);
|
|
_JumpIfError(hr, error, "VerifyKMSExportFile");
|
|
|
|
*pfBadTag = FALSE;
|
|
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "SetFilePointer");
|
|
}
|
|
|
|
// Load the KMS recipient cert to be used to decrypt user keys
|
|
|
|
hr = LoadKMSCert(pwszCertId, &pccKMS, &hProvKMS, &hkeyKMS);
|
|
_JumpIfError(hr, error, "LoadKMSCert");
|
|
|
|
// import the KMS data
|
|
|
|
hr = ImportKMSExportedUsers(hFile, cbFile, hProvKMS, hkeyKMS);
|
|
_JumpIfError(hr, error, "ImportKMSExportedUsers");
|
|
|
|
error:
|
|
if (NULL != pccKMS)
|
|
{
|
|
CertFreeCertificateContext(pccKMS);
|
|
}
|
|
if (NULL != pccSigner)
|
|
{
|
|
CertFreeCertificateContext(pccSigner);
|
|
}
|
|
if (NULL != hkeyKMS)
|
|
{
|
|
CryptDestroyKey(hkeyKMS);
|
|
}
|
|
if (NULL != hProvKMS)
|
|
{
|
|
CryptReleaseContext(hProvKMS, 0);
|
|
}
|
|
if (INVALID_HANDLE_VALUE != hFile)
|
|
{
|
|
CloseHandle(hFile);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbImportKMS(
|
|
IN WCHAR const *pwszOption,
|
|
IN WCHAR const *pwszfnKMS,
|
|
IN WCHAR const *pwszCertId,
|
|
IN WCHAR const *pwszArg3,
|
|
IN WCHAR const *pwszArg4)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fBadTag;
|
|
|
|
hr = ImportKMSExportFile(pwszfnKMS, pwszCertId, &fBadTag);
|
|
if (S_OK != hr)
|
|
{
|
|
_PrintError(hr, "ImportKMSExportFile");
|
|
if (!fBadTag)
|
|
{
|
|
goto error;
|
|
}
|
|
hr = ImportKMSPFXFile(pwszfnKMS);
|
|
_JumpIfError(hr, error, "ImportKMSPFXFile");
|
|
}
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
WCHAR const g_wszProviderNameDefault[] = MS_DEF_PROV_W;
|
|
DWORD g_dwProviderType = PROV_RSA_FULL;
|
|
|
|
|
|
HRESULT
|
|
EnumKeys(
|
|
IN WCHAR const *pwszProvName,
|
|
IN DWORD dwProvType,
|
|
IN BOOL fSkipKeys,
|
|
OPTIONAL IN WCHAR const *pwszKeyContainerName)
|
|
{
|
|
HRESULT hr;
|
|
KEY_LIST *pKeyList = NULL;
|
|
KEY_LIST *pKeyT;
|
|
WCHAR *pwszRevert = NULL;
|
|
CERT_PUBLIC_KEY_INFO *pPubKeyInfoSig = NULL;
|
|
CERT_PUBLIC_KEY_INFO *pPubKeyInfoXchg = NULL;
|
|
WCHAR const *pwszPrefix;
|
|
|
|
if (!fSkipKeys)
|
|
{
|
|
hr = csiGetKeyList(
|
|
dwProvType, // dwProvType
|
|
pwszProvName, // pwszProvName
|
|
!g_fUserRegistry, // fMachineKeyset
|
|
!g_fCryptSilent, // inverted fSilent: default is Silent!
|
|
&pKeyList);
|
|
_JumpIfErrorStr(hr, error, "csiGetKeyList", pwszProvName);
|
|
}
|
|
if (fSkipKeys || NULL != pKeyList)
|
|
{
|
|
wprintf(L"%ws:\n", pwszProvName);
|
|
}
|
|
for (pKeyT = pKeyList; NULL != pKeyT; pKeyT = pKeyT->next)
|
|
{
|
|
DWORD dwProvTypeT;
|
|
|
|
hr = myRevertSanitizeName(pKeyT->pwszName, &pwszRevert);
|
|
_JumpIfError(hr, error, "myRevertSanitizeName");
|
|
|
|
if (NULL == pwszKeyContainerName ||
|
|
0 == lstrcmpi(pwszKeyContainerName, pwszRevert))
|
|
{
|
|
wprintf(L" %ws", pwszRevert);
|
|
if (g_fVerbose && 0 != lstrcmp(pKeyT->pwszName, pwszRevert))
|
|
{
|
|
wprintf(L" -- %ws", pKeyT->pwszName);
|
|
}
|
|
wprintf(wszNewLine);
|
|
|
|
dwProvTypeT = dwProvType;
|
|
hr = cuLoadKeys(
|
|
pwszProvName,
|
|
&dwProvTypeT,
|
|
pKeyT->pwszName,
|
|
!g_fUserRegistry, // fMachineKeyset
|
|
TRUE,
|
|
NULL,
|
|
&pPubKeyInfoSig,
|
|
&pPubKeyInfoXchg);
|
|
if (S_OK != hr)
|
|
{
|
|
cuPrintError(IDS_ERR_FORMAT_LOADKEYS, hr);
|
|
}
|
|
if (NULL != pPubKeyInfoSig || NULL != pPubKeyInfoXchg)
|
|
{
|
|
pwszPrefix = g_wszPad4;
|
|
if (NULL != pPubKeyInfoSig)
|
|
{
|
|
wprintf(L" AT_SIGNATURE");
|
|
LocalFree(pPubKeyInfoSig);
|
|
pPubKeyInfoSig = NULL;
|
|
pwszPrefix = L", ";
|
|
}
|
|
if (NULL != pPubKeyInfoXchg)
|
|
{
|
|
wprintf(L"%wsAT_KEYEXCHANGE", pwszPrefix);
|
|
LocalFree(pPubKeyInfoXchg);
|
|
pPubKeyInfoXchg = NULL;
|
|
}
|
|
wprintf(wszNewLine);
|
|
}
|
|
if (NULL == pwszKeyContainerName)
|
|
{
|
|
wprintf(wszNewLine);
|
|
}
|
|
}
|
|
LocalFree(pwszRevert);
|
|
pwszRevert = NULL;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pPubKeyInfoSig)
|
|
{
|
|
LocalFree(pPubKeyInfoSig);
|
|
}
|
|
if (NULL != pPubKeyInfoXchg)
|
|
{
|
|
LocalFree(pPubKeyInfoXchg);
|
|
}
|
|
if (NULL != pwszRevert)
|
|
{
|
|
LocalFree(pwszRevert);
|
|
}
|
|
if (NULL != pKeyList)
|
|
{
|
|
csiFreeKeyList(pKeyList);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbKey(
|
|
IN WCHAR const *pwszOption,
|
|
OPTIONAL IN WCHAR const *pwszKeyContainerName,
|
|
OPTIONAL IN WCHAR const *pwszProvider,
|
|
IN WCHAR const *pwszArg3,
|
|
IN WCHAR const *pwszArg4)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszProvName = NULL;
|
|
DWORD i;
|
|
DWORD dwProvType;
|
|
BOOL fSkipKeys = FALSE;
|
|
|
|
if (NULL == pwszProvider)
|
|
{
|
|
pwszProvider = g_wszProviderNameDefault; // default CSP
|
|
}
|
|
else if (0 == lstrcmp(L"*", pwszProvider))
|
|
{
|
|
pwszProvider = NULL; // all CSPs
|
|
}
|
|
if (NULL != pwszKeyContainerName)
|
|
{
|
|
if (0 == lstrcmp(L"*", pwszKeyContainerName))
|
|
{
|
|
pwszKeyContainerName = NULL; // all keys
|
|
}
|
|
else if (0 == lstrcmp(L"-", pwszKeyContainerName))
|
|
{
|
|
pwszKeyContainerName = NULL; // all keys
|
|
fSkipKeys = TRUE;
|
|
}
|
|
}
|
|
|
|
if (NULL != pwszProvider)
|
|
{
|
|
hr = csiGetProviderTypeFromProviderName(pwszProvider, &dwProvType);
|
|
_JumpIfErrorStr(hr, error, "csiGetProviderTypeFromProviderName", pwszProvider);
|
|
|
|
hr = EnumKeys(
|
|
pwszProvider,
|
|
dwProvType,
|
|
fSkipKeys,
|
|
pwszKeyContainerName);
|
|
_JumpIfErrorStr(hr, error, "EnumKeys", pwszProvider);
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; ; i++)
|
|
{
|
|
CSASSERT(NULL == pwszProvName);
|
|
hr = myEnumProviders(i, NULL, 0, &dwProvType, &pwszProvName);
|
|
if (S_OK != hr)
|
|
{
|
|
if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr ||
|
|
NTE_FAIL == hr)
|
|
{
|
|
// no more providers under type, out of i loop
|
|
break;
|
|
}
|
|
|
|
// invalid csp entry, skip it
|
|
wprintf(L"Failed to enum CSP at (%ld)\n", i);
|
|
}
|
|
else
|
|
{
|
|
hr = EnumKeys(
|
|
pwszProvName,
|
|
dwProvType,
|
|
fSkipKeys,
|
|
pwszKeyContainerName);
|
|
_JumpIfErrorStr(hr, error, "EnumKeys", pwszProvName);
|
|
|
|
LocalFree(pwszProvName);
|
|
pwszProvName = NULL;
|
|
}
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszProvName)
|
|
{
|
|
LocalFree(pwszProvName);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
cuSanitizeNameWithSuffix(
|
|
IN WCHAR const *pwszName,
|
|
OUT WCHAR **ppwszNameOut)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR const *pwszSuffix;
|
|
WCHAR const *pwsz;
|
|
WCHAR *pwszBase = NULL;
|
|
WCHAR *pwszSanitizedName = NULL;
|
|
DWORD cwc;
|
|
|
|
pwsz = wcsrchr(pwszName, wcLPAREN);
|
|
pwszSuffix = pwsz;
|
|
if (NULL != pwsz)
|
|
{
|
|
BOOL fSawDigit = FALSE;
|
|
|
|
pwsz++;
|
|
while (iswdigit(*pwsz))
|
|
{
|
|
pwsz++;
|
|
fSawDigit = TRUE;
|
|
}
|
|
if (fSawDigit &&
|
|
wcRPAREN == *pwsz &&
|
|
(L'.' == pwsz[1] || L'\0' == pwsz[1]))
|
|
{
|
|
cwc = SAFE_SUBTRACT_POINTERS(pwszSuffix, pwszName);
|
|
|
|
pwszBase = (WCHAR *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(cwc + 1) * sizeof(WCHAR));
|
|
if (NULL == pwszBase)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
CopyMemory(pwszBase, pwszName, cwc * sizeof(WCHAR));
|
|
pwszBase[cwc] = L'\0';
|
|
pwszName = pwszBase;
|
|
}
|
|
else
|
|
{
|
|
pwszSuffix = NULL;
|
|
}
|
|
}
|
|
hr = mySanitizeName(pwszName, &pwszSanitizedName);
|
|
_JumpIfError(hr, error, "mySanitizeName");
|
|
|
|
if (NULL == pwszSuffix)
|
|
{
|
|
*ppwszNameOut = pwszSanitizedName;
|
|
pwszSanitizedName = NULL;
|
|
}
|
|
else
|
|
{
|
|
*ppwszNameOut = (WCHAR *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(wcslen(pwszSanitizedName) +
|
|
wcslen(pwszSuffix) +
|
|
1) * sizeof(WCHAR));
|
|
if (NULL == *ppwszNameOut)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
wcscpy(*ppwszNameOut, pwszSanitizedName);
|
|
wcscat(*ppwszNameOut, pwszSuffix);
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszSanitizedName)
|
|
{
|
|
LocalFree(pwszSanitizedName);
|
|
}
|
|
if (NULL != pwszBase)
|
|
{
|
|
LocalFree(pwszBase);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
verbDelKey(
|
|
IN WCHAR const *pwszOption,
|
|
IN WCHAR const *pwszKeyContainerName,
|
|
OPTIONAL IN WCHAR const *pwszProvider,
|
|
IN WCHAR const *pwszArg3,
|
|
IN WCHAR const *pwszArg4)
|
|
{
|
|
HRESULT hr;
|
|
HCRYPTPROV hProv = NULL;
|
|
WCHAR *apwszKeyContainer[3];
|
|
DWORD i, dwProviderType;
|
|
DWORD dwFlags = CRYPT_DELETEKEYSET;
|
|
|
|
// If supplied provider is NULL, use the default provider.
|
|
if (pwszProvider == NULL)
|
|
{
|
|
pwszProvider = g_wszProviderNameDefault;
|
|
}
|
|
|
|
apwszKeyContainer[0] = const_cast<WCHAR *>(pwszKeyContainerName);
|
|
apwszKeyContainer[1] = NULL;
|
|
apwszKeyContainer[2] = NULL;
|
|
|
|
hr = mySanitizeName(pwszKeyContainerName, &apwszKeyContainer[1]);
|
|
_JumpIfError(hr, error, "mySanitizeName");
|
|
|
|
hr = cuSanitizeNameWithSuffix(pwszKeyContainerName, &apwszKeyContainer[2]);
|
|
_JumpIfError(hr, error, "cuSanitizeNameWithSuffix");
|
|
|
|
hr = csiGetProviderTypeFromProviderName(pwszProvider, &dwProviderType);
|
|
_JumpIfError(hr, error, "csiGetProviderTypeFromProviderName");
|
|
|
|
if (g_fCryptSilent)
|
|
{
|
|
dwFlags |= CRYPT_SILENT;
|
|
}
|
|
|
|
for (i = 0; i < ARRAYSIZE(apwszKeyContainer); i++)
|
|
{
|
|
if (!myCertSrvCryptAcquireContext(
|
|
&hProv,
|
|
apwszKeyContainer[i],
|
|
pwszProvider,
|
|
dwProviderType,
|
|
dwFlags,
|
|
!g_fUserRegistry)) // fMachineKeyset
|
|
{
|
|
hr = myHLastError();
|
|
_PrintErrorStr2(
|
|
hr,
|
|
"myCertSrvCryptAcquireContext",
|
|
apwszKeyContainer[i],
|
|
hr);
|
|
}
|
|
else
|
|
{
|
|
DWORD j;
|
|
|
|
wprintf(L" %ws", apwszKeyContainer[i]);
|
|
if (g_fVerbose)
|
|
{
|
|
wprintf(L" --");
|
|
for (j = 0; j < ARRAYSIZE(apwszKeyContainer); j++)
|
|
{
|
|
wprintf(L" %ws", apwszKeyContainer[j]);
|
|
}
|
|
}
|
|
wprintf(wszNewLine);
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
_JumpIfError(hr, error, "myCertSrvCryptAcquireContext");
|
|
|
|
error:
|
|
for (i = 1; i < ARRAYSIZE(apwszKeyContainer); i++)
|
|
{
|
|
if (NULL != apwszKeyContainer[i])
|
|
{
|
|
LocalFree(apwszKeyContainer[i]);
|
|
}
|
|
}
|
|
return(hr);
|
|
}
|