Signing a CSR with an Enrollment Agent certificate

Often certificate templates in AD CS are configured to require a set of authorised signatures for issuance:


This is useful e.g. when issuance approval is delegated to a component outside of AD CS roles, such as some CA management or registration authority portal software. Combined with the template security settings this can prevent requests from being raised directly from the CA without going through a custom standard request process, and depending on the level of security required, multiple signatures can be enforced and optionally also additional CA certificate manager approval.

In order to produce each of this signatures an "Enrollment Agent" certificate is used, a certificate with the "Certificate Request Agent (1.3.6.1.4.1.311.20.2.1)"  Enhanced Key Usage.

Sometimes it is required to raise requests directly towards the CA, e.g. using the RPC/DCOM interface from the command line, but it is not desirable and/or possible to change the template configuration, so that the authorised signatures are still required.

We will look at a scenario where we have a PKCS#10 certificate signing request, in PEM format, and we want to sign it with an enrollment agent certificate we also have, so that we can then submit the request to the CA fulfilling the issuance requirements configured in the template.

We take as input a PKCS#10 file in PEM format that we want to sign:


 

And an Enrollment Agent certificate is available in the certificate store of the user running the procedure.


(make sure the private key of the certificate is available).

The certreq.exe command seems to be the right tool for the job in this case, and looking at the documentation:

https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/certreq_1

it would seem like -sign is the right parameter:

 with the relevant options and parameters being:

CertReq [-Submit] [Options] [RequestFileIn [CertFileOut [CertChainFileOut [FullResponseFileOut]]]]

-cert CertId        - Specify signing certificate by common name,
                      serial number, or by sha-1 key or                                  certificate hash

 RequestFileIn      - Base64-encoded or binary input file name:
                      PKCS10 certificate request,
                      CMS certificate request,
                      PKCS7 certificate renewal request,
                      X.509 certificate to be cross-certified, or
                      KeyGen tag format certificate request

RequestFileOut      - Base64-encoded output file name

So the right command should be:

certreq -sign -cert "5abc1ddea44056f3ab9d2ab8cb761834f2207ea6" unsigned.csr signed.csr

but when we try it:


The format of the CSR does not seem to be the right one, with the following error being displayed:

Certificate Request Processor: The data is invalid. 0x8007000d (WIN32: 13 ERROR_INVALID_DATA)
unsigned.csr

Even if according to the documentation a Base64 formatted PKCS#10 certificate request is an acceptable input in reality that doesn't seem to be the case. Such a command works with CMS certificate requests but for some reason not with PKCS#10 requests.

Looking at the different certreq parameters that accept an enrollment agent certificate as an option, it looks like the -policy parameter might also work, since it also takes the same options and additionally it requires a policy file:

The policy file is a required option and must follow the CAPolicy.inf syntax:

https://docs.microsoft.com/en-us/windows-server/networking/core-network-guide/cncg/server-certs/prepare-the-capolicy-inf-file

Since our goal is simply to sign the CSR, we will create a simple policy file with a "harmless" parameter, e.g. "Signature":

[Version]
Signature="$Windows NT$"

and save it as a file named e.g. policy.inf:

 

If we now try the certreq command with the -policy parameter:

certreq -policy -cert "5abc1ddea44056f3ab9d2ab8cb761834f2207ea6" unsigned.csr policy.inf signed.csr

A message is displayed stating that it is searching for the private key and then, depending on client computer configuration, it might attempt to read from external readers that might contain the private key, even if the certificate is locally available:


After cancelling those attempts the right certificate is found:

Selecting it will complete the signing process, and if we look at the output file it is a PKCS#7 file including the original PKCS#10 content and the required authorised signature via the Enrollment Agent certificate:



In order to avoid the pop-ups, in case the signing command is to be run as part of an automated process, and additional parameter can be added to the certreq command, namely "-q" for quiet:

certreq -policy -q -cert "5abc1ddea44056f3ab9d2ab8cb761834f2207ea6unsigned.csr policy.inf signed.csr

The new certificate request can now be submitted to the CA, since it contains the required authorised signature, e.g.:

certreq -config "<CA config string>" -attrib "CertificateTemplate:<Template>" -submit signed.csr

According to the documentation the above should be achievable with the certreq -sign parameter, which would be the logical one, but since that does not seem to be possible the -policy parameter can be used instead.










Comments

Post a Comment

Popular posts from this blog

Decoding OCSP GET requests

Compacting an AD CS database