Other posts in the series:


Continuing our CertEnroll discussion (part1, part2) we will talk about request submission and issued certificate installation.

Certificate request submission to a Standalone CA

In the previous post we created an offline request and generated a key pair which remains on a local system. The next step is to submit the request to a CA server. Our request contains no template information and is supposed to be submitted to a Standalone CA or 3rd party CA. Depending on CA version and configuration, various methods can be used. In the case of Windows CA, you can use command-line utility 'certreq.exe' with '-submit' switch, Certification Authority MMC snap-in (certsrv.msc) or CryptoAPI COM interface. In this section we will explore ICertRequest2 and other related COM interfaces. ICertRequest2 interface contains Submit method which submits request to a specified CA server. CA server is specified by passing it's configuration string in the following format: CAHostName\CAName. For example: caserver\contoso-ca, where 'caserver' is CA server host name (NetBIOS or DNS name) and 'contoso-ca' is CA name.

At first we instantiate ICertRequest2 object:

$CertRequest = New-Object -ComObject CertificateAuthority.Request

You can omit the version of the interface, because the system automatically loads the right one. Now we read request contents to a variable:

$ReqFile = [IO.File]::ReadAllText(".\request.req")

now we need to identify CA location. If the CA is hosted on a local server, you can instantiate ICertConfig COM interface and call GetConfig method as follows:

$CertConfig = New-Object -ComObject CertificateAuthority.Config
$ConfigString = $CertConfig.GetConfig(4)

$ConfigString will contain local CA configuration string which will be passed to ICertRequest2::Submit. Here we go:

$Status = $CertRequest.Submit(0,$ReqFile,$null,$ConfigString)

the default behavior for standalone CA is to store all requests pending until they are reviewed by CA manager/administrator. Request status is saved to $Status variable. Status code definitions are defined in §3.2.1.4.3.1.2 of the MS-WCCE protocol specifications. Here is an output in my environment:

PS C:\> $Status = $CertRequest.Submit(0,$ReqFile,$null,$ConfigString)
PS C:\> $Status
5
PS C:\>

As per mentioned [MS-WCCE] documentation, 5 means "request pending". If the requester has CA manager or CA administrator permissions, we can use another CryptoAPI COM interface to issue the certificate — ICertAdmin::ResubmitRequest method. The method takes two arguments:

  1. CA configuration string (which is already stored in $ConfigString variable);
  2. Request ID.

In order to retrieve request ID, we call ICertRequest3::GetRequestId method:

$RequestID = $CertRequest.GetRequestId()

and call ICertAdmin::ResubmitRequest method as follows:

# instantiate ICertAdmin COM interface object:
$CertAdmin = New-Object -ComObject CertificateAuthority.Admin
# call ResubmitRequest method to issue pending request
$CertAdmin.ResubmitRequest($ConfigString, $RequestID)

Here is example output in my environment:

PS C:\> $RequestID = $CertRequest.GetRequestId()
PS C:\> $RequestID
45
PS C:\> $CertAdmin = New-Object -ComObject CertificateAuthority.Admin
PS C:\> $CertAdmin.ResubmitRequest($ConfigString, $RequestID)
3
PS C:\>

The method returns disposition code. If the disposition code is 3, then the certificate was successfully issued. In order to retrieve issued certificate, we need to return to ICertRequest interface and call ICertRequest3::RetrievePending method:

$CertRequest.RetrievePending($RequestID, $ConfigString)

The certificate is now retrieved. The next step is to get the issued certificate and save it to a file. To get certificate we call ICertRequest3::GetCertificate method by specifying output encoding. Just to remember, output encoding values are defined in EncodingType enumeration. Base64 with headers (0) is enough for us:

$Base64 = $CertRequest.GetCertificate(0)
Set-Content .\issuedcert.cer -Value $Base64

Now certificate issuance is completed and you can move issued certificate to the original client where we generated the request.

Certificate request submission to an Enterprise CA

While our request is not intended to use with Enterprise CA, because it doesn't contains certificate template information, we are still able to submit the request to an Enterprise CA. The main question is "how to put template information?". Since the request is digitally signed, we cannot edit request contents. However there is a tricky workaround (below).

Before we submit the request to an Enterprise CA, the following prerequisites must be completed:

The default behavior for Enterprise CA is to automatically issue the certificate unless the template is explicitly configured to store all request for the template pended. As we already know, we need to retrieve Enterprise CA configuration string to locate CA server. ICertConfig2::GetConfig accepts CC_UIPICKCONFIG argument to display UI window where you can select appropriate Enterprise CA server. When selected, the method returns configuration string for selected CA server:

$CertConfig = New-Object -ComObject CertificateAuthority.Config
$ConfigString = $CertConfig.GetConfig(1)

Now we instantiate ICertRequest2 interface object and read the request file contents:

$CertRequest = New-Object -ComObject CertificateAuthority.Request
$ReqFile = [IO.File]::ReadAllText("C:\request.req")

Now we explore ICertRequest2::Submit again and check the strAttributes parameter. To specify certificate template attribute we must use the following format: CertificateTemplate:TemplateCommonName. For example: CertificateTemplate:WebServer.

$CertRequest = New-Object -ComObject CertificateAuthority.Request
$ReqFile = [IO.File]::ReadAllText("C:\request.req")
$Status = $CertRequest.Submit(0,$ReqFile,"CertificateTemplate:WebServer",$ConfigString)

Here is an example from my environment:

PS C:\> $CertConfig = New-Object -ComObject CertificateAuthority.Config
PS C:\> $ConfigString = $CertConfig.GetConfig(1)
PS C:\> $CertRequest = New-Object -ComObject CertificateAuthority.Request
PS C:\> $ReqFile = [IO.File]::ReadAllText("C:\request.req")
PS C:\> $Status = $CertRequest.Submit(0,$ReqFile,"CertificateTemplate:WebServer",$ConfigString)
PS C:\> $status
3
PS C:\>

We see that the status is now 3, which means that the certificate was successfully issued. Therefore we can directly call ICertRequest3::GetCertificate method to retrieve certificate and save it to a file:

$Base64 = $CertRequest.GetCertificate(0)
Set-Content C:\issuedcert.cer -Value $Base64

Completing certificate request

When the certificate is issued we need to install it to original client. Certificate installation is performed by using the same COM interface as was used for request creation — IX509Enrollment. This interface has InstallResponse method which will complete enrollment task. At first we need to instantiate interface object:

$Response = New-Object -ComObject X509Enrollment.CX509Enrollment

then we need to initialize the response with the appropriate context — user or computer. If the request is intended for user, the context is 1, if the request is intended for computer, then context is 2. In the case of response installation, we use generic Initialize method to initialize the object:

$Response.Initialize(0x2)

InstallResponse method has strResponse parameter which takes the DER-encoded response. The response is our issued certificate. Therefore we need to get certificate's raw data and convert to a string (because parameter expects the string). The most simple way to get certificate's raw data is to create X509Certificate2 object and read RawData property. RawData property contains DER-encoded byte array which can be easily converted to a Base64 string:

$Cert = New-Object Security.Cryptography.X509Certificates.X509Certificate2 c:\issuedcert.cer
$Base64 = [Convert]::ToBase64String($Cert.RawData)

and call InstallResponse method with appropriate parameters:

$Response.InstallResponse(0,$Base64,0x1,"")

if the method succeed, then the full certificate enrollment process is completed. Original request will be deleted from Certificate Enrollment Requests container in certificate store and new certificate will appear in Personal store.

To be continued…


Share this article:

Comments:

Tom
Tom 22.10.2015 12:57 (GMT+2) Introducing to certificate enrollment APIs (part 3) — certificate request submission and response installation

Hi Vadims

Thank you veeeerry much for this great tutorial! I I've studied almost 100 Tutorials and finally found your version. It's just great :-)

Thanks a lot,
kind regards, Tom

Simon
Simon 12.02.2016 13:14 (GMT+2) Introducing to certificate enrollment APIs (part 3) — certificate request submission and response installation

Thanks a lot for your excellent pages. You enabled me to get my task finished with your explanations and examples.


Post your comment:

Please, solve this little equation and enter result below. Captcha