The PowerShell script discussed in this post uses certreq.exe to generate certificate signing request (CSR) files with a maintained Subject Alternative Name (SAN) field.

To avoid transmitting credentials in clear text, SSL/TLS should protect administrative web frontends. The primary purpose of certificates is proving authenticity. Certificate authorities (CAs) only issue certificates after proving the requester's information is correct and legit.

Browser showing certificate naming mismatch

Browser showing certificate naming mismatch

CSR files via Internet Information Services (IIS) Microsoft Management Console (MMC) only provide the common name (CN) attribute as the name holder. The problem is that Chrome since version 58 does not support the CN attribute anymore. It requires the name in a correctly maintained Subject Alternative Name (SAN) field. By using the SAN section, it is possible to add multiple alias names to a certificate. My PowerShell script simplifies CSR file creation with alias name support.

Chrome's error message when connecting to a site with SSL certificate that has no maintained SAN field

Chrome's error message when connecting to a site with SSL certificate that has no maintained SAN field

Windows maintains a storage of trusted root certificate authorities. As a result, it automatically trusts the identity that presents a certificate coming from a trusted root certificate authority. In a subsequent step, it will check whether the CN of the certificate matches the name of the accessed resource. If not, error messages appear in the Internet Explorer.

Creating a certificate with certreq.exe

Besides the wizard within IIS, certreq.exe can create CSR files. This is a built-in Windows command-line utility. To generate a new CSR file, use the following syntax:

certreq.exe -new [Options] <INF file> <CSR file>

The INF file needs to store detailed information required to generate the file. Save the following content as plain text to use it with certreq.exe:

[Version]
Signature = $Windows NT$	

[NewRequest]
Subject	= "CN=ServerName.Nwtraders.msft"
KeySpec	= 1
KeyLength	= 2048
Exportable = False
MachineKeySet = True
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
RequestType	= PKCS10
KeyUsage =	0xa0

[EnhancedKeyUsageExtension]
OID = 1.3.6.1.5.5.7.3.1

[Extensions]
2.5.29.17  = "{text}"
_continue_ = "dns=servername.nwtraders.msft&"
_continue_ = "dns=servername&"
_continue_ = "dns=serveralias.nwtraders.msft&"
_continue_ = "dns=serveralias&"

The table below explains the content of the INF file for better understanding:

Parameter Value (example) Meaning
[Version]
Signature $Windows NT$ Indicates the operating systems for which this INF is valid—on Windows it must be $Windows NT$
[NewRequest]
Subject CN=ServerName.Nwtraders.msft CN used for the fully qualified domain name (FQDN) of the individual resource
KeySpec 1 Indicates use of the certificate for encryption and signature
KeyLength 2048 Length of public and private keys—2048 bits is a common value
Exportable False Disallows exporting the private key—someone can only use the certificate on the machine where the request is performed
MachineKeySet true Indicates it's a computer certificate, not suitable for user-related scenarios
ProviderName Microsoft RSA SChannel Cryptographic Provider Specifies the encryption algorithm
RequestType PKCS10 Determines the format of the request file type sent to the CA
KeyUsage 0xa0 Further restricts of the certificate—0xa0 stands for digital signature and key encipherment
[EnhancedKeyUsageExtension]
OID 1.3.6.1.5.5.7.3.1 Server authentication is the intended use of this certificate.
[Extensions]
2.5.29.17 "{text}" If the client OS is Windows Server 2008 and higher, we can include SANs in the extensions section by using the text format below
_continue_ "dns=servername.nwtraders.msft&"

 

Must be the same as the CN in the subject parameter
_continue_ "dns=servername&" e.g., NetbiosName
_continue_ "dns=serveralias.nwtraders.msft&" e.g., Alias as FQDN
_continue_ "dns=serveralias&" e.g., Alias as NetbiosName

The PowerShell script

The Powershell script New-CertReqWithAlias.ps1 uses certreq.exe to generate CSR files with a maintained SAN field. The SAN field may contain alias names as well. I've explained how the script works in the comments.

<#
.SYNOPSIS
Creates a certificate signing request (CSR) file, which can contain aliases (CNames).
.DESCRIPTION
- Creates a CSR file for a certificate authority, which includes aliases (CNames).
- Alias names need to be comma separated.
- Run the script on the server that hosts the web service (e.g., IIS), since it needs to access the private key storage.

- This uses certreq.exe to transform the input of this script into a well-formed request file.
- For easy usage, the script exposes only the required and a few popular certutil parameters.
.LINK
https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/ff625722(v=ws.10)
.EXAMPLE
$certReqParams = @{
    FQDN                = servername01.nwtraders.msft
    Aliases             = servername01, devmachine, devmachine.nwtraders.msft, 192.168.0.1
    DestinationFilePath = C:\Temp
}
New-CertFileReqWithAlias.ps1 @certReqParams
.PARAMETER FQDN
Requests a certificate for the fully qualified domain name (FQDN) for the system (e.g., a website or a server)
.PARAMETER KeyLength
Length of keys used for encryption; 2048 bits is the standard and default value
.PARAMETER Exportable
Defines exportability of the private key included in the certificate; the default for security reasons is false
.PARAMETER EncryptionAlgorithm
Sets the algorithm used for encryption; the available choices are the most popular according to research
.PARAMETER Aliases
Comma-separated list of alias names (CNames) to include in the certificate;
all names in the list can access the target resource without name-mismatch errors
.PARAMETER DestinationFilePath
Specifies the name and path for the certificate signing request (CSR) file; the name of the CSR file will be FQDN.csr
.INPUTS
None--you cannot pipe objects to New-CertReqWithAlias
.OUTPUTS
PKCS10-formatted CSR file
.NOTES
AUTHOR: Ruben Zimmermann @ruben8z
LASTEDIT: 2018-08-27
REQUIRES: PowerShell Version 4, Windows Management Foundation 4, and at least Windows 7 or Windows Server 2008 R2.
NAME: New-CertFileReqWithAlias
KEYWORDS: certreq, pki, ca, alias
REMARK:
This PS script comes with ABSOLUTELY NO WARRANTY; for details see gnu-gpl. This is free software, and you are welcome to redistribute it under certain conditions; see gnu-gpl for details.
#>

param(

    [Parameter(Mandatory = $true)]
    [String]$FQDN,

    [Parameter(Mandatory = $false)]
    [ValidateSet(1024,2048,4096)]
    [int]$KeyLength = 2048,

    [Parameter(Mandatory = $false)]
    [ValidateSet('True','False')]
    [string]$Exportable = 'False',

    [Parameter(Mandatory = $false)]
    [ValidateSet('Microsoft RSA SChannel Cryptographic Provider',`
    'Microsoft Enhanced DSS and Diffie-Hellman Cryptographic Provider')]
    [string]$EncryptionAlgorithm = 'Microsoft RSA SChannel Cryptographic Provider',

    [Parameter(Mandatory=$false)]
    [object]$Aliases,

    [Parameter(Mandatory=$true)]
    [string]$DestinationFilePath

)

#region checking_parameters

#Check if the FQDN matches valid syntax
if ($FQDN -notMatch '\w{1,}\.\w{1,}\.?[\w.]*') {
    Write-Warning -Message "The FQDN: $($FQDN) seems to be invalid.`n The expected syntax is host.domain.<optional>"
    exit
}

#Check if aliases match valid syntax
if ($Aliases -notMatch '[\w\.\s,]{1,}') {
    Write-Warning -Message "Aliases: $($Aliases) don't seem to be valid. Use a comma ',' to separate multiple aliases."
    exit
}

#Check if the destination file path exists
if (-not (Test-Path -Path $DestinationFilePath)) {
    Write-Warning -Message "Path: $($DestinationFilePath) does not exist. Please specify a valid path."
    exit
}

#Check if the specified file path has a training backslash; if not, add it.
if ($DestinationFilePath.Substring($DestinationFilePath.Length -1,1) -eq '\') {
    $DestinationFilePath = $DestinationFilePath + $FQDN + '.csr'
} else {
    $DestinationFilePath = $DestinationFilePath + '\' + $FQDN + '.csr'
}

#endregion checking_parameters

#region program_main

<#
    If a comma occurs in an aliases value, 'split' will convert the string
    to an array. Building a valid extensions section requires a loop.
    In case only one value is specified as an alias value, the script will embed it into the required information.
    [System.Environment]::NewLine ensures one alias per line.
#>

if ($Aliases -match ',') {
    $tmpAliases = $Aliases -split ','
    foreach($itmAlias in $tmpAliases) {
        $dnsAliases += '_continue_ = "DNS=' + $itmAlias + '&"' + [System.Environment]::NewLine
    }
} else {
    $dnsAliases = '_continue_ = "DNS=' + $Aliases + '&"' + [System.Environment]::NewLine
}

$certificateINF = @"
[Version]
Signature= '`$Windows NT$'

[NewRequest]
Subject = "CN=${FQDN}"
KeySpec = 1
KeyLength = ${KeyLength}
Exportable = ${Exportable}
MachineKeySet = TRUE
ProviderName = ${EncryptionAlgorithm}
RequestType = PKCS10
KeyUsage = 0xa0

[EnhancedKeyUsageExtension]
OID=1.3.6.1.5.5.7.3.1

[Extensions]
2.5.29.17 = "{text}"
_continue_ = "DNS=${FQDN}&"
${dnsAliases}
"@

<#
[System.IO.Path]::GetTmpFileName() creates a temporary file to store the information of the
certificateINF variable. The operating system will automatically drop it.
#>
$tmpFile        = [System.IO.Path]::GetTempFileName()
$certificateINF | Out-File $tmpFile

& certreq.exe -new $tmpFile $DestinationFilePath

#endregion program_main

Example using the script

Open PowerShell with elevated rights on the computer you require the certificate for. The command below shows how you can use the script:

.\New-CertReqWithAlias.ps1 -FQDN servername.nwtraders.msft -Aliases "servername,serveralias.nwtraders.msft,serveralias,10.0.0.1" -DestinationFilePath C:\Temp

The command creates a CSR file in the folder C:\Temp for the FQDN servername.nwtraders.msft with the aliases servername, serveralias.nwtraders.msft, and serveralias. Use Notepad to open the request file:

notepad.exe .\servername.nwtraders.msft.csr

Paste the file content into the CA's certificate enrollment page, and issue the certificate.

Pasting the file content into the CA web enrollment page

Pasting the file content into the CA web enrollment page

The resulting certificate will look as follows:

Subscribe to 4sysops newsletter!

Certificate general page

Certificate general page

Certificate details page showing specified aliases

Certificate details page showing specified aliases

Further reading

This Microsoft document explains certreq.exe and all you need to know in greater detail, and here is more on Chrome's support for CN matching in certificates.

avataravatar
14 Comments
  1. Nathan 5 years ago

    Thank you so much for sharing this.

  2. Zack 5 years ago

    Hello Ruben. This script is awesome and exactly what I was looking for – I’m used to making custom requests through the GUI and it just gets tiresome. However, I’m running in to one issue that I was hoping you may know a quick fix for.

    When running the PS code going by the suggested example:

    .\New-CertReqWithAlias.ps1 -FQDN workstation.domain.local -Aliases workstation, zackssys.domain.local, zackssys, 192.168.1.5 -DestinationFilePath C:\Temp

    The resulting cert has something like this for the SAN entry:

    DNS Name=workstation.domain.local
    DNS Name=workstation zackssys.domain.local zackssys 192.168.1.5

    as opposed to what I would expect:

    DNS Name=workstation.domain.local
    DNS Name=workstation
    DNS Name=zackssys.domain.local
    DNS Name=zackssys
    DNS Name=192.168.1.5

    Any idea what I may have botched?

    Thanks!

     

    • @Zack

      Did you try putting your aliases between quotes?

      -Aliases 'workstation', 'zackssys.domain.local', 'zackssys', '192.168.1.5'

  3. Trevor 4 years ago

    @Zack

    I was having the same issue.  Finally figured out that I had to put the arguments passed to the Aliases like so:

    -Aliases “workstation,zackssys.domain.local,zackssys,192.168.1.5”

    Note the addition of the double quotes and the removal of the spaces.

    avataravatar
  4. Jeremiah 4 years ago

    Hello Ruben. Could this same script be applied to an appliance with an embedded web server (i.e. printer or other IoT device)?  Often times customers purchase equipment from vendors that have web server functionality enabled and the default certificate does not comply company policy.  I'm looking for a better way to generate a CSR for appliances that meet this criteria and your script seems like it could fit the bill.

  5. You can absolutely create the certificate request with the script.  The main thing is to mark the key as exportable, so that when you generate the cert from the requesting machine, you can export it as a PFX with the key.  Your devices need to be able to import that PFX though.

     

    David F. 

  6. Zachary 4 years ago

    Brilliant and oh so helpful!  Thank you!

    One question:  When using the GUI, one can add a custom permission for the Private Key.  While I can do this after the key is installed, is there a way to add READ permission for "mydomain\NETWORK SERVICE" account in the request?

  7. Ben 4 years ago

    Thank you for the article, it was a very useful start point.

    I've noticed a small issue with the first example INF file, you are missing the "=" equals signs between the _continue_ and the values in the Extensions section.

    avatar
  8. Steve 4 years ago

    Love it but hate it. I haven't figured out how to get this to be successful for an IIS web cert.

  9. Fred 3 years ago

    Having a heck of a time getting a certificate added via netsh. I generate the CSR, download the DER file from /certsrv CA, add it to the Personal Computer store, but repeatedly get this error adding ti:

    PS D:\software> netsh http add sslcert ipport=0.0.0.0:9000 certhash=$Hash appid='{00112233-4455-6677-8899-AABBCCDDEEFF}’

    SSL Certificate add failed, Error: 1312
    A specified logon session does not exist. It may already have been terminated.

    However, using a self signed cert works fine.

    • Joe Smo IT 1 year ago

      Run the netsh command from an admin elevated command prompt.

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account