Categories
Security

How to Self-Sign PowerShell Scripts

I was trying to figure out how to sign PowerShell scripts with a self-signed certificate. This is a simple step-by-step guide on how you can provision your own certificate for testing and sign and verify PowerShell scripts.

If you plan to use a publicly trusted code signing certificate, this is however not the exact process as you should be using an HSM to secure your certificate.

Step by Step Guide

  1. First, create a private key (in bash):
openssl genrsa -out codesign.key 4096
  1. Create a configuration file named codesign.cnf with the certificate details:
[req]
default_bits = 4096
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[req_distinguished_name]
C = US
ST = YourState
L = YourCity
O = YourOrganization
OU = YourDepartment
CN = YourName

[v3_req]
basicConstraints = critical,CA:FALSE
keyUsage = critical,digitalSignature
extendedKeyUsage = critical,codeSigning

Remember to adjust the certificate details in the configuration file (codesign.cnf) to match your information.

  1. Generate private key and certificate (in bash):
# Generate private key
openssl genrsa -out codesign.key 4096

# Create certificate with extensions
openssl req -new -x509 -key codesign.key -out codesign.crt -config codesign.cnf -days 365 -extensions v3_req

# Create PFX with private key (you'll be prompted for a password - remember it!)
openssl pkcs12 -export -out codesign.pfx -inkey codesign.key -in codesign.crt
  1. Import certificate (in PowerShell):
# Import the certificate (replace "pass" with the password you set)
Import-PfxCertificate -FilePath codesign.pfx -CertStoreLocation Cert:\CurrentUser\My -Password (ConvertTo-SecureString -String "pass" -Force -AsPlainText)

# Verify it's imported and shows as a code signing cert
Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert
  1. Export public certificate and add to root store:
# Export public certificate
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Where-Object {$_.Subject -like "*Unic AG*"} | Select-Object -First 1
Export-Certificate -Cert $cert -FilePath "codesign_public.cer"

# Import to root store (requires admin privileges)
Import-Certificate -FilePath "codesign_public.cer" -CertStoreLocation Cert:\LocalMachine\Root
  1. Now you can sign PowerShell scripts. Here’s an example:
# Script path
$scriptPath = "D:\codesign\signme.ps1"

# Get the certificate
$cert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Where-Object {$_.Subject -like "*YourName*"} | Select-Object -First 1

# Sign the script
Set-AuthenticodeSignature -FilePath $scriptPath -Certificate $cert

Example

Set-AuthenticodeSignature -FilePath $scriptPath -Certificate $cert

    Directory: D:\codesign

SignerCertificate                         Status                    StatusMessage             Path
-----------------                         ------                    -------------             ----
22B58180A93B5062C97258A382646741604F8BAA  Valid                     Signature verified.       signme.ps1

To verify the signature on a script:

Get-AuthenticodeSignature -FilePath "D:\codesign\signme.ps1"

    Directory: D:\codesign

SignerCertificate                         Status                    StatusMessage             Path
-----------------                         ------                    -------------             ----
22B58180A93B5062C97258A382646741604F8BAA  Valid                     Signature verified.       signme.ps1