Saturday, 4 June 2016

PowerShell is limited on certificate operations

This is a story of failure and how PowerShell is lacking when it comes to certificate operations. It is not lacking when it comes to operating with the Windows certificate stores or when it comes to managing the PKI with PowerShell cmdlets, but it is weak when operating with certificates themselves, converting and manipulating them and working with 'open' standards.

My requirements were as follows: write a PowerShell script which will download the certificate chain from a Microsoft CA, convert the chain to a format that is OpenSSL compliant and can be imported to F5 BIG-IP. User ONLY PowerShell and .NET namespaces, not OpenSSL for Windows or whatever else one could imagine.
The first part is not that complicated. The snippet below is not very beautiful but it illustrates the process of downloading the chain to a file and the required user input.

In the example below I used variables and hard coded the URL, this should better be done with parameters in a function.

## Variables
$cred = Get-Credential -UserName "$env:USERDOMAIN\$env:USERNAME" -Message "Enter Password"
$PKIurl = "https://my-issuingca"

## Get Chain from CA
$WebResponse = (Invoke-WebRequest -Uri ("$PKIurl/certsrv/certnew.p7b?ReqID=CACert&Renewal=1&Enc=b64") -Credential ($cred)).RawContent
$WebResponse | Out-File $env:TMP\WebResponse.tmp
Get-Content $env:TMP\WebResponse.tmp | select -Skip 10 | Set-Content .\certnew.p7b
Remove-Item $env:TMP\WebResponse.tmp -Force

There are two things worth to explain:
* The protocol (http or https) and hostname of the CA server are required as well as the username and password of the current user are required in order to authenticate to the IIS on the CA server.
* The part right of the protocol/hostname is always the same and can be hardcoded in the script.
* You may notice that I save the response to a temporary file and remove the first 10 lines. Why? Because they are the response header for the web server, from line 11 the actual certificate chain begins. This is what I need.
* And, as you can see, I download the chain in Base64 encoding. Again - why? Because the .NET namespace used in the second part of the script (for converting the certificates) requires reading from Base64 and cannot read from binary encoded file.

However the second part is also where PowerShell fails. I did not find a way, to decode the chain in a way where PowerShell will properly convert the line breaks.
I can read all certs from the chain, export each of them to single files, compose a new chain file with all certificates from the chain - but OpenSSL or F5 BIG-IP will not be able to read it properly, because the line breaks are not in the proper format.

This is the second script, suggestions for fixing it are welcome: