Skip to content

Commit 23b48e6

Browse files
committed
Add Request-Certificate.ps1
1 parent 021d4cd commit 23b48e6

File tree

2 files changed

+345
-2
lines changed

2 files changed

+345
-2
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
Personal PowerShell Script collection
33

44
## Content:
5-
* **StorageReplica/New-StrechedFileCluster.ps1:**
6-
Simple Script to build a virutal two node, streched, general purpose file server cluster with storage replica
5+
* **StorageReplica/New-StrechedFileCluster.ps1:**
6+
Simple Script to build a virtual two node, stretched, general purpose file server cluster with storage replica
77

8+
* **Request-Certificate.ps1**
9+
Request certificates from a Enterprise CA and export it optionally directly to a .pfx file.
10+
Head over to the [TechNet Script Center](https://gallery.technet.microsoft.com/scriptcenter/Request-certificates-from-b6a07151) for more information.

Request-Certificate.ps1

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
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

Comments
 (0)