2025-11-28 00:35:46 +09:00

337 lines
10 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.
//---------------------------------------------------------------------
// Create a CMC EOBO request, sign it with a Enrollment Agent
// certificate (containing EKU of Certificate Request Agent),
// enroll to an enterprise CA, export the enrolled cert into
// a PFX file, delete the enrolled cert and its private key
#include <stdio.h>
#include <certenroll.h>
#include <certsrv.h>
#include <certcli.h>
#include <wincrypt.h>
#include "enrollCommon.h"
void Usage()
{
wprintf(L"Usage:\n");
wprintf(L"enrollEOBOCMC <Template> <Requester> <FileOut> ");
wprintf(L"<Password> [<EATemplate>]\n");
wprintf(L"Example: enrollEOBOCMC User Domain\\User pfx.out ");
wprintf(L"1111 EnrollmentAgent\n");
}
HRESULT __cdecl wmain(__in int argc, __in_ecount(argc) wchar_t *argv[])
{
HRESULT hr = S_OK;
bool fCoInit = false;
IX509Enrollment* pEnroll = NULL;
IX509CertificateRequest* pRequest = NULL;
IX509CertificateRequest* pInnerRequest = NULL;
IX509CertificateRequestPkcs10* pPkcs10 = NULL;
IX509CertificateRequestCmc* pCmc = NULL;
IX509PrivateKey *pKey = NULL;
ISignerCertificate* pSignerCertificate = NULL;
ISignerCertificates* pSignerCertificates = NULL;
HCERTSTORE hStore = NULL;
CERT_CONTEXT const *pCert = NULL;
CERT_CONTEXT const *pCertContext = NULL;
PCWSTR pwszTemplateName;
PCWSTR pwszRequester;
PCWSTR pwszFileOut;
PCWSTR pwszPassword;
PCWSTR pwszEATemplateName = L"EnrollmentAgent";
BSTR strTemplateName = NULL;
BSTR strRequester = NULL;
BSTR strEACert = NULL;
BSTR strCert = NULL;
BSTR strPFX = NULL;
BSTR strPassword = NULL;
// Process command line arguments
if (argc != 6 && argc != 5 ) {
Usage();
hr = E_INVALIDARG;
_JumpError(hr, error, "invalid arg");
}
else
{
pwszTemplateName = argv[1];
pwszRequester = argv[2];
pwszFileOut = argv[3];
pwszPassword = argv[4];
if (argc == 6)
pwszEATemplateName = argv[5];
}
// CoInitializeEx
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
_JumpIfError(hr, error, "CoInitializeEx");
fCoInit = true;
// Allocate BSTR for template name
strTemplateName = SysAllocString(pwszTemplateName);
if (NULL == strTemplateName)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "SysAllocString");
}
// Allocate BSTR for requester name
strRequester = SysAllocString(pwszRequester);
if (NULL == strRequester)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "SysAllocString");
}
// Allocate BSTR for the password of PFX file
strPassword = SysAllocString(pwszPassword);
if (NULL == strPassword)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "SysAllocString");
}
// Create IX509CertificateRequestCmc
hr = CoCreateInstance(
__uuidof(CX509CertificateRequestCmc),
NULL, // pUnkOuter
CLSCTX_INPROC_SERVER,
__uuidof(IX509CertificateRequestCmc),
(void **) &pCmc);
_JumpIfError(hr, error, "CoCreateInstance");
// Initialize IX509CertificateRequestCmc
hr = pCmc->InitializeFromTemplateName(
ContextUser,
strTemplateName);
_JumpIfError(hr, error, "InitializeFromTemplateName");
// Add requester name since it is an EOBO request
hr = pCmc->put_RequesterName(strRequester);
_JumpIfError(hr, error, "put_RequesterName");
/* Find a EA certificate first */
// Find a cert that has EKU of Certificate Request Agent
hr = findCertByEKU(szOID_ENROLLMENT_AGENT, &pCert);
if (S_OK != hr) // Cert not found
{
// Enroll an EA cert first
hr = enrollCertByTemplate(pwszEATemplateName);
_JumpIfError(hr, error, "enrollCertByTemplate");
// Search again
hr = findCertByEKU(szOID_ENROLLMENT_AGENT, &pCert);
_JumpIfError(hr, error, "findCertByEKU");
}
// Verify the certificate chain and its EKU
hr = verifyCertContext(pCert, szOID_ENROLLMENT_AGENT);
_JumpIfError(hr, error, "verifyCertContext");
// Convert PCCERT_CONTEXT to BSTR
strEACert = SysAllocStringByteLen(
(CHAR const *) pCert->pbCertEncoded,
pCert->cbCertEncoded);
if (NULL == strEACert)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "SysAllocStringByteLen");
}
/* Sign the EOBO request with EA certificate */
// Create ISignerCertificate
hr = CoCreateInstance(
__uuidof(CSignerCertificate),
NULL, // pUnkOuter
CLSCTX_INPROC_SERVER,
__uuidof(ISignerCertificate),
(void **)&pSignerCertificate);
_JumpIfError(hr, error, "CoCreateInstance");
// Initialize ISignerCertificate from EA certificate
hr = pSignerCertificate->Initialize(
VARIANT_FALSE,
VerifyNone,
XCN_CRYPT_STRING_BINARY,
strEACert);
_JumpIfError(hr, error, "Initialize");
// Retrieve ISignerCertificates collection from CMC request
hr = pCmc->get_SignerCertificates(&pSignerCertificates);
_JumpIfError(hr, error, "get_SignerCertificates");
// Add EA certificate into ISignerCertificates collection
hr = pSignerCertificates->Add(pSignerCertificate);
_JumpIfError(hr, error, "Add");
/* Enroll for EOBO request */
// Create IX509Enrollment
hr = CoCreateInstance(
__uuidof(CX509Enrollment),
NULL, // pUnkOuter
CLSCTX_INPROC_SERVER,
__uuidof(IX509Enrollment),
(void **) &pEnroll);
_JumpIfError(hr, error, "CoCreateInstance");
// Initialize IX509Enrollment
hr = pEnroll->InitializeFromRequest(pCmc);
_JumpIfError(hr, error, "InitializeFromRequest");
// Enroll
hr = pEnroll->Enroll();
_JumpIfError(hr, error, "Enroll");
// Check enrollment status
hr = checkEnrollStatus(pEnroll);
_JumpIfError(hr, error, "checkEnrollStatus");
/* Export the enrolled cert to a PFX file */
// Create PFX output in binary format
hr = pEnroll->CreatePFX(
strPassword,
PFXExportEEOnly,
XCN_CRYPT_STRING_BINARY,
&strPFX);
_JumpIfError(hr, error, "checkEnrollStatus");
// Save the PFX output to file in binary format
hr = EncodeToFileW(
pwszFileOut,
(BYTE const *) strPFX,
SysStringByteLen(strPFX),
CR_OUT_BINARY | DECF_FORCEOVERWRITE);
_JumpIfError(hr, error, "EncodeToFileW");
/* Retrieve and delete the enrolled cert from store */
// Get the cert just enrolled in binary format
hr = pEnroll->get_Certificate(XCN_CRYPT_STRING_BINARY, &strCert);
_JumpIfError(hr, error, "get_Certificate");
// Get the PCCERT_CONTEXT handle out of the certificate
pCert = CertCreateCertificateContext(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
(BYTE const *)strCert,
SysStringByteLen(strCert));
if (NULL == pCert)
{
hr = GetLastError();
_JumpError(hr, error, "CertCreateCertificateContext");
}
// 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");
}
// Search for the cert based on CERT_CONTEXT
pCertContext = CertFindCertificateInStore(
hStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_FIND_EXISTING,
pCert,
NULL);
if (NULL == pCertContext)
{
hr = GetLastError();
_JumpError(hr, error, "CertFindCertificateInStore");
}
// Delete the cert from store
if (!CertDeleteCertificateFromStore(pCertContext))
{
hr = GetLastError();
_JumpError(hr, error, "CertDeleteCertificateFromStore");
}
/* Delete the private key as well */
// Retrieve the request
hr = pEnroll->get_Request(&pRequest);
_JumpIfError(hr, error, "get_Request");
// Get the innermost request
hr = pRequest->GetInnerRequest(LevelInnermost, &pInnerRequest);
_JumpIfError(hr, error, "GetInnerRequest");
// QueryInterface for the pkcs10 request
hr = pInnerRequest->QueryInterface(
__uuidof(IX509CertificateRequestPkcs10),
(VOID **)&pPkcs10);
_JumpIfError(hr, error, "QueryInterface");
// Get the private key
hr = pPkcs10->get_PrivateKey(&pKey);
_JumpIfError(hr, error, "get_PrivateKey");
// Close the private key
hr = pKey->Close();
_JumpIfError(hr, error, "Close");
// Delete the private key
hr = pKey->Delete();
_JumpIfError(hr, error, "Delete");
error:
SysFreeString(strTemplateName);
SysFreeString(strRequester);
SysFreeString(strEACert);
SysFreeString(strCert);
SysFreeString(strPFX);
SysFreeString(strPassword);
if (NULL != pEnroll) pEnroll->Release();
if (NULL != pRequest) pRequest->Release();
if (NULL != pInnerRequest) pInnerRequest->Release();
if (NULL != pPkcs10) pPkcs10->Release();
if (NULL != pCmc) pCmc->Release();
if (NULL != pKey) pKey->Release();
if (NULL != pSignerCertificate) pSignerCertificate->Release();
if (NULL != pSignerCertificates) pSignerCertificates->Release();
if (NULL != pCert) CertFreeCertificateContext(pCert);
if (NULL != pCertContext) CertFreeCertificateContext(pCertContext);
if (NULL != hStore) CertCloseStore(hStore, 0);
if (fCoInit) CoUninitialize();
return hr;
}