How to Set Up a PKI Infrastructure with AD CS on Windows Server 2025

Public Key Infrastructure (PKI) is the backbone of trust in any modern enterprise network. TLS certificates for internal web services, smart card logon, code signing, and BitLocker key escrow all depend on a functioning, well-designed certificate authority (CA) hierarchy. Active Directory Certificate Services (AD CS) on Windows Server 2025 delivers an enterprise-grade PKI that integrates natively with Active Directory, Group Policy, and autoenrollment — eliminating the need for a third-party CA for internal certificates. The industry-recommended design is a two-tier hierarchy: an offline root CA that you power down after issuing the subordinate CA certificate, and an online enterprise issuing CA that handles day-to-day certificate operations. This tutorial builds that architecture from scratch.

Prerequisites

  • Two Windows Server 2025 instances: one for the offline root CA (workgroup member, never domain-joined) and one for the online issuing CA (domain member)
  • Active Directory domain with Domain Admin credentials for the issuing CA setup
  • PowerShell with the PSPKI community module (optional but useful): Install-Module PSPKI
  • A USB drive or secure network share for transferring the root CA certificate and CRL between the offline root and the issuing CA
  • Static IP addresses and DNS entries for the issuing CA — clients need to reach it for CRL distribution points and AIA

Step 1: Install and Configure the Offline Root CA

The root CA is intentionally air-gapped after initial configuration. Its only job is to sign the issuing CA’s subordinate certificate and publish a Certificate Revocation List (CRL). Configure it on a standalone server that is not domain-joined:

# On the OFFLINE ROOT CA server (workgroup, NOT domain-joined)
# Install AD CS role
Install-WindowsFeature AD-Certificate, RSAT-ADCS-Mgmt -IncludeManagementTools

# Install the Standalone Root CA
Install-AdcsCertificationAuthority `
    -CAType StandaloneRootCA `
    -CACommonName "Contoso Root CA" `
    -CADistinguishedNameSuffix "DC=contoso,DC=com" `
    -CryptoProviderName "RSA#Microsoft Software Key Storage Provider" `
    -KeyLength 4096 `
    -HashAlgorithmName SHA256 `
    -ValidityPeriod Years `
    -ValidityPeriodUnits 20 `
    -DatabaseDirectory "C:WindowsSystem32CertLog" `
    -LogDirectory "C:WindowsSystem32CertLog" `
    -Force

After installation, configure the CRL publication interval and set the CRL Distribution Point (CDP) and Authority Information Access (AIA) extensions to URLs that will be reachable from all clients on your network — even though those URLs point to the online issuing CA’s web server, not the offline root itself:

# Set CRL validity to 6 months — root CA CRLs are rarely updated
certutil -setreg CACRLPeriodUnits 6
certutil -setreg CACRLPeriod "Months"
certutil -setreg CACRLOverlapPeriodUnits 3
certutil -setreg CACRLOverlapPeriod "Weeks"

# Set CDP and AIA extensions (these must be web-accessible URLs)
$cdpUrl  = "http://pki.contoso.com/CertEnroll/ContosRootCA.crl"
$aiaUrl  = "http://pki.contoso.com/CertEnroll/ContosoRootCA.crt"

certutil -setreg CACRLPublicationURLs "65:$cdpUrl"
certutil -setreg CACACertPublicationURLs "32:$aiaUrl"

# Restart the CA service and publish the CRL
Restart-Service CertSvc
certutil -crl

# Export the root CA certificate to a file for transfer to the issuing CA
$rootCertPath = "C:RootCAContosoRootCA.cer"
New-Item -ItemType Directory -Path C:RootCA -Force | Out-Null
certutil -ca.cert $rootCertPath

Write-Host "Root CA cert exported to $rootCertPath — copy this to your USB drive"

Step 2: Publish the Root CA Certificate and CRL to Active Directory

Before the issuing CA can be trusted by domain computers, the root CA certificate must be published into the NTAuthCertificates container and the Trusted Root Certification Authorities store in AD. Do this from a domain-joined machine using the root CA cert file transferred via USB:

# On a DOMAIN-JOINED machine (can be the issuing CA server)
# Transfer ContosoRootCA.cer from USB to C:PKI

# Publish root cert to AD (requires Enterprise Admin)
certutil -dspublish -f "C:PKIContosoRootCA.cer" RootCA

# Also add it to the NTAuth store (required for smart card logon and client cert auth)
certutil -dspublish -f "C:PKIContosoRootCA.cer" NTAuthCertificates

# Verify publication
certutil -viewstore "ldap:///CN=NTAuthCertificates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com?cACertificate"

# Copy the root CA CRL to the CertEnroll web virtual directory on the issuing CA
# so clients can download it from the CDP URL defined on the root
Copy-Item "\ROOTCAC$WindowsSystem32CertSrvCertEnroll*.crl" `
          "C:inetpubwwwrootCertEnroll"

Step 3: Install and Configure the Online Issuing CA

The enterprise issuing CA is domain-joined and integrated with Active Directory, which enables certificate templates, autoenrollment, and automatic publication of issued certificates into user and computer AD objects:

# On the ISSUING CA server (domain member)
Install-WindowsFeature AD-Certificate, RSAT-ADCS-Mgmt, Web-WebServer -IncludeManagementTools

# Install Enterprise Subordinate CA — this generates a certificate signing request (CSR)
Install-AdcsCertificationAuthority `
    -CAType EnterpriseSubordinateCA `
    -CACommonName "Contoso Issuing CA 01" `
    -CADistinguishedNameSuffix "DC=contoso,DC=com" `
    -CryptoProviderName "RSA#Microsoft Software Key Storage Provider" `
    -KeyLength 2048 `
    -HashAlgorithmName SHA256 `
    -OutputCertRequestFile "C:PKIContosoIssuingCA01.req" `
    -Force

Write-Host "CSR generated at C:PKIContosoIssuingCA01.req"
Write-Host "Transfer this file to the offline root CA for signing"

Step 4: Sign the Subordinate CA Request on the Root CA

Copy the .req file to the offline root CA via USB, submit it, and export the signed certificate:

# On the OFFLINE ROOT CA — submit the issuing CA's CSR
$reqFile  = "C:RootCAContosoIssuingCA01.req"
$certFile = "C:RootCAContosoIssuingCA01.cer"

# Submit the request to the root CA
certreq -submit -attrib "CertificateTemplate:SubCA" $reqFile

# Get the request ID from the output above (e.g. RequestId = 2), then issue and retrieve:
$requestId = 2
certutil -resubmit $requestId
certreq -retrieve $requestId $certFile

# Export the root CRL again (in case it has been updated)
certutil -crl

Write-Host "Signed issuing CA cert at $certFile — transfer back to the issuing CA"

Step 5: Install the Signed Certificate and Complete Issuing CA Setup

Back on the issuing CA server, install the signed certificate returned by the root CA and configure the CDP/AIA extensions for the issuing CA’s own certificates:

# On the ISSUING CA server
# Install the signed subordinate CA certificate
certutil -installcert "C:PKIContosoIssuingCA01.cer"

# Start the CA service
Start-Service CertSvc

# Configure CRL and AIA publishing URLs for certificates issued by THIS CA
certutil -setreg CACRLPeriodUnits 1
certutil -setreg CACRLPeriod "Weeks"
certutil -setreg CACRLOverlapPeriodUnits 12
certutil -setreg CACRLOverlapPeriod "Hours"

$issuingCdp = "http://pki.contoso.com/CertEnroll/ContosoIssuingCA01%8%9.crl"
$issuingAia = "http://pki.contoso.com/CertEnroll/%1_%3%4.crt"

certutil -setreg CACRLPublicationURLs "65:$issuingCdp"
certutil -setreg CACACertPublicationURLs "32:$issuingAia"

Restart-Service CertSvc
certutil -crl

# Verify CA is running and AD-integrated
certutil -getconfig
certutil -ping

Step 6: Create Certificate Templates

Enterprise CAs use certificate templates stored in Active Directory to define the key usage, subject name, validity period, and enrollment permissions for each certificate type. Duplicate the built-in templates rather than modifying them directly:

# Use the Certification Authority MMC snap-in (certsrv.msc) for GUI-based template management.
# For scripted template creation, use the PSPKI module:

Import-Module PSPKI

# Connect to the issuing CA
$ca = Connect-CertificationAuthority -ComputerName 'ISSUINGCA01.contoso.com'

# Duplicate the WebServer template as a custom internal web server template
$template = Get-CertificateTemplate -Name 'WebServer'
$newTemplate = $template | Copy-CertificateTemplate -Name 'ContosoInternalWebServer'

# Set validity to 2 years
$newTemplate | Set-CertificateTemplateProperty -ValidityPeriod (New-TimeSpan -Days 730)

# Allow Domain Computers group to enroll
$newTemplate | Add-CertificateTemplateAcl -Identity 'Domain Computers' `
    -AccessType Allow `
    -AccessMask Read, Enroll

# Publish the template to the issuing CA
$ca | Add-CATemplate -Template $newTemplate

Write-Host "Template 'ContosoInternalWebServer' published to issuing CA"

Step 7: Configure Autoenrollment via Group Policy

Autoenrollment allows domain computers and users to automatically request, receive, and renew certificates without manual intervention — essential for deploying certificates at scale:

Import-Module GroupPolicy

$gpoName  = 'PKI — Certificate Autoenrollment'
$domainDN = (Get-ADDomain).DistinguishedName

New-GPO -Name $gpoName -Comment 'Enables certificate autoenrollment for computers and users'

# Enable autoenrollment for computers via registry policy
# HKLMSOFTWAREPoliciesMicrosoftCryptographyAutoEnrollment
# AEPolicy = 7 (Enroll, Renew, Update Certificates, Use existing key on smart card)

Set-GPRegistryValue -Name $gpoName `
    -Key 'HKLMSOFTWAREPoliciesMicrosoftCryptographyAutoEnrollment' `
    -ValueName 'AEPolicy' `
    -Type DWord `
    -Value 7

# Enable autoenrollment for users
Set-GPRegistryValue -Name $gpoName `
    -Key 'HKCUSOFTWAREPoliciesMicrosoftCryptographyAutoEnrollment' `
    -ValueName 'AEPolicy' `
    -Type DWord `
    -Value 7

# Link to domain root so all OUs inherit
New-GPLink -Name $gpoName -Target $domainDN -LinkEnabled Yes

Write-Host "Autoenrollment GPO linked to domain root. Computers will enroll on next gpupdate."

A properly designed two-tier AD CS PKI built on Windows Server 2025 eliminates the cost and complexity of third-party certificate management for internal services while providing the automation hooks that modern zero-trust architectures require. Once your issuing CA is live, expand the infrastructure by adding an Online Certificate Status Protocol (OCSP) responder for faster revocation checking than CRL polling, configure Network Device Enrollment Service (NDES) if you need certificates on network devices, and integrate with Microsoft Intune via the Certificate Connector to extend autoenrollment to mobile and remote devices. Keep the offline root CA’s private key backed up to encrypted offline media and store that media in physically separate locations — it is the single most valuable cryptographic asset in your environment.