PowerShell DSC: Self Signed SSL Certs

Hello!

First, this isn’t a best practices guide for SSL certificates, it’s a how-to for creating functional ones. As always, only use self-signed certs when you’ve specifically validated that they’re a sufficiently secure solution.

When I do need self-signed certs and I’m working in Windows, I generate them with PowerShell DSC and its Script Resource. It works great for my cases. There are more robust ways that may also be worth looking at, like Custom Resources.

I always need certs with no password to make it easy to start apps unattended, so that’s what these instructions create.

This assumes you’ve already installed OpenSSL. I use Chocolatey.

Pre-requisites out of the way. Now, the code:

Script SelfSignedCert {
    GetScript = {@{Result = ''}}
    SetScript = {
        New-Item -ItemType Directory -Force -Path 'C:\Tmp\Ssl\'

        # Generate PFX with a temporary password
        $Cert = New-SelfSignedCertificate `
            -CertStoreLocation 'cert:\localmachine\my' `
            -DnsName 'localhost'
        $Password = ConvertTo-SecureString `
            -String 'temppass' `
            -Force `
            -AsPlainText
        Export-PfxCertificate `
            -Cert "cert:\localmachine\my\$($Cert.Thumbprint)" `
            -FilePath 'C:\Tmp\Ssl\Cert.pfx' `
            -Password $Password

        # Convert PFX to Key/PEM with no password
        C:\Program` Files\OpenSSL-Win64\bin\openssl.exe pkcs12 `
            -in 'C:\Tmp\Ssl\Cert.pfx' `
            -nocerts `
            -nodes `
            -out 'C:\Tmp\Ssl\Pkcs12.pem' `
            -passin 'pass:temppass'
        # 'openssl.exe rsa' sends 'writing RSA key' to the error stream
        # on success. We have to redirect that output or the Script
        # resource errors.
        C:\Program` Files\OpenSSL-Win64\bin\openssl.exe rsa `
            -in 'C:\Tmp\Ssl\Pkcs12.pem' `
            -out 'C:\Tmp\Ssl\Rsa.key' `
            2> Out-Null
        C:\Program` Files\OpenSSL-Win64\bin\openssl.exe pkcs12 `
            -clcerts `
            -in 'C:\Tmp\Ssl\Cert.pfx' `
            -nokeys `
            -out 'C:\Tmp\Ssl\Cert.pem' `
            -passin 'pass:temppass'

        # Clean up leftovers from the conversion
        Remove-Item 'C:\Tmp\Ssl\Cert.pfx'
        Remove-Item 'C:\Tmp\Ssl\Pkcs12.pem'
    }
    TestScript = {Test-Path 'C:\Tmp\Ssl'}
}

Line 32 (highlighted) is the tricky one. The script resource fails when its code outputs to the error stream. OpenSSL’s rsa command sends the string "writing RSA key" to the error stream when it succeeds. So we get failures like these:

Stderr from the command:

powershell.exe : writing RSA key
    + CategoryInfo          : NotSpecified: (writing RSA key:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
    + CategoryInfo          : NotSpecified: (writing RSA key:) [], CimException
    + FullyQualifiedErrorId : NativeCommandError
    + PSComputerName        : localhost

I bet the reason is this: OpenSSL was designed for Linux. In Linux, it’s common to send informational output to stderr. That keeps it out of stdout and therefor keeps it from passing to other apps via pipes (|). In PowerShell, there are many streams, including a dedicated one for informative output (the “verbose” stream). That makes it an anti-pattern to send informative output to PowerShell’s error stream; you should use the verbose stream. So it makes sense for DSC to assume that anything on the error stream is a real error, unlike in Linux. The person who ported OpenSSL didn’t account for this.

The only workaround I could find was to redirect the error stream to Out-Null. OpenSSL didn’t recognize the ErrorAction flag.

I couldn’t reproduce this behavior in raw PowerShell, only in the DSC script resource. If you know the details on why that is, I’d love to hear from you.

Hope this helps! Happy automating,

Adam

Need more than just this article? I’m available to consult.

You might also want to check out these related articles: