Last active
January 4, 2024 00:42
-
-
Save sevaa/802a01649aa0746c959240146074c2b5 to your computer and use it in GitHub Desktop.
RequestTLSCert.ps1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.