Update deployments/services/authentication/Active Directory/Certificate Services.md
All checks were successful
Automatic Documentation Deployment / Sync Docs to https://kb.bunny-lab.io (push) Successful in 10s

This commit is contained in:
2026-04-26 17:20:16 -06:00
parent de4757b0c7
commit 8a686fad19

View File

@@ -9,226 +9,272 @@ tags:
This document outlines the Microsoft-recommended best practices for deploying a secure, internal-use-only, two-tier Public Key Infrastructure (PKI) using Windows Server 2022 or newer. The PKI supports securing S/MIME email, 802.1X Wi-Fi with NPS, and LDAP over SSL (LDAPS). This document outlines the Microsoft-recommended best practices for deploying a secure, internal-use-only, two-tier Public Key Infrastructure (PKI) using Windows Server 2022 or newer. The PKI supports securing S/MIME email, 802.1X Wi-Fi with NPS, and LDAP over SSL (LDAPS).
!!! abstract "CA Deployment Breakdown" !!! abstract "CA Deployment Breakdown"
The environment will consist of at least 2 virtual machines. For the purposes of this document they will be named `LAB-CA-01` and `LAB-CA-02`. This stands for "*Lab Certificate Authority [01|02]*". In a two-tier hierarchy, an offline (*you intentionally keep this VM offline*) Root CA signs a single "*Subordinate*" Enterprise CA certificate. The Subordinate CA is domain-joined and handles all certificate requests. Clients trust the PKI via Group Policy and Active Directory integration. The environment will consist of at least 2 virtual machines. For the purposes of this document they will be named `LAB-CA-01` and `LAB-CA-02`.
In this case, `LAB-CA-01` is the Root CA, while `LAB-CA-02` is the Intermediary/Subordinate CA. You can add more than one subordinate CA if you desire more redundancy in your environment. Making them operate together is generally automatic and does not require manual intervention. In a two-tier hierarchy:
- `LAB-CA-01` = Offline Root CA (not domain-joined)
- `LAB-CA-02` = Enterprise Subordinate CA (domain-joined)
!!! note "Certificate Authority Server Provisioning Assumptions" The Root CA signs the Subordinate CA certificate. The Subordinate CA handles all certificate issuance. Clients trust the PKI via Group Policy and Active Directory integration.
- OS = Windows Server 2022/2025 bare-metal or as a VM
- You should give it at least 4GB of RAM.
- [Change the edition of Windows Server from "**Evaluation**" to "**Standard**" via DISM](../../../../workflows/operations/windows/change-windows-edition.md)
- Ensure the server is fully updated
- [Ensure the server is activated](../../../../workflows/operations/windows/change-windows-edition.md#force-activation-edition-switcher)
- Ensure the timezone is correctly configured
- Ensure the hostname is correctly configured
!!! note "Domain Environment Assumptions" ---
It is assumed that you already have existing infrastructure hosting an Active Directory Domain with at least one domain controller. This document does not outline how to set up a domain controller, you will need to figure that out on your own.
## Offline (Non-Domain-Joined) Root CA `LAB-CA-01` !!! note "Critical Requirement: CRL and AIA"
### Role Deployment CRL Distribution Points (CDP) and Authority Information Access (AIA) **are required for all ADCS deployments**, including LDAPS-only environments.
This is the initial deployment of the root certificate authority, the settings here should be double and triple checked before proceeding through each step.
- Provision a **non-domain-joined** Windows Server Without properly configured CRL distribution:
- This is critical that this device is not domain-joined for security purposes - Certificate Services may fail to start
- Navigate to "**Server Manager > Manage > Add Roles and Features**" - Certificate validation may fail
- Check "**Active Directory Certificate Services**" - Revocation checking will break
- When prompted to confirm, click the "**Add Features**" button
- Ensure the "**Include management tools (if applicable)**" checkbox is checked.
- Click "**Next**" > "**Next**" > "**Next**"
- You will be told that the name of the server cannot be changed after this point, and it will be associated with `WORKGROUP` > This is fine and you can proceed.
- Check the boxes for the following role services:
- `Certification Authority`
- `Certification Authority Web Enrollment`
- When prompted to confirm multiple times, click the "**Add Features**" button
- Ensure the "**Include management tools (if applicable)**" checkbox is checked.
- There are additional steps such as `Configure AIA and CDP extensions with HTTP paths` and `Publish root cert and CRL to AD and internal HTTP`, but these do not apply to an LDAPS-only deployment, and are more meant for websites / webhosting. (current understanding)
- Click "**Next**" > "**Next**" > "**Next**" > "**Install**"
- Restart the Server
### Role Configuration ---
We have a few things we need to configure within the CA to make it ready to handle certificate requests.
- Navigate to "**Server Manager > (Alert Flag) > Post-deployment Configuration: Active Directory Certificate Services**" ## PKI HTTP Distribution (Required)
- You will be prompted for an admin user, in this example, you will use the pre-populated `LAB-CA-01\Administrator`
- Check the boxes for `Certification Authority` and `Certification Authority Web Enrollment` then click "**Next**"
- Check the "**Standalone CA**" radio box then click "**Next**"
- Check the "**Root CA** radio box then click "**Next**"
- Check the "**Create a new private key**" radio box then click "**Next**"
- Click the dropdown menu for "**Select a crypotographic provider**" and ensure that "**RSA#Microsoft Software Key Storage Provider**" is selected
- *Microsoft Software Key Storage Provider (KSP) is the latest, most flexible provider designed to work with the Cryptography Next Generation (CNG) APIs. It offers better support for modern algorithms and improved security management (such as support for key attestation, better hardware integration, and improved key protection mechanisms).*
- Set the key length to `4096`
- Set the hash algorithm to `SHA256`
- Click "**Next**"
- **Common Name for this CA**: `BunnyLab-RootCA`
- **Distinguished name suffix**: `O=Bunny Lab,C=US`
- **Preview of distinguished name**: `CN=BunnyLab-RootCA,O=Bunny Lab,C=US`
- Click "**Next**"
- Specify the validity period: `10 Years` then click "**Next**" > "**Next**" > "**Configure**"
You will see a finalization screen confirming everything we have configured, it should look something like what is seen below: The PKI requires an HTTP endpoint for distributing:
- CRLs
- CA certificates
| **Field** | **Value** | ### Install IIS on LAB-CA-02
| :--- | :--- |
| CA Type | Standalone Root |
| Cryptographic provider | RSA#Microsoft Software Key Storage Provider |
| Hash Algorithm | SHA256 |
| Key Length | 4096 |
| Allow Administrator Interaction | Disabled |
| Certificate Validity Period | `<10 Years from Today>` |
| Distinguished Name | CN=BunnyLab-RootCA,O=Bunny Lab,C=US |
| Certificate Database Location | C:\Windows\system32\CertLog |
| Certificate Database Log Location | C:\Windows\system32\CertLog |
!!! success "Active Directory Certificate Services"
If everything went well, you will see that the "**Certificate Authority**" and "**Certification Authority Web Enrollment**" both have a status of "**Configuration succeeded**". At this point, you can click the "**Close**" button to conclude the Root CA configuration.
## Online (Domain-Joined) Subordinate/Intermediary CA `LAB-CA-02`
### Role Deployment
Now that we have set up the root certificate authority, we can focus on setting up the subordinate CA.
!!! warning "Enterprise Admin Requirement"
When you are setting up the role, you **absolutely** have to use an "*Enterprise*" Admin account. This could be a service account like `svcCertAdmin` or something similar.
- Navigate to "**Server Manager > (Alert Flag) > Post-deployment Configuration: Active Directory Certificate Services**"
- Under credentials, enter the username for an Enterprise Admin. (e.g. `BUNNY-LAB\nicole.rappe`)
- Click "**Next**"
- Check the following roles (*we will add the rest after setting up the core CA functionality*)
- `Certification Authority`
- `Certification Authority Web Enrollment`
- Check the "**Enterprise CA**" radio box then click "**Next**"
- Check the "**Subordinate CA**" radio box then click "**Next**"
- Check the "**Create a new private key**" radio box then click "**Next**"
- Click the dropdown menu for "**Select a crypotographic provider**" and ensure that "**RSA#Microsoft Software Key Storage Provider**" is selected
- *Microsoft Software Key Storage Provider (KSP) is the latest, most flexible provider designed to work with the Cryptography Next Generation (CNG) APIs. It offers better support for modern algorithms and improved security management (such as support for key attestation, better hardware integration, and improved key protection mechanisms).*
- Set the key length to `4096`
- Set the hash algorithm to `SHA256`
- Click "**Next**"
- **Common Name for this CA**: `BunnyLab-SubordinateCA-01`
- **Distinguished name suffix**: `DC=bunny-lab,DC=io`
- This will be auto-filled based on the domain that the CA is joined to
- **Preview of distinguished name**: `CN=BunnyLab-SubordinateCA-01,DC=bunny-lab,DC=io`
- Click "**Next**"
- Select the "**Save a certificate request to file on the target machine**" radio button
- This will auto-populate the destination to something like "`C:\LAB-CA-02.bunny-lab.io_bunny-lab-LAB-CA-02-CA.req`"
- Click "**Next**" > "**Next**" > "**Configure**"
You will see a finalization screen confirming everything we have configured, it should look something like what is seen below:
| **Field** | **Value** |
| :--- | :--- |
| CA Type | Enterprise Subordinate |
| Cryptographic provider | RSA#Microsoft Software Key Storage Provider |
| Hash Algorithm | SHA256 |
| Key Length | 4096 |
| Allow Administrator Interaction | Disabled |
| Certificate Validity Period | Determined by the parent CA |
| Distinguished Name | CN=BunnyLab-SubordinateCA-01,DC=bunny-lab,DC=io |
| Offline Request File Location | `C:\LAB-CA-02.bunny-lab.io_bunny-lab-LAB-CA-02-CA.req` |
| Certificate Database Location | C:\Windows\system32\CertLog |
| Certificate Database Log Location | C:\Windows\system32\CertLog |
!!! quote "Pending Certificate Signing Request"
You will see a screen telling you that the **Certification Authority Web Enrollment** was successful but it will give a warning about the **Certification Authority**. The Active Directory Certificate Services installation is incomplete. To complete the installation, use the request file <file-name> to obtain a certificate from the parent CA [*The RootCA*]. Then, use the Certification Authority snap-in to install the certificate. To complete this procedure, right-click the node with the name of the CA, and then click "Install CA Certificate".
### Role Configuration
At this point, we will need to focus on getting the certificate signing request generated on `LAB-CA-02` to `LAB-CA-01` (the rootCA), this can be via temporary network access or via a USB flashdrive.
!!! danger
If using a USB flashdrive is not viable, don't leave the RootCA server on the network any longer than what is absolutely necessary.
- Once the certificate signing request file `C:\LAB-CA-02.bunny-lab.io_bunny-lab-LAB-CA-02-CA.req` is on `LAB-CA-01` (RootCA) we can proceed to get it signed.
- Navigate to "**Server Manager > Tools > Certification Authority**"
- Right-click the CA node in the treeview on the left-hand sidebar (e.g. `BunnyLab-RootCA`)
- Click on "**All Tasks" > "Submit new request...**"
- Browse to and select the subordinate CAs .req file (e.g. `LAB-CA-02.bunny-lab.io_bunny-lab-LAB-CA-02-CA.req`)
- Click on "**BunnyLab-RootCA > Pending Requests**
- Right-click the request we just imported, and select "**All Tasks > Issue**"
- Click on ""**BunnyLab-RootCA > Issued Certificates**"
- Locate the new subordinate CA certificate, and double-click it.
- Click the "**Details**" tab
- Click the "**Copy to File**" button
- Click "**Next**"
- Choose `DER encoded binary X.509 (.CER)` and save as `LAB-CA-02-SubCA.cer`.
- Export the Root CA certificate:
- Right-click the `BunnyLab-RootCA` node > Properties > View Certificate > Details > Copy to File...
- Save as `RootCA.cer`
- Copy both `LAB-CA-02-SubCA.cer` (the signed subordinate CA cert) and `RootCA.cer` (the root CA cert) to the subordinate CA (`LAB-CA-02`), using a secure method (e.g. USB drive).
- On `LAB-CA-02` (Subordinate CA), Navigate to "**Server Manager > Tools > Certification Authority**"
- Right-click the CA node in the treeview on the left-hand sidebar (e.g. `BunnyLab-SubordinateCA-01`)
- Click on "**All Tasks" > "Install CA Certificate**"
- Browse to and select `LAB-CA-02-SubCA.cer` (*you may need to change the cert file extension filter to `X.509 Certificate`*)
- When prompted for the CA chain or root certificate, browse for and select the `RootCA.cer` you transferred earlier along with the `LAB-CA-02-SubCA.cer`
- Launch `certlm.msc` to open the `[Certificates - Local Computer]` management window
- Right-Click "**Trusted Root Certification Authorities**" > All Tasks > Import
- Click "**Next**"
- Browse to the `BunnyLab-RootCA.crl` located on `\\LAB-CA-01\CertEnroll\BunnyLab-RootCA.crl` (*if the RootCA is temporarily on the network*) or copy the file manually via USB drive from `C:\Windows\System32\certsrv\CertEnroll\BunnyLab-RootCA.crl`
- Place all certificates in the following store: "Trusted Root Certification Authorities"
- Click "**Next**" and finish importing the Certificate Revocation List
- Right-click the CA node in the treeview on the left-hand sidebar (e.g. `BunnyLab-SubordinateCA-01`)
- Click on "**All Tasks" > "Start Service**"
- Verify that the CA status is now green (running).
## Create Auto-Enrollment Group Policy
The Certificate Auto-Enrollment Group Policy enables domain-joined devices (*computers, including domain controllers*) to automatically request, renew, and install certificates from the Enterprise CA (in this case, the Subordinate CA `LAB-CA-02`).
### Create GPO
- Open the Group Policy Management editor on one of your domain controllers, then "Create a GPO in this domain, and link it here" wherever it will be able to target the domain controllers, this may be at the root, or in a specific OU that holds domain controllers. (e.g. `bunny-lab.io\Domain Controllers` )
- Name the new GPO something like "**Certificate Auto-Enrollment**"
- Edit the GPO
- Navigate to "**Computer Configuration > Policies > Windows Settings > Security Settings > Public Key Policies**"
- Find and open "**Certificate Services Client - Auto-Enrollment.**"
- Set the Configuration Model to "**Enabled**"
- Check both checkboxes for "**Renew expired certificates, update pending certificates, and remove revoked certificates**" and "**Update certificates that use certificate templates**"
- Click "**OK**"
- Navigate to "**Computer Configuration > Policies > Windows Settings > Security Settings > Public Key Policies > Trusted Root Certification Authorities**"
- Right-click the "**Trusted Root Certification Authorities**" folder and select "**Import...**" > Proceed to browse for the `RootCA.cer` that you previously generated. (*copy it to the domain controller if needed from one of the Certificate Authorities*)
- Proceed to import the certificate, clicking-through all of the prompts and confirmations until it finishes the import.
- Navigate to "**Computer Configuration > Policies > Windows Settings > Security Settings > Public Key Policies > Intermediate Certification Authorities**"
- Right-click the "**Trusted Root Certification Authorities**" folder and select "**Import...**" > Proceed to browse for the `LAB-CA-02-SubCA.cer` that you previously generated. (*copy it to the domain controller if needed from one of the Certificate Authorities*)
- Proceed to import the certificate, clicking-through all of the prompts and confirmations until it finishes the import.
- Run a `gpupdate /force` on your domain controller(s) and give it a few minutes to pull down their new domain controller certificates
### Validate Auto-Enrollment Functionality
At this point, you need to check that there is a certificate installed within "**Certificates - Local Computer > Personal > Certificates**" for "Domain Controller Server Authentication"
- Load the Certificate - Local Machine (`certlm.msc`) and navigate to "**Personal > Certificates**" > You should see something similar to the following:
| **Issued To** | **Issued By** | **Expiration Date** | **Intended Purposes** | **Certificate Template** |
| :--- | :--- | :--- | :--- | :--- |
| LAB-DC-01.bunny-lab.io | BunnyLab-SubordinateCA-01 | 7/15/2026 | Directory Service Email Replication | Directory Email Replication |
| LAB-DC-01.bunny-lab.io | BunnyLab-SubordinateCA-01 | 7/15/2026 | Client Authentication, Server Authentication, Smart Card Logon | Domain Controller Authentication |
| LAB-DC-01.bunny-lab.io | BunnyLab-SubordinateCA-01 | 7/15/2026 | Client Authentication, Server Authentication, Smart Card Logon, KDC Authentication | Kerberos Authentication |
### Validate LDAPS Connectivity
Lastly, we want to ensure that LDAPS is functioning. By default, once these certs are enrolled on the domain controller(s), LDAPS *should* just work out of the box. To verify this, you can run this command on any device on the same network as the domain controllers. If it comes back successful like in the following example output, then you are golden:
```powershell ```powershell
PS C:\Users\nicole.rappe> Test-NetConnection LAB-DC-01.bunny-lab.io -Port 636 Install-WindowsFeature Web-Server -IncludeManagementTools
ComputerName : LAB-DC-01.bunny-lab.io ````
RemoteAddress : 192.168.3.25
RemotePort : 636
InterfaceAlias : Ethernet
SourceAddress : 192.168.3.254
TcpTestSucceeded : True
PS C:\Users\nicole.rappe> Test-NetConnection LAB-DC-02.bunny-lab.io -Port 636 ### Create PKI Directory
ComputerName : LAB-DC-02.bunny-lab.io
RemoteAddress : 192.168.3.26 ```powershell
RemotePort : 636 mkdir C:\inetpub\wwwroot\pki
InterfaceAlias : Ethernet
SourceAddress : 192.168.3.254
TcpTestSucceeded : True
``` ```
!!! success "Successful LDAPS Connectivity" ### Configure DNS
LDAPS should now be functional on your domain controller(s).
!!! abstract "Raw Unprocessed/Unimplemented Steps" Create:
Publish CRLs regularly, configure overlap periods, and monitor expiration. Enable Delta CRLs on the Subordinate CA, but not on the Root.
Security Recommendations
- Harden CA servers; limit access to PKI admins. ```text
- Use BitLocker or HSM for key protection. pki.bunny-lab.io → LAB-CA-02.bunny-lab.io
- Monitor issuance and renewals with audit logs and scripts. ```
### Validate
```powershell
echo test > C:\inetpub\wwwroot\pki\test.txt
```
Browse:
```
http://pki.bunny-lab.io/pki/test.txt
```
---
## Offline (Non-Domain-Joined) Root CA `LAB-CA-01`
### Role Deployment
(Same as your original steps — unchanged)
---
### Role Configuration
After installing the Root CA, **configure CDP and AIA BEFORE issuing certificates**:
```powershell
certutil -setreg CA\CRLPublicationURLs "65:C:\Windows\System32\CertSrv\CertEnroll\%3%8%9.crl\n78:http://pki.bunny-lab.io/pki/%3%8%9.crl"
certutil -setreg CA\CACertPublicationURLs "1:C:\Windows\System32\CertSrv\CertEnroll\%1_%3%4.crt\n2:http://pki.bunny-lab.io/pki/%1_%3%4.crt"
```
Restart CA:
```powershell
net stop certsvc
net start certsvc
```
Generate CRL:
```powershell
certutil -crl
```
---
### Publish Root CA Files
From:
```
C:\Windows\System32\CertSrv\CertEnroll\
```
Copy to:
```
\\LAB-CA-02\c$\inetpub\wwwroot\pki\
```
Files:
* `BunnyLab-RootCA.crl`
* `LAB-CA-01_BunnyLab-RootCA.crt`
---
## Online Subordinate CA `LAB-CA-02`
### Role Deployment
(Same as your original steps — unchanged)
---
### Role Configuration
Proceed with CSR generation as normal.
---
### Submit Request to Root CA
(Same steps — unchanged)
---
### Install SubCA Certificate
* Install `LAB-CA-02-SubCA.cer`
* Import Root CA cert into Trusted Root store
---
### ⚠️ IMPORTANT: Do NOT manually import CRLs
Remove this step from your original process:
> Import CRL manually into Trusted Root store
Replace with:
```powershell
certutil -verify -urlfetch RootCA.cer
```
This validates CRL via HTTP (correct method).
---
## Reissue SubCA Certificate (Critical Recovery Step)
If CDP/AIA was configured after initial deployment:
### On SubCA:
```powershell
certutil -renewCert ReuseKeys
```
Click **Cancel** (offline root)
---
### On Root CA:
* Submit request
* Issue certificate
* Export `.cer`
---
### Back on SubCA:
Install new certificate:
```
certsrv.msc → Install CA Certificate
```
---
## CRL Publishing Operations
### Root CA
```powershell
certutil -crl
```
Copy CRL to IIS:
```
C:\inetpub\wwwroot\pki\
```
---
### SubCA
```powershell
certutil -crl
```
Copy:
* SubCA CRL
* SubCA certificate
To IIS folder.
---
## Create Auto-Enrollment Group Policy
(Unchanged from your original doc)
---
## Validate Auto-Enrollment
(Unchanged)
---
## Validate LDAPS Connectivity
(Unchanged)
---
## Validation Commands
Run on any system:
```powershell
certutil -verify -urlfetch <certificate>.cer
```
You should NOT see:
```
CRYPT_E_REVOCATION_OFFLINE
```
---
## Security & Operational Notes
* Root CA should remain offline except when issuing or renewing
* CRLs must be periodically regenerated and published
* Automate CRL copy to IIS if possible
* Monitor CRL expiration
---
## Key Lessons Learned
* CDP/AIA must be configured BEFORE issuing certificates
* HTTP CRL distribution is mandatory
* SubCA certificates must be reissued if CDP was missing
* Manual CRL import is not a valid solution