Skip to content

Instantly share code, notes, and snippets.

@sevaa
Last active January 4, 2024 00:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sevaa/802a01649aa0746c959240146074c2b5 to your computer and use it in GitHub Desktop.
Save sevaa/802a01649aa0746c959240146074c2b5 to your computer and use it in GitHub Desktop.
RequestTLSCert.ps1
# Requires .NET 4.7.2+ or Core 2.0+
# Basic parameters. For TLS certs for IIS, this is sufficient
$Hostname = "foo.example.com" # Goes into Subject as the CN
$AltHostnames = @("foo.example.com", "bar.example.com", "baz.example.com") # These go into Subject Alternative Name
$DNPostfix = "OU=IT;O=Acme;L=Chicago;S=Illinois;C=US" # What follows the CN in the Subject field. Optional!
$FriendlyName = "foo" # Optional but highly recommended
$TermDays = 3650 # Expiration date for the temporary self-signed cert. The CA will probably override the exp date anyway.
$CSRPath = "c:\\MyCert.csr" # Your path WILL vary.
$SaveUnderLocalMachine = $false # False for current user, true for local machine.
# Create a RSA key pair
$KeyParams = New-Object System.Security.Cryptography.CngKeyCreationParameters
$KeyParams.ExportPolicy = [System.Security.Cryptography.CngExportPolicies]::AllowExport
if($SaveUnderLocalMachine)
{
$KeyParams.KeyCreationOptions += [System.Security.Cryptography.CngKeyCreationOptions]::MachineKey
}
[byte[]]$KeyLen = @(0, 8, 0, 0) # 2048 in little endian binary
$KeyProp = New-Object System.Security.Cryptography.CngProperty -ArgumentList "Length",$KeyLen,0
$KeyParams.Parameters.Add($KeyProp)
$KeyName = [Guid]::NewGuid().ToString()
$AlgName = [System.Security.Cryptography.CngAlgorithm]::Rsa
$KeyPair = [System.Security.Cryptography.CngKey]::Create($AlgName, $KeyName, $KeyParams)
$RSAKey = New-Object System.Security.Cryptography.RSACng -ArgumentList $KeyPair
# Create a CSR with that key pair
$DN = "CN=" + $Hostname
if($DNPostfix)
{
$DN += ";" + $DNPostfix
}
$DN = New-Object System.Security.Cryptography.X509Certificates.X500DistinguishedName -ArgumentList $DN
$HashAlg = [System.Security.Cryptography.HashAlgorithmName]::SHA256
$Padding = [System.Security.Cryptography.RSASignaturePadding]::Pkcs1
$Request = New-Object System.Security.Cryptography.X509Certificates.CertificateRequest -ArgumentList $DN,$RSAKey,$HashAlg,$Padding
# Populate the extensions, mimicking what the Microsoft tools do
$Exts = $Request.CertificateExtensions
# Key Usage
$Usage = [System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::KeyEncipherment
$Usage += [System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::DigitalSignature
$Usage += [System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::DataEncipherment
$Ext = New-Object System.Security.Cryptography.X509Certificates.X509KeyUsageExtension -ArgumentList $Usage,$true
$Exts.Add($Ext)
# Enhanced Key Usage - Server Auth, Client Auth
$Oids = New-Object System.Security.Cryptography.OidCollection
$Oid = New-Object System.Security.Cryptography.Oid -ArgumentList "1.3.6.1.5.5.7.3.1"
$Oids.Add($Oid) | Out-Null
$Oid = New-Object System.Security.Cryptography.Oid -ArgumentList "1.3.6.1.5.5.7.3.2"
$Oids.Add($Oid) | Out-Null
$Ext = New-Object System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension -ArgumentList $Oids,$false
$Exts.Add($Ext)
# Subject Alternative Name - assuming only "DNS Name" entries
$SANBuilder = New-Object System.Security.Cryptography.X509Certificates.SubjectAlternativeNameBuilder
foreach($AltHostname in $AltHostnames)
{
$SANBuilder.AddDnsName($AltHostname);
}
$Exts.Add($SANBuilder.Build())
# Subject Key Identifier
$PubKey = $Request.PublicKey
$Ext = New-Object System.Security.Cryptography.X509Certificates.X509SubjectKeyIdentifierExtension -ArgumentList $PubKey,$false
$Exts.Add($Ext)
# Application Policies - HARDCODED to "Server Auth, Client Auth"
[byte[]]$Policies = @(48, 24, 48, 10, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 48, 10, 6, 8, 43, 6, 1, 5, 5, 7, 3, 2)
$Ext = New-Object System.Security.Cryptography.X509Certificates.X509Extension -ArgumentList "1.3.6.1.4.1.311.21.10",$Policies,$false
$Exts.Add($Ext)
# Friendly Name
if($FriendlyName)
{
$Value = [System.Text.Encoding]::Unicode.GetBytes($FriendlyName + "`0")
$Ext = New-Object System.Security.Cryptography.X509Certificates.X509Extension -ArgumentList "1.3.6.1.4.1.311.10.11.11",$Value,$false
$Exts.Add($Ext)
}
# Save the CSR as a self signed cert to the "Certificate Enrollment Requests" store
# So that "Install signed certificate" can pick it up later
$Cert = $Request.CreateSelfSigned([DateTimeOffset]::UtcNow, [DateTimeOffset]::UtcNow.AddDays($TermDays))
$Cert.FriendlyName = $FriendlyName
if($SaveUnderLocalMachine)
{
$Location = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
}
else
{
$Location = [System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser
}
$Store = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList "REQUEST",$Location
$RW = [System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite
$Store.Open($RW)
$Store.Add($Cert)
$Store.Close()
$Cert.Dispose()
# Save the CSR to a file
$Request.CreateSigningRequest() | Set-Content $CSRPath -Encoding Byte
# Basic parameters. For TLS certs for IIS, this is sufficient
$Hostname = "foo.example.com" # Goes into Subject as the CN
$AltHostnames = @("foo.example.com", "bar.example.com", "baz.example.com") # These go into Subject Alternative Name
$DNPostfix = "OU=IT;O=Acme;L=Chicago;S=Illinois;C=US" # What follows the CN in the Subject field. Optional!
$FriendlyName = "foo" # Optional but highly recommended
$TermDays = 3650 # Expiration date for the temporary self-signed cert. The CA will probably override the exp date anyway.
$CSRPath = "c:\\MyCert.csr" # Your path WILL vary.
$SaveUnderLocalMachine = $false # False for current user, true for local machine.
# Helper for extension value serialization
function TLV($Tag, $Data)
{
if($Data)
{
$l = $Data.Length
if($l -lt 0x80)
{
[byte[]]$Header = @($Tag, $l)
}
elseif($l -lt 0x100)
{
[byte[]]$Header = @($Tag, 0x81, $l)
}
elseif($l -lt 0x10000)
{
$lh = ($l -shr 8) -band 0xFF
$ll = $l -band 0xFF
[byte[]]$Header = @($Tag, 0x82, $lh, $ll)
}
else
{
$l2 = ($l -shr 16) -band 0xFF
$l1 = ($l -shr 8) -band 0xFF
$l0 = $l -band 0xFF
[byte[]]$Header = @($Tag, 0x83, $l2, $l1, $l0)
}
return $Header + $Data
}
else
{
return [byte[]]@($Tag, 0)
}
}
# Create a RSA key pair
$KeyParams = New-Object System.Security.Cryptography.CngKeyCreationParameters
$KeyParams.ExportPolicy = [System.Security.Cryptography.CngExportPolicies]::AllowExport
if($SaveUnderLocalMachine)
{
$KeyParams.KeyCreationOptions += [System.Security.Cryptography.CngKeyCreationOptions]::MachineKey
}
[byte[]]$KeyLen = @(0, 8, 0, 0) # 2048 in little endian binary
$KeyProp = New-Object System.Security.Cryptography.CngProperty -ArgumentList "Length",$KeyLen,0
$KeyParams.Parameters.Add($KeyProp)
$KeyName = [Guid]::NewGuid().ToString()
$AlgName = [System.Security.Cryptography.CngAlgorithm]::Rsa
$KeyPair = [System.Security.Cryptography.CngKey]::Create($AlgName, $KeyName, $KeyParams)
$RSAKey = New-Object System.Security.Cryptography.RSACng -ArgumentList $KeyPair
# Create a CSR with that key pair
$DN = "CN=" + $Hostname
if($DNPostfix)
{
$DN += ";" + $DNPostfix
}
$DN = New-Object System.Security.Cryptography.X509Certificates.X500DistinguishedName -ArgumentList $DN
$HashAlg = [System.Security.Cryptography.HashAlgorithmName]::SHA256
$Padding = [System.Security.Cryptography.RSASignaturePadding]::Pkcs1
$Request = New-Object System.Security.Cryptography.X509Certificates.CertificateRequest -ArgumentList $DN,$RSAKey,$HashAlg,$Padding
# Populate the extensions, mimicking what the Microsoft tools do
$Exts = $Request.CertificateExtensions
# Key Usage
$Usage = [System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::KeyEncipherment
$Usage += [System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::DigitalSignature
$Usage += [System.Security.Cryptography.X509Certificates.X509KeyUsageFlags]::DataEncipherment
$Ext = New-Object System.Security.Cryptography.X509Certificates.X509KeyUsageExtension -ArgumentList $Usage,$true
$Exts.Add($Ext)
# Enhanced Key Usage - Server Auth, Client Auth
$Oids = New-Object System.Security.Cryptography.OidCollection
$Oid = New-Object System.Security.Cryptography.Oid -ArgumentList "1.3.6.1.5.5.7.3.1"
$Oids.Add($Oid) | Out-Null
$Oid = New-Object System.Security.Cryptography.Oid -ArgumentList "1.3.6.1.5.5.7.3.2"
$Oids.Add($Oid) | Out-Null
$Ext = New-Object System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension -ArgumentList $Oids,$false
$Exts.Add($Ext)
# Subject Alternative Name - assuming only "DNS Name" entries
$HostBytes = $AltHostnames | %{$b = [System.Text.Encoding]::ASCII.GetBytes($_);TLV 130 $b}
$SAN = TLV 48 $HostBytes
$Ext = New-Object System.Security.Cryptography.X509Certificates.X509Extension -ArgumentList "2.5.29.17",$SAN,$false
$Exts.Add($Ext)
# Subject Key Identifier
$PubKey = $Request.PublicKey
$Ext = New-Object System.Security.Cryptography.X509Certificates.X509SubjectKeyIdentifierExtension -ArgumentList $PubKey,$false
$Exts.Add($Ext)
# Application Policies - HARDCODED to "Server Auth, Client Auth"
[byte[]]$Policies = @(48, 24, 48, 10, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 48, 10, 6, 8, 43, 6, 1, 5, 5, 7, 3, 2)
$Ext = New-Object System.Security.Cryptography.X509Certificates.X509Extension -ArgumentList "1.3.6.1.4.1.311.21.10",$Policies,$false
$Exts.Add($Ext)
# Friendly Name
if($FriendlyName)
{
$Value = [System.Text.Encoding]::Unicode.GetBytes($FriendlyName + "`0")
$Ext = New-Object System.Security.Cryptography.X509Certificates.X509Extension -ArgumentList "1.3.6.1.4.1.311.10.11.11",$Value,$false
$Exts.Add($Ext)
}
# Save the CSR as a self signed cert to the "Certificate Enrollment Requests" store
# So that "Install signed certificate" can pick it up later
$Cert = $Request.CreateSelfSigned([DateTimeOffset]::UtcNow, [DateTimeOffset]::UtcNow.AddDays($TermDays))
$Cert.FriendlyName = $FriendlyName
if($SaveUnderLocalMachine)
{
$Location = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
}
else
{
$Location = [System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser
}
$Store = New-Object System.Security.Cryptography.X509Certificates.X509Store -ArgumentList "REQUEST",$Location
$RW = [System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite
$Store.Open($RW)
$Store.Add($Cert)
$Store.Close()
$Cert.Dispose()
# Save the CSR to a file
$Request.CreateSigningRequest() | Set-Content $CSRPath -Encoding Byte
@sevaa
Copy link
Author

sevaa commented May 11, 2020

Generating a certificate signing request (CSR) in PowerShell, with interoperability with the rest of the Windows world in mind. It's a companion gist to this blog post.

An expanded, more capable version of this script became an Azure DevOps extension. The sources are here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment