Implementing Two-Tier PKI on Windows Server 2022 – Part 2

In part 1 of Two-Tier PKI implementation on Windows Server 2022 we did some introduction to PKI, you can check LAB config and offline Root CA setup. In this part we will go a bit further and work on Subordinate CA.


Please refer to Part 1 of this guide to understand what is all about. Also, before we start, read introduction part of Part 1 – I cannot be responsible if you mess up anything with this – purpose of these articles is only for testing and learning – I cannot be held responsible for any type of damage you do with this (financial, physical, emotional…) PKI is tricky thing and you should learn about topic before following this.

For LAB setup and additional details look into Part 1 of the guide.

Test-CA is already part of the test.local domain. Let’s start configuring.

Please do not turn of Root CA until you are done with entire PKI implementation!

Subordinate CA Configuration

First step will be done on our domain controller in DNS zone (in my case test.local) we should add following CNAME records



In alias name of the new CNAME record enter OCSP (and for second record enter PKI)

In FQDN field of the CNAME record enter test-CA1.test.local (name and domain of subordinate CA) – and for second PKI record also test-CA1.test.local

This is how everything should look like

If we did ok, when we ping from our client addresses pki.test.local or oscp.test.local it should resolve to IP of subordinate CA

We are now moving onto Subordinate CA for configuration, which in my case is named Test-CA1

First, like with Root CA, we need to create CAPolicy.inf file and put it into root directory of Windows – C:\Windows\CAPolicy.inf

If you have your PEN number, modify OID line in InternalPolicy section to OID= (XX being your PEN number)

Here is the CAPolicy.inf file we need for Subordinate CA

Signature="$Windows NT$"
Policies = AllIssuancePolicy, InternalPolicy
Critical = FALSE
Notice = "The Test.local Certification Authority is internal only."
URL = http:∕∕pki.test.local∕cps.html
RenewalKeyLength = 4096
RenewalValidityPeriod = Years
RenewalValidityPeriodUnits = 5
AlternateSignatureAlgorithm = 0
LoadDefaultTemplates = 1

Now, let’s head for AD CS role installation.

Before we begin, make sure you are logged in to the Subordinate CA as Domain Administrator! Otherwise we will have problems installing and configuring! We will do the installation throufh Server Manager. Again, as in Part 1, I assume you know Windows Server very well, so I won’t screenshot every step in adding roles in Server Manager.

Select Active Directory Certificate Services and confirm pop-up window

Select Certification Authority and Certification Authority Web Enrollment – confirm additional pop-up – Next

On Web Server Screen just confirm everything and click Next

We will now configure Subordinate CA with following settings:

Certificate Authority Setup Type: Enterprise CA

Certificate Authority Type: Subordinate CA

Cryptographic Provider: RSA#Microsoft Software Key Storage Provider

Key Length: 4096 Bits

Signature Hash Algorithm: SHA256

CA Common Name: Test-CA1-CA

Validity Period: 5 years

Again, we will do this part in Server Manager since I had issues with Powershell. From Server Manager select Configure Active Directory Certificate Services to the destination server

We will use domain administrator!!

We wkill select Certification Authority and Certification Authority Web Enrollment – Next

Enterprise CA – Next

Subordinate CA – Next

Create a new private key – Next

RSA#Microsoft Software Key Storage Provider, Key length 4096, SHA256

Common name – I will change to TEST-CA1-CA, rest leave as is, don’t touch – Next

I will save cert request in default location (root of C:\) and click on Next

Result is expected – we need to send generated .req file to Root CA and issue a certificate.

Ok so we did something, but we are not done. We need to issue Subordinate certificate from Root CA. We will do this on Root CA.

Let’s now validate what we’ve done by starting certsrv.msc on Subordinate CA

So, service is stopped. It will be stopped until we get certificate from Root CA. In the process above, we created certificate request, which will have to go through Root CA.

On C:\ I created CertData folder – which will host all the important stuff for our certs. Also, as you can see there is also a .req file in the root of the C drive which we will need to copy to Root CA via USB or Virtual floppy.

.req file form Subordinate CA to Root CA

We need created test-ca1.test.local_Test Domain Enterprise CA.req to transfer for Subordinate CA (Test-CA1) to Root CA – Test-RootCA machine. As I mentioned in the first part – having Root CA on the netowrk is not smart idea. Copy file to USB drive or Virtual floppy (if you are dealing with Hyper-V) and transfer .req file from Subordinate to Root CA.

I copied the .req file to C:\RootCA on Root CA server

Screenshot is old, filenames are different from other lab, but principle is the same…

Now, let’s leave that there and do one more thing before we get back to issuing Subordinate Certificate.

Creating CertData virtual directory

On Subordinate CA (Test-CA) we create C:\CertData folder, to that folder we need to copy .crl and .crt file we created on the Root CA in the first part of the guide.

Now, we will create virtual directory for those files on Subordinate CA

Start IIS Manager

Expand Sites and right click on Default Web Site and select Add Virtual Directory – for Alias enter CertData and for physical path C:\CertData – confirm with ok and select newly created virtual directory.

Select Directory Browsing in main window of CertData virtual dir and click on Enable

After this I restarted IIS.

Let’s quickly validate what we’ve done. From Client machine I will try to access in browser following link


All good!!

Enable Double Escaping in IIS

On Subordinate CA, while we are still in IIS, go to Default Website, from middle window select Request Filtering – when Request Filtering opens, select Edit Feature Settings – New window will open – select Allow double escaping. Confirm with OK. Restart IIS.

This is pretty important and should be done, since there will be problems with deploying Delta CRL and validation of PKI.

Subordinate Cert Creation

Ok, we already copied .req file from Subordinate CA to Root CA. Let’s now create certificate for Subordinate CA.

On the Root CA start Certification Authority console (certsrv.msc)

Right click on Root CA, select All Tasks and Submit new request…

Browse to C:\RootCA and select .req file from Subordinate CA

At this point I would get sometimes when I practice in labs with Windows Server 2022 this error – Error Parsing Request The configuration data for this product is corrupt. Contact your support personel. 0x8007064a. (WIN32: 1610 ERROR_BAD_CONFIGURATION) The error was result of inaccurate CDP and AIA commands, I did something wrong, space or deleted something – so be careful when you are configuring CDP and AIA on Root CA

Ok, let’s proceed, now, right click on request id 2 and select All Tasks – Issue

We did it, certificate is now in Issued Certificates

If we double click on the certificate we will see that it is valid for 5 years

And RSA 4096, SHA256… While we are on Details tab, we will click on Copy to File…

From Copy to File… we will start Certificate Export Wizard – click Next

We will select .P7B and “Include all certificates in the certification path if possible.

I will save it in C:\RootCA folder as SubordinateCA.p7b

Ok, we are done.

Now, we need to transfer SubordinateCA.p7b from RootCA to SubordinateCA (Test-CA1). You can use usb drive, virtual floppy, or network, it is all up to your, your company and security posture at your company. We discussed this in the first part.

I copied SubordinateCA.p7b to C:\ on Test-CA1

Now we are ack on the Subordinate CA (TEST-CA1) and will continue our work on it.

Start certsrv.msc – right click on your server – select All Tasks – Install CA Certificate…

I got error that says “Cannot verify certificate chain.” Do you wish to ignore the error and continue? The revocation function was unable to check revocation because the revocation server was offline 0x80092013 . I will confirm with OK.

We will try and start the service now

Ok, bump in the road again…

On Subordinate CA, start command prompt as admin and type in following command

certutil –setreg ca\CRLFlags +CRLF_REVCHECK_IGNORE_OFFLINE

If we try to start server then again…

It works now! The error above happened because Root CA is offline at the time of issuance or Subordinate CA.

Set Maximum Certificate Age

We want certs that our Test-CA1 issues to be valid only a year. Open Powershell as admin and type in

certutil.exe -setreg CA\ValidityPeriodUnits 1
certutil.exe -setreg CA\ValidityPeriod "Years"
net stop CertSvc
net start CertSvc

We will also set a few other things while we are at it

Certutil -setreg CA\CRLPeriodUnits 1
Certutil -setreg CA\CRLPeriod “Weeks”
Certutil -setreg CA\CRLDeltaPeriodUnits 1
Certutil -setreg CA\CRLDeltaPeriod “Days”
Certutil -setreg CA\CRLOverlapPeriodUnits 12
Certutil -setreg CA\CRLOverlapPeriod “Hours”
net stop CertSvc
net start CertSvc

Next stop is IIS, we will select CertEnroll and from the main screen select “Directory Browsing” – Enable. After that restart IIS

Now, let’s test the link http://pki.test.local/CertEnroll


If you click on files you should also be able to download them. If not – check double escaping we mentioned and configured earlier.

Enable Auditing

Audit object access in Local Security Policy should be enabled for both failure and success

Open Powershell as admin and type in

certutil.exe -setreg CA\AuditFilter 127
net stop CertSvc
net start CertSvc

Subordinate CA CDP and AIA Configuration

We will repeat CDP and AIA configuration also on Subordinate CA. Run COmmand Prompt as admin and type in following (in one line)


certutil -setreg CA\CACertPublicationURLs “1:C:\Windows\system32\CertSrv\CertEnroll\%1_%3%4.crt\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11\n2:http://pki.test.local/CertEnroll/%1_%3%4.crt”


certutil -setreg CA\CRLPublicationURLs “65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10\n6:http://pki.test.local/CertEnroll/%3%8%9.crl”

Check values with

certutil -getreg CA\CRLPublicationURLs
certutil -getreg CA\CACertPublicationURLs

Let’s restart the services

net stop CertSvc
net start CertSvc

and publish them

certutil -crl

Open certsrv.msc – select Properties of the server and select Extension tab. On CDP select http and make sure that Include in CRLs… and Include in CDP… are selected.

For AIA make sure that when you select http link “Include in AIA extension” is selected.

Certificate Practice Statement Document

You can use this to inform your users of Certificate Authority policies.

Create cps.html and copy following code in it. Then copy cps.html to c:\inetpub\wwwroot

<!DOCTYPE html>
<meta charset="utf-8">
<title>Test Domain Certification Practice Statement</title>
<h1>Test Domain Certification Practice Statement</h1>
<p>The Test Domain Certification Authority is internal only.</p>
<p>All issued certificates are for internal usage only.</p>
<p>Unauthorized usage strictly forbidden.</p>

Works fine, and you can edit it according to your needs

Firewall rules

Firewall should be on, following rules should be enabled

Echo Request - ICMPv4-in (ICMP) so we can ping
World Wide Web Services HTTP Traffic-In TCP/80 
World Wide Web Services HTTPS Traffic-In TCP/443

Add Root CA to Active Directory

Root CA is offline, so we need to add it AD, so Active Directory is aware that there is another Certificate Authority. Run Powershell as admin and type in following

certutil.exe -dspublish `
-f "C:\CertData\test-rootca_TEST-ROOTCA-CA.crt" RootCA

certutil.exe -addstore `
-f root "C:\CertData\test-rootca_TEST-ROOTCA-CA.crt"

and then one more

certutil.exe -addstore `
-f root "C:\CertData\TEST-ROOTCA-CA.crl"

Not everything went well for me from Powershell, so I opened Command Prompt (as admin) and navigated to C:\CertData where I have RootCA crl and crt file. From there I executed following command

certutil -f -dspublish TEST-ROOTCA-CA.crl

I had problems with CDP in PKIVIEW and this is the command I was missing.

Before we conclude, make sure you copy ROOTCA crl and crt files from C:\CertData to C:\Windows\System32\certsrv\CertEnroll folder

Let’s now quickly jump to our Domain Controller and open Active Directory Sites and Services – select Active Directory Sites and Services on the top – clik on View and select Show Services Node

Open Public Key Services and click on AIA – you should see both CA1 and ROOTCA.

Under CDP you should have two subfolders named TEST-CA1 (SubordinateCA) and test-rootca (Offline Root CA) both should have distrib points inside.

Ok, we now went through all.

Now, we can finally verify our PKI infrastructure, and see if everything works.

Verifying PKI infrastructure

On TEST-CA1 run pkiview.msc

There shouldn’t be any red crosses or unable to download statuses for our offline Root CA

Same goes for Subordinate CA.

I gave you few hints above, I had problems with offline root ca, with complete CDP part and one AIA http link. Solution was to copy rootca crl and crt files to C:\Windows\System32\certsrv\CertEnroll as I mentioned above, and to execute extra dspublish command for crl in command prompt – and that fixed all.

We have functional Two-Tier CA!!!!

Please do not turn of Root CA until you are done with entire PKI implementation!

More details and how to’s on topic to follow.

Implementing Two-Tier PKI on Windows Server 2022 – Part 1

Implementing Two-Tier PKI on Windows Server 2022 – Part 2 – you are here

Implementing Two-Tier PKI on Windows Server 2022 – Part 3

Implementing Two-Tier PKI on Windows Server 2022 – Part 4

Implementing Two-Tier PKI on Windows Server 2022 – Part 5

Implementing Two-Tier PKI on Windows Server 2022 – Part 6

Implementing Two-Tier PKI on Windows Server 2022 – Part 7

Implementing Two-Tier PKI on Windows Server 2022 – Part 8