Building My Home Lab – Part 7: Building out a Public Key Infrastructure (PKI)

Building My Home Lab – Part 7: Building out a Public Key Infrastructure (PKI)

Introduction

The lab network is really taking shape now! Active Directory has been deployed and, since the last post, I've deployed a backup Domain Controller (SRV-DC-02). It's running on a Virtual Machine, hosted by Microsoft Hyper-V (running on the HPE DL360 Server):

Now it's time to further secure the Domain by implementing a Private Key Infrastructure (PKI), using Microsoft Active Directory Certificate Services.

💡 What Is a PKI?

A Public Key Infrastructure (PKI) is the framework that establishes digital trust across an organization. It uses cryptographic keys and digital certificates to prove identity, secure communications, and ensure data integrity.

At the heart of every PKI is a Certificate Authority (CA) — a trusted server that issues, signs, and manages certificates for users, computers, and services. The CA acts like a digital notary, vouching that a public key truly belongs to the entity named in the certificate.

Together with supporting components like Registration Authorities (RA), CRLs/OCSP responders, and certificate templates, a PKI enables secure operations such as:

  • Authenticating users and devices
  • Encrypting network traffic (HTTPS, VPN, 802.1X Wi-Fi)
  • Digitally signing files, emails, and software

In short: The CA is the trust anchor, and the PKI is the system that enforces and distributes that trust throughout a Domain.

Having an internal Certificate Authority (CA) is one of the foundational pillars of enterprise security and trust management — especially in environments with an Active Directory Domain, where identity, encryption, and authentication all rely on digital certificates.

An internal Certificate Authority acts as the root of trust within an organization's network. It issues and signs digital certificates that Domain-joined systems, users, and applications can trust. The internal CA’s root certificate is distributed through Group Policy or directory services and thus automatically trusted.

Without an internal CA:
Browsers and operating systems are configured by default to trust only public CAs (e.g., DigiCert, GoDaddy, etc.). Certificates that are self-signed or issued internally (for internal Domains such as edbradleyweb.local) will display “Invalid Certificate” warnings, even though they may be perfectly secure in intent:

With an internal CA:
All internal HTTPS services, VPNs, RADIUS servers, and other infrastructure components can use valid, trusted certificates that are recognized by all Domain-joined systems. These certificates are issued internally, do not depend on the public Internet, and incur no per-certificate cost.

Building an Enterprise-Grade Internal PKI

In this post, I’ll be setting up an enterprise-grade, two-tier Public Key Infrastructure (PKI) using Microsoft Certificate Services. This implementation will include:

  • A stand-alone, offline Root Certificate Authority (ROOT CA) – the ultimate trust anchor, responsible for signing subordinate CAs only.
  • A Domain-joined Subordinate (Enterprise) Certificate Authority (ENTERPRISE CA) – responsible for issuing and managing certificates for endpoint entities (servers, workstations, and services) within the Active Directory Domain.

Below is a sketch of the overall implementation:

Deploying the Core Certificate Servers

This post will focus on deploying the core certificate serversSRV-ROOT-CA-1 and SRV-CA-01.

These servers form the backbone of the internal PKI, providing the trust framework required for secure authentication and encrypted communications within the Domain.

I'll wrap up by verifying that Domain-joined workstations are enrolled in the PKI and receiving certificates. I'll also set up a basic internal web site that utilizes the PKI and is trusted by Domain-joined computers.

In subsequent posts, I’ll demonstrate, as illustrated above, how the certificates issued from this environment are used to implement secure wireless Domain authentication (via RADIUS/802.1X), ensuring seamless and encrypted connectivity across the enterprise network.


Phase 1 — Build the Offline Root CA (SRV-ROOT-CA-1)

Using my designated administrative workstation for the lab, I deployed the Offline Root Certificate Authority (CA) on a Windows Server 2022 Standard virtual machine running under VMware Workstation. After applying all Windows Updates, I created and placed the CAPolicy.inf file in the correct directory (C:\Windows\CAPolicy.inf):

The CAPolicy.inf file defines the Root CA’s PKI policy and configuration:

[Version]
Signature="$Windows NT$"

; ----- Policy Statement (optional text blob shown in cert) -----
[PolicyStatementExtension]
Policies=InternalPolicy
Critical=0

[InternalPolicy]
OID=1.3.6.1.4.1.311.21.8.1000.1
Notice="EDW Internal Root CA for edbradleyweb.local"

; ----- Controls what the ROOT issues to subordinates -----
[BasicConstraints]
PathLength=1
Critical=Yes

; ----- Root CRL schedule (long; no delta) -----
[CRLDistributionPoint]
Empty

[AuthorityInformationAccess]
Empty

[Certsrv_Server]
RenewalKeyLength=4096
RenewalValidityPeriod=Years
RenewalValidityPeriodUnits=16
CRLPeriod=Weeks
CRLPeriodUnits=26
CRLOverlapUnits=1
CRLOverlapPeriod=Days
LoadDefaultTemplates=0
AlternateSignatureAlgorithm=1

The key points of the CA Root policy are:

  • RenewalValidityPeriod = 16 years
    The Root certificate will remain valid for 16 years — sufficient for the useful life of the lab.
  • PathLength = 1
    Allows one issuing tier (the EDW-Enterprise-Issuing-CA) and no subordinate tiers beneath it.
  • CDP / AIA = Empty
    Omitting LDAP and AIA references keeps the Root certificate clean. Instead, the PKI will publish the Root .cer and .crl files over HTTP via the issuing CA. This prevents Wi-Fi and Non-Domain clients from attempting LDAP lookups.
  • CRL Period = 26 Weeks
    The Root CA is offline, so a long CRL validity is appropriate. Since it only signs the Issuing CA certificate (and doesn’t issue end-entity certificates), it requires minimal updates. The Root CA should be brought online roughly every six months to regenerate and publish a new CRL.

Below is a walkthrough for deploying the offline Root CA server:

1.) I invoked the Wizard to install the Active Directory Certificate Services feature on the stand-alone Windows Server (Manage Add Roles and Features Active Directory Certificate Services):

2.) I checked Active Directory Certificate Services and installed the required additional features:

I clicked Next to continue with the Wizard:

Please Note: The computer name must remain the same after CA feature is installed:

3.) I checked Certification Authority. This Server will be the ultimate authority in the PKI hierarchy:

I reviewed the confirmation screen and clicked Install:

The Feature installation proceeded...:

4.) After the initial installation completed, I performed the required post-installation configuration:

By clicking the link, I invoked the Wizard. The designated local administrator account is used to configure the role services:

5.) I checked Certification Authority (the only option for an offline / Non-Domain-joined server):

6.) I checked Standalone CA (the only option for an offline / Non-Domain-joined server) and click Next:

This will be a Root CA type of Server:

7.) Since this is the first time deploying the CA, I selected Create a new private key and clicked Next:

For cryptography, I selected the recommended Key length of 4096 and the SHA256 hash algorithm:

I entered the planned CA Name (EDW-Offline-Root-CA) and clicked Next:

I set the validity period to the planned 16 years and clicked Next:

8.) I left the default database locations and clicked Next:

I confirmed the configuration and clicked Configure:

The configuration completed successfully, I clicked Close to complete the task:

Active Directory Certificate Services (AD CS) then appeared on the Windows Server Manager Dashboard:

The Certificate Authority can be managed through the installed Certificate Manager (Microsoft Management Console Certification Authority Snap-in):

With the Root CA Server is deployed, the root certificate and current CRL were ready to be exported and be stored in the Active Directory Domain environment.

First, I created a C:\PKI\ROOT\ folder to store the certificate and revocation list files.

Using the CA Manager, I exported the Revoked Certificates to generate the required .crl file:

1.) I right-clicked on the Revoked Certificates and selected All Tasks → Publish:

2.) New CRL is only option:

The published CRL file is saved in the C:\Windows\System32\CertSrv\CertEnroll folder:

I copied the .crl file from the default publishing output folder to the C:\PKI\ROOT\ folder.

Next, I exported the Root CA certificate:

1.) I right-clicked on the Root CA and clicked Properties:

2.) On the General tab, I clicked View Certificate:

3.) I select Details tab and clicked the Copy to file... button:

4.) I followed the Wizard:

The C:\PKI\ROOT\ folder contained the Root CA Certificate files:

Offline Operation

The Root CA server operates completely offline, physically separated from the lab network. This ensures the Root’s private key — the ultimate authority in the PKI hierarchy — remains secure.

If the Domain’s PKI ever becomes compromised or corrupted, the offline Root can be used to rebuild the hierarchy from a trusted source.

In the next phase, I deployed the Domain-joined Issuing CA, which handles all certificate requests, and published the Root CA’s certificate and CRL, via HTTP, to the Lab Domain (http://pki.edbradleyweb.local).


Phase 2 — Build the Domain-Joined Issuing CA (SRV-CA-01)

Now that the Root CA server is established and the Root Certificate has been generated, it was time to build the Enterprise CA Server. This tier handles the day-to-day certificate operations — generating and signing the certificates used by entities within the edbradleyweb.local Domain, including workstations, laptops (wireless), servers, and applications.

The Enterprise CA server runs as a Hyper-V virtual machine on the Lab network. It’s a Windows Server instance that has been joined to the Active Directory Domain as srv-ca-01.edbradleyweb.local.

To begin, I configured the server to host the Root CA certificate and publish it to the Domain via HTTP. This was accomplished using Internet Information Services (IIS).

I launched the Add Roles and Features Wizard and installed the Web Server (IIS) role using the default settings:

Next, I created a folder at C:\PKI\Pub, where I copied the Root CA certificate and CRL files previously exported from the offline Root CA.

Within IIS, I configured this folder as a Virtual Directory, allowing these files to be served over HTTP to Domain entities during the certificate validation process:

I enabled Directory browsing to allow me to verify the connection in a Web browser:

With IIS in place, I ran the feature Wizard again, to install Active Directory Certificate Services. The installation is the same as with the Root CA. However, the following differences occur during the post-installation configuration process:

1.) The type of Server is a Enterprise CA:

2.) The type of CA is Subordinate CA:

3.) The name and default Distinguished name suffix (Domain Name) are as shown below:

4.) Since this is a Subordinate CA, it needs to be signed by the Root CA. So, I chose the file option for generating the Certificate Request:

With the post-install configuration Wizard completed, I used the generated request file to manually submit it to the Root CA Server:

Then, I manually issued the certificate from the pending request:

Next, I clicked the Copy to File... button (on the Details tab) to export the signed certificate for the subordinate Enterprise CA Sever:

I specified the PKCS format, including the complete certificate chain (Root CA → Subordinate Enterprise CA):

Lastly, I installed the Root CA Certificate on the Enterprise CA:

With the signed certificate from the Root CA installed, the Enterprise CA started successfully!:

With the Enterprise CA up and running, I published it's Certificate and CRL files to the designated Virtual folder used by the IIS Web Server:

To finish up, I registered the new CA Root as being trusted within the Active Directory Domain, using the certutil utility:

I created additional Certificate Templates from the base certificates provided by Active Directory Certificate Services. I customized these duplicate Certificate Templates for the Lab Domain (leaving the originals in-tact for future use):

1.) On SRV-CA-01, I ran → certtmpl.msc (Certificate Templates Console).

2.) I Duplicated each base template below:

A.) Internal Web Server (base: Web Server)

  • General: name = Internal Web Server
  • Subject Name: Supply in the request (for SANs)
  • Extensions: ensure Server Authentication EKU
  • Security: allow Enroll to Domain Computers


B.) Wireless Computer TLS (base: Computer)

  • General: name = Wireless Computer TLS
  • Extensions: ensure Client Authentication EKU
  • Security: Domain ComputersEnroll + Auto-enroll


C.) NPS Server Authentication (base: RAS and IAS Server)

  • General: name = NPS Server Authentication
  • Extensions: Server Auth (and RAS/IAS)
  • Security: allow Enroll to SRV-NPS-01$

3.) I opened the Certification Authority (certsrv.msc) → Enterprise CA → Certificate TemplatesRight-click → New → Certificate Template to Issue → and selected the three templates above.

This process made the Domain ready to provide and process certificates for endpoint entities!


Phase 3 — Enable Auto-Enrollment & verify Domain machines get certificates

With the PKI infrastructure in place and registered in Active Directory, it was time to deploy certificates to Domain-joined computers. I constructed a Group Policy Object to enable auto-enrollment:

I also added the new Root CA as a Trusted Root Certificate Authority for the Domain:

I then linked the new GPO Domain-wide:

To test the PKI workflow on a Domain-joined PC, I forced a Group Policy refresh:

The new internal Root CA was then listed as a Trusted Root Certification Authority on the PC:

I then verified that the PC received it's own certificate from the internal CA:

The certificate shows the entire trust chain, back to the Root CA, as expected:

This verifies that the PKI workflow is functioning within the Active Directory Domain!


Phase 4 — Stand up a quick internal HTTPS site on NGINX with an internal CA certificate

With auto-enrollment successfully verified, I moved on to testing internal web certificate deployment by setting up a simple intranet web server at intranet.edbradleyweb.local.

I provisioned an Ubuntu Linux VM on Hyper-V and vibe-coded a lightweight HTML/CSS/JavaScript site running behind an NGINX reverse proxy.

Since the Ubuntu server is not Domain-joined, I went through the manual process of requesting, issuing, and applying a certificate from the internal Enterprise CA:

1.) Using OpenSSL on the Linux server, I created a new private key and certificate request:

openssl req -new -newkey rsa:2048 -nodes \
  -keyout /etc/ssl/private/intranet.key \
  -out /etc/ssl/certs/intranet.csr \
  -subj "/CN=intranet.edbradleyweb.local"

This produced two files:

  • /etc/ssl/private/intranet.key — the private key
  • /etc/ssl/certs/intranet.csr — the certificate request

2.) After a bit of trial and error, I successfully submitted the CSR to the Enterprise CA, using PowerShell, and generated a signed certificate for the Web Server.

3.) Through additional testing and research, I learned that NGINX expects the full certificate chain — from the Web Server’s certificate up to the Root CA.
I created a single .crt file containing all certificates in order (Web Server → Enterprise CA → Root CA):

Finally, I updated the NGINX configuration to reference the full-chain certificate and private key:

server {
    listen 443 ssl;
    server_name intranet.edbradleyweb.local;

    ssl_certificate     /etc/nginx/certs/intranet-fullchain.crt;# full chain (server + issuing + root)
    ssl_certificate_key /etc/nginx/certs/intranet.key;

    location / {
        return 200 "Hello from NGINX over HTTPS!\n";
        add_header Content-Type text/plain;
    }
}

When I accessed the intranet site from the Domain-joined workstation, Chrome displayed the connection as secure! — confirming that the internal PKI chain was properly trusted by the Domain and that the certificate validation process worked end-to-end:


🧩 Wrap Up

With the internal PKI now fully operational, the Lab Domain has gained a new level of security, identity integrity, and trust management. The two-tier Certificate Authority architecture — anchored by the offline Root CA and powered by the Domain-joined Enterprise CA — provides a robust foundation for authenticating systems, encrypting communications, and securing internal services. Auto-enrollment ensures Domain-joined devices automatically receive and renew trusted certificates, while manual issuance workflows support Non-Domain systems. Successfully deploying and validating the internal HTTPS site confirmed that the PKI is functioning end-to-end, enabling seamless certificate validation across the network. This marks a major milestone in building an enterprise-grade lab environment — one that mirrors real-world security infrastructure and lays the groundwork for upcoming projects such as 802.1X wireless authentication and advanced identity integration.

Stay tunned!