Introduction: Signing My Powershell Script

This instruction is for those that are not able to do a "Set-ExecutionPolicy RemoteSigned" yet want to run their powershell script from a doubleclick or from a DOS-script.

Remark: if the ExecutionPolicy is set to Restricted, and you can't change it to Allsigned, then stop reading. Try this to start a DOS-box with Administrator permissions, type "powershell", then type "set-ExecutionPolicy AllSigned".

It uses openssl, so you need to download and install, but you don't need to know how to work with it, and afterwards you can remove openssl. It also needs the Microsoft Management Console (MMC), and admin rights to do some things with it.

This instruction tells you to do the following, which I'll explain.

  1. Download and install openssl
  2. Create a PKI CA certificate and a user certificate for object signing
  3. Import both certificates in the Microsoft Certificate store
  4. Sign your powershell script and run it

Thanx to the curious geek for this page.

This instruction was created in june 2016.

Step 1: Download, Install and Configure Openssl​

Microsoft also have tools to create PKI certtificates, like Makecert.exe, but that needs .Net SDK. Powershell also has a New-SelfSignedCertificate cmdlet, but only on Windows 8 and higher. So I prefer openssl.

The openssl site does not offer executables or installers, but on this wiki page you can find some. I used the Indy Fulgan distribution. Scroll down to the latest, most secure version (well, you really don't need a secure version at all for this), download the win32 or win64 (depending on your PC hardware) and unpack all files in any directory you like. I installed it in D:\openssl.

The certificates you're going to create better go in another folder, so you can re-do your work if it was fun to do. Also, to import a certificate in the certificate store, it must be on a local drive. So create a folder where you store your certificates in. I used D:\SignPS. Navigate to that folder.

Next thing you need is an openssl config file, for a template was not packed in the Indy Fulgan kit. There are several out there, most of them similar. I took one from the Massachusetts Institute of Technology. Make a textfile named "openssl.cnf" and copy-paste the text in it.

Let's test if openssl works. Start a DOS box (cmd.exe) and do this ( > is your DOS prompt).

> set OPENSSL_CONF=D:\SignPS\openssl.cnf
> D:\openssl\openssl version
OpenSSL 1.0.2h  3 May 2016

Yep.

Step 2: Create a PKI Self-signed (root) CA Certificate

The CA tool of openssl needs some files and directories to work well. I also tend to keep the original config file unchanged as much as possible, so you can easily find and understand examples everywhere. So do the following in the D:\SignPS folder.

  • Create a folder "demoCA", and navigate into it.
  • Create folders "certs", "crl", "newcerts" and "private".
  • Create an empty file "index.txt".
  • Create a file "cacert.srl" and put "00" in it (just two zero's on one line)
  • Navigate up to D:\SignPS
  • Start a DOS-box and navigate to D:\SignPS\demoCA.

  • Give the following commands to create a self-signed CA certificate.

set OPENSSL_CONF=D:\SignPS\openssl.cnf
set RANDFILE=D:\SignPS\demoCA\private\.rand
D:\Openssl\openssl.exe genrsa -out private\cakey.pem 2048
D:\Openssl\openssl.exe req -new -x509 -days 3650 -key private\cakey.pem -out cacert.crt -subj "/CN=CA-SignPS"

There is a "cacert.crt" file, double-click to see its content.

Note: the default openssl.cfg tells me to name the CA certificate "cacert.pem" but Windows doesn't recognise that, so I renamed it to "cacert.crt" so you can doubleclick. Also, I also did not use "serial" but "cacert.srl".

Step 3: Create a PKI User Certificate for Object Signing

The user certificate must be usable to sign code. This is not provided in "standard" user certificates which are mostly ssl-certificates. So we need to add some features to the config (these features can't be provided as openssl command-line arguments).

  • Edit the openssl.cnf and find the section "[ v3_req ]". Add the following lines to this section:

extendedKeyUsage = codeSigning, msCodeInd, msCodeCom 
nsCertType = client, email, objsign

  • Create in the demoCA folder a file "v3.cfg" and put the following lines in it:

# Extensions to add to a certificate request 
subjectKeyIdentifier=hash
basicConstraints = CA:FALSE
keyUsage = digitalSignature
extendedKeyUsage = codeSigning, msCodeInd, msCodeCom
nsCertType = client, email, objsign

  • In the same DOS-box as before, give the following commands to create a private key and a certificate signing request (csr), and to sign it by the CA which issues the user certificate.
D:\Openssl\openssl.exe genrsa -out private\userkey.pem 2048
D:\Openssl\openssl.exe req -new -key private\userkey.pem -reqexts v3_req -out user.csr -nodes -subj "/CN=localhost" D:\Openssl\openssl.exe x509 -req -days 3650 -in user.csr -CA cacert.crt -CAkey private\cakey.pem -extfile v3.cfg -out certs\user.crt

Doubleclick on the certs\user.crt to view the user certificate. Also note the extensions.

Step 4: Import Both Certificates in MMC

Because signing of code requires the private key, and powershell uses the certificate store to get the key, we need to pack the certificate and the key into one importable thing, which is called a pkcs#12 file.

  • Do in the DOS-box:

D:\Openssl\openssl.exe pkcs12 -export -in certs\user.crt -inkey private\userkey.pem -out user.pfx

You need to enter a password twice, I allways use "openssl".

  • To import the pkcs#12, doubleclick the pfx-file, the Import Certificate wizard will appear. In the 3rd screen you need to enter the password. Also mark the key exportable (not sure why). Leave the rest default.
  • Now, in the DOS-box, type "mmc". On my Windows 7 laptop, I need to OK on elevated rights, I hope you can do so.
  • File --> Snap-in the Certificates module, and choose “Myuser account”.
  • Within mmc, navigate to Certificates --> Private --> Certificates, and you will see the localhost certificate, which was issued by CA-SignPS.

We also need to import the CA-SignPS certificate. Moreover, we need to trust the certificate.

  • Within mmc, navigate to Certificates --> Trusted Roor Certification Authorities --> Certificates
  • Richt-click on Certificates, choose all tasks --> Import
  • Select the D:\SignPS\demoCA\cacert.crt Leave the rest default.
  • You will get an alert-box asking you if you really want to trust this certificate, click Yes

In Trusted Root Certification Authorities --> Certificates you will find the CA-SignPS certificate, issued by itself.

Step 5: Finally Sign Your Powershell Script

Your powershell script can be anywhere, even on a network share.

  • Start a DOS-box, and navigate to the folder where your powershell script is.
  • Type powershell
  • Then type this command to see if the user certificate is visible

PS D:\> dir cert:CurrentUser\My -CodeSigningCert

Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\My

Thumbprint Subject ---------- ------- 0416C2888A4BD94547FB24D5891A7A48930C489F CN=localhost

PS D:\>

  • Now sign your powershell script, in my case "test.ps1" with echo "Hello world"

$cert=(dir cert:currentuser\my\ -CodeSigningCert)

Set-AuthenticodeSignature test.ps1 $cert

Directory: D:\Powershell

SignerCertificate Status Path

----------------- ------ ----

33875C...D1F7A81A6415E72695421 Valid test.ps1

If the status is not valid, type this to see what's wrong (output here truncated).

Get-AuthenticodeSignature test.ps1 | fl *

StatusMessage : Signature verified.

  • Run the powershell script by typing in the DOS-box on the PS prompt:
.\test1.ps

The first time you will get a warning, answer A for Allways run.

Step 6: Automatically Sign Your Powershell Script

Your powershell script is now signed, which means the code is trusted. But if you alter the code, it will become invalid. Just try.

So after each edit of your powershell script, you have to re-sign it before you can run it. Now that can be easy.

  • Create a CMD-script, I named it "test.cmd", and put these lines in it.

powershell -noprofile -command "Set-AuthenticodeSignature test.ps1 (dir cert:currentuser\my\ -CodeSigningCert)" > test_sign.out

powershell .\test.ps1

pause

  • Doubleclick the test.cmd

That's all folks.

Have fun!