Signing Powershell scripts
I wanted to write a post on signing Powershell scripts, but doing a few internet searches found this article. Which is great if you have a certificate store, and this is the recommended method, but using a certificate store is not always an available option. There are several alternate methods to create a certificate and I found this method that used makecert. Makecert should be available if you have installed the Windows SDK, or it can be downloaded with the SDK at http://msdn.microsoft.com/en-us/windowsserver/bb980924.aspx. I noticed a couple of issues with the code snippet; it has the wrong algorithm (sh1 instead of sha1) and the certificate file should be specified as the –ic option and not –c. This was reported and hopefully it will be corrected. Another issue running the script is that it assumes that makecert.exe is on the PATH or in the current directory. I came up with the following script that will search for an instance of makecert.exe and then run it.
$pvk = "C:\powershell\root.pvk"
$cer = "C:\powershell\root.cer"
Remove-Item $pvk, $cer -ErrorAction:SilentlyContinue
$file = @(get-psdrive -p "FileSystem" | % {get-childitem $_.Root -include makecert.exe -r -ErrorAction:SilentlyContinue })[0]
& $file.fullName -n "CN=MyRoot" -a sha1 -eku 1.3.6.1.5.5.7.3.3 -r -sv $pvk $cer -ss Root -sr CURRENTUSER
& $file.fullName -pe -n "CN=MyCertificate" -ss MY -a sha1 -eku 1.3.6.1.5.5.7.3.3 -iv $pvk -ic $cer
Another change I made was to the Subject's certificate store location (-sr option) from localhost to CURRENTUSER. Without this change I got permissions errors that gave the error:
Error: Save encoded certificate to store failed => 0x5 (5)
Failed
when the command was run. The certificate will still be installed, but it won’t be trusted and so can’t be used to sign scripts.
When the script works correctly you will be prompted with three dialogs for various passwords. You will also be asked to confirm that you wish to install the certificate as the certification authority can not be confirmed:
To see if the certificate installed correctly use the command:
get-childitem cert:\CurrentUser\My -codesigning
You should see something like:
Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\My
Thumbprint Subject
---------- -------
2950019745D88A46C94B46A06571EA3E4522F4B4 CN=MyCertificate
You can also view the certificate with certmgr

By ckicking on the certificate you can view the details:

To sign the script you can run something like:
$cert = @(gci cert:\currentuser\my -codesigning)[0]
$script = "C:\powershell\script.ps1"
Set-AuthenticodeSignature $script $cert
When this script is run the output will be something like:
Directory: C:\powershell
SignerCertificate Status Path
----------------- ------ ----
3DE67752A34AB7980FAF4AE58A2788DE511C8801 Valid script.ps1
If the look at script.ps1 file you will see the signature appended to the file. To test the script change the execution policy to allsigned. To check the execution policy use the command:
Get-ExecutionPolicy
If the policy needs setting use the command:
Set-ExecutionPolicy –executionpolicy AllSigned –Force
Scripts will then only execute if they are signed (so do this AFTER after signing the script!). If you don’t sign the script you will get an error message like:
File C:\powershell\script.ps1 cannot be loaded. The file C:\powershell\script.ps1 is not digitally signed. The script will not exec
ute on the system. Please see "get-help about_signing" for more details..
At line:1 char:25
+ C:\powershell\script.ps1 <<<<
+ CategoryInfo : NotSpecified: (:) [], PSSecurityException
+ FullyQualifiedErrorId : RuntimeException
Because this is a self-signed certificate you will be prompted when the script is run as to whether it is trustworthy e.g.

or for Powershell ISE

If you reply to Always run scripts with this certificate, then any script that is signed using this certificate will run without re-prompting the user.