Basic Enrollment Process Using the Windows Server 2008 Certificate Enrollment Control

Applies To: Windows Server 2008

Figure 3 shows a typical process flow from a client generating a request to submitting the request to a Windows Certificate Server. This can be achieved by using the IX509Enrollment interface. The IX509Enrollment interface handles the creation of a request based on a template, enumeration of Enterprise Certificate Servers that are able to handle the request, enrollment, and installing the issued certificate into the local certificate store.

 

 

Figure 3   Basic Enrollment Process

In this example, the IX509Enrollment interface will create a request in an appropriate format based on the certificate template, submit the request to a Microsoft Enterprise CA, and then install the issued certificate.

The following code demonstrates a simple enrollment for a certificate using IX509Enrollment interface using either C#, VB.NET, C++, or VBScript.

C#:
CX509EnrollmentClass enroll = new CX509EnrollmentClass();
enroll.InitializeFromTemplateName( X509CertificateEnrollmentContext.ContextUser, templatename);
enroll.Enroll();

VB.NET:
Dim enroll As New CX509EnrollmentClass
enroll.InitializeFromTemplateName( X509CertificateEnrollmentContext.ContextUser, templatename)
enroll.Enroll()

C++:
IX509Enrollment* pEnroll = NULL;
HRESULT hr = S_OK;

hr = CoCreateInstance(
        __uuidof(CX509Enrollment),
        NULL,       // pUnkOuter
        CLSCTX_INPROC_SERVER,
        __uuidof(IX509Enrollment),
        (void **) &pEnroll);
_JumpIfError(hr, error, _T("CoCreateInstance CX509Enrollment"));

hr = pEnroll->InitializeFromTemplateName( ContextUser, templatename );
_JumpIfError(hr, error, _T("InitializeFromTemplateName"));

hr = pEnroll->Enroll();
_JumpIfError(hr, error, _T("Enroll"));

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

VBScript:
Dim Enroll
Set Enroll = CreateObject( "X509Enrollment.CX509Enrollment" )
Enroll.InitializeFromTemplateName ContextUser, templatename
Enroll.Enroll

For version 2 and 3 (Windows Server 2003 and Windows Server 2008) templates, the object identifier (OID) of the template should be passed rather than the name. An example template OID template string would be the following:

1.3.6.1.4.1.311.21.8.W.X.Y.X.207.6637102.9026972

OID strings can be found by querying the “msPKI-Cert-Template-OID” attribute for the template within the Configuration container of Microsoft Active Directory®.

Creating a PKCS #10 Request Using the Certificate Enrollment Control

The PKCS #10 request is the standard format most widely supported by systems for certificate requests. This section shows how to create a simple PKCS #10 request using the new Windows Server 2008 Enrollment Control. The code generates a PKCS #10 request with default settings, adds a template name, and sets the subject name.

Creation of PKCS #10 requests can be achieved using either C#, VB.NET, C++, or VBScript.

C#:
CX509CertificateRequestPkcs10Class request = new CX509CertificateRequestPkcs10Class();

request.InitializeFromTemplateName( X509CertificateEnrollmentContext.ContextUser, myTemplate);
CX500DistinguishedNameClass subject = new CX500DistinguishedNameClass();
subject.Encode(userSubject, X500NameFlags.XCN_CERT_NAME_STR_NONE);
request.Subject = subject;
request.Encode();

C++:
IX509CertificateRequestPkcs10* request = NULL;
IX500DistinguishedName* SubjectName = NULL;
HRESULT hr = S_OK;

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

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

hr = CoCreateInstance(
        __uuidof(CX500DistinguishedName),
        NULL,       // pUnkOuter
        CLSCTX_INPROC_SERVER,
        __uuidof(IX500DistinguishedName),
        (void **) &SubjectName);
_JumpIfError(hr, error, _T("CoCreateInstance CX500DistinguishedName"));

hr = SubjectName->Encode( userSubject, XCN_CERT_NAME_STR_NONE);
_JumpIfError(hr, error, _T("SubjectName->Encode"));

request->put_Subject( SubjectName );

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

goto noerror;

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

noerror:
if (NULL != SubjectName ) SubjectName->Release();

return request;

VBScript:
Dim request
Set request = CreateObject( "X509Enrollment.CX509CertificateRequestPkcs10" )
request.InitializeFromTemplateName ContextUser, myTemplate

request.Subject = CreateObject( "X509Enrollment.CX500DistinguishedName" )
request.Subject.Encode userSubject, XCN_CERT_NAME_STR_NONE
request.Encode

VB.NET:
Dim request As New CX509CertificateRequestPkcs10Class
request.InitializeFromTemplateName( X509CertificateEnrollmentContext.ContextUser, myTemplate)
Dim subject As New CX500DistinguishedNameClass
subject = New CX500DistinguishedNameClass
subject.Encode(userSubject, X500NameFlags.XCN_CERT_NAME_STR_NONE)
request.Subject = subject

request.Encode()

Creating an Advanced PKCS #10 Request Using the Certificate Enrollment Control

This section shows how to create an Elliptical Curve Digital Signature Request as a PKCS #10 request using the new Windows Server 2008 Enrollment Control. The example signs the request using the SHA-256 rather than the older SHA-1 (160 bit).

C#:
CX509CertificateRequestPkcs10Class request = new CX509CertificateRequestPkcs10Class();

request.Initialize(X509CertificateEnrollmentContext.ContextUser);
CX500DistinguishedNameClass subject = new CX500DistinguishedNameClass();
subject.Encode(userSubject, X500NameFlags.XCN_CERT_NAME_STR_NONE);
request.Subject = subject;

request.PrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE;
request.PrivateKey.ProviderName = myProvider;

CObjectId algobj = new CObjectId();
algobj.InitializeFromAlgorithmName( ObjectIdGroupId.XCN_CRYPT_ANY_GROUP_ID, ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlags.AlgorithmFlagsNone, "ECDSA_P521");
request.PrivateKey.Algorithm = algobj;

CObjectId hashobj = new CObjectId();
hashobj.InitializeFromAlgorithmName( ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlags.AlgorithmFlagsNone, "SHA256");
request.HashAlgorithm = hashobj;

request.Encode();

C++:
BSTR alg = SysAllocString(L"ECDSA_P521");
BSTR hashAlg = SysAllocString(L"SHA256");
IX509CertificateRequestPkcs10* request = NULL;
IX500DistinguishedName* SubjectName = NULL;
IObjectId* Algorithm = NULL;
IObjectId* HashObj = NULL;
IX509PrivateKey *pKey = NULL;
HRESULT hr = S_OK;


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


hr = request->Initialize( ContextUser );
_JumpIfError(hr, error, _T("request->Initialize"));


hr = CoCreateInstance(
        __uuidof(CX500DistinguishedName),
        NULL,       // pUnkOuter
        CLSCTX_INPROC_SERVER,
        __uuidof(IX500DistinguishedName),
        (void **) &SubjectName);
_JumpIfError(hr, error, _T("CoCreateInstance CX500DistinguishedName"));


hr = SubjectName->Encode( userSubject, XCN_CERT_X500_NAME_STR );
_JumpIfError(hr, error, _T("SubjectName->Encode"));


hr = request->put_Subject( SubjectName );
_JumpIfError(hr, error, _T("request->put_Subject"));


hr = request->get_PrivateKey( &pKey );
_JumpIfError(hr, error, _T("request->get_PrivateKey"));

hr = pKey->put_KeySpec( XCN_AT_SIGNATURE );
_JumpIfError(hr, error, _T("pKey->put_KeySpec()"));


hr = pKey->put_ProviderName( myProvider );
_JumpIfError(hr, error, _T("pKey->put_ProviderName()"));


hr = CoCreateInstance(
        __uuidof(CObjectId),
        NULL,       // pUnkOuter
        CLSCTX_INPROC_SERVER,
        __uuidof(IObjectId),
        (void **) &Algorithm);
_JumpIfError(hr, error, _T("CoCreateInstance CObjectId - Algorithm"));


hr = Algorithm->InitializeFromAlgorithmName(XCN_CRYPT_ANY_GROUP_ID, XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlagsNone, alg);
_JumpIfError(hr, error, _T("Algorithm->InitializeFromAlgorithmName"));


hr = pKey->put_Algorithm( Algorithm );
_JumpIfError(hr, error, _T("pKey->put_Algorithm()"));


hr = CoCreateInstance(
        __uuidof(CObjectId),
        NULL,       // pUnkOuter
        CLSCTX_INPROC_SERVER,
        __uuidof(IObjectId),
        (void **) &HashObj);
_JumpIfError(hr, error, _T("CoCreateInstance CObjectId - Hash"));


hr = HashObj->InitializeFromAlgorithmName(XCN_CRYPT_HASH_ALG_OID_GROUP_ID, XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlagsNone, hash);
_JumpIfError(hr, error, _T("HashObj->InitializeFromAlgorithmName"));


hr = request->put_HashAlgorithm( HashObj );
_JumpIfError(hr, error, _T("request->put_HashAlgorithm"));


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


goto noerror;

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

noerror:
if (NULL != pKey ) pKey->Release();
if (NULL != SubjectName ) SubjectName->Release();
if (NULL != Algorithm ) Algorithm->Release();
if (NULL != HashObj ) HashObj->Release();

VBScript:
Dim request, algobj, hashobj, template

Set request = CreateObject( "X509Enrollment.CX509CertificateRequestPkcs10" )

request.Initialize( ContextUser )
request.Subject = CreateObject( "X509Enrollment.CX500DistinguishedName" )
request.Subject.Encode userSubject, XCN_CERT_NAME_STR_NONE

If sign Then
    request.PrivateKey.KeySpec = XCN_AT_SIGNATURE
Else
    request.PrivateKey.KeySpec = XCN_AT_KEYEXCHANGE
End If

request.PrivateKey.ProviderName = myProvider

Set algobj = CreateObject( "X509Enrollment.CObjectId" )
algobj.InitializeFromAlgorithmName XCN_CRYPT_ANY_GROUP_ID, XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlagsNone, "ECDSA_P521"
request.PrivateKey.Algorithm = algobj

Set hashobj = CreateObject( "X509Enrollment.CObjectId" )
hashobj.InitializeFromAlgorithmName XCN_CRYPT_HASH_ALG_OID_GROUP_ID, XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlagsNone, "SHA256"
request.HashAlgorithm = hashobj

request.Encode

VB.NET:
Dim request As New CX509CertificateRequestPkcs10Class
request.Initialize(X509CertificateEnrollmentContext.ContextUser)
request.Subject = New CX500DistinguishedNameClass
request.Subject.Encode(userSubject, X500NameFlags.XCN_CERT_NAME_STR_NONE)
request.PrivateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE
request.PrivateKey.ProviderName = myProvider

Dim algobj As CObjectId = New CObjectIdClass
algobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_ANY_GROUP_ID, ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlags.AlgorithmFlagsNone, "ECDSA_P521")
request.PrivateKey.Algorithm = algobj

Dim hashobj As CObjectId = New CObjectIdClass
hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID, ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlags.AlgorithmFlagsNone, "SHA256")
request.HashAlgorithm = hashobj

request.Encode()

This code will generate a PKCS #10 request which (when dumped using Certutil) has the following public key values.

Public Key Algorithm:
    Algorithm ObjectId: 1.2.840.10045.2.1 ECC
    Algorithm Parameters:
    06 05 2b 81 04 00 23
        1.3.132.0.35 ECDSA_P521 (ECDH_P521)
Public Key Length: 521 bits
Public Key: UnusedBits = 0

The request will also be signed using SHA256 as follows:

Signature Algorithm:
    Algorithm ObjectId: 1.2.840.10045.4.3.2 sha256ECDSA
    Algorithm Parameters: NULL

This code can be modified to create an encryption key (rather than signing) by changing the KeySpec and the algorithm as follows:

request.PrivateKey.KeySpec = X509KeySpec.XCN_AT_KEYEXCHANGE;
request.PrivateKey.Algorithm.InitializeFromAlgorithmName( ObjectIdGroupId.XCN_CRYPT_ANY_GROUP_ID, ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, AlgorithmFlags.AlgorithmFlagsNone, "ECDH_P521");