Create Enroll on Behalf of Another User Request

Applies To: Windows Server 2008

Create a request on behalf of another user by using an enrollment agent certificate. First, find the enrollment agent certificate by opening the user certificate store and searching the extended key usages (EKUs) for the Enrollment Agent OID.

After the enrollment agent certificate has been found (and validated), the inner request is then created based on a template name. This is followed by creating an outer request from the inner request, and finally, adding the RequesterName and a signer certificate to the request.

C#:
string EKUOid = "2.5.29.37";
string EnrollmentAgentOid = "1.3.6.1.4.1.311.20.2.1";

X509Store store = new X509Store("My", StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2 cert = null;
foreach (X509Certificate2 c in store.Certificates)
{
    // Check that there is a private key for the enrollment agent and also that it not expired.
    // This does not check for revocation or for a valid chain etc.
    if (c.HasPrivateKey && c.NotBefore <= DateTime.UtcNow && DateTime.UtcNow <= c.NotAfter )
    {
        foreach (X509Extension ext in c.Extensions)
        {
            if (ext.Oid.Value == EKUOid)
            {
                X509EnhancedKeyUsageExtension eku = (X509EnhancedKeyUsageExtension)ext;

                foreach (Oid myusage in eku.EnhancedKeyUsages)
                {
                    if (myusage.Value == EnrollmentAgentOid)
                    {
                        cert = c;
                        break;
                    }
                }
            }
        }
    }
}
if (cert == null)
{
    Console.WriteLine("Cannot find enrollment agent certificate");
    return null;
}

// Create a PKCS 10 inner request.
CX509CertificateRequestPkcs10Class pkcs10_req = new CX509CertificateRequestPkcs10Class();
pkcs10_req.InitializeFromTemplateName( X509CertificateEnrollmentContext.ContextUser, myTemplate );

// Create a CMC outer request and initialize
CX509CertificateRequestCmcClass cmc_req = new CX509CertificateRequestCmcClass();
cmc_req.InitializeFromInnerRequest(pkcs10_req);
cmc_req.RequesterName = OtherUser;

CSignerCertificateClass signer = new CSignerCertificateClass();
signer.Initialize(false, X509PrivateKeyVerify.VerifyAllowUI, EncodingType.XCN_CRYPT_STRING_BASE64, Convert.ToBase64String(cert.GetRawCertData()));
cmc_req.SignerCertificate = signer;

// encode the request
cmc_req.Encode();

C++:
IX509CertificateRequestPkcs10*pkcs10_req = NULL;
IX509CertificateRequestCmc*cmc_req = NULL;
ISignerCertificate*signer = NULL;
HRESULT hr = S_OK;
LPSTRpszOIDs[1]={szOID_ENROLLMENT_AGENT};
CERT_ENHKEY_USAGEstCertUsage = {1,pszOIDs};
HCERTSTORE hStore = NULL;


hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_CURRENT_USER, L"MY" );
if ( hStore == NULL )
{
_tprintf( _T("CertOpenStore: 0x%x\n"), GetLastError()); 
goto error; 
}

// Simple check only for the Enrollment Agent EKU in a certificate. This does not perform a chain or policy check.
PCCERT_CONTEXT pcert = CertFindCertificateInStore( hStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ENHKEY_USAGE, &stCertUsage, NULL );
if ( pcert == NULL )
{
_tprintf( _T("CertFindCertificateInStore: 0x%x\n"), GetLastError()); goto error; 
}


hr = CoCreateInstance(
        __uuidof(CX509CertificateRequestPkcs10),
        NULL,       // pUnkOuter
        CLSCTX_INPROC_SERVER,
        __uuidof(IX509CertificateRequestPkcs10),
        (void **) &pkcs10_req);
_JumpIfError(hr, error, _T("CoCreateInstance CX509CertificateRequestPkcs10"));

hr = pkcs10_req->InitializeFromTemplateName( ContextUser, myTemplate );
_JumpIfError(hr, error, _T("pkcs10_req->InitializeFromTemplateName"));


// Create a CMC outer request and initialize
hr = CoCreateInstance(
        __uuidof(CX509CertificateRequestCmc),
        NULL,       // pUnkOuter
        CLSCTX_INPROC_SERVER,
        __uuidof(IX509CertificateRequestCmc),
        (void **) &cmc_req);
_JumpIfError(hr, error, _T("CoCreateInstance CX509CertificateRequestCmc"));

hr = cmc_req->InitializeFromInnerRequest(pkcs10_req);
_JumpIfError(hr, error, _T("cmc_req->InitializeFromInnerRequest"));


hr = cmc_req->put_RequesterName( OtherUser );
_JumpIfError(hr, error, _T("cmc_req->put_RequesterName"));

hr = CoCreateInstance(
        __uuidof(CSignerCertificate),
        NULL,       // pUnkOuter
        CLSCTX_INPROC_SERVER,
        __uuidof(ISignerCertificate),
        (void **) &signer);
_JumpIfError(hr, error, _T("CoCreateInstance CSignerCertificate"));


BSTR BinCert = SysAllocStringLen( NULL, pcert->cbCertEncoded );
memcpy( BinCert, pcert->pbCertEncoded, pcert->cbCertEncoded );

hr = signer->Initialize(false, VerifyAllowUI, XCN_CRYPT_STRING_BINARY, BinCert );
_JumpIfError(hr, error, _T("signer->Initialize"));

hr = cmc_req->put_SignerCertificate( signer );
_JumpIfError(hr, error, _T("cmc_req->put_SignerCertificate"));


hr = cmc_req->Encode();
_JumpIfError(hr, error, _T("cmc_req->Encode"));


goto noerror;

error:
if (NULL != cmc_req ) cmc_req->Release();

noerror:
if ( NULL != signer ) signer->Release();
if ( NULL != pkcs10_req ) pkcs10_req->Release();
if ( hStore != NULL ) CertCloseStore( hStore, 0 );

VBScript:
Dim store
Set store = CreateObject( "CAPICOM.Store")
Store.Open CAPICOM_CURRENT_USER_STORE, "My", CAPICOM_STORE_OPEN_READ_ONLY OR CAPICOM_STORE_OPEN_EXISTING_ONLY

Dim cert, stat, eku, enrolcert
For each cert in Store.Certificates
    Set stat = cert.IsValid
    stat.EKU.OID = "1.3.6.1.4.1.311.20.2.1"
    If stat.Result Then
        For Each eku in Cert.ExtendedKeyUsage.EKUs
            If eku.oid = "1.3.6.1.4.1.311.20.2.1" Then
                enrolcert = cert.Export( CAPICOM_ENCODE_BASE64 )
            End If    
        Next
    End If
Next

Dim pkcs10_req
Set pkcs10_req = CreateObject( "X509Enrollment.CX509CertificateRequestPkcs10" )
pkcs10_req.InitializeFromTemplateName ContextUser, templatename

Dim cmc_req
Set cmc_req = CreateObject( "X509Enrollment.CX509CertificateRequestCmc" )
cmc_req.InitializeFromInnerRequest pkcs10_req
cmc_req.RequesterName = OtherUser

Dim signer
Set signer = CreateObject( "X509Enrollment.CSignerCertificate" )
signer.Initialize false, VerifyAllowUI, XCN_CRYPT_STRING_BASE64, enrolcert
cmc_req.SignerCertificate = signer

' encode the request
cmc_req.Encode

VB.NET:
Dim eku As X509EnhancedKeyUsageExtension
Dim myusage As Oid
Dim store As New X509Store("My", StoreLocation.CurrentUser)
store.Open(OpenFlags.ReadOnly)
Dim cert As X509Certificate2 = Nothing
Dim c As X509Certificate2
For Each c In store.Certificates
    ' Check that there is a private key for the enrollment agent and also that it not expired.
    ' This does not check for revocation or for a valid chain etc.
    If c.HasPrivateKey And c.NotBefore <= DateTime.UtcNow And DateTime.UtcNow <= c.NotAfter Then
        Dim ext As X509Extension
        For Each ext In c.Extensions
            If ext.Oid.Value = EKUOid Then
                eku = ext
                For Each myusage In eku.EnhancedKeyUsages
                    If myusage.Value = EnrollmentAgentOid Then
                        cert = c
                        Exit For
                    End If
                Next
            End If
        Next
    End If
Next
If (cert Is Nothing) Then
    Return Nothing
End If
Dim pkcs10_req As New CX509CertificateRequestPkcs10Class
pkcs10_req.InitializeFromTemplateName(X509CertificateEnrollmentContext.ContextUser, myTemplate)

Dim cmc_req As New CX509CertificateRequestCmcClass
cmc_req.InitializeFromInnerRequest(pkcs10_req)
cmc_req.RequesterName = OtherUser

Dim signer As New CSignerCertificateClass
signer.Initialize(False, X509PrivateKeyVerify.VerifyAllowUI, EncodingType.XCN_CRYPT_STRING_BASE64, Convert.ToBase64String(cert.GetRawCertData))
cmc_req.SignerCertificate = signer
cmc_req.Encode()