├── Install-ADCSOfflineCA.ps1
├── Install-ADCSSubordinateCA.ps1
├── LICENSE
├── README.md
└── images
├── 0_PKI.png
├── 10_PKI.png
├── 11_PKI.png
├── 12_PKI.png
├── 13_PKI.png
├── 14_PKI.png
├── 15_PKI.png
├── 16_PKI.png
├── 17_PKI.png
├── 18_PKI.png
├── 19_PKI.png
├── 1_PKI.png
├── 20_PKI.png
├── 21_PKI.png
├── 22_PKI.png
├── 23_PKI.png
├── 24_PKI.png
├── 25_PKI.png
├── 26_PKI.png
├── 27_PKI.png
├── 28_PKI.png
├── 29_PKI.png
├── 2_PKI.png
├── 30_PKI.png
├── 31_PKI.png
├── 32_PKI.png
├── 33_PKI.png
├── 34_PKI.png
├── 35_PKI.png
├── 36_PKI.png
├── 37_PKI.png
├── 38_PKI.png
├── 39_PKI.png
├── 3_PKI.png
├── 40_PKI.png
├── 41_PKI.png
├── 42_PKI.png
├── 43_PKI.png
├── 44_PKI.png
├── 45_PKI.png
├── 46_PKI.png
├── 47_PKI.png
├── 4_PKI.png
├── 5_PKI.png
├── 6_PKI.png
├── 7_PKI.png
├── 8_PKI.png
└── 9_PKI.png
/Install-ADCSOfflineCA.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Installs the Root (Offline) Certificate Authority role and configures it.
4 |
5 | .DESCRIPTION
6 | Script developed for to install a Root (Offline) Certificate Authority.
7 | The configuration uses certutil.exe to modify the CA settings.
8 | Tested on Windows Server 2012R2 and Server 2016.
9 | Never use a network connection an a Root (Offline) Certificate Authority
10 |
11 | .EXAMPLE
12 | Install-ADCSOfflineCA.ps1 -Company DEMO -DomainURL pki.demo.com -ConfigNC 'CN=Configuration,DC=demo,DC=lab'
13 |
14 | This will install the install and configure the Certificate Authority Service with the CA name "DEMO-Root-CA".
15 | It will create the PKI folder in the default location ("$env:SystemDrive\PKI").
16 | The PKI folder contains the Database and paths for AIA and CRL.
17 |
18 | .EXAMPLE
19 | Install-ADCSOfflineCA.ps1 -Company Contoso -DomainURL pki.contoso.com -ConfigNC 'CN=Configuration,DC=contoso,DC=com' -LocalPKIPath E:\CALocation
20 |
21 | This will install the install and configure the Certificate Authority Service with the CA name "Contoso-Root-CA".
22 | It will create a folder named PKI in CALocation on the disk E:\.
23 | The PKI folder contains the Database and paths for AIA and CRL.
24 |
25 |
26 | .NOTES
27 | Created on: 2016-05-11 09:15
28 | Created by: Philip Haglund
29 | Organization: Gonjer.com
30 | Filename: Install-ADCSOfflineCA.ps1
31 | Version: 0.4
32 | Requirements: Powershell 4.0 (Module: ServerManager)
33 | Changelog: 2016-05-11 09:15 - Creation of script
34 | 2016-09-19 16:20 - Removed LDAP paths CRL:"\n10:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10" AIA:"\n2:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11"
35 | 2016-10-07 15:21 - Minor bugfixes and corrections based on PSSharper.
36 | 2017-01-10 15:34 - Correction of typos. Clean up help text.
37 | .LINK
38 | https://www.gonjer.com
39 | #>
40 | #requires -Version 4.0
41 | [cmdletbinding()]
42 | param (
43 |
44 | # Company name that will belong in the Certificate Authority Name.
45 | # Example: 'Contoso' will be "Contoso-ROOT-CA"
46 | [Parameter(
47 | Mandatory = $true,
48 | HelpMessage = "Company name that will belong in the Certificate Authority Name.`nExample: 'Contoso'`n'Contoso' will be 'Contoso-ROOT-CA'"
49 | )]
50 | [string]$Company,
51 |
52 | # URL for CRL and AIA publishing from the Subordinate CA.
53 | # Example: 'pki.contoso.com'
54 | [Parameter(
55 | Mandatory = $true,
56 | HelpMessage = "Domain URL for CRL and AIA publishing.`nExample: 'pki.contoso.com'"
57 | )]
58 | [string]$DomainURL,
59 |
60 | # Active Directory Configuration Naming Context where the Subordinate Certificate Authority will be placed.
61 | # Example: 'CN=Configuration,DC=contoso,DC=com'
62 | [Parameter(
63 | Mandatory = $true,
64 | HelpMessage = "Active Directory Configuration Naming Context where the Subordinate Certificate Authority will be placed.`nExample: 'CN=Configuration,DC=contoso,DC=com'"
65 | )]
66 | [string]$ConfigNC,
67 |
68 | # Local file path to store Certificate Database and Logs. Also used for creating Web directory and fileshare location.
69 | # Example: 'C:\PKI'
70 | [Alias('LocalPath')]
71 | [string]$LocalPKIPath = "$env:SystemDrive\PKI"
72 |
73 | )
74 | begin
75 | {
76 | function Confirm-ToContinue
77 | {
78 | $caption = 'Manual Step'
79 | $message = 'Are you done with the manual step?'
80 | [int]$defaultChoice = 1
81 | $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes', 'Done with manual step.'
82 | $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No', 'Not done, continue to prompt.'
83 | $cancel = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Cancel', 'Not done, exit script.'
84 | $options = [Management.Automation.Host.ChoiceDescription[]]($yes, $no, $cancel)
85 |
86 | $do = $true
87 | do
88 | {
89 | $choice = $Host.ui.PromptForChoice($caption,$message, $options,$defaultChoice)
90 | switch ($choice)
91 | {
92 | 0
93 | {
94 | $do = $false
95 | }
96 | 1
97 | {
98 | 'Issued: Not done, continue to prompt.'
99 | $do = $true
100 | }
101 | Default
102 | {
103 | 'Cancel, exiting script.'
104 | $do = $false
105 | break
106 | }
107 | }
108 | }
109 | while ($do)
110 | }
111 |
112 | # Clear all errors
113 | $Error.Clear()
114 | }
115 | process
116 | {
117 | if ($PSCmdlet.ShouldProcess("$($Company)($($DomainURL)) - $($LocalPKIPath)",'Configure Offline CA'))
118 | {
119 | #region Create directories
120 | $certdb = New-Item -Path "$($LocalPKIPath)\Database\CertDB" -ItemType Directory -Force
121 | $certlog = New-Item -Path "$($LocalPKIPath)\Database\CertLog" -ItemType Directory -Force
122 | $null = New-Item -Path "$($LocalPKIPath)\Web" -ItemType Directory -Force
123 | $crlpath = New-Item -Path "$($LocalPKIPath)\Web\CRL" -ItemType Directory -Force
124 | $aiapath = New-Item -Path "$($LocalPKIPath)\Web\AIA" -ItemType Directory -Force
125 | #endregion Create directories
126 |
127 | #region Install Windowsfeature ADCS-Cert-Authority
128 | try
129 | {
130 | $null = Install-WindowsFeature -Name ADCS-Cert-Authority -IncludeManagementTools
131 | }
132 | catch
133 | {
134 | Write-Warning -Message 'Unable to install Windows Feature ADCS-Cert-Authority'
135 | Write-Warning -Message "$($_.Exception.Message)"
136 | return
137 | }
138 | #endregion Install Windowsfeature ADCS-Cert-Authority
139 |
140 | #region Generate a CAPolicy.inf file for "$($env:windir)\CAPolicy.inf"
141 | $capolicy = '[Version]
142 | Signature="$Windows NT$"
143 |
144 | [PolicyStatementExtension]
145 | Policies=InternalUseOnly
146 |
147 | [Certsrv_Server]
148 | RenewalKeyLength=4096
149 | RenewalValidityPeriod=Years
150 | RenewalValidityPeriodUnits=10
151 | CRLPeriod=Years
152 | CRLPeriodUnits=1
153 | CRLDeltaPeriod=Days
154 | CRLDeltaPeriodUnits=0
155 |
156 | [BasicConstraintsExtension]
157 | PathLength=1
158 | Critical=Yes
159 |
160 | [CRLDistributionPoint]
161 | Empty=True
162 |
163 | [AuthorityInformationAccess]
164 | Empty=True'
165 |
166 | $capolicy | Out-File -FilePath "$($env:windir)\capolicy.inf" -Encoding default -Force
167 | #region Generate a CAPolicy.inf file for "$($env:windir)\CAPolicy.inf"
168 |
169 | #region Install-AdcsCertificationAuthority
170 | try
171 | {
172 | $null = Install-AdcsCertificationAuthority -CAType StandaloneRootCA `
173 | -CACommonName "$Company-ROOT-CA" `
174 | -CADistinguishedNameSuffix "OU=PKI,O=$Company,C=SE" `
175 | -KeyLength 4096 `
176 | -HashAlgorithmName SHA256 `
177 | -CryptoProviderName 'RSA#Microsoft Software Key Storage Provider' `
178 | -DatabaseDirectory $($certdb.FullName) `
179 | -LogDirectory $($certlog.FullName) `
180 | -ValidityPeriod Years `
181 | -ValidityPeriodUnits 10 `
182 | -OverwriteExistingKey `
183 | -WarningAction SilentlyContinue `
184 | -ErrorAction Stop `
185 | }
186 | catch
187 | {
188 | Write-Warning -Message 'Unable to Install-AdcsCertificationAuthority -CAType StandaloneRootCA'
189 | Write-Warning -Message "$($_.Exception.Message)"
190 | Write-Output -InputObject 'Manual install AdcsCertificationAuthority with the following properties:'
191 | Write-Output -InputObject "
192 | -CAType StandaloneRootCA
193 | -CACommonName '$Company-ROOT-CA'
194 | -CADistinguishedNameSuffix 'OU=PKI,O=$Company,C=SE'
195 | -KeyLength 4096
196 | -HashAlgorithmName SHA256
197 | -CryptoProviderName 'RSA#Microsoft Software Key Storage Provider'
198 | -DatabaseDirectory '$($certdb.FullName)'
199 | -LogDirectory '$($certlog.FullName)'
200 | -ValidityPeriod Years
201 | -ValidityPeriodUnits 10"
202 | Confirm-ToContinue
203 | }
204 | #endregion Install-AdcsCertificationAuthority
205 |
206 |
207 | # Declare Configuration and Domain NCs
208 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\DSConfigDN $ConfigNC
209 |
210 | # Set Validity Period for Issued Certificates (Subordinate) to 5 years
211 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\ValidityPeriodUnits 5
212 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\ValidityPeriod 'Years'
213 |
214 | # Define CRL Publication Intervals
215 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\CRLPeriodUnits 52
216 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\CRLPeriod 'Weeks'
217 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\CRLDeltaPeriodUnits 0
218 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\CRLDeltaPeriod 'Days'
219 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\CRLOverlapPeriodUnits 12
220 | $null = & "$env:windir\system32\certutil.exe" -setreg ca\CRLOverlapPeriod 'Hours'
221 |
222 | # Enable CA Audit
223 | $null = & "$env:windir\system32\certutil.exe" -setreg CA\AuditFilter 127
224 |
225 | #region Remove any CRL Distribution Points
226 | $crllist = Get-CACrlDistributionPoint -ErrorAction SilentlyContinue
227 | foreach ($crl in $crllist)
228 | {
229 | $null = Remove-CACrlDistributionPoint -Uri $crl.uri -Force -ErrorAction SilentlyContinue
230 | }
231 | #endregion Remove any CRL Distribution Points
232 |
233 |
234 | #region Remove any certificates Authority Information Access
235 | $aialist = Get-CAAuthorityInformationAccess -ErrorAction SilentlyContinue
236 | foreach ($aia in $aialist)
237 | {
238 | $null = Remove-CAAuthorityInformationAccess -Uri $aia.uri -Force -ErrorAction SilentlyContinue
239 | }
240 | #endregion Remove any Authority Information Access certificates
241 |
242 | # Set New correct URLs for CRL and AIA
243 | $null = & "$env:windir\system32\certutil.exe" -setreg CA\CRLPublicationURLs "1:$($crlpath.FullName)\%3%8.crl\n2:http://$($DomainURL)/CRL/%3%8.crl"
244 | $null = & "$env:windir\system32\certutil.exe" -setreg CA\CACertPublicationURLs "1:$($aiapath.FullName)\%3%4.crt\n2:http://$($DomainURL)/AIA/%3%4.crt"
245 | #& "$env:windir\system32\certutil.exe" -getreg ca\CRLDeltaPeriodUnits
246 |
247 |
248 | Start-Sleep -Seconds 3
249 | try
250 | {
251 | $null = Stop-Service -Name 'certsvc' -Force -ErrorAction Stop -WarningAction SilentlyContinue
252 | }
253 | catch
254 | {
255 | Write-Warning -Message 'The Certificate Services (certsvc) did not stop correctly.'
256 | Write-Output -InputObject 'Please stop the service manually.'
257 | Confirm-ToContinue
258 | }
259 | try
260 | {
261 | $null = Start-Service -Name 'certsvc' -Confirm:$false -WarningAction SilentlyContinue -ErrorAction Stop
262 | }
263 | catch
264 | {
265 | Write-Warning -Message 'The Certificate Services (certsvc) did not start correctly.'
266 | Write-Output -InputObject 'Please start the service manually.'
267 | Confirm-ToContinue
268 | }
269 |
270 | #region Copy all AIA (Certificates) from the original store to the new AIA-Path
271 | try
272 | {
273 | Copy-Item -Path "$($env:windir)\system32\certsrv\certenroll\*.crt" -Destination $aiapath.FullName -ErrorAction Stop
274 | }
275 | catch
276 | {
277 | Write-Warning -Message "Unable to Copy-Item $($env:windir)\system32\certsrv\certenroll\*.crt to $($aiapath.FullName)"
278 | Write-Warning -Message "$($_.Exception.Message)"
279 | Write-Output -InputObject "Do a manual copy of '($env:windir)\system32\certsrv\certenroll\*.crt' to '$($aiapath.FullName)'"
280 | Confirm-ToContinue
281 | }
282 | #region Copy all AIA (Certificates) from the original store to the new AIA-Path
283 |
284 | #region View all errors
285 | if ($Error.Count -eq 0)
286 | {
287 | Clear-Host
288 | }
289 | else
290 | {
291 | Write-Output -InputObject 'Errors were deteted while running the script, displaying errors:'
292 | Start-Sleep -Milliseconds 500
293 | foreach ($e in $Error)
294 | {
295 | "$($e.Exception.Message)"
296 | }
297 | Write-Output -InputObject 'Does everything look OK?'
298 | Confirm-ToContinue
299 | }
300 | #region View all errors
301 |
302 | Write-Output -InputObject "`nFinished installing and configuring the Root Certificate Authority."
303 | Write-Output -InputObject 'The next step is to install the Subordinate Certificate Authority.'
304 | }
305 | }
306 | end
307 | {
308 |
309 | }
--------------------------------------------------------------------------------
/Install-ADCSSubordinateCA.ps1:
--------------------------------------------------------------------------------
1 | #requires -Modules NetTCPIP, ServerManager
2 | <#
3 | .SYNOPSIS
4 | Installs the Subordinate Certificate Authority role and configures it.
5 |
6 | .DESCRIPTION
7 | Script developed for to install a Subordinate Certificate Authority when a Root (Offline) Certificate Authority is already installed and configured.
8 | The configuration uses certutil.exe to modify the CA settings.
9 | Tested on Windows Server 2012R2 and Server 2016.
10 |
11 | .EXAMPLE
12 | Install-ADCSSubordinateCA.ps1 -Company Demo -DomainURL pki.demo.com -SMTPServer smtp.demo.com -ToAddress 'recipient@demo.com' -FromAddress 'sender@demo.com' -City 'Gothenburg' -State 'VG'
13 |
14 | This will install the install and configure the Certificate Authority Service with the CA name "Demo-Subordinate-CA".
15 | It will create the PKI folder in the default location ("$env:SystemDrive\PKI").
16 | The PKI folder contains the Database and paths for AIA and CRL files.
17 | A Web Virtual Directory will be created with the name PKI mapped to "$env:SystemDrive\PKI\Web"
18 | Three scheduled tasks will be created containg backup scripts and a PKI health information script.
19 | The ADCS Web Enrollment page will contain information used in the City and State parameters.
20 |
21 |
22 | .EXAMPLE
23 | Install-ADCSSubordinateCA.ps1 -Company Contoso -DomainURL pki.contoso.com -LocalPKIPath E:\CALocation -SMTPServer smtp.contoso.com -ToAddress 'recipient@contoso.com' -FromAddress 'sender@contoso.com' -City 'Copenhagen' -State 'Hovedstaden'
24 |
25 | This will install the install and configure the Certificate Authority Service with the CA name "Contoso-Subordinate-CA".
26 | It will create a folder named CALocation in E:\.
27 | The PKI (CALocation) folder contains the Database and paths for AIA and CRL files.
28 | A Web Virtual Directory will be created with the name PKI mapped to "E:\CALocation\Web"
29 | Three scheduled tasks will be created containg backup scripts and a PKI health information script.
30 | The ADCS Web Enrollment page will contain information used in the City and State parameters.
31 |
32 |
33 | .INPUTS
34 | String
35 | IO.DirectoryInfo
36 | mailaddress
37 |
38 | .NOTES
39 | Created on: 2016-05-11 09:15
40 | Created by: Philip Haglund
41 | Organization: Gonjer.com
42 | Filename: Install-ADCSSubordinateCA.ps1
43 | Version: 0.5
44 | Requirements: Powershell 4.0 (Module: NetTCPIP, ServerManager)
45 | Changelog: 2016-05-11 09:15 - Creation of script
46 | 2016-09-19 16:25 - Removed LDAP paths CRL: "\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10" AIA: \n3:ldap:///CN=%7,CN=AIA,CN=Public Key Services,CN=Services,%6%11
47 | 2016-09-20 09:10 - Removed SMB Share requirements.
48 | 2016-10-07 15:21 - Completly changed the prompt text for the manual steps. Minor bugfixes and corrections based on PSSharper.
49 | 2017-01-10 14:03 - Set web.config to hidden. Added Group Policy recommendation for auto enrollment.
50 | 2017-01-11 14:57 - Added functions 'Register-CABackup' and 'Add-ScheduledPKIMaintenance'. Change ValidityPeriod to 30 months instead of 2 years.
51 | .LINK
52 | https://www.gonjer.com
53 | #>
54 | #requires -Version 4.0
55 | [cmdletbinding(
56 | SupportsShouldProcess = $true
57 | )]
58 | param (
59 |
60 | # Company name that will belong in the Certificate Authority Name.
61 | # Example: 'DEMO' will be "DEMO-Subordinate-CA"
62 | [Parameter(
63 | Mandatory = $true,
64 | HelpMessage = "Company name that will belong in the Certificate Authority Name.`nExample: 'DEMO'`n'DEMO' will be 'DEMO-Subordinate-CA'"
65 | )]
66 | [string]$Company,
67 |
68 | # Domain URL for CRL and AIA publishing. Also used for the fileshare path.
69 | # Example: 'pki.demo.com'
70 | [Parameter(
71 | Mandatory = $true,
72 | HelpMessage = "Domain URL for CRL and AIA publishing.`nExample: 'pki.demo.com'"
73 | )]
74 | [string]$DomainURL,
75 |
76 | # Local file path to store Certificate Database and Logs. Also used for creating Web directory and fileshare location.
77 | # Example: 'C:\PKI'
78 | [Alias('Path')]
79 | [IO.DirectoryInfo]$LocalPKIPath = "$env:SystemDrive\PKI",
80 |
81 | # A valid SMTP Server used to send information messages about PKI maintenance.
82 | [Parameter(
83 | Mandatory = $True,
84 | ValueFromPipelineByPropertyName = $True,
85 | HelpMessage = 'Enter a valid SMTPServer. Example: smtp.contoso.net'
86 | )]
87 | [ValidateNotNullOrEmpty()]
88 | [string]$SMTPServer,
89 |
90 | # One or more valid TO mailaddresses used to send information messages about PKI maintenance.
91 | # Example: noreply@contoso.com
92 | [Parameter(
93 | Mandatory = $True,
94 | ValueFromPipelineByPropertyName = $True,
95 | HelpMessage = 'One or more valid TO mailaddresses. Example: recipient@contoso.com'
96 | )]
97 | [ValidateNotNullOrEmpty()]
98 | [mailaddress[]]$ToAddress,
99 |
100 | # A valid FROM mailaddress used to send information messages about PKI maintenance.
101 | # Example: noreply@contoso.com
102 | [Parameter(
103 | Mandatory = $True,
104 | ValueFromPipelineByPropertyName = $True,
105 | HelpMessage = 'Enter a valid mailaddress. Example: noreply@contoso.com'
106 | )]
107 | [ValidateNotNullOrEmpty()]
108 | [mailaddress]$FromAddress,
109 |
110 | # A city used to populate the certdat.inc file with correct information.
111 | # Example: Gothenburg
112 | [Parameter(
113 | Mandatory = $True,
114 | ValueFromPipelineByPropertyName = $True,
115 | HelpMessage = 'Gothenburg'
116 | )]
117 | [ValidateNotNullOrEmpty()]
118 | [string]$City,
119 |
120 | # A state used to populate the certdat.inc file with correct information.
121 | # Example: VG
122 | [Parameter(
123 | Mandatory = $True,
124 | ValueFromPipelineByPropertyName = $True,
125 | HelpMessage = 'VG'
126 | )]
127 | [ValidateNotNullOrEmpty()]
128 | [string]$State
129 |
130 | )
131 |
132 | begin
133 | {
134 | function Add-ScheduledPKIMaintenance
135 | {
136 | <#
137 | .SYNOPSIS
138 | Creates a Scheduled Task that mail a PKI maintenance job every third month.
139 |
140 | .DESCRIPTION
141 | Creates and register a Scheduled Task containing a Powershell script action that will send a mail containing a recommended to do list for a three month PKI maintenance job.
142 | Default the task will be run every 91 days.
143 |
144 | .EXAMPLE
145 | Add-ScheduledPKIMaintenance -CRTFile 'C:\Windows\system32\certsrv\CertEnroll\Contoso-Subordinate-CA.crt' -MaintenanceScriptFile 'C:\PKI\PKI-MaintenanceJob.ps1' -Company Contoso -SMTPServer smtp.contoso.net -ToAddress recipient@contoso.com -FromAddress noreply@contoso.com
146 |
147 | Creates and register a Scheduled Task containing a Powershell script action that will send a mail containing a recommended to do list for a three month PKI maintenance job.
148 | The parameters ToAddress, FromAddress and SMTP server will be outputed in the Action script (Example: C:\PKI\PKI-MaintenanceJob.ps1).
149 | The parameter Company is used to populate the HTML body text and Subject string in POwershell action script.
150 |
151 | .INPUTS
152 | String
153 | IO.FileInfo
154 | mailaddress
155 |
156 | .NOTES
157 | Created on: 2017-01-04 09:22
158 | Created by: Philip Haglund
159 | Organization: Gonjer.com
160 | Filename: Add-ScheduledPKIMaintenance.ps1
161 | Version: 0.2
162 | Requirements: Powershell 3.0
163 | Changelog: 2017-01-04 09:22 - Creation of script.
164 | 2017-01-11 07:39 - Change HTML body. Rewrite help. Fix typos.
165 | 2017-01-20 15:01 - More bug fixes and typo corrections.
166 |
167 | .LINK
168 | https://www.gonjer.com
169 | #>
170 | [cmdletbinding()]
171 | param (
172 | # Specify a fully qualified file path for the .CRT file.
173 | [Parameter(
174 | Mandatory = $True,
175 | ValueFromPipelineByPropertyName = $True,
176 | HelpMessage = 'C:\Windows\system32\certsrv\CertEnroll\Contoso-Subordinate-CA.crt'
177 | )]
178 | [ValidateNotNullOrEmpty()]
179 | [ValidatePattern('^.*\.crt$')]
180 | [ValidateScript({
181 | if (Test-Path -Path $_)
182 | {
183 | $True
184 | }
185 | else
186 | {
187 | throw "The path '$_' is not available."
188 | }
189 | })]
190 | [Alias('CRT')]
191 | [IO.FileInfo]$CRTFile,
192 |
193 | # Specify a fully qualified file path for the script file that contains the mail information for PKI Maintenance.
194 | [Parameter(
195 | Mandatory = $True,
196 | ValueFromPipelineByPropertyName = $True,
197 | HelpMessage = 'C:\PKI\PKI-MaintenanceJob.ps1'
198 | )]
199 | [ValidateNotNullOrEmpty()]
200 | [ValidatePattern('^.*\.ps1$')]
201 | [IO.FileInfo]$MaintenanceScriptFile,
202 |
203 | # A Company name used to populate the email template with correct information.
204 | [Parameter(
205 | Mandatory = $True,
206 | ValueFromPipelineByPropertyName = $True,
207 | HelpMessage = 'Contoso'
208 | )]
209 | [ValidateNotNullOrEmpty()]
210 | [string]$Company,
211 |
212 | # A valid SMTP Server used to send information messages about PKI maintenance.
213 | [Parameter(
214 | Mandatory = $True,
215 | ValueFromPipelineByPropertyName = $True,
216 | HelpMessage = 'Enter a valid SMTPServer. Example: smtp.contoso.net'
217 | )]
218 | [ValidateNotNullOrEmpty()]
219 | [string]$SMTPServer,
220 |
221 | # One or more valid TO mailaddresses used to send information messages about PKI maintenance.
222 | # Example: noreply@contoso.com
223 | [Parameter(
224 | Mandatory = $True,
225 | ValueFromPipelineByPropertyName = $True,
226 | HelpMessage = 'One or more valid TO mailaddresses. Example: recipient@contoso.com'
227 | )]
228 | [ValidateNotNullOrEmpty()]
229 | [mailaddress[]]$ToAddress,
230 |
231 | # A valid FROM mailaddress used to send information messages about PKI maintenance.
232 | # Example: noreply@contoso.com
233 | [Parameter(
234 | Mandatory = $True,
235 | ValueFromPipelineByPropertyName = $True,
236 | HelpMessage = 'Enter a valid mailaddress. Example: noreply@contoso.com'
237 | )]
238 | [ValidateNotNullOrEmpty()]
239 | [mailaddress]$FromAddress
240 |
241 | )
242 | begin
243 | {
244 | $body = @"
245 | "
It's time for PKI maintenance!
246 |
247 |
It was three months ago since the last PKI maintenance for $($Company).
248 |
249 |
Follow the to-do list below to keep the PKI structure/environment healthy and up to date.
250 | The to-do list is just a recomendation, not a forced task list.
251 |
252 |
253 |
254 |
Remove unused "Issued Certificate Templates".
255 | Review the the Issued Certificate Cemplates that are published.
256 | Issued Certificate Templates that are not relevant or valid shall be removed from publication.
257 |
258 | How do you know if a certificate template is not valid? Check the following three items:
259 | * Are the any Issued Certificates that use Certificate Template (see Issued Certificates).
260 | * What AD objects or groups can request certificates, are these AD objects available in Active Directory or does the template contain broken SIDs (see Manage Certificate Templates).
261 | * Is the name of the certificate template using the correct naming standard (standard refers to the internal company naming standard or company name followed by underscore, no spaces anywhere).
262 |
263 |
Remove/Revoke stale/unsused "Issued Certificates".
264 | Review the the certificates are issued and revoke expired certificates.
265 | Sort the list on the "Certificate Expiration Date" (see Issued Certificates).
266 | Select the certificates that has expired. Right-click on of the selected certificates, select "All Tasks", then select "Revoke Certificate".
267 |
268 | In the "Reason Code" option we choose "Cease of Operation". The date and time that is the default is today's date, which is correct.
269 |
270 |
Fix ACL Access rights for "Certificate Templates".
271 | There should be no "single AD Objects" as ACL access rights on a certificate template.
272 | It's recomended to use an associated AD group to link together the enroll privileges. The reason for this is that it makes you manage who can enroll certificates more easily and you can utilize "Delegated Administration" in Active Directory.
273 |
274 |
Verifiy the max Validity Period on Certificate Templates.
275 | The Certificate Templates validity period shall not exced the CA-certificate chain expiration date.
276 | So if the root CA certificate expires 2024-02-22 and Subordinate CA certificate expires 2019-01-26 and the date today is 2017-01-11 the max validity period of issued certificates does no exced the CA-certificate chain expiration date.
277 | If the date today is 2017-03-17 and Suborindate CA certificate expires 2019-01-26 the max validity period of two years exceeds the Subordinate CA-Certificate.
278 | The recommended maximum length of certificate templates is 2 years. In exceptional cases, it may be possible to use three years.
279 | If the validity period exceds the CA-certificate chain the recommended action is to make a plan/change request to renew the CA-certificate.
280 |
281 |
Verify PKI Backup.
282 | Verification of the backuped up files need tobe made. Make a restore test from the file backup.
283 | Good way to verify the files is to go throughthe registry file andCAPolicy.infis to open and verify the content inte notepad. CA certificate MMC and verify that it is intact.
284 | If the system canread the filesand the manual verify looks OK, the backup is intact.
285 | There is no need to run a CA-restore.
286 |
287 |