1181 lines
29 KiB
C++
1181 lines
29 KiB
C++
//---------------------------------------------------------------------
|
|
// This file is part of the Microsoft .NET Framework SDK Code Samples.
|
|
//
|
|
// Copyright (C) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//This source code is intended only as a supplement to Microsoft
|
|
//Development Tools and/or on-line documentation. See these other
|
|
//materials for detailed information regarding Microsoft code samples.
|
|
//
|
|
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
|
|
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
//PARTICULAR PURPOSE.
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <certenroll.h>
|
|
#include <certcli.h>
|
|
#include <wincrypt.h>
|
|
#include "enrollCommon.h"
|
|
|
|
|
|
|
|
// Converts WCHAR * to CHAR *
|
|
// Returns true is succeed, false otherwise
|
|
BOOL
|
|
convertWszToSz(
|
|
__deref_out PSTR *ppsz,
|
|
__in_ecount(cwc) WCHAR const *pwc,
|
|
__in LONG cwc)
|
|
{
|
|
HRESULT hr;
|
|
LONG cch = 0;
|
|
|
|
*ppsz = NULL;
|
|
for (;;)
|
|
{
|
|
cch = WideCharToMultiByte(
|
|
GetACP(),
|
|
0, // dwFlags
|
|
pwc,
|
|
cwc, // cchWideChar, -1 => null terminated
|
|
*ppsz,
|
|
cch,
|
|
NULL,
|
|
NULL);
|
|
if (0 >= cch &&
|
|
(0 != cch || (0 != cwc && (MAXLONG != cwc || L'\0' != *pwc))))
|
|
{
|
|
hr = GetLastError();
|
|
_PrintError(hr, "WideCharToMultiByte");
|
|
|
|
if (NULL != *ppsz)
|
|
{
|
|
LocalFree(*ppsz);
|
|
*ppsz = NULL;
|
|
}
|
|
break;
|
|
}
|
|
if (NULL != *ppsz)
|
|
{
|
|
(*ppsz)[cch] = '\0';
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
*ppsz = (CHAR *) LocalAlloc(LMEM_FIXED, cch + 1);
|
|
if (NULL == *ppsz)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
}
|
|
if (S_OK != hr)
|
|
{
|
|
SetLastError(hr);
|
|
}
|
|
return(S_OK == hr);
|
|
}
|
|
|
|
// Converts CHAR * to WCHAR *
|
|
// Returns true is succeed, false otherwise
|
|
BOOL
|
|
convertSzToWsz(
|
|
__deref_out PWSTR *ppwsz,
|
|
__in_ecount(cch) CHAR const *pch,
|
|
__in LONG cch)
|
|
{
|
|
HRESULT hr;
|
|
LONG cwc = 0;
|
|
|
|
*ppwsz = NULL;
|
|
for (;;)
|
|
{
|
|
cwc = MultiByteToWideChar(GetACP(), 0, pch, cch, *ppwsz, cwc);
|
|
if (0 >= cwc)
|
|
{
|
|
hr = GetLastError();
|
|
_PrintError(hr, "MultiByteToWideChar");
|
|
|
|
if (NULL != *ppwsz)
|
|
{
|
|
LocalFree(*ppwsz);
|
|
*ppwsz = NULL;
|
|
}
|
|
break;
|
|
}
|
|
if (NULL != *ppwsz)
|
|
{
|
|
(*ppwsz)[cwc] = L'\0';
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
*ppwsz = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR));
|
|
if (NULL == *ppwsz)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
}
|
|
if (S_OK != hr)
|
|
{
|
|
SetLastError(hr);
|
|
}
|
|
return(S_OK == hr);
|
|
}
|
|
|
|
// Converts CHAR * to BSTR
|
|
// Returns true is succeed, false otherwise
|
|
BOOL
|
|
convertSzToBstr(
|
|
__deref_out BSTR *pbstr,
|
|
__in_ecount(cch) CHAR const *pch,
|
|
__in LONG cch)
|
|
{
|
|
HRESULT hr;
|
|
BSTR bstr = NULL;
|
|
LONG cwc = 0;
|
|
|
|
if (-1 == cch)
|
|
{
|
|
cch = (LONG)strlen(pch);
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
cwc = MultiByteToWideChar(GetACP(), 0, pch, cch, bstr, cwc);
|
|
if (0 >= cwc)
|
|
{
|
|
hr = GetLastError();
|
|
_PrintError(hr, "MultiByteToWideChar");
|
|
SysFreeString(bstr);
|
|
break;
|
|
}
|
|
if (NULL != bstr)
|
|
{
|
|
bstr[cwc] = L'\0';
|
|
SysFreeString(*pbstr);
|
|
*pbstr = bstr;
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
bstr = SysAllocStringLen(NULL, cwc);
|
|
if (NULL == bstr)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
}
|
|
if (S_OK != hr)
|
|
{
|
|
SetLastError(hr);
|
|
}
|
|
return(S_OK == hr);
|
|
}
|
|
|
|
// Converts WCHAR * to BSTR
|
|
// Returns true is succeed, false otherwise
|
|
BOOL
|
|
convertWszToBstr(
|
|
__deref_out BSTR *pbstr,
|
|
__in_ecount(cb) WCHAR const *pwc,
|
|
__in LONG cb)
|
|
{
|
|
HRESULT hr;
|
|
BSTR bstr;
|
|
|
|
bstr = NULL;
|
|
if (NULL != pwc)
|
|
{
|
|
if (-1 == cb)
|
|
{
|
|
cb = (LONG)(wcslen(pwc) * sizeof(WCHAR));
|
|
}
|
|
bstr = SysAllocStringByteLen((char const *) pwc, cb);
|
|
if (NULL == bstr)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "SysAllocStringByteLen");
|
|
}
|
|
}
|
|
SysFreeString(*pbstr);
|
|
*pbstr = bstr;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (S_OK != hr)
|
|
{
|
|
SetLastError(hr);
|
|
}
|
|
return(S_OK == hr);
|
|
}
|
|
|
|
|
|
// Check enrollment status
|
|
// Return S_OK if enrollment succeeds
|
|
HRESULT
|
|
checkEnrollStatus(
|
|
__in IX509Enrollment* pEnroll)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HRESULT hEnrollError = S_OK;
|
|
IX509EnrollmentStatus* pStatus = NULL;
|
|
EnrollmentEnrollStatus EnrollStatus;
|
|
BSTR strText = NULL;
|
|
BSTR strErrorText = NULL;
|
|
|
|
// Get IX509EnrollmentStatus
|
|
hr = pEnroll->get_Status(&pStatus);
|
|
_JumpIfError(hr, error, "get_Status");
|
|
|
|
// Get enrollment status
|
|
hr = pStatus->get_Status(&EnrollStatus);
|
|
_JumpIfError(hr, error, "get_Status");
|
|
|
|
// Get enrollment HRESULT
|
|
hr = pStatus->get_Error(&hEnrollError);
|
|
_JumpIfError(hr, error, "get_Error");
|
|
|
|
// Get enrollment error text
|
|
hr = pStatus->get_ErrorText(&strErrorText);
|
|
_JumpIfError(hr, error, "get_ErrorText");
|
|
|
|
// Get enrollment info text
|
|
hr = pStatus->get_Text(&strText);
|
|
_JumpIfError(hr, error, "get_Text");
|
|
|
|
if (Enrolled != EnrollStatus) // Not enrolled
|
|
{
|
|
if (EnrollPended != EnrollStatus) // Enroll failed
|
|
{
|
|
wprintf(L"Request failed: %ws -- %ws\n", strErrorText, strText);
|
|
hr = hEnrollError;
|
|
_JumpError(hr, error, "EnrollError");
|
|
}
|
|
|
|
// Enroll pending
|
|
wprintf(L"Request is pending: %ws -- %ws\n", strErrorText, strText);
|
|
hr = E_FAIL; // Return failure even if the status is pending
|
|
}
|
|
else // Enrolled
|
|
wprintf(L"Cert Issued: %ws -- %ws\n", strErrorText, strText);
|
|
error:
|
|
SysFreeString(strText);
|
|
SysFreeString(strErrorText);
|
|
if (NULL != pStatus) pStatus->Release();
|
|
return hr;
|
|
}
|
|
|
|
// Find a cert in user my store that has key usage containing usageFlags
|
|
// Return S_OK is succeeds
|
|
HRESULT
|
|
findCertByKeyUsage(
|
|
__in BYTE usageFlags,
|
|
__deref_out CERT_CONTEXT const **ppCert)
|
|
{
|
|
HRESULT hr;
|
|
HCERTSTORE hStore = NULL;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
BYTE KeyUsage;
|
|
BOOL fMatch = FALSE;
|
|
|
|
// Open user MY store
|
|
hStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
X509_ASN_ENCODING,
|
|
NULL,
|
|
CERT_SYSTEM_STORE_CURRENT_USER,
|
|
L"MY" );
|
|
|
|
if (NULL == hStore)
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
// Enumerate cert in MY store
|
|
pCert = CertEnumCertificatesInStore(hStore, pCert);
|
|
if (NULL == pCert)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Get key usage
|
|
if (!CertGetIntendedKeyUsage(
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
pCert->pCertInfo,
|
|
&KeyUsage,
|
|
1))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Check if key usage matches
|
|
if ((KeyUsage & usageFlags) == usageFlags)
|
|
{
|
|
fMatch = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fMatch)
|
|
{
|
|
*ppCert = pCert;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
|
|
error:
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, 0);
|
|
}
|
|
return hr;
|
|
|
|
}
|
|
|
|
// Find a cert in user my store that has EKU containing pszObjId
|
|
// Return S_OK is succeeds
|
|
HRESULT
|
|
findCertByEKU(
|
|
__in CHAR const *pszObjId,
|
|
__deref_out CERT_CONTEXT const **ppCert)
|
|
{
|
|
HRESULT hr;
|
|
HCERTSTORE hStore = NULL;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
CERT_ENHKEY_USAGE *pKeyUsage = NULL;
|
|
DWORD cbKeyUsage = 0;
|
|
DWORD i;
|
|
BOOL fMatch = FALSE;
|
|
|
|
// Open user MY store
|
|
hStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
X509_ASN_ENCODING,
|
|
NULL,
|
|
CERT_SYSTEM_STORE_CURRENT_USER,
|
|
L"MY" );
|
|
|
|
if (NULL == hStore)
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
// Enumerate cert in MY store
|
|
pCert = CertEnumCertificatesInStore(hStore, pCert);
|
|
if (NULL == pCert)
|
|
{
|
|
break;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
// Get enhanced key usage OIDs
|
|
if (!CertGetEnhancedKeyUsage(
|
|
pCert,
|
|
CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
|
|
pKeyUsage,
|
|
&cbKeyUsage))
|
|
{
|
|
break;
|
|
}
|
|
if (NULL != pKeyUsage)
|
|
{
|
|
break; // EKU extension fetched; break out of while loop
|
|
}
|
|
pKeyUsage = (CERT_ENHKEY_USAGE *)LocalAlloc
|
|
(LMEM_FIXED, cbKeyUsage);
|
|
if (NULL == pKeyUsage)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "out of memory");
|
|
}
|
|
}
|
|
|
|
if (NULL != pKeyUsage)
|
|
{
|
|
if (0 == pKeyUsage->cUsageIdentifier)
|
|
{
|
|
hr = GetLastError(); // set by CertGetEnhancedKeyUsage
|
|
if (S_OK != hr)
|
|
{
|
|
fMatch = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < pKeyUsage->cUsageIdentifier; i++)
|
|
{
|
|
if (0 == strcmp(
|
|
pKeyUsage->rgpszUsageIdentifier[i],
|
|
pszObjId))
|
|
{
|
|
fMatch = TRUE; // found matching EKU OID
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
LocalFree(pKeyUsage);
|
|
pKeyUsage = NULL;
|
|
}
|
|
|
|
if (fMatch)
|
|
break;
|
|
}
|
|
|
|
if (fMatch)
|
|
{
|
|
*ppCert = pCert;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
|
|
error:
|
|
if (NULL != pKeyUsage)
|
|
{
|
|
LocalFree(pKeyUsage);
|
|
}
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, 0);
|
|
}
|
|
return hr;
|
|
|
|
}
|
|
|
|
// Find a cert in user my store that has a string template
|
|
// name matches pwszNameTemplate or OID template name
|
|
// matches pszObjIdTemplate
|
|
// Return S_OK is succeeds
|
|
HRESULT
|
|
findCertByTemplate(
|
|
__in_opt PCWSTR pwszNameTemplate,
|
|
__deref_out CERT_CONTEXT const **ppCert)
|
|
{
|
|
HRESULT hr;
|
|
HCERTSTORE hStore = NULL;
|
|
CERT_CONTEXT const *pCert = NULL;
|
|
BOOL fMatch = FALSE;
|
|
CERT_EXTENSION *pExt;
|
|
DWORD cExtension;
|
|
CERT_EXTENSION const *rgExtension;
|
|
WCHAR *pwszCertTypeNameV1 = NULL;
|
|
WCHAR *pwszCertTypeObjId = NULL;
|
|
CERT_TEMPLATE_EXT *pTemplate = NULL;
|
|
CERT_NAME_VALUE *pName = NULL;
|
|
PSTR pszObjIdTemplate = NULL;
|
|
__bound DWORD cb;
|
|
|
|
if (NULL == pwszNameTemplate)
|
|
{
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
_JumpError(hr, error, "findOIDFromTemplateName");
|
|
}
|
|
|
|
hr = findOIDFromTemplateName(pwszNameTemplate, &pszObjIdTemplate);
|
|
_JumpIfError(hr, error, "findOIDFromTemplateName");
|
|
|
|
// Open user MY store
|
|
hStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W,
|
|
X509_ASN_ENCODING,
|
|
NULL,
|
|
CERT_SYSTEM_STORE_CURRENT_USER,
|
|
L"MY" );
|
|
|
|
if (NULL == hStore)
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CertOpenStore");
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
// Enumerate cert in MY store
|
|
pCert = CertEnumCertificatesInStore(hStore, pCert);
|
|
if (NULL == pCert)
|
|
{
|
|
break;
|
|
}
|
|
|
|
cExtension = pCert->pCertInfo->cExtension;
|
|
rgExtension = pCert->pCertInfo->rgExtension;
|
|
|
|
// Look for the V1 cert type extension first
|
|
|
|
if (NULL != pwszNameTemplate)
|
|
{
|
|
pExt = CertFindExtension(
|
|
szOID_ENROLL_CERTTYPE_EXTENSION,
|
|
cExtension,
|
|
const_cast<CERT_EXTENSION *>(rgExtension));
|
|
|
|
if (NULL != pExt)
|
|
{
|
|
if (!CryptDecodeObjectEx(
|
|
X509_ASN_ENCODING,
|
|
X509_UNICODE_ANY_STRING,
|
|
pExt->Value.pbData,
|
|
pExt->Value.cbData,
|
|
CRYPT_DECODE_ALLOC_FLAG, // dwFlags
|
|
NULL,
|
|
(VOID **) &pName,
|
|
&cb))
|
|
|
|
{
|
|
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CryptDecodeObjectEx");
|
|
}
|
|
|
|
cb = (pName->Value.cbData + 1) * sizeof(WCHAR);
|
|
pwszCertTypeNameV1 = (WCHAR *) LocalAlloc(LMEM_FIXED, cb);
|
|
if (NULL == pwszCertTypeNameV1)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
CopyMemory(pwszCertTypeNameV1, pName->Value.pbData, cb);
|
|
|
|
if (0 == _wcsicmp(pwszNameTemplate, pwszCertTypeNameV1))
|
|
{
|
|
fMatch = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NULL != pszObjIdTemplate && !fMatch)
|
|
{
|
|
pExt = CertFindExtension(
|
|
szOID_CERTIFICATE_TEMPLATE,
|
|
cExtension,
|
|
const_cast<CERT_EXTENSION *>(rgExtension));
|
|
|
|
if (NULL != pExt)
|
|
{
|
|
if (!CryptDecodeObjectEx(
|
|
X509_ASN_ENCODING,
|
|
X509_CERTIFICATE_TEMPLATE,
|
|
pExt->Value.pbData,
|
|
pExt->Value.cbData,
|
|
CRYPT_DECODE_ALLOC_FLAG, // dwFlags
|
|
NULL,
|
|
(VOID **) &pTemplate,
|
|
&cb))
|
|
|
|
{
|
|
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CryptDecodeObjectEx");
|
|
}
|
|
|
|
if (0 == strcmp(pszObjIdTemplate, pTemplate->pszObjId))
|
|
{
|
|
fMatch = TRUE;
|
|
break;
|
|
}
|
|
if (!convertSzToWsz(
|
|
&pwszCertTypeObjId,
|
|
pTemplate->pszObjId,
|
|
-1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "convertSzToWsz");
|
|
}
|
|
if (0 == _wcsicmp(pwszNameTemplate, pwszCertTypeObjId))
|
|
{
|
|
fMatch = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (NULL != pwszCertTypeNameV1)
|
|
{
|
|
LocalFree(pwszCertTypeNameV1);
|
|
pwszCertTypeNameV1 = NULL;
|
|
|
|
}
|
|
if (NULL != pwszCertTypeObjId)
|
|
{
|
|
LocalFree(pwszCertTypeObjId);
|
|
pwszCertTypeObjId = NULL;
|
|
|
|
}
|
|
if (NULL != pTemplate)
|
|
{
|
|
LocalFree(pTemplate);
|
|
pTemplate = NULL;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if (fMatch)
|
|
{
|
|
*ppCert = pCert;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
hr = CRYPT_E_NOT_FOUND;
|
|
|
|
error:
|
|
if (NULL != pwszCertTypeNameV1)
|
|
{
|
|
LocalFree(pwszCertTypeNameV1);
|
|
}
|
|
if (NULL != pwszCertTypeObjId)
|
|
{
|
|
LocalFree(pwszCertTypeObjId);
|
|
}
|
|
if (NULL != pTemplate)
|
|
{
|
|
LocalFree(pTemplate);
|
|
}
|
|
if (NULL != hStore)
|
|
{
|
|
CertCloseStore(hStore, 0);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// Verify the certificate chain and EKU based on pCert
|
|
// Returns S_OK if verified
|
|
HRESULT
|
|
verifyCertContext(
|
|
__in CERT_CONTEXT const *pCert,
|
|
__in_opt PSTR pszEKU)
|
|
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CERT_CHAIN_PARA ChainParams;
|
|
CERT_CHAIN_POLICY_PARA ChainPolicy;
|
|
CERT_CHAIN_POLICY_STATUS PolicyStatus;
|
|
CERT_CHAIN_CONTEXT const *pChainContext = NULL;
|
|
|
|
ZeroMemory(&ChainParams, sizeof(ChainParams));
|
|
ChainParams.cbSize = sizeof(ChainParams);
|
|
|
|
if ( NULL != pszEKU)
|
|
{
|
|
ChainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
|
|
ChainParams.RequestedUsage.Usage.cUsageIdentifier = 1;
|
|
ChainParams.RequestedUsage.Usage.rgpszUsageIdentifier =
|
|
const_cast<char **>(&pszEKU);
|
|
}
|
|
|
|
// Get the chain and verify the cert:
|
|
if (!CertGetCertificateChain(
|
|
NULL, // hChainEngine
|
|
pCert, // pCertContext
|
|
NULL, // pTime
|
|
NULL, // hAdditionalStore
|
|
&ChainParams, // pChainPara
|
|
0, // dwFlags
|
|
NULL, // pvReserved
|
|
&pChainContext)) // ppChainContext
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CertGetCertificateChain");
|
|
}
|
|
|
|
ZeroMemory(&ChainPolicy, sizeof(ChainPolicy));
|
|
ChainPolicy.cbSize = sizeof(ChainPolicy);
|
|
ChainPolicy.dwFlags = CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG;
|
|
|
|
ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
|
|
PolicyStatus.cbSize = sizeof(PolicyStatus);
|
|
PolicyStatus.lChainIndex = -1;
|
|
PolicyStatus.lElementIndex = -1;
|
|
|
|
if (!CertVerifyCertificateChainPolicy(
|
|
CERT_CHAIN_POLICY_BASE,
|
|
pChainContext,
|
|
&ChainPolicy,
|
|
&PolicyStatus))
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CertVerifyCertificateChainPolicy");
|
|
}
|
|
|
|
if (S_OK != PolicyStatus.dwError)
|
|
{
|
|
hr = PolicyStatus.dwError;
|
|
_JumpError(hr, error, "PolicyStatus.dwError");
|
|
}
|
|
|
|
error:
|
|
if (NULL != pChainContext) CertFreeCertificateChain(pChainContext);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Enroll cert by template name
|
|
// Returns S_OK if succeeds
|
|
HRESULT
|
|
enrollCertByTemplate(
|
|
__in PCWSTR pwszTemplateName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IX509Enrollment* pEnroll = NULL;
|
|
BSTR strTemplateName = NULL;
|
|
|
|
// Allocate BSTR for template name
|
|
strTemplateName = SysAllocString(pwszTemplateName);
|
|
if (NULL == strTemplateName)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "SysAllocString");
|
|
}
|
|
|
|
// Create IX509Enrollment
|
|
hr = CoCreateInstance(
|
|
__uuidof(CX509Enrollment),
|
|
NULL, // pUnkOuter
|
|
CLSCTX_INPROC_SERVER,
|
|
__uuidof(IX509Enrollment),
|
|
(void **) &pEnroll);
|
|
_JumpIfError(hr, error, "CoCreateInstance");
|
|
|
|
// Initiate IX509Enrollment
|
|
hr = pEnroll->InitializeFromTemplateName(
|
|
ContextUser,
|
|
strTemplateName);
|
|
_JumpIfError(hr, error, "InitializeFromTemplateName");
|
|
|
|
// Enroll
|
|
hr = pEnroll->Enroll();
|
|
_JumpIfError(hr, error, "Enroll");
|
|
|
|
// Check enrollment status
|
|
hr = checkEnrollStatus(pEnroll);
|
|
_JumpIfError(hr, error, "checkEnrollStatus");
|
|
|
|
error:
|
|
SysFreeString(strTemplateName);
|
|
if (NULL != pEnroll) pEnroll->Release();
|
|
return hr;
|
|
}
|
|
|
|
// Converts unicode string or ansi string to ansi string
|
|
// Returns S_OK is succeeds
|
|
HRESULT
|
|
decConvertFromUnicode(
|
|
__deref_inout_ecount(*pcb) BYTE **ppb,
|
|
__inout DWORD *pcb)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (2 * sizeof(WCHAR) <= *pcb && 0 == ((sizeof(WCHAR) - 1) & *pcb))
|
|
{
|
|
WCHAR *pwcIn = (WCHAR *) *ppb;
|
|
DWORD cwcIn = *pcb;
|
|
bool fUnicode = true;
|
|
|
|
if (wcBOM == pwcIn[0] || wcBOMBIGENDIAN == pwcIn[0])
|
|
{
|
|
if (wcBOMBIGENDIAN == pwcIn[0])
|
|
{
|
|
_swab((char *) *ppb, (char *) *ppb, *pcb);
|
|
}
|
|
pwcIn++;
|
|
cwcIn--;
|
|
}
|
|
else
|
|
{
|
|
for (DWORD i = 0; i < cwcIn; i++)
|
|
{
|
|
WCHAR wc = pwcIn[i];
|
|
|
|
switch (wc)
|
|
{
|
|
case L'\t':
|
|
case L'\r':
|
|
case L'\n':
|
|
continue;
|
|
}
|
|
if (L' ' > wc || L'~' < wc)
|
|
{
|
|
fUnicode = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (fUnicode)
|
|
{
|
|
PSTR pszT;
|
|
|
|
if (!convertWszToSz(&pszT, pwcIn, cwcIn))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "convertWszToSz");
|
|
}
|
|
LocalFree(*ppb);
|
|
*ppb = (BYTE *) pszT;
|
|
*pcb = (DWORD)strlen(pszT);
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return hr;
|
|
}
|
|
|
|
// Read request or certificate file
|
|
// Accepts binary, base64 or unicode base64 format
|
|
// Returns S_OK if succeeds
|
|
HRESULT
|
|
DecodeFileW(
|
|
__in TCHAR const *pszfn,
|
|
__out BYTE **ppbOut,
|
|
__out DWORD *pcbOut,
|
|
__in DWORD Flags)
|
|
{
|
|
HRESULT hr;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
BYTE *pbIn = NULL;
|
|
BYTE *pbOut = NULL;
|
|
DWORD cbIn;
|
|
DWORD cbRead;
|
|
DWORD cbOut;
|
|
|
|
hFile = CreateFile(
|
|
pszfn,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CreateFile");
|
|
}
|
|
if (FILE_TYPE_DISK != GetFileType(hFile))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
_JumpError(hr, error, "GetFileType");
|
|
}
|
|
|
|
cbIn = GetFileSize(hFile, NULL);
|
|
if (INVALID_FILE_SIZE == cbIn || 0 == cbIn)
|
|
{
|
|
if (0 == cbIn)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
}
|
|
else
|
|
{
|
|
hr = GetLastError();
|
|
}
|
|
_JumpError(hr, error, "GetFileSize");
|
|
}
|
|
|
|
pbIn = (BYTE *) LocalAlloc(LMEM_FIXED, cbIn);
|
|
if (NULL == pbIn)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
if (!ReadFile(hFile, pbIn, cbIn, &cbRead, NULL))
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "ReadFile");
|
|
}
|
|
|
|
if (cbRead != cbIn)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
_JumpError(hr, error, "ReadFile(cbRead)");
|
|
}
|
|
|
|
if (CRYPT_STRING_BINARY == Flags)
|
|
{
|
|
pbOut = (BYTE *) pbIn;
|
|
cbOut = cbIn;
|
|
pbIn = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = decConvertFromUnicode(&pbIn, &cbIn);
|
|
_JumpIfError(hr, error, "decConvertUnicode");
|
|
|
|
// Decode file contents.
|
|
|
|
if (!CryptStringToBinaryA(
|
|
(PCSTR) pbIn,
|
|
cbIn,
|
|
Flags,
|
|
pbOut,
|
|
&cbOut,
|
|
NULL,
|
|
NULL))
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CryptStringToBinaryA");
|
|
}
|
|
|
|
pbOut = (BYTE *) LocalAlloc(LMEM_FIXED, cbOut);
|
|
if (NULL == pbOut)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
if (!CryptStringToBinaryA(
|
|
(PCSTR) pbIn,
|
|
cbIn,
|
|
Flags,
|
|
pbOut,
|
|
&cbOut,
|
|
NULL,
|
|
NULL))
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CryptStringToBinaryA");
|
|
}
|
|
|
|
}
|
|
*pcbOut = cbOut;
|
|
*ppbOut = pbOut;
|
|
pbOut = NULL;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (INVALID_HANDLE_VALUE != hFile) CloseHandle(hFile);
|
|
if (NULL != pbIn) LocalFree(pbIn);
|
|
if (S_OK != hr && NULL != pbOut) LocalFree(pbOut);
|
|
return(hr);
|
|
}
|
|
|
|
// Save request or certificate to file
|
|
// Save to binary, base64 or unicode base64 format
|
|
// Returns S_OK if succeeds
|
|
HRESULT
|
|
EncodeToFileW(
|
|
__in TCHAR const *pszfn,
|
|
__in_bcount(cbIn) BYTE const *pbIn,
|
|
__in DWORD cbIn,
|
|
__in DWORD Flags)
|
|
{
|
|
HRESULT hr;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
DWORD cbWritten;
|
|
DWORD cbOut;
|
|
CHAR *pchOut = NULL;
|
|
DWORD cch;
|
|
WCHAR *pwcOut = NULL;
|
|
DWORD cwc;
|
|
WCHAR *pwcT = NULL;
|
|
BYTE const *pbOut;
|
|
bool fForceOverWrite = 0 != (DECF_FORCEOVERWRITE & Flags);
|
|
bool fUnicode = 0 != (DECF_WRITEUNICODE & Flags);
|
|
|
|
Flags &= ~(DECF_FORCEOVERWRITE | DECF_WRITEUNICODE);
|
|
|
|
if (CRYPT_STRING_BINARY == (CR_OUT_ENCODEMASK & Flags))
|
|
{
|
|
if (CRYPT_STRING_BINARY != Flags)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "Flags");
|
|
}
|
|
pbOut = pbIn;
|
|
cbOut = cbIn;
|
|
}
|
|
else if (fUnicode)
|
|
{
|
|
if (!CryptBinaryToStringW(pbIn, cbIn, Flags, pwcOut, &cwc))
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CryptBinaryToStringW");
|
|
}
|
|
|
|
pwcOut = (WCHAR *) LocalAlloc(LMEM_FIXED, cwc * sizeof(WCHAR));
|
|
if (NULL == pwcOut)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
if (!CryptBinaryToStringW(pbIn, cbIn, Flags, pwcOut, &cwc))
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CryptBinaryToStringW");
|
|
}
|
|
|
|
// move the string (overwrite the terminating L'\0') to make room
|
|
// for wcBOM.
|
|
|
|
cwc = (DWORD)wcslen(pwcOut);
|
|
|
|
|
|
for (pwcT = &pwcOut[cwc - 1]; pwcT >= pwcOut; pwcT--)
|
|
{
|
|
pwcT[1] = pwcT[0];
|
|
}
|
|
pwcT[1] = wcBOM;
|
|
|
|
cbOut = (1 + cwc) * sizeof(WCHAR);
|
|
pbOut = (BYTE const *) pwcOut;
|
|
}
|
|
else
|
|
{
|
|
if (!CryptBinaryToStringA(pbIn, cbIn, Flags, pchOut, &cch))
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CryptBinaryToStringA");
|
|
}
|
|
|
|
pchOut = (char *) LocalAlloc(LMEM_FIXED, cch * sizeof(char));
|
|
if (NULL == pchOut)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
if (!CryptBinaryToStringA(pbIn, cbIn, Flags, pchOut, &cch))
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CryptBinaryToStringA");
|
|
}
|
|
|
|
cbOut = (DWORD)strlen(pchOut);
|
|
pbOut = (BYTE const *) pchOut;
|
|
}
|
|
|
|
// Write encoded certificate to file
|
|
|
|
hFile = CreateFile(
|
|
pszfn,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_NEW,
|
|
0,
|
|
NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
{
|
|
hr = GetLastError();
|
|
if (fForceOverWrite &&
|
|
HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == HRESULT_FROM_WIN32(hr))
|
|
{
|
|
hFile = CreateFile(
|
|
pszfn,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
0,
|
|
NULL);
|
|
}
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "CreateFile");
|
|
}
|
|
}
|
|
if (FILE_TYPE_DISK != GetFileType(hFile))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
_JumpError(hr, error, "GetFileType");
|
|
}
|
|
|
|
if (!WriteFile(hFile, pbOut, cbOut, &cbWritten, NULL))
|
|
{
|
|
hr = GetLastError();
|
|
_JumpError(hr, error, "WriteFile");
|
|
}
|
|
if (cbWritten != cbOut)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
|
|
_JumpError(hr, error, "WriteFile(cbWritten)");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (INVALID_HANDLE_VALUE != hFile)
|
|
{
|
|
CloseHandle(hFile);
|
|
}
|
|
if (NULL != pchOut)
|
|
{
|
|
LocalFree(pchOut);
|
|
}
|
|
if (NULL != pwcOut)
|
|
{
|
|
LocalFree(pwcOut);
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
// Find template oid from template name
|
|
// Returns S_OK if succeeds
|
|
HRESULT
|
|
findOIDFromTemplateName(
|
|
__in PCWSTR pwszTemplateName,
|
|
__deref_out PSTR *ppszTemplateOID)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
IX509CertificateRequestPkcs10* pPkcs10 = NULL;
|
|
IObjectId* pObjectId = NULL;
|
|
BSTR strTemplateName = NULL;
|
|
BSTR strTemplateOID = NULL;
|
|
|
|
// Create IX509CertificateRequestPkcs10
|
|
hr = CoCreateInstance(
|
|
__uuidof(CX509CertificateRequestPkcs10),
|
|
NULL, // pUnkOuter
|
|
CLSCTX_INPROC_SERVER,
|
|
__uuidof(IX509CertificateRequestPkcs10),
|
|
(void **) &pPkcs10);
|
|
_JumpIfError(hr, error, "CoCreateInstance");
|
|
|
|
// Allocate BSTR for template name
|
|
strTemplateName = SysAllocString(pwszTemplateName);
|
|
if (NULL == strTemplateName)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "SysAllocString");
|
|
}
|
|
|
|
// Initialize IX509CertificateRequestPkcs10
|
|
hr = pPkcs10->InitializeFromTemplateName(
|
|
ContextUser,
|
|
strTemplateName);
|
|
_JumpIfError(hr, error, "InitializeFromTemplateName");
|
|
|
|
// Initialize IX509CertificateRequestPkcs10
|
|
hr = pPkcs10->get_TemplateObjectId(&pObjectId);
|
|
_JumpIfError(hr, error, "get_TemplateObjectId");
|
|
|
|
hr = pObjectId->get_Value(&strTemplateOID);
|
|
_JumpIfError(hr, error, "get_Value");
|
|
|
|
if (!convertWszToSz(ppszTemplateOID, (PCWSTR)strTemplateOID, -1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "convertWszToSz");
|
|
}
|
|
|
|
error:
|
|
SysFreeString(strTemplateName);
|
|
SysFreeString(strTemplateOID);
|
|
if (NULL != pPkcs10) pPkcs10->Release();
|
|
if (NULL != pObjectId) pObjectId->Release();
|
|
return hr;
|
|
|
|
} |