1+ <#
2+ . SYNOPSIS
3+ Requests a certificate from a Windows CA
4+
5+ . DESCRIPTION
6+ Requests a certificates with the specified subject name from am Windows CA and saves the resulting certificate with the private key in the local computer store.
7+
8+ You must specify at least the CN for the subject name.
9+
10+ With the SAN parameter you can also specify values for subject alternative name to request a SAN certificate.
11+ The CA must support this type of certificate otherwise the request will fail.
12+
13+ With the Export parameter it's also possible to export the requested certificate (with private key) directly to a .pfx file instead of storing it in the local computer store.
14+
15+ You can also use the Import-CSV cmdlet with Request-Certificate.ps1 to request multiple certificates.
16+ To do this, use the Import-CSV cmdlet to create custom objects from a comma-separated value (CSV) file that contains a list of object properties (such as CN, SAN etc. ). Then pass these objects through the pipeline to Request-Certificate.ps1 to request the certificates.
17+
18+ . PARAMETER CN
19+ Specifies the common name for the subject of the certificate(s).
20+ Mostly its the FQDN of a website or service.
21+ e.g. test.jofe.ch
22+
23+ . PARAMETER SAN
24+ Specifies a comma separated list of subject alternate names (FQDNs) for the certificate
25+ The syntax is {tag}={value}.
26+ Valid tags are: email, upn, dns, guid, url, ipaddress, oid
27+ e.g. dns=test.jofe.ch,[email protected] 28+
29+ . PARAMETER TemplateName
30+ Specifies the name for the temple of the CA to issue the certificate(s).
31+ The default value is "WebServer".
32+
33+ . PARAMETER CAName
34+ Specifies the name of the CA to send the request to in the format FQDN\CAName
35+ If the CAName is not specified the user becomes a prompt to choose a enterprise CA from the local Active Directory.
36+
37+ . PARAMETER Export
38+ Exports the certificate and private key to a pfx file instead of installing it in the local computer store.
39+ By default the certificate will be installed in the local computer store.
40+
41+ . PARAMETER ExportPath
42+ Path to wich the pfx file should be saved when -Export is specified.
43+
44+ . PARAMETER Country
45+ Specifies two letter for the optional country value in the subject of the certificate(s).
46+ e.g. CH
47+
48+ . PARAMETER State
49+ Specifies the optional state value in the subject of the certificate(s).
50+ e.g. Berne
51+
52+ . PARAMETER City
53+ Specifies the optional city value in the subject of the certificate(s).
54+ e.g. Berne
55+
56+ . PARAMETER Organisation
57+ Specifies the optional organisation value in the subject of the certificate(s).
58+ e.g. jofe.ch
59+
60+ . PARAMETER Department
61+ Specifies the optional department value in the subject of the certificate(s).
62+ e.g. IT
63+
64+ . INPUTS
65+ System.String
66+ Common name for the subject, SAN , Country, State etc. of the certificate(s) as a string
67+
68+ . OUTPUTS
69+ None. Request-Certificate.ps1 does not generate any output.
70+
71+ . EXAMPLE
72+ C:\PS> .\Request-Certificate.ps1
73+
74+ Description
75+ -----------
76+ This command requests a certificate form the enterprise CA in the local Active Directory.
77+ The user will be asked for the value for the CN of the certificate.
78+
79+ . EXAMPLE
80+ C:\PS> .\Request-Certificate.ps1 -CAName "testsrv.test.ch\Test CA"
81+
82+ Description
83+ -----------
84+ This command requests a certificate form the CA testsrv.test.ch\Test CA.
85+ The user will be asked for the value for the CN of the certificate.
86+
87+ . EXAMPLE
88+ C:\PS> .\Request-Certificate.ps1 -CN "webserver.test.ch" -CAName "testsrv.test.ch\Test CA" -TemplateName "Webservercert"
89+
90+ Description
91+ -----------
92+ This command requests a certificate form the CA testsrv.test.ch\Test CA with the certificate template "Webservercert"
93+ and a CN of webserver.test.ch
94+ The user will be asked for the value for the SAN of the certificate.
95+
96+
97+ . EXAMPLE
98+ Get-Content .\certs.txt | .\Request-Certificate.ps1 -Export
99+
100+ Description
101+ -----------
102+ Gets common names from the file certs.txt and request for each a certificate.
103+ Each certificate will then be saved withe the private key in a .pfx file.
104+
105+ . EXAMPLE
106+ C:\PS> .\Request-Certificate.ps1 -CN "webserver.test.ch" -SAN "DNS=webserver.test.ch,DNS=srvweb.test.local"
107+
108+ Description
109+ -----------
110+ This command requests a certificate with a CN of webserver.test.ch and subject alternative names (SANs)
111+ The SANs of the certificate are the DNS names webserver.test.ch and srvweb.test.local.
112+
113+ . EXAMPLE
114+ C:\PS> Import-Csv .\sancertificates.csv -UseCulture | .\Request-Certificate.ps1 -verbose -Export -CAName "testsrv.test.ch\Test CA"
115+
116+ Description
117+ -----------
118+ This example requests multiple SAN certificates from the "Test CA" CA running on the server "testsrv.test.ch".
119+ The first command creates custom objects from a comma-separated value (CSV) file thats contains a list of object properties. The objects are then passed through the pipeline to Request-Certificate.ps1 to request the certificates form the "J0F3's Issuing CA" CA.
120+ Each certificate will then be saved with the private key in a .pfx file.
121+
122+ The CSV file look something like this:
123+ CN;SAN
124+ test1.test.ch;DNS=test1san1.test.ch,DNS=test1san2.test.ch
125+ test2.test.ch;DNS=test2san1.test.ch,DNS=test2san2.test.ch
126+ test3.test.ch;DNS=test3san1.test.ch,DNS=test3san2.test.ch
127+
128+ . NOTES
129+ Version : 1.3, 10/20/2018
130+ Changes : Improvements in temp file handling, Additional parameter to specify the export path for pfx file, Requesting SAN certs with Extensions instead of Attributes
131+ File Name : Request-Certificate.ps1
132+ Requires : PowerShell V2 or higher
133+
134+ . LINK
135+ © Jonas Feller c/o J0F3, May 2011 / October 2018
136+ www.jfe.cloud
137+
138+ #>
139+
140+ [CmdletBinding ()]
141+ Param (
142+ [Parameter (Mandatory = $True , ValueFromPipeline = $True , ValueFromPipelineByPropertyName = $True )]
143+ [string ]$CN ,
144+ [Parameter (Mandatory = $False , ValueFromPipelineByPropertyName = $True )]
145+ [string []]$SAN ,
146+ [Parameter (Mandatory = $False , ValueFromPipelineByPropertyName = $True )]
147+ [String ]$TemplateName = " WebServer" ,
148+ [Parameter (Mandatory = $False , ValueFromPipelineByPropertyName = $True )]
149+ [string ]$CAName ,
150+ [Parameter (Mandatory = $False , ValueFromPipelineByPropertyName = $True )]
151+ [switch ]$Export ,
152+ [Parameter (Mandatory = $False , ValueFromPipelineByPropertyName = $True )]
153+ [ValidateScript ( {Resolve-Path - Path $_ })]
154+ [string ]$ExportPath ,
155+ [Parameter (Mandatory = $False , ValueFromPipelineByPropertyName = $True )]
156+ [string ]$Country ,
157+ [Parameter (Mandatory = $False , ValueFromPipelineByPropertyName = $True )]
158+ [string ]$State ,
159+ [Parameter (Mandatory = $False , ValueFromPipelineByPropertyName = $True )]
160+ [string ]$City ,
161+ [Parameter (Mandatory = $False , ValueFromPipelineByPropertyName = $True )]
162+ [string ]$Organisation ,
163+ [Parameter (Mandatory = $False , ValueFromPipelineByPropertyName = $True )]
164+ [string ]$Department
165+ )
166+ BEGIN {
167+ # internal function to do some cleanup
168+ function Remove-ReqTempfiles () {
169+ param (
170+ [String []]$tempfiles
171+ )
172+ Write-Verbose " Cleanup temp files..."
173+ Remove-Item - Path $tempfiles - Force - ErrorAction SilentlyContinue
174+ }
175+
176+ function Remove-ReqFromStore {
177+ param (
178+ [String ]$CN
179+ )
180+ Write-Verbose " Remove pending certificate request form cert store..."
181+
182+ # delete pending request (if a request exists for the CN)
183+ $certstore = new-object system.security.cryptography.x509certificates.x509Store(' REQUEST' , ' LocalMachine' )
184+ $certstore.Open (' ReadWrite' )
185+ foreach ($certreq in $ ($certstore.Certificates )) {
186+ if ($certreq.Subject -eq " CN=$CN " ) {
187+ $certstore.Remove ($certreq )
188+ }
189+ }
190+ $certstore.close ()
191+ }
192+ }
193+
194+ PROCESS {
195+ # disable debug confirmation messages
196+ if ($PSBoundParameters [' Debug' ]) {$DebugPreference = " Continue" }
197+
198+ Write-Verbose " Generating request inf file"
199+ $file = @"
200+ [NewRequest]
201+ Subject = "CN=$CN ,c=$Country , s=$State , l=$City , o=$Organisation , ou=$Department "
202+ MachineKeySet = TRUE
203+ KeyLength = 2048
204+ KeySpec=1
205+ Exportable = TRUE
206+ RequestType = PKCS10
207+ [RequestAttributes]
208+ CertificateTemplate = "$TemplateName "
209+ "@
210+
211+ # check if SAN certificate is requested
212+ if ($PSBoundParameters.ContainsKey (' SAN' )) {
213+ # each SAN must be a array element
214+ # if the array has ony one element then split it on the commas.
215+ if (($SAN ).count -eq 1 ) {
216+ $SAN = @ ($SAN -split ' ,' )
217+
218+ Write-Host " Requesting SAN certificate with subject $CN and SAN: $ ( $SAN -join ' ,' ) " - ForegroundColor Green
219+ Write-Debug " Parameter values: CN = $CN , TemplateName = $TemplateName , CAName = $CAName , SAN = $ ( $SAN -join ' ' ) "
220+ }
221+
222+ Write-Verbose " A value for the SAN is specified. Requesting a SAN certificate."
223+ Write-Debug " Add Extension for SAN to the inf file..."
224+ $file += @'
225+
226+ [Extensions]
227+ ; If your client operating system is Windows Server 2008, Windows Server 2008 R2, Windows Vista, or Windows 7
228+ ; SANs can be included in the Extensions section by using the following text format. Note 2.5.29.17 is the OID for a SAN extension.
229+
230+ 2.5.29.17 = "{text}"
231+
232+ '@
233+
234+ foreach ($an in $SAN ) {
235+ $file += " _continue_ = `" $ ( $an ) &`"`n "
236+ }
237+ }
238+ else {
239+ Write-Host " Requesting certificate with subject $CN " - ForegroundColor Green
240+ Write-Debug " Parameter values: CN = $CN , TemplateName = $TemplateName , CAName = $CAName "
241+ }
242+
243+ try {
244+ # create temp files
245+ $inf = [System.IO.Path ]::GetTempFileName()
246+ $req = [System.IO.Path ]::GetTempFileName()
247+ $cer = Join-Path - Path $env: TEMP - ChildPath " $CN .cer"
248+ $rsp = Join-Path - Path $env: TEMP - ChildPath " $CN .rsp"
249+
250+ Remove-ReqTempfiles - tempfiles $inf , $req , $cer , $rsp
251+ # create new request inf file
252+ Set-Content - Path $inf - Value $file
253+
254+ # show inf file if -verbose is used
255+ Get-Content - Path $inf | Write-Verbose
256+
257+ Write-Verbose " generate .req file with certreq.exe"
258+ Invoke-Expression - Command " certreq -new `" $inf `" `" $req `" "
259+ if (! ($LastExitCode -eq 0 )) {
260+ throw " certreq -new command failed"
261+ }
262+
263+ write-verbose " Sending certificate request to CA"
264+ Write-Debug " CAName = $CAName "
265+
266+ if ($PSBoundParameters.ContainsKey (' CAName' )) {
267+ Write-Debug " certreq -submit -config `" $CAName `" `" $req `" `" $cer `" "
268+ Invoke-Expression - Command " certreq -submit -config `" $CAName `" `" $req `" `" $cer `" "
269+ }
270+ else {
271+ Invoke-Expression - Command " certreq -submit `" $req `" `" $cer `" "
272+ }
273+
274+ if (! ($LastExitCode -eq 0 )) {
275+ throw " certreq -submit command failed"
276+ }
277+ Write-Debug " request was successful. Result was saved to `" $cer `" "
278+
279+ write-verbose " retrieve and install the certificate"
280+ Invoke-Expression - Command " certreq -accept `" $cer `" "
281+
282+ if (! ($LastExitCode -eq 0 )) {
283+ throw " certreq -accept command failed"
284+ }
285+
286+ if (($LastExitCode -eq 0 ) -and ($? -eq $true )) {
287+ Write-Host " Certificate request successfully finished!" - ForegroundColor Green
288+
289+ }
290+ else {
291+ throw " Request failed with unknown error. Try with -verbose -debug parameter"
292+ }
293+
294+
295+ if ($export ) {
296+ Write-Debug " export parameter is set. => export certificate"
297+ Write-Verbose " exporting certificate and private key"
298+ $cert = Get-Childitem " cert:\LocalMachine\My" | where-object {$_.Thumbprint -eq (New-Object System.Security.Cryptography.X509Certificates.X509Certificate2((Get-Item $cer ).FullName, " " )).Thumbprint}
299+ Write-Debug " Certificate found in computerstore: $cert "
300+
301+ # create a pfx export as a byte array
302+ $certbytes = $cert.export ([System.Security.Cryptography.X509Certificates.X509ContentType ]::pfx)
303+
304+ # write pfx file
305+ if ($PSBoundParameters.ContainsKey (' ExportPath' )) {
306+ $pfxPath = Join-Path - Path (Resolve-Path - Path $ExportPath ) - ChildPath " $CN .pfx"
307+ }
308+ else {
309+ $pfxPath = " .\$CN .pfx"
310+ }
311+ $certbytes | Set-Content - Encoding Byte - Path $pfxPath - ea Stop
312+ Write-Host " Certificate successfully exported to `" $pfxPath `" !" - ForegroundColor Green
313+
314+ Write-Verbose " deleting exported certificate from computer store"
315+ # delete certificate from computer store
316+ $certstore = new-object system.security.cryptography.x509certificates.x509Store(' My' , ' LocalMachine' )
317+ $certstore.Open (' ReadWrite' )
318+ $certstore.Remove ($cert )
319+ $certstore.close ()
320+
321+ }
322+ else {
323+ Write-Debug " export parameter is not set. => script finished"
324+ Write-Host " The certificate with the subject $CN is now installed in the computer store !" - ForegroundColor Green
325+ }
326+ }
327+ catch {
328+ # show error message (non terminating error so that the rest of the pipeline input get processed)
329+ Write-Error $_
330+ }
331+ finally {
332+ # tempfiles and request cleanup
333+ Remove-ReqTempfiles - tempfiles $inf , $req , $cer , $rsp
334+ Remove-ReqFromStore - CN $CN
335+ }
336+ }
337+
338+ END {
339+ Remove-ReqTempfiles - tempfiles $inf , $req , $cer , $rsp
340+ }
0 commit comments