Skip to content

Commit

Permalink
Fixed some issues around New-DomainSignedCertificate
Browse files Browse the repository at this point in the history
  • Loading branch information
claudiospizzi committed Sep 5, 2019
1 parent ce49ef9 commit ccc5f46
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 79 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is mainly based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## 2.6.1 - 2019-09-05

* Fixed: Use Base64 only optionally in New-DomainSignedCertificate
* Fixed: Export New-DomainSignedCertificate in psd1

## 2.6.0 - 2019-08-31

* Added: Add cmdlet New-DomainSignedCertificate
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
<#
.SYNOPSIS
Create a new certificate signed by an domain-based enterprise ca.
Create a new certificate signed by a domain-based enterprise CA.
.DESCRIPTION
Use the tools certreq.exe and openssl.exe to request a domain signed
certificate and export it as Windows (.cer, .pfx) and Linux (.pem, .key)
formatted certificate.
Use the tools certreq.exe and optionally openssl.exe to request a domain
signed certificate. The certificate is always a SAN certificate where
wildcard entries and e.g. IP adresses are allowed. The subject is
inserted at the first position of the SAN list.
The certificate will be exported in Windows compatible binary formats as
X.509 DER (.cer) and PKCS #12 (.pfx). If the flag -Base64 is specified,
the certifcate is converted into Linux/Unix compatible Base64 PEM format
as X.509 PEM (.pem) and RSA (.key).
.EXAMPLE
PS C:\> New-DomainSignedCertificate
Create a new certificate. PowerShell will prompt for subject, dns name
and password.
.EXAMPLE
PS C:\> New-DomainSignedCertificate -Subject 'contoso.com' -DnsName 'contoso.com', '*.contoso.com'
Create a new wildcard SAN certificate for contoso.
.EXAMPLE
PS C:\> New-DomainSignedCertificate -Subject 'contoso.com' -DnsName 'contoso.com', '*.contoso.com' -Base64
Create a new wildcard SAN certificate for contoso and include Linux/Unix
formatted certificate and key files.
.NOTES
Author : Claudio Spizzi
License : MIT License
Expand All @@ -25,7 +39,8 @@ function New-DomainSignedCertificate
param
(
# Subject of the certificate, without the 'CN=' prefix. This subject is
# always included as dns name in the subject alternative name.
# always included as dns name or IP address in the subject alternative
# name.
[Parameter(Mandatory = $true, Position = 0)]
[System.String]
$Subject,
Expand All @@ -36,7 +51,7 @@ function New-DomainSignedCertificate
[System.String[]]
$DnsName,

# Add ip addresses to the subject alternative name.
# Add IP addresses to the subject alternative name.
[Parameter(Mandatory = $false, Position = 2)]
[AllowEmptyCollection()]
[System.String[]]
Expand All @@ -45,7 +60,7 @@ function New-DomainSignedCertificate
# Specify the key usage extensions.
[Parameter(Mandatory = $false)]
[ValidateSet('ServerAuthentication', 'ClientAuthentication')]
[System.String]
[System.String[]]
$EnhancedKeyUsage = 'ServerAuthentication',

# Length of the private key, by default 2048.
Expand All @@ -63,43 +78,42 @@ function New-DomainSignedCertificate
[System.Security.SecureString]
$Password,

# Overwrite the existing files.
# Option to export the certificate as Base64. Requires openssl.exe.
[Parameter(Mandatory = $false)]
[switch]
$Force,
$Base64,

# Path to the working folder where the files will be stored.
[Parameter(Mandatory = $false)]
[System.String]
$Path = (Get-Location).Path,

# Keep all extra files like the policy definition or request file.
# Overwrite the existing files.
[Parameter(Mandatory = $false)]
[switch]
$Keep
$Force
)

$ErrorActionPreference = 'Stop'

try
{
Write-Progress -Activity "Generate Certificate for CN=$Subject" -Status 'Setup' -PercentComplete 0
$activity = "Generate Certificate for CN=$Subject"
Write-Progress -Activity $activity -Status 'Setup' -PercentComplete 0

# Policy template definitions
$enhancedKeyUsageOidMap = @{
'ServerAuthentication' = '1.3.6.1.5.5.7.3.1'
'ClientAuthentication' = '1.3.6.1.5.5.7.3.2'
}

# Check if the current user is admin
# Check if the current user is admin. This is required to request the
# certifcate and create the private key.
if (-not (Test-AdministratorRole))
{
throw 'Access denied. Restart this command as administrator.'
}

# Check and get native commands
$certReqCmd = Get-CommandPath -Name 'certreq.exe'
$openSslCmd = Get-CommandPath -Name 'openssl.exe' -WarningMessage 'Download OpenSSL for Windows from https://slproweb.com/products/Win32OpenSSL.html'
$certReqCmd = Get-CommandPath -Name 'certreq.exe'
if ($Base64.IsPresent)
{
$openSslCmd = Get-CommandPath -Name 'openssl.exe' -WarningMessage 'Download OpenSSL for Windows from https://slproweb.com/products/Win32OpenSSL.html'
}

# Append subject to the dns name or ip address
if ($Subject -match '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$')
Expand Down Expand Up @@ -135,7 +149,11 @@ function New-DomainSignedCertificate
$policy += '[EnhancedKeyUsageExtension]'
foreach ($currentEnhancedKeyUsage in $EnhancedKeyUsage)
{
$policy += 'OID = {0}' -f $enhancedKeyUsageOidMap[$currentEnhancedKeyUsage]
switch ($currentEnhancedKeyUsage)
{
'ServerAuthentication' { $policy += 'OID = 1.3.6.1.5.5.7.3.1' }
'ClientAuthentication' { $policy += 'OID = 1.3.6.1.5.5.7.3.2' }
}
}
$policy += ''
$policy += '[Extensions]'
Expand All @@ -152,14 +170,19 @@ function New-DomainSignedCertificate
$policy += '[RequestAttributes]'
$policy += 'CertificateTemplate = {0}' -f $CertificateTemplate


if ($PSCmdlet.ShouldProcess("CN=$Subject", 'Crate'))
if ($PSCmdlet.ShouldProcess("CN=$Subject", 'Create'))
{
# Trim the path and cleanup existing files
# Trim the path and cleanup existing files if the -Force parameter
# is specified. Else stop the script with an exception.
$Path = $Path.TrimEnd('\')
foreach ($extension in 'cer', 'inf', 'key', 'pem', 'pfx', 'req', 'rsp')
$extensions = 'inf', 'req', 'rsp', 'cer', 'pfx'
if ($Base64.IsPresent)
{
$extensions += 'pem', 'key'
}
foreach ($extension in $extensions)
{
$filePath ="$Path\$Subject.$extension"
$filePath = "$Path\$Subject.$extension"
if (Test-Path -Path $filePath)
{
if ($Force.IsPresent)
Expand All @@ -179,18 +202,19 @@ function New-DomainSignedCertificate

Write-Verbose "Create policy file $Subject.inf"

Write-Progress -Activity "Generate Certificate for CN=$Subject" -Status "Create policy file $Subject.inf" -PercentComplete 14
Write-Progress -Activity $activity -Status "Create policy file $Subject.inf" -PercentComplete 14

Set-Content -Path "$Path\$Subject.inf" -Value $policy


# Step 2
# Create a certificate request and store the private key in the user session
# Create a certificate request and store the private key in the
# current user session

Write-Verbose "Create request file $Subject.req"
Write-Verbose " certreq.exe -new -q -f `"$Path\$Subject.inf`" `"$Path\$Subject.req`""
Write-Verbose "> certreq.exe -new -q -f `"$Path\$Subject.inf`" `"$Path\$Subject.req`""

Write-Progress -Activity "Generate Certificate for CN=$Subject" -Status "Create request file $Subject.req" -PercentComplete 28
Write-Progress -Activity $activity -Status "Create request file $Subject.req" -PercentComplete 28

$result = (& $certReqCmd -new -q -f "`"$Path\$Subject.inf`"" "`"$Path\$Subject.req`"")

Expand All @@ -204,9 +228,9 @@ function New-DomainSignedCertificate
# Submit the certificate request to the CA

Write-Verbose "Sign request and export to $Subject.cer"
Write-Verbose " certreq.exe -submit -q -f `"$Path\$Subject.req`" `"$Path\$Subject.cer`""
Write-Verbose "> certreq.exe -submit -q -f `"$Path\$Subject.req`" `"$Path\$Subject.cer`""

Write-Progress -Activity "Generate Certificate for CN=$Subject" -Status "Sign request and export to $Subject.cer" -PercentComplete 28
Write-Progress -Activity $activity -Status "Sign request and export to $Subject.cer" -PercentComplete 28

$result = (& $certReqCmd -submit -q -f "`"$Path\$Subject.req`"" "`"$Path\$Subject.cer`"")

Expand All @@ -220,9 +244,9 @@ function New-DomainSignedCertificate
# Accept the request and import it into the local cert store

Write-Verbose "Accept and import signed certificate $Subject.cer"
Write-Verbose " certreq.exe -accept -q `"$Path\$Subject.cer`""
Write-Verbose "> certreq.exe -accept -q `"$Path\$Subject.cer`""

Write-Progress -Activity "Generate Certificate for CN=$Subject" -Status "Accept and import signed certificate $Subject.cer" -PercentComplete 42
Write-Progress -Activity $activity -Status "Accept and import signed certificate $Subject.cer" -PercentComplete 42

$result = (& $certReqCmd -accept -q "`"$Path\$Subject.cer`"")

Expand All @@ -233,11 +257,12 @@ function New-DomainSignedCertificate


# Step 5
# Export certificate as PFX file
# Export certificate as PKCS #12 (.pfx) file

Write-Verbose "Export the certificate as PFX to $Subject.pfx"
Write-Verbose "Export the certificate as PKCS #12 to $Subject.pfx"
Write-Verbose "> Export-PfxCertificate -FilePath '$Path\$Subject.pfx'"

Write-Progress -Activity "Generate Certificate for CN=$Subject" -Status "Export the certificate as PFX to $Subject.pfx" -PercentComplete 56
Write-Progress -Activity $activity -Status "Export the certificate as PKCS #12 to $Subject.pfx" -PercentComplete 56

# Extract thumbprint from the cer file
$thumbprint = Get-PfxCertificate -FilePath "$Path\$Subject.cer" | Select-Object -ExpandProperty 'Thumbprint'
Expand All @@ -247,61 +272,58 @@ function New-DomainSignedCertificate


# Step 6
# Export certificate as PEM file
# Export certificate as X.509 PEM (.pem) file

Write-Verbose "Export the certificate as PEM to $Subject.pem"
Write-Verbose " openssl.exe pkcs12 -passin pass:`"***`" -in `"$Path\$Subject.pfx`" -clcerts -nokeys -out `"$Path\$Subject.pem`""
Write-Verbose " openssl.exe x509 -in `"$Path\$Subject.pem`" -out `"$Path\$Subject.pem`""
if ($Base64.IsPresent)
{
Write-Verbose "Export the certificate as X.509 PEM to $Subject.pem"
Write-Verbose "> openssl.exe pkcs12 -passin pass:`"***`" -in `"$Path\$Subject.pfx`" -clcerts -nokeys -out `"$Path\$Subject.pem`""
Write-Verbose "> openssl.exe x509 -in `"$Path\$Subject.pem`" -out `"$Path\$Subject.pem`""

Write-Progress -Activity "Generate Certificate for CN=$Subject" -Status "Export the certificate as PEM to $Subject.pem" -PercentComplete 70
Write-Progress -Activity $activity -Status "Export the certificate as X.509 PEM to $Subject.pem" -PercentComplete 70

$result = (& $openSslCmd pkcs12 -passin "pass:`"$(Unprotect-SecureString -SecureString $Password)`"" -in "`"$Path\$Subject.pfx`"" -clcerts -nokeys -out "`"$Path\$Subject.pem`"")
$result = (& $openSslCmd pkcs12 -passin "pass:`"$(Unprotect-SecureString -SecureString $Password)`"" -in "`"$Path\$Subject.pfx`"" -clcerts -nokeys -out "`"$Path\$Subject.pem`"")

if ($Global:LASTEXITCODE -ne 0)
{
throw "Failed to convert the certificate from pfx to pem!"
}
if ($Global:LASTEXITCODE -ne 0)
{
throw "Failed to convert the certificate from pfx to pem!"
}

$result = (& $openSslCmd x509 -in "`"$Path\$Subject.pem`"" -out "`"$Path\$Subject.pem`"")
$result = (& $openSslCmd x509 -in "`"$Path\$Subject.pem`"" -out "`"$Path\$Subject.pem`"")

if ($Global:LASTEXITCODE -ne 0)
{
throw "Failed to convert the certificate from pfx to pem!"
if ($Global:LASTEXITCODE -ne 0)
{
throw "Failed to convert the certificate from pfx to pem!"
}
}


# Step 7
# Export certificate as KEY file

Write-Verbose "Export the certificate as KEY to $Subject.key"
Write-Verbose " openssl.exe pkcs12 -passin pass:`"***`" -in `"$Path\$Subject.pfx`" -nocerts -out `"$Path\$Subject.key`" -nodes"
Write-Verbose " openssl.exe rsa -in `"$Path\$Subject.key`" -out `"$Path\$Subject.key`""

Write-Progress -Activity "Generate Certificate for CN=$Subject" -Status "Export the certificate as KEY to $Subject.key" -PercentComplete 84
# Export certificate as RSA (.key) file

$result = (& $openSslCmd pkcs12 -passin "pass:`"$(Unprotect-SecureString -SecureString $Password)`"" -in "`"$Path\$Subject.pfx`"" -nocerts -out "`"$Path\$Subject.key`"" -nodes)

if ($Global:LASTEXITCODE -ne 0)
if ($Base64.IsPresent)
{
throw "Failed to convert the certificate from pfx to key!"
}
Write-Verbose "Export the certificate as RSA to $Subject.key"
Write-Verbose "> openssl.exe pkcs12 -passin pass:`"***`" -in `"$Path\$Subject.pfx`" -nocerts -out `"$Path\$Subject.key`" -nodes"
Write-Verbose "> openssl.exe rsa -in `"$Path\$Subject.key`" -out `"$Path\$Subject.key`""

$ErrorActionPreference = 'SilentlyContinue'
$result = (& $openSslCmd rsa -in "`"$Path\$Subject.key`"" -out "`"$Path\$Subject.key`"" 2>&1)
$ErrorActionPreference = 'Stop'
Write-Progress -Activity $activity -Status "Export the certificate as RSA to $Subject.key" -PercentComplete 84

if ($Global:LASTEXITCODE -ne 0)
{
throw "Failed to convert the certificate from pfx to key!"
}
$result = (& $openSslCmd pkcs12 -passin "pass:`"$(Unprotect-SecureString -SecureString $Password)`"" -in "`"$Path\$Subject.pfx`"" -nocerts -out "`"$Path\$Subject.key`"" -nodes)

if (-not $Keep.IsPresent)
{
Get-Item -Path "Cert:\LocalMachine\My\$thumbprint"
if ($Global:LASTEXITCODE -ne 0)
{
throw "Failed to convert the certificate from pfx to key!"
}

$ErrorActionPreference = 'SilentlyContinue'
$result = (& $openSslCmd rsa -in "`"$Path\$Subject.key`"" -out "`"$Path\$Subject.key`"" 2>&1)
$ErrorActionPreference = 'Stop'

Remove-Item -Path "$Path\$Subject.inf" -Force -Confirm:$false
Remove-Item -Path "$Path\$Subject.req" -Force -Confirm:$false
Remove-Item -Path "$Path\$Subject.rsp" -Force -Confirm:$false
if ($Global:LASTEXITCODE -ne 0)
{
throw "Failed to convert the certificate from pfx to key!"
}
}
}
}
Expand All @@ -311,6 +333,6 @@ function New-DomainSignedCertificate
}
finally
{
Write-Progress -Activity "Generate Certificate for CN=$Subject" -PercentComplete 100 -Completed
Write-Progress -Activity $activity -PercentComplete 100 -Completed
}
}
4 changes: 3 additions & 1 deletion Modules/SecurityFever/SecurityFever.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
RootModule = 'SecurityFever.psm1'

# Version number of this module.
ModuleVersion = '2.6.0'
ModuleVersion = '2.6.1'

# Supported PSEditions
# CompatiblePSEditions = @()
Expand Down Expand Up @@ -70,6 +70,8 @@
# Audit Policy
'Get-SecurityAuditPolicy'
'Get-SecurityAuditPolicySetting'
# Certificate
'New-DomainSignedCertificate'
# Common
'Get-TimeBasedOneTimePassword'
'Invoke-Elevated'
Expand Down

0 comments on commit ccc5f46

Please sign in to comment.