Monday, February 22, 2010

powershell code signing

Code Signing Certificates for PowerShell; how to apply for them and how to use them.

I’ve spent a lot of time this weekend trying to achieve the above. I spent all this time since I wanted an easy way (if there actually is such a thing) to sign PowerShell scripts.

When you see what I had to go through (and the length of this post), it is easy to imagine many giving up half way or even earlier in the process and simply opting for the Unrestricted policy, which lets you run any kind of script (local or remote, signed or not) without errors or warnings. This is of course the path of least resistance and can be fraught with (security) problems down the line... I know that ‘easy of use’ and secure computing do not really go together very well but it seems to me that the whole process could be at least a little more friendly (not to mention cheaper), if the information on how to do this was more easily available. Consider this my contribution to making it easier (I can’t make it any cheaper unfortunately).

In PowerShell the default (secure) installation sets a policy that does not allow execution of any scripts. If you want to be able to run scripts in your PowerShell, then you need to turn the policy down one notch and select the next best setting which is AllSigned (run the following command; Set-Executionpolicy AllSigned). What this does is it requires a valid signature on all your powershell scripts (including any modules, profile scripts etc.) when you try to run them.


References and credits:

I’ve gathered the information used in this posting from the following links:

1. https://forum.startcom.org/viewtopic.php?f=15&t=1654

2. http://www.jasonhelmick.com/journal/2009/9/20/one-little-two-little-three-little-endian.html


Options for Code Signing

If you want people to be able to validate your signatures under most circumstance, you’ll need to apply for a codesigning certificate from an officially trusted Certificate Authority (CA). Officially trusted here means a CA whose root certificate is already present in the Windows LocalMachine certificate store (or the equivalent on other platforms). It is also possible to create so called self-signed signature with a Microsoft tool called makecert. This tool comes as part of the Microsoft Windows SDK and is not redistributable. The disadvantage of self-signing is that no one will be able to validate your signature, unless you also supply them with the root CA certificate this tool also generates. Thus, not a great solution if you want your scripts to run on multiple machines. The other option you have is to use the root CA you might have in your company. Companies that have a Microsoft Active Directory implementation, often have a so called offline root CA (if they’re not using PKI extensively or else if would be online). You’ll need to bring this online and then generate your codesigning certificate from there (I do not have instruction on how to do that here). The disadvantange of this solution is that your scripts will only validate on machines that are part of your AD. This may be fine if you do not intend your scripts to be used outside your company.


Applying for a certificate

So once you’ve decided that you want to go through the effort of applying for a code signing certificate, you’ll need to get one from a trusted certificate authority. You’ll find that most certifcates (if the CA even assigns them to private persons) cost from $99 for the cheapest from K-Software (https://secure.ksoftware.net/code_signing.html?gclid=CMCfwLf3gKACFZss3godS1Fqjw) which is a comodo reseller through to Thawte which sells them for $299 for a year and it goes up from there. However, there is an alternative that I’ve used succesfully and that is the StartCom CA (https://www.startssl.com/). Start by signing up and you’ll get a free class 1 certificate that allows you to sign your emails. For $49 you can actually get a class 2 code signing certificate. In order to get this you’ll need to provide valid credit card or PayPal details and copies of identification documents (such as your passport and ID cards). They will then follow up with a phone call to cross check that you are really who you claim to be. After this process is done (which is pretty fast) and assuming all went well, your class 2 validation will be approved and you can create a class 2 code signing certificate. They have a wizard on their site that guides you through the process. It does ask you to provide a CSR by pasting it in a field they show. So, you’ll now need to generate a CSR (Certificate Signing Request). I was stumped at how to do this at first but with the help of the friendly people at StartCom, I found out how. See below...

[EDIT]

For those interested (too late for me unfortunately), I discovered that the Certum CA has a great offer for OpenSource developers that I wanted to make you aware of. You can get a code signing certificate for free!


Code signing with a StartSSL Level 2 certificate

The text from this section is found in the a StartCom forum discussion (see link 1 in the reference section). Since it is kinda buried in between other posts, I’ve highlighted the one here that worked for me:

For Windows you will need:

OpenSSL for windows version 0.9.8 or better. You can get it from:

http://slproweb.com/products/Win32OpenSSL.html

If you have a Mac, you’re lucky, it has OpenSSL built-in.

Once you have Openssl for your platform (Windows, Mac or Linux) you can use the following command to generate your certificate request:

openssl req -new -newkey rsa:4096 -nodes -keyout codesign_privatekey.pem -out codesign_certificate_request.csr

Enter the asked values or use the default ones - it doesn't matter as StartCom will insert the appropriate values from their database (where you have entered your individual/organization data). NOTE: do not set a password for your private key.

You should use a stronger key than 1024 bit (I've used 4096 bit in the line above - just alter to suit your needs)...

Now submit the content via copy & paste (i.e. open file with notepad.exe) to StartCom when asked for your CSR file.

When StartCom lists the text-content of your Certificate (select and do a copy & paste to your editor), save it to a new file, for example: codesign_certificate.crt

Now we are going to create a 'handy' PFX-file:

openssl pkcs12 -export -out codesign.pfx -inkey codesign_privatekey.pem -in codesign_certificate.crt


NOTE: do not set a password when asked.


Now you’ll need to add this to your certificate store. On windows you do this as follows:

Start the MMC (Microsoft Management Console): Start -> Run -> enter: mmc -> click: OK

In the File-menu select: Add/Remove Snap-In -> click: Add -> select: Certificates -> click: Add -> select: My User account -> click: Finish -> click: Close -> click: OK

Now open the Certificates - Current User tree -> select: Personal -> right-click & select: AllTasks -> Import -> point to your PFX file and install it.

Leave the password box empty when asked and set the option to mark your key as exportable, DO NOT enable strong key protection!


Signing your script

Armed with this newfangled certificate, you can now start signing all your scripts. If you have modules and profile scripts, you should probably start there. First things first; you need to find out where your new certificate ‘lives’. You can do that from the PowerShell command line as follows:

gci cert:\currentuser\my

That should give you a listing of your certificates. You need to figure out which one is you new Class 2 certificate and its position. If the certificate is for instance the second in the list, then you can issue the following command to assign it to a PowerShell Object variable, so it can be used for subquent signing:

$cert = @(gci cert:\currentuser\my)[1]

Now the $cert variable contains the certificate the came second in the list you produced earlier (remember, counting starting with 0). To sign all your scripts in the current directory (assuming for argument’s sake all files are PS1 files), you could now issue the following command:

gci | %{Set-AuthenticodeSignature $_.Name $cert}

To sign a single script you would of course simply use the following command:

Set-AuthenticodeSignature .\myscript.ps1 $cert

You may run into a problem when you try to sign scripts that you have created (and saved) with the PowerShell ISE. For reasons totally unfathomable, the standard text format for PS1 files saved with the ISE is Big Endian Unicode and that is a problem for certificate signing. For a more thorough discussion see the 2nd link I referred to above. If you do get the dreaded ‘unknown error’ when trying to sign your scripts, I suggest you first try opening the script in notepad and saving it as normal unicode and then retry your signing.

Now that you have the hang of that, you can no doubt come up with ways to make life easier for yourself (hint: module function that get loaded from your profile).

Once you’ve signed all the scripts you need to be able to run, you can change the execution policy to AllSigned with the following command:

Set-ExecutionPolicy AllSigned

From now on, you will need to explicitly approve the running of scripts that are signed with a valid signature (meaning a signature that Windows is able to trace back to a trusted root CA).


Running your signed script

Running a signed script for the first time gives you the following prompt:

Although this looks ‘scary’ it is actually good. It simply tells you the signature is not (yet) trusted, but it validated Ok. Eventhough it does not actually tell you it validated Ok, it is so because if the signature did not validate Ok, you wouldn’t get this prompt and the possibility to run the script, instead you’d get an error stating that the script could not be validated. If you want to permanently trust the author, simply type “A” for always and all scripts signed by this author (and that have not been tampered with) with run.

That concludes this post. Happy code signing!