├── Build-RootCA.ps1 ├── Build-SubCA.ps1 └── README.md /Build-RootCA.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Build Root CA in a two-tier PKI infrastructure 4 | .DESCRIPTION 5 | Automate the installation and configuration of a Root Certificate Authority using 6 | the Microsoft PKI Services. This is designed to be executed on a Server Core instance. 7 | .INPUTS 8 | None 9 | .OUTPUTS 10 | None 11 | .NOTES 12 | Version: 1.5 13 | Author: Marc Bouchard 14 | Creation Date: 2021/03/04 15 | .EXAMPLE 16 | Install from Github using: 17 | Invoke-WebRequest -usebasicparsing -uri "https://raw.githubusercontent.com/SUBnet192/PKI/master/Build-RootCA.ps1" | Invoke-Expression 18 | #> 19 | 20 | #------------------------------------------------------[ Initialization ]--------------------------------------------------------- 21 | 22 | $response = $null 23 | $RootCAName = $null 24 | $httpCRLPath = $null 25 | $OID = $null 26 | 27 | #--------------------------------------------------------[ Declaration ]---------------------------------------------------------- 28 | 29 | Function Show-Disclaimer { 30 | Clear-Host 31 | Write-Host "8888888b. 888 .d8888b. d8888 " -ForegroundColor Yellow 32 | Write-Host "888 Y88b 888 d88P Y88b d88888 " -ForegroundColor Yellow 33 | Write-Host "888 888 888 888 888 d88P888 " -ForegroundColor Yellow 34 | Write-Host "888 d88P .d88b. .d88b. 888888 888 d88P 888 " -ForegroundColor Yellow 35 | Write-Host "8888888P' d88''88b d88''88b 888 888 d88P 888 " -ForegroundColor Yellow 36 | Write-Host "888 T88b 888 888 888 888 888 888 888 d88P 888 " -ForegroundColor Yellow 37 | Write-Host "888 T88b Y88..88P Y88..88P Y88b. Y88b d88P d8888888888 " -ForegroundColor Yellow 38 | Write-Host "888 T88b 'Y88P' 'Y88P' 'Y888 'Y8888P' d88P 888 " -ForegroundColor Yellow 39 | Write-Host "" 40 | Write-Host "IMPORTANT INFORMATION - PLEASE READ" -ForegroundColor Yellow 41 | Write-Host "" 42 | Write-Host "This script is used to build a Root Certificate server in a 2-tier Microsoft PKI solution" -ForegroundColor Yellow 43 | Write-Host "Please REVIEW the contents of this script to ensure the default values provided meet your requirements." -ForegroundColor Yellow 44 | Write-Host "" 45 | Write-Host "Tips:" -ForegroundColor Yellow 46 | Write-Host " - If running on a virtual machine, take a snapshot before starting, and another one at completion." -ForegroundColor Yellow 47 | Write-Host " This allows you to either restart fresh or recover/revert if anything fails with the subordinate CA." -ForegroundColor Yellow 48 | Write-Host " - Once the subordinate CA is built and active, disconnect the network and shutdown the Root CA until needed again." -ForegroundColor Yellow 49 | Write-Host "" 50 | Write-Host -NoNewLine "Press any key to continue..." -ForegroundColor Yellow 51 | $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown') 52 | } 53 | 54 | Function Report-Status { 55 | Param( 56 | [parameter(Mandatory = $true)][String]$Msg, 57 | [parameter(Mandatory = $true)][INT]$Lvl, 58 | [parameter(Mandatory = $true)][String]$Color 59 | ) 60 | Switch ($Lvl) { 61 | 0 { Write-Host -Foreground $Color "[EXEC]" $Msg } 62 | 1 { Write-Host -Foreground $Color "[QUERY]" $Msg } 63 | } 64 | } 65 | 66 | #---------------------------------------------------------[ Execution ]---------------------------------------------------------- 67 | 68 | Show-Disclaimer 69 | 70 | Clear-Host 71 | Report-Status "Building Root CA" 0 Green 72 | 73 | Report-Status "Enable PS Remoting" 0 Green 74 | Enable-PSRemoting -SkipNetworkProfileCheck -Force | Out-Null 75 | 76 | Report-Status "Configuring Auditing" 0 Green 77 | auditpol /set /category:"Object Access" /failure:enable /success:enable | Out-Null 78 | 79 | #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=[ User Input ]=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 80 | 81 | $response = $null 82 | [regex] $OIDRegex = "^\d{5}$" 83 | do { 84 | # Query user for Root CA Common Name 85 | Report-Status "Enter the Common Name for the Root CA (ex: Corp-Root-CA):" 1 Yellow 86 | $RootCAName = Read-Host 87 | 88 | do { 89 | Report-Status "Please enter your 5 digit OID number:" 1 Yellow 90 | $OID = read-host 91 | } while ($OID -inotmatch $OIDRegex) 92 | 93 | Report-Status "Enter the URL where the CRL files will be located (ex: pki.mycompany.com): " 1 Yellow 94 | $httpCRLPath = Read-Host 95 | 96 | Report-Status "You have provided the following information:" 1 Yellow 97 | Write-Host "CA Common Name: $RootCAName" 98 | Write-Host "OID : $OID" 99 | Write-Host "CRL URL path : $httpCRLPath" 100 | 101 | Report-Status "Are you satisfied with these answers? [y/n]" 1 Yellow 102 | $response = Read-Host 103 | } until ($response -eq 'y') 104 | 105 | #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=[ End User Input ]=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 106 | 107 | Report-Status "Create CAPolicy file" 0 Green 108 | 109 | #-------------------------- 110 | # CAPolicy.INF file content 111 | #-------------------------- 112 | 113 | $CAPolicyInf = @" 114 | [Version] 115 | Signature="`$Windows NT$" 116 | [PolicyStatementExtension] 117 | Policies=InternalPolicy 118 | [InternalPolicy] 119 | OID= 1.3.6.1.4.1.$OID 120 | Notice="Legal Policy Statement" 121 | URL=http://$httpCRLPath/pki/cps.html 122 | [Certsrv_Server] 123 | RenewalKeyLength=4096 124 | RenewalValidityPeriod=Years 125 | RenewalValidityPeriodUnits=10 126 | CRLPeriod=Years 127 | CRLPeriodUnits=10 128 | CRLDeltaPeriod=Days 129 | CRLDeltaPeriodUnits=0 130 | LoadDefaultTemplates=0 131 | AlternateSignatureAlgorithm=1 132 | "@ 133 | 134 | $CAPolicyInf | Out-File "C:\Windows\CAPolicy.inf" -Encoding utf8 -Force | Out-Null 135 | Get-Content C:\Windows\CAPolicy.inf 136 | Report-Status "Would you like to edit CAPolicy.Inf? [y/n]" 1 Yellow 137 | $response = Read-Host 138 | If ($response -eq "y") { Start-Process -Wait -FilePath "notepad.exe" -ArgumentList "c:\windows\capolicy.inf" } 139 | $response = $null 140 | 141 | Report-Status "Installing required Windows Features" 0 Green 142 | Add-WindowsFeature -Name ADCS-Cert-Authority -IncludeManagementTools | Out-Null 143 | 144 | Report-Status "Install and configure AD Certificate Services" 0 Green 145 | 146 | # Configure Root CA 147 | # Certificate Validity: 10 years 148 | # Key Length: 4096 149 | # Hash: SHA256 150 | 151 | Install-AdcsCertificationAuthority -CAType StandaloneRootCA -CACommonName $RootCAName -KeyLength 4096 -HashAlgorithm SHA256 -CryptoProviderName "RSA#Microsoft Software Key Storage Provider" -ValidityPeriod Years -ValidityPeriodUnits 10 -Force | Out-Null 152 | 153 | Report-Status "Customizing AD Certificate Services" 0 Green 154 | Get-CACrlDistributionPoint | Remove-CACrlDistributionPoint -Force | Out-Null 155 | 156 | Add-CACRLDistributionPoint -Uri "$env:windir\system32\CertSrv\CertEnroll\.crl" -PublishToServer -PublishDeltaToServer -Force | Out-Null 157 | Add-CACRLDistributionPoint -Uri "C:\CAConfig\.crl" -PublishToServer -PublishDeltaToServer -Force | Out-Null 158 | Add-CACRLDistributionPoint -Uri "http://$httpCRLPath/certenroll/.crl" -AddToCertificateCDP -AddToFreshestCrl -Force | Out-Null 159 | 160 | Get-CAAuthorityInformationAccess | where { $_.Uri -like '*ldap*' -or $_.Uri -like '*http*' -or $_.Uri -like '*file*' } | Remove-CAAuthorityInformationAccess -Force | Out-Null 161 | Add-CAAuthorityInformationAccess -Uri "http://$httpCRLPath/certenroll/.crt" -AddToCertificateAia -Force | Out-Null 162 | 163 | # Set Validity period and other settings of certificates generated by this CA 164 | certutil.exe -setreg CA\CRLPeriodUnits 10 | Out-Null 165 | certutil.exe -setreg CA\CRLPeriod "Years" | Out-Null 166 | certutil.exe -setreg CA\ValidityPeriodUnits 5 | Out-Null 167 | certutil.exe -setreg CA\ValidityPeriod "Years" | Out-Null 168 | certutil.exe -setreg CA\CRLOverlapPeriodUnits 3 | Out-Null 169 | certutil.exe -setreg CA\CRLOverlapPeriod "Weeks" | Out-Null 170 | certutil.exe -setreg CA\AuditFilter 127 | Out-Null 171 | Report-Status "Restarting AD Certificate Services" 0 Green 172 | Restart-Service certsvc | Out-Null 173 | Start-Sleep 5 174 | Report-Status "Publishing CRL" 0 Green 175 | certutil -crl | Out-Null 176 | Report-Status "Root CA Build Completed!" 0 Green 177 | Report-Status "NOTE: Take a snapshot at this point before proceeding with the Subordinate CA installation." 0 Green 178 | -------------------------------------------------------------------------------- /Build-SubCA.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Build Subordinate CA in a two-tier PKI infrastructure 4 | .DESCRIPTION 5 | Automate the installation and configuration of a Subordinate Certificate Authority using 6 | the Microsoft PKI Services. This is designed to be executed on a Server Core instance. 7 | .INPUTS 8 | None 9 | .OUTPUTS 10 | None 11 | .NOTES 12 | Version: 1.5 13 | Author: Marc Bouchard 14 | Creation Date: 2021/03/04 15 | .EXAMPLE 16 | Install from Github using: 17 | Invoke-WebRequest -usebasicparsing -uri "https://raw.githubusercontent.com/SUBnet192/PKI/master/Build-SubCA.ps1" | Invoke-Expression 18 | #> 19 | 20 | #------------------------------------------------------[ Initialization ]--------------------------------------------------------- 21 | 22 | $response = $null 23 | $httpCRLPath = $null 24 | $OID = $null 25 | $SubordinateCAName = $null 26 | 27 | #--------------------------------------------------------[ Declaration ]---------------------------------------------------------- 28 | 29 | Function Show-Disclaimer { 30 | Clear-Host 31 | Write-Host " .d8888b. 888 .d8888b. d8888 " -ForegroundColor Yellow 32 | Write-Host "d88P Y88b 888 d88P Y88b d88888 " -ForegroundColor Yellow 33 | Write-Host "Y88b. 888 888 888 d88P888 " -ForegroundColor Yellow 34 | Write-Host " 'Y888b. 888 888 88888b. 888 d88P 888 " -ForegroundColor Yellow 35 | Write-Host " 'Y88b. 888 888 888 '88b 888 d88P 888 " -ForegroundColor Yellow 36 | Write-Host " '888 888 888 888 888 888 888 d88P 888 " -ForegroundColor Yellow 37 | Write-Host "Y88b d88P Y88b 888 888 d88P Y88b d88P d8888888888 " -ForegroundColor Yellow 38 | Write-Host " 'Y8888P' 'Y88888 88888P' 'Y8888P' d88P 888 " -ForegroundColor Yellow 39 | Write-Host "" 40 | Write-Host "IMPORTANT INFORMATION - PLEASE READ" -ForegroundColor Yellow 41 | Write-Host "" 42 | Write-Host "This script is used to build an Enterprise Subordinate Certificate server in a 2-tier Microsoft PKI solution" -ForegroundColor Yellow 43 | Write-Host "Please REVIEW the contents of this script to ensure the default values provided meet your requirements." -ForegroundColor Yellow 44 | Write-Host "" 45 | Write-Host "Tips:" -ForegroundColor Yellow 46 | Write-Host " - If running on a virtual machine, take a snapshot before starting, and another one at completion." -ForegroundColor Yellow 47 | Write-Host " This allows you to either restart fresh or recover/revert if anything fails with the subordinate CA." -ForegroundColor Yellow 48 | Write-Host "" 49 | Write-Host -NoNewLine "Press any key to continue..." -ForegroundColor Yellow 50 | $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown') 51 | } 52 | 53 | Function Report-Status { 54 | Param( 55 | [parameter(Mandatory = $true)][String]$Msg, 56 | [parameter(Mandatory = $true)][INT]$Lvl, 57 | [parameter(Mandatory = $true)][String]$Color 58 | ) 59 | Switch ($Lvl) { 60 | 0 { Write-Host -Foreground $Color "[EXEC]" $Msg } 61 | 1 { Write-Host -Foreground $Color "[QUERY]" $Msg } 62 | } 63 | } 64 | 65 | #---------------------------------------------------------[ Execution ]---------------------------------------------------------- 66 | 67 | Show-Disclaimer 68 | 69 | Clear-Host 70 | Report-Status "Building Subordinate CA" 0 Green 71 | 72 | Report-Status "Configure WinRM" 0 Green 73 | Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force | Out-Null 74 | 75 | Report-Status "Configuring Auditing" 0 Green 76 | auditpol /set /category:"Object Access" /failure:enable /success:enable | Out-Null 77 | 78 | 79 | #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=[ User Input ]=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 80 | 81 | $response = $null 82 | [regex] $OIDRegex = "^\d{5}$" 83 | do { 84 | # Query user for Subordinate CA Common Name 85 | Report-Status "Enter the Common Name for the Subordinate CA (ex: Corp-Sub-CA):" 1 Yellow 86 | $SubordinateCAName = Read-Host 87 | 88 | do { 89 | Report-Status "Please enter your 5 digit OID number:" 1 Yellow 90 | $OID = read-host 91 | } while ($OID -inotmatch $OIDRegex) 92 | 93 | Report-Status "Enter the URL where the CRL files will be located (ex: pki.mycompany.com): " 1 Yellow 94 | $httpCRLPath = Read-Host 95 | 96 | Report-Status "Enter the Name of the Root CA server" 1 Yellow 97 | $RootCAServer = Read-Host 98 | 99 | $RootCACreds = Get-Credential -Message "Credentials for the Root CA Server." 100 | 101 | Report-Status "You have provided the following information:" 1 Yellow 102 | Write-Host "CA Common Name: $SubordinateCAName" 103 | Write-Host "OID : $OID" 104 | Write-Host "CRL URL path : $httpCRLPath" 105 | Write-Host "Root CA Server: $RootCAServer" 106 | 107 | Report-Status "Are you satisfied with these answers? [y/n]" 1 Yellow 108 | $response = Read-Host 109 | } until ($response -eq 'y') 110 | 111 | #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=[ End User Input ]=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 112 | 113 | Report-Status "Create CAPolicy file" 0 Green 114 | 115 | #-------------------------- 116 | # CAPolicy.INF file content 117 | #-------------------------- 118 | 119 | $CAPolicyInf = @" 120 | [Version] 121 | Signature="`$Windows NT$" 122 | [PolicyStatementExtension] 123 | Policies=InternalPolicy 124 | [InternalPolicy] 125 | OID= 1.3.6.1.4.1.$OID 126 | Notice="Legal Policy Statement" 127 | URL=http://$httpCRLPath/pki/cps.html 128 | [Certsrv_Server] 129 | RenewalKeyLength=4096 130 | RenewalValidityPeriod=Years 131 | RenewalValidityPeriodUnits=5 132 | LoadDefaultTemplates=1 133 | AlternateSignatureAlgorithm=1 134 | "@ 135 | 136 | $CAPolicyInf | Out-File "C:\Windows\CAPolicy.inf" -Encoding utf8 -Force | Out-Null 137 | Get-Content C:\Windows\CAPolicy.inf 138 | Report-Status "Would you like to edit CAPolicy.Inf? [y/n]" 1 Yellow 139 | $response = Read-Host 140 | If ($response -eq "y") { Start-Process -Wait -FilePath "notepad.exe" -ArgumentList "c:\windows\capolicy.inf" } 141 | $response = $null 142 | 143 | Report-Status "Installing required Windows Features" 0 Green 144 | Add-WindowsFeature -Name ADCS-Cert-Authority, ADCS-Web-Enrollment, Web-Mgmt-Service -IncludeManagementTools | Out-Null 145 | 146 | Report-Status "Install and configure AD Certificate Services" 0 Green 147 | Install-AdcsCertificationAuthority -CAType EnterpriseSubordinateCA -CACommonName $SubordinateCAName -KeyLength 4096 -HashAlgorithm SHA256 -CryptoProviderName "RSA#Microsoft Software Key Storage Provider" -Force | Out-Null 148 | Install-AdcsWebEnrollment -Force | Out-Null 149 | 150 | Report-Status "Mapping X: to CertConfig share on Root CA" 0 Green 151 | New-PSDrive -Name "X" -Root "\\$RootCAServer\CertConfig" -PSProvider "FileSystem" -Credential $RootCACreds | Out-Null 152 | 153 | # Copy request from Subordinate CA to Root CA 154 | Report-Status "Copy Certificate Request to X:" 0 Green 155 | Copy-Item C:\*.REQ -Destination X:\ | Out-Null 156 | 157 | #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=[ Remote Execution ]=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 158 | 159 | Report-Status "Triggering remote execution of certificate request" 0 Green 160 | Invoke-Command $RootCAServer -credential $RootCACreds -scriptblock { 161 | # Initialize variables 162 | Write-Host "[REMOTE] Initialize variables" -ForegroundColor Magenta 163 | $RootCAName = (get-itemproperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration).Active 164 | $RootCAServer = hostname 165 | $SubordinateCAReq = Get-ChildItem "C:\CAConfig\*.req" 166 | 167 | # Submit CSR from Subordinate CA to the Root CA 168 | Write-Host "[DEBUG] ORCAServer:$RootCAServer" -ForegroundColor Yellow 169 | Write-Host "[DEBUG] ORCAName:$RootCAName" -ForegroundColor Yellow 170 | Write-Host "[DEBUG] SubordinateCAReq:$SubordinateCAReq" -ForegroundColor Yellow 171 | Write-Host "[REMOTE] Submitting Subordinate certificate request to Root CA" -ForegroundColor Magenta 172 | certreq -config $RootCAServer\$RootCAName -submit -attrib "CertificateTemplate:SubCA" $SubordinateCAReq.Fullname | Out-Null 173 | 174 | # Authorize Certificate Request 175 | Write-Host "[REMOTE] Issuing Subordinate certificate" -ForegroundColor Magenta 176 | certutil -resubmit 2 | Out-Null 177 | 178 | # Retrieve Subordinate CA certificate 179 | Write-Host "[REMOTE] Retrieving/Exporting Subordinate certificate" -ForegroundColor Magenta 180 | certreq -config $RootCAServer\$RootCAName -retrieve 2 "C:\CAConfig\SubordinateCA.crt" | Out-Null 181 | 182 | # Rename Root CA certificate (remove server name) 183 | Write-Host "[REMOTE] Correcting certificate filename and cleanup" -ForegroundColor Magenta 184 | $Source = "C:\CAConfig\$RootCAServer" + "_" + "$RootCAName.crt" 185 | $Target = "$RootCAName.crt" 186 | Rename-Item $Source $Target | Out-Null 187 | Remove-Item C:\CAConfig\*.REQ | Out-Null 188 | } 189 | 190 | #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=[ End Remote Execution ]=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 191 | 192 | # Copy certificate/CRL from Root CA to Subordinate CA 193 | Report-Status "Copy certificates and CRL from Root CA to Subordinate CA" 0 Green 194 | 195 | Copy-Item X:\*.CRT -Destination C:\Windows\system32\CertSrv\CertEnroll | Out-Null 196 | Copy-Item X:\*.CRL -Destination C:\Windows\system32\CertSrv\CertEnroll | Out-Null 197 | 198 | $RootCACert = Get-ChildItem "C:\Windows\system32\CertSrv\CertEnroll\*.crt" -exclude "SubordinateCA.crt" 199 | $RootCACRL = Get-ChildItem "C:\Windows\system32\CertSrv\CertEnroll\*.crl" 200 | 201 | # Publish Root CA certificate to AD 202 | Report-Status "Publish Root CA certificate to AD" 0 Green 203 | certutil.exe -dsPublish -f $RootCACert.FullName RootCA | Out-Null 204 | 205 | # Publish Root CA certificates to Subordinate server 206 | Report-Status "Add Root CA certificate to Subordinate CA server" 0 Green 207 | certutil.exe -addstore -f root $RootCACert.FullName | Out-Null 208 | certutil.exe -addstore -f root $RootCACRL.FullName | Out-Null 209 | 210 | Report-Status "Install Subordinate CA certificate to server" 0 Green 211 | certutil.exe -installcert C:\Windows\System32\CertSrv\CertEnroll\SubordinateCA.crt | Out-Null 212 | 213 | Report-Status "Customizing AD Certificate Services" 0 Green 214 | Report-Status "Setting up CRL distribution points" 0 Green 215 | $crllist = Get-CACrlDistributionPoint 216 | foreach ($crl in $crllist) { 217 | Remove-CACrlDistributionPoint $crl.uri -Force | Out-Null 218 | } 219 | 220 | Add-CACRLDistributionPoint -Uri "C:\Windows\system32\CertSrv\CertEnroll\.crl" -PublishToServer -PublishDeltaToServer -Force | Out-Null 221 | Add-CACRLDistributionPoint -Uri "http://$httpCRLPath/certenroll/.crl" -AddToCertificateCDP -AddToFreshestCrl -Force | Out-Null 222 | 223 | Get-CAAuthorityInformationAccess | where { $_.Uri -like '*ldap*' -or $_.Uri -like '*http*' -or $_.Uri -like '*file*' } | Remove-CAAuthorityInformationAccess -Force | Out-Null 224 | Add-CAAuthorityInformationAccess -Uri "http://$httpCRLPath/certenroll/.crt" -AddToCertificateAia -Force | Out-Null 225 | 226 | Report-Status "Setting default values for issued certificates" 0 Green 227 | certutil.exe -setreg CA\CRLPeriodUnits 2 | Out-Null 228 | certutil.exe -setreg CA\CRLPeriod "Weeks" | Out-Null 229 | certutil.exe -setreg CA\CRLDeltaPeriodUnits 1 | Out-Null 230 | certutil.exe -setreg CA\CRLDeltaPeriod "Days" | Out-Null 231 | certutil.exe -setreg CA\CRLOverlapPeriodUnits 12 | Out-Null 232 | certutil.exe -setreg CA\CRLOverlapPeriod "Hours" | Out-Null 233 | certutil.exe -setreg CA\ValidityPeriodUnits 1 | Out-Null 234 | certutil.exe -setreg CA\ValidityPeriod "Years" | Out-Null 235 | certutil.exe -setreg CA\AuditFilter 127 | Out-Null 236 | 237 | Report-Status "Restarting AD Certificate Services" 0 Green 238 | Restart-Service certsvc | Out-Null 239 | Start-Sleep 5 240 | 241 | Report-Status "Publishing CRL" 0 Green 242 | certutil -crl | Out-Null 243 | 244 | # Rename Subordinate CA certificate (remove server name) 245 | Report-Status "Correcting certificate filename and cleanup" 0 Green 246 | $RootCAName = (get-itemproperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration).Active 247 | $FQDN = "$env:computername.$env:userdnsdomain" 248 | $Source = "C:\Windows\System32\CertSrv\CertEnroll\$FQDN" + "_" + "$RootCAName.crt" 249 | $Target = "$RootCAName.crt" 250 | Rename-Item $Source $Target | Out-Null 251 | Remove-Item C:\*.REQ | Out-Null 252 | 253 | # Get the service 254 | $webManagementService = Get-Service WMSVC -ErrorAction Stop | Out-Null 255 | 256 | # Stop the WMSVC, if running 257 | if ($webManagementService.Status -eq "Running") { 258 | Stop-Service WMSVC | Out-Null 259 | } 260 | # Enable double escaping as per BPA 261 | c:\windows\system32\inetsrv\appcmd set config /section:requestfiltering /allowdoubleescaping:true 262 | 263 | # Modify the EnableRemoteManagement property in the Windows Registry 264 | Report-Status "Setting the IIS EnableRemoteManagement property" 0 Green 265 | $enableRemoteManagement = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\WebManagement\Server -Name "EnableRemoteManagement" 266 | if ($enableRemoteManagement.EnableRemoteManagement -eq 0) { 267 | Set-ItemProperty HKLM:\SOFTWARE\Microsoft\WebManagement\Server -Name "EnableRemoteManagement" -Value 1 -ErrorAction Stop | Out-Null 268 | } 269 | 270 | # Ensure automatic start of the WMSVC service 271 | Report-Status "Starting the WMSVC service and enabling automatic startup" 0 Green 272 | Start-Service WMSVC | Out-Null 273 | Set-Service WMSVC -StartupType Automatic | Out-Null 274 | 275 | # Final message 276 | Report-Status "Subordinate CA Build Completed!" 0 Green 277 | Report-Status "NOTE: Don't forget to remove any snapshots you created during this installation." 0 Green 278 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PKI 2 | Microsoft PKI 2-Tier infrastructure build 3 | 4 | In the past year I have been working on ransomware recovery/infrastructure improvements post-incident. One thing that is always missing at each customer location is a PKI infrastructure, to implement LDAPs amongst other things. 5 | 6 | Last year I attempted to do so with a DSC script but I didn't like the end result, so I rebuilt it from scratch over the past 2 weeks. 7 | 8 | Steps: 9 | - Obtain your own OID at https://pen.iana.org/pen/PenApplication.page 10 | - Create a DNS CNAME named "pki" or something else for your Enterprise Subordinate CA. 11 | - This is designed to be deployed on Server Core servers (Tested on Windows 2019 Core) 12 | - Deploy 2 server core instances. 13 | - One for the Root CA 14 | - One for the Enterprise Subordinate CA 15 | - Setup your IP information on both servers 16 | (Root CA is not supposed to be network attached. While there is a small risk, I would say that having it connected for the duration of the build and then shut down after the Subordinate is issued isn't a major concern.) 17 | - On the Root CA server (not domain joined), run the Build-RootCA.ps1 18 | - On the Subordinate CA server (domain joined, and logged in using a domain account), run the Build-SubCA.ps1 19 | - Root CA certificate is valid for 10 years. 20 | - Subordinate Enterprise CA certificate is valid for 5 years 21 | - Issued certificates are valid for 1 year 22 | 23 | There are some prompts during the installation, so it's not fully unattended, but all prompts are made at the beginning of the script. 24 | 25 | End result is a working PKI infrastructure in 15 mins max (if you're starting from Windows virtual templates). 26 | 27 | NOTE: These scripts must be run LOCALLY on the servers, not through remote powershell 28 | 29 | Video of the Root CA installation 30 |
31 | 32 | ORCA 36 | 37 |
38 | --------------------------------------------------------------------------------