Other posts in the series:


Prologue

In previous posts we have discussed and explored certificate enrollment APIs (CertEnroll) to create offline request, submit it to Standalone or  Enterprise CA and install the response. In this post we will cover direct certificate enrollment by using Enterprise CA in the same forest as the client. While previous examples can be used for both domain and workgroup environments, this post assumes that the client is domain member and current forest contains at least one Enterprise CA.

Enrolling user certificate

Direct certificate enrollment is much easier than previous examples, but may be very tricky. We have to start with IX509Enrollment interface. While in previous example we started with IX509CertificateRequestPkcs10 interface, in this case this step can be skipped, because IX509Enrollment will automatically generate PKCS#10 request based on template settings. At first we instantiate IX509Enrollment object:

$Request = New-Object -ComObject X509Enrollment.CX509Enrollment

this interface contains a very useful method IX509Enrollment::InitializeFromTemplateName where we specify enrollment context and template common name. Now we will look at some client certificates (say, a certificate based on a 'User' template:

$Request.InitializeFromTemplateName(0x1,"User")

You can explore inner PKCS#10 request information in Request property:

PS C:\> $Request.Request


Type                        : 1
EnrollmentContext           : 1
Silent                      : False
ParentWindow                :
UIContextMessage            : %2Enrolling for: User
SuppressDefaults            : False
ClientId                    : 5
CspInformations             : System.__ComObject
HashAlgorithm               :
AlternateSignatureAlgorithm : False
TemplateObjectId            : System.__ComObject
PublicKey                   :
PrivateKey                  : System.__ComObject
NullSigned                  : False
ReuseKey                    : False
Subject                     :
CspStatuses                 : System.__ComObject
SmimeCapabilities           : True
SignatureInformation        : System.__ComObject
KeyContainerNamePrefix      : le
CryptAttributes             : System.__ComObject
X509Extensions              : System.__ComObject
CriticalExtensions          : System.__ComObject
SuppressOids                : System.__ComObject
PolicyServer                : System.__ComObject
Template                    : System.__ComObject



PS C:\> $Request.Request.X509Extensions

ObjectId                                                               Critical TemplateName
--------                                                               -------- ------------
System.__ComObject                                                        False User
System.__ComObject                                                        False
System.__ComObject                                                         True


PS C:\>

If necessary, you can edit only subject information (in the case of 'User' template it is not necessary, because CA automatically constructs subject information based on information from Active Directory and will ignore subject and subject alternative names (SAN) information) private key settings and certificate friendly name. If you edit other information in the inner request, they will be silently ignored by CA server.

If you are fine with default settings, you can start enrollment process by invoking Enroll() method with no parameters:

$Request.Enroll()

If you look at $Request object you will see that some properties are filled with new data:

PS C:\> $Request


Request                 : System.__ComObject
Silent                  : False
ParentWindow            :
NameValuePairs          : System.__ComObject
EnrollmentContext       : 1
Status                  : System.__ComObject
CertificateFriendlyName :
CertificateDescription  :
RequestId               : 33
CAConfigString          : dc3.adatum.lv\adatum-DC3-CA
PolicyServer            : System.__ComObject
Template                : System.__ComObject
RequestIdString         : 33



PS C:\>

Now we may want to get enrollment status. Enrollment status is saved to Status property:

PS C:\> $Request.Status


Text      :
Selected  : 1
Display   : 1
Status    : 1
Error     :
ErrorText : The operation completed successfully.



PS C:\>

Look at the second Status property (from IX509EnrollmentStatus type), it contains status code. Status codes are explained in the EnrollmentEnrollStatus enumeration. That is, if we see 1, then the certificate is successfully enrolled and installed. The same command sequence could be used for computer certificate enrollment where subject information is constructed by using Active Directory information. The only thing you must change is enrollment context during object initialization.

Enrolling offline computer certificate

As already defined, an offline request is the request where subject information is supplied in the request. As in previous example we instantiate IX509Enrollment interface object and initialize it by using IX509Enrollment::InitializeFromTemplateName method:

$Request = New-Object -ComObject X509Enrollment.CX509Enrollment
$Request.InitializeFromTemplateName(0x2,"WebServerV2")

Since this is a web server template (even customized) we have to provide at least some subject information. We will use existing techniques as already explained in part 2:

# fill the subject
$SubjectDN = New-Object -ComObject X509Enrollment.CX500DistinguishedName
$SubjectDN.Encode("CN=www.adatum.lv", 0x0)
# add SAN extension if necessary
$SAN = New-Object -ComObject X509Enrollment.CX509ExtensionAlternativeNames
$IANs = New-Object -ComObject X509Enrollment.CAlternativeNames
"www.adatum.lv", "owa.adatum.lv" | ForEach-Object {
    # instantiate a IAlternativeName object
    $IAN = New-Object -ComObject X509Enrollment.CAlternativeName
    # initialize the object by using current element in the pipeline
    $IAN.InitializeFromString(0x3,$_)
    # add created object to an object collection of IAlternativeNames
    $IANs.Add($IAN)
}
# finally, initialize SAN extension from a collection of alternative names:
$SAN.InitializeEncode($IANs)

and we must add these objects to our request. But:

PS C:\Users\Administrator> $Request.Request


Type                        : 3
EnrollmentContext           : 2
Silent                      : False
ParentWindow                :
UIContextMessage            : %2Enrolling for: Web Server V2
SuppressDefaults            : False
ClientId                    : 5
CspInformations             : System.__ComObject
HashAlgorithm               : System.__ComObject
AlternateSignatureAlgorithm : False
RequesterName               :
SignerCertificate           :
TemplateObjectId            : System.__ComObject
NullSigned                  : False
CryptAttributes             : System.__ComObject
NameValuePairs              : System.__ComObject
X509Extensions              : System.__ComObject
CriticalExtensions          : System.__ComObject
SuppressOids                : System.__ComObject
TransactionId               :
SignatureInformation        : System.__ComObject
ArchivePrivateKey           : False
EncryptionAlgorithm         :
EncryptionStrength          :
SignerCertificates          : System.__ComObject
PolicyServer                : System.__ComObject
Template                    : System.__ComObject



PS C:\Users\Administrator>

we don't see Subject property as expected. This is because, the request is not PKCS#10, but CMC. Request type is identified by Type property. And IX509CertificateRequest abstract interface contains GetInnerRequest method which returns nested PKCS#10 request object.

IX509CertificateRequest interface is abstract interface and is a base interface for other available certificate request interfaces. All certificate interfaces inherit method from IX509CertificateRequest interface.

The following trick should be used:

$Request.Request.GetInnerRequest(0).Subject = $SubjectDN
$Request.Request.GetInnerRequest(0).X509Extensions.Add($SAN)

And, finally, you can call Enroll() method to enroll a certificate. In that way you can change (if necessary) inner PKCS#10 properties.

Epilogue

Today we have covered two basic enrollment scenarios with Enterprise CA. In next post I'll cover how to perform Enrollment On Behalf Of (EOBO) with CertEnroll interfaces.

to be continued…


Share this article:

Comments:

Kjetil Bustnes

Hi Vadim,

Initially I want to thank you for a very useful site!

We are working on a RA to issue certificates based on existing PKCS#10's.  Using the information I have found on your site I've been able to create a script which will take the PKCS#10, create a request and get a certificate.  We want to be able to override the subject provided in the PKCS#10, however, I'm not able to set the Subject. The following snippet shows my attempt:
 

$PKCS10 = New-Object -ComObject X509Enrollment.CX509CertificateRequestPkcs10
$pkcs10.InitializeDecode("ContextMachine", 0x2) #XCN_CRYPT_STRING_BINARY
$pkcs10.InitializeDecode($p10,0x0) #XCN_CRYPT_STRING_BASE64HEADER

$dn=New-Object -ComObject X509Enrollment.CX500DistinguishedName
$dn.Encode('cn=test,o=somedomain,c=no')
$PKCS10.Subject = $dn

This results in the following error:
CertEnroll::CX509CertificateRequestPkcs10::put_Subject: The specified file is read only. 0x80071779 (WIN32: 6009 ERROR_FILE_READ_ONLY)
At C:\temp\get-request.ps1:103 char:1
+ $CertRequest.Request.GetInnerRequest(0).Subject = $dn
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (:) [], COMException
    + FullyQualifiedErrorId : System.Runtime.InteropServices.COMException

 

Do you have any suggestions as to why this happening and what can be done to get this to work?

With regards

Kjetil R Bustnes

 

Vadims Podāns

You cannot modify any property once Enode or InitializeDecode methods are called. Why this happens -- CSR is digitally signed, therefore you cannot alter it in any way, because you will break the signature. What you can try -- is to embed signed request in CMC and try to set the subject again. You will have to re-sign entire request again with Enrollment Agent certificate.


Post your comment:

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