Updated 2017-06-08: Fixed some bugs (d'oh!) and added option to export certificate to .pfx
Assuming you have Microsoft PKI in place in your organization, then requesting webserver certificates is easy. But when you suddenly got 20 or 2000 new certificates that you need, clicking through the Microsoft Active Directory Certificate Services dialogues and submitting requests, manually issuing pending requests (you should not automatically issue a certificate with any subject name, only after approval - hey, it's security!) and copying the certificates back to you computer - starts to feel like too much work.
I stumbled upon a nice PowerShell script which after some modifications I turned into a custom function "New-MyCompanyCertificate". To the function you can pass hostname, SANs, IPAddresses, Emails and other values as parameters and it will generate the certificate request, submit it, issue pending certificate request and retrieve the newly generated certificate for you. With this updated version (June 8th, 2017) you can export the certificate to .pfx file. Nice, huh?
I hope you'll find this helpful, please ping me at Twitter @arisaastamoinen
Cheers, Ari
function New-MyCompanyCertificate{
<#
.Synopsis
Create new Certificate
.DESCRIPTION
Create new MyCompany CA issued certificate
.EXAMPLE
New-MyCompanyCertificate -Hostname my-dmz-host -ExportPFX -Verbose
This will generate new .cer and .pfx certificate files to be copied and imported to host "my-dmz-host"
.EXAMPLE
New-MyCompanyCertificate -Hostname MyHost -FriendlyName "My Super Host" -SubjectAlternateName "host1","host2" -OrganizationalUnit "Dev"
Creates a new certificate for MyHost with a frienldy name My Super Host. Certificate has two alternate names, issuing organization unit is Dev.
.EXAMPLE
New-MyCompanyCertificate -Hostname mhheltps1 -FriendlyName "MyCompany Super Server" -SubjectAlternateName "aaa1.local","aaa1.dmz","aaa1.dmz.local" -Email admin@MyCompany.com -IPAddress 172.20.0.34 -ExportPFX -Verbose
Create new certificate for My Super Server with three alternate names
#>
[CmdletBinding()]
param(
[Parameter(ParameterSetName='Default',
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string]$Hostname,
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string]$FriendlyName=$Hostname,
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string[]]$SubjectAlternateName,
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string[]]$Email,
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string[]]$IPAddress,
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string]$Organization='MyCompany Co.',
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string]$OrganizationalUnit='IT',
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string]$Locality='City',
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string]$State='Province',
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string]$Country='FI', # hell yeah!
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[ValidateSet("mycompanyca01.local\MyCompany CA 01", "mycompanyca02.local\MyCompany CA 02", IgnoreCase=$true)]
[string]$CertificateAuthority='mycompanyca01.local\MyCompany CA 01',
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[int]$KeyLength = 2048,
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[ValidateSet("MyCompanyWebServer", IgnoreCase=$true)]
[string]$CertificateTemplate = "MyCompanyWebServer",
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[switch]$ExportPFX=$true,
[Parameter(ParameterSetName='Default',
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string]$PFXPassword='Passw0rd!'
)
[string]$workDir = $env:Temp
[string]$outDir = 'C:\Temp'
[string]$fileBaseName = $Hostname -replace "\.", "_"
$fileBaseName = $fileBaseName -replace "\*", ""
[string]$infFile = $workDir + "\" + $fileBaseName + ".inf"
[string]$requestFile = $workDir + "\" + $fileBaseName + ".req"
[string]$CertFileOut = $workDir + "\" + $fileBaseName + ".cer"
[string]$PFXFileOut = $workDir + "\" + $fileBaseName + ".pfx"
[string]$certSubject = "CN=$Hostname, OU=$OrganizationalUnit, O=$Organization, L=$Locality, S=$State, C=$Country"
Try {
Write-Verbose "Creating the certificate request information file ..."
$inf = @"
[Version]
Signature="`$Windows NT`$"
[NewRequest]
Subject = "$certSubject"
KeySpec = 1
KeyLength = $Keylength
Exportable = TRUE
FriendlyName = "$FriendlyName"
MachineKeySet = TRUE
SMIME = False
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0
"@
if ($SubjectAlternateName -ne $null -or $Email -ne $null -or $IPAddress -ne $null) {
$inf += @"
`n
[Extensions]
2.5.29.17 = "{text}"`n
"@
Write-Verbose "Include Hostname in SAN"
$inf += @"
_continue_ = "dns=$Hostname&"`n
"@
if ($SubjectAlternateName -ne $null) {
$arr = $SubjectAlternateName.Split(",")
foreach ($item in $arr) {
$inf += @"
_continue_ = "dns=$item&"`n
"@
}
} # if SubjectAlternateName
if ($Email -ne $null) {
$arr = $Email.Split(",")
foreach ($item in $arr) {
$inf += @"
_continue_ = "email=$item&"`n
"@
}
} # if Email
if ($IPAddress -ne $null) {
$arr = $IPAddress.Split(",")
foreach ($item in $arr) {
$inf += @"
_continue_ = "ipaddress=$item&"`n
"@
}
} # if IPAddress
}
write-verbose "Generating INF: $inf"
$inf | Set-Content -Path $infFile
Write-Verbose "Creating the certificate request ..."
$catchOutput = [string](& certreq.exe -new "$infFile" "$requestFile")
Write-Verbose $catchOutput
Write-Verbose "Submitting the certificate request to the certificate authority ..."
$catchOutput = [string](& certreq.exe -submit -config "$CertificateAuthority" -attrib "CertificateTemplate:$($CertificateTemplate)" "$requestFile" "$CertFileOut")
Write-Verbose $catchOutput
$findString = [string]$catchOutput
if ($findString -match "RequestId:(?<requestid>.*)RequestId:") {
$RequestId = [int]$matches['requestid']
Write-Verbose "Found RequestID ($RequestId) in reponse, trying to re-submit and recieve"
Get-ChildItem "$workDir\$fileBaseName.rsp" | remove-item
$catchOutput = [string](& certutil.exe -config $CertificateAuthority -resubmit $RequestId )
Write-Verbose [$catchOutput
$catchOutput = [string](& certreq.exe -config $CertificateAuthority -Retrieve $RequestId $CertFileOut)
Write-Verbose $catchOutput
write-Verbose "Recieved certificate:"
Get-Content $CertFileOut
Copy-Item $CertFileOut $outDir + "\" + $fileBaseName.cer
}
if ($ExportPFX) {
Write-Verbose "Exporting certificate to PFX file"
Write-Verbose "Importing certificate to Personal Store"
$catchOutput = [string](& certutil.exe -addstore -f MY $CertFileOut)
Write-Verbose $catchOutput
Write-Verbose "Repairing Personal Store"
$catchOutput = [string](& certutil.exe -repairstore MY $Hostname)
Write-Verbose $catchOutput
Write-Verbose "Exporting certificate to PFX file"
$catchOutput = [string](& certutil.exe -p $PFXPassword -exportPFX $Hostname $PFXFileOut)
Write-Verbose $catchOutput
Write-Verbose "Delete certificate from Personal Store"
$catchOutput = [string](& certutil.exe -privatekey -delstore MY $Hostname)
Write-Verbose $catchOutput
Copy-Item $PFXFileOut $outDir + "\" + $fileBaseName.pfx
}
}
Finally {
Get-ChildItem "$workDir\$fileBaseName.*" | remove-item
}
}
No comments:
Post a Comment