How to sign a PowerShell script

As a DevOps engineer, I frequently come across talented developers that underestimate some security aspects of the deployments, for instance, just to name a couple:  integrity and authenticity of the code or artefacts that we deploy.

Python and Powershell are powerful languages to develop quick and robust solutions that are extremely popular among attackers, for this reason, our ecosystem should take security very seriously.

Security is now far beyond the (old) perimeter of the company’s premises and infrastructure, indeed network or systems is abstracted away with or without cloud/hybrid deployments and just the enforcing identity is not enough in most cases.

In my opinion, white-listing applications around code-signing and checking the integrity of our code it’s more effective and less painful than you can think a good habit to build on a daily basis.

Code Signing must be easy and it can be done at any step that it’s meaningful for you. Remember we can sign the script again if needed. It must become standard practice, not an exception.

Code Signing practice with a Self-Signed Certificate

Let’s start creating a self-signed certificate and use it for practice

How to check a pfx certificate

Ok, but how can we check if the certificate generated is correct without even install it?

Looks good and it will expire in 1 year.

In the long run, purchasing a signing certificate can save us time,  but for now a self-signed certificate it’s enough for our practice examples.

Let’s create a test.ps1 from:

Let’s check the content and run it:

How to sign a PowerShell script

Let’s sign this script:

How to check the signer certificate of a Powershell script

At this stage, “UnknownError” is expected and it’s a positive result, I will explain later why.

Integrity Check test tempering a PowerShell script

Let’s temper adding a simple space character to our signed PowerShell script to prove that the integrity check is effective:

What is the “Unknown Error” Status and how to fix it

Why the Unknown error when the PS1 was signed? Because the status is a result of a lookup on your certificate store/repository.

After installing the certificate in these certificate stores we will have this result:

Finally, we will get now a VALID as a status result!

How to sign multiple scripts at once

Do we need to sign all ps1 scripts? No problem.

Very nice and not as painful. Right?

How to find the certificate subject from the thumbprint

But who signed those scripts? Let’s find out!

How To add a TimeStamp

I’ve added a timestamp server as another step to provide more information and compliance from a security standpoint. Using a TimeStamp Server will tell us exactly when the code was signed. Another benefit is that if the certificate expires the timestamp signature will prevent it from failing until also the timestamp certificate that countersigned the file it’s still valid.
You will notice that the self-signed certificate created expires on 15/02/2020 10:00:49 PM and the timestamped server certificate will get us a “grace” period of few months until 30/12/2020 10:59:59 AM prevent it from failing.
Thanks to the Twitter comment of Vegard (@Alsinet) that asked for it.

How to check all the details of the signature

Before closing let’s print out all the details from our signed script and the certificate used and the timestamp.

More interesting docs to read

Wrap-up

After all the efforts of managing code through all the stages the software development our code is deployed, we for sure run a git pull from master to check if it’s already up to date, but how can be sure that the code is been reviewed and approved for my environment and haven’t been tampered? Code Integrity (not tampered), Owner Signature (identity) are the solution after reviewing e testing carefully the code. As usual visit my GitHub for these code examples.

5 Replies to “How to sign a PowerShell script”

  1. I do not see the -type param on the New-Self… cmdlet.
    my $psversion = 5.1.14409.1018
    OS = MS Windows Server 2012 R2 Datacenter

    i am running powershell ISE as administrator.

    I do have the cmdlet but only with these params:

    SYNTAX
    New-SelfSignedCertificate [-CertStoreLocation ] [-CloneCert ] [-DnsName ] [-Confirm]
    [-WhatIf] []

    what am i missing? TIA !

    1. Hi Marcelo,
      Yes, there are important differences between powershell versions. I always need to check the documentation (with get-help) and test if the cmdlet I’m using or the scripts are working as expected.
      Please have a look and compare the documentation with these 2 links:
      https://docs.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps
      https://docs.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=winserver2012-ps

  2. I do not see the -type param on the New-Self… cmdlet.
    my $psversion = 5.1.14409.1018
    OS = MS Windows Server 2012 R2 Datacenter

    i am running powershell ISE as administrator.

    I do have the cmdlet but only with these params:

    SYNTAX
    New-SelfSignedCertificate [-CertStoreLocation ] [-CloneCert ] [-DnsName ] [-Confirm]
    [-WhatIf] []

    what am i missing? TIA !

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.