Signing PowerShell Scripts

PowerShell scripts used in production environments should be signed to prevent modification, and server configurations should enforce script signing to mitigate risks of malicious code being run on them.

  1. Get and install a signing certificate, ideally in an environment that’s completely separate from your production environment where the signed script will be run. I used StartSSL but you may wish to go with a less shady Certificate Authority. If you go with a self-signed certificate, you’ll need to deploy your CA certificate on all your machines where you plan to run this script.
  2. Ensure the signing certificate is available to PowerShell. I assume you only have 1 cert installed in the step below since I use [0] in the next step.
    > Get-ChildItem cert:\CurrentUser\My -codesign
         
         
    Thumbprint                                Subject
    ----------                                -------
    3355EBE8EB05071263E1B0DC989EEE072DFE0A72  CN=Justin Ho, L=Toronto, S=Ontario, C=CA
    
  3. Sign the script, which I’ve named somescript.ps1 in the example below.
    1. Pull the cert from the local store
       > Set-AuthenticodeSignature somescript.ps1 @(Get-ChildItem cert:\CurrentUser\My -codesigning)[0] -IncludeChain "All" -TimestampServer "http://timestamp.verisign.com/scripts/timstamp.dll"
      
    2. You can also use http://timestamp.comodoca.com per https://support.comodo.com/index.php?/Knowledgebase/Article/View/68/0/time-stamping-server or http://timestamp.digicert.com/.
    3. If you don’t want to import the certificate, you can just load the certificate for this process:
         > $Cert = Get-PfxCertificate -FilePath "mycert.pfx"
         > Set-AuthenticodeSignature -FilePath somescript.ps1 -Certificate $Cert -IncludeChain "All" -TimeStampServer "http://timestamp.verisign.com/scripts/timstamp.dll"
      
  4. Verify that PowerShell recognizes the signature is valid. You should see a TimeStamperCertificate in addition to the SignerCertificate and the Status should be Valid.
    > Get-AuthenticodeSignature .\somescript.ps1 -Verbose | fl
    
  5. Check the current Execution Policy and update it for the local machine.
    Get-ExecutionPolicy -List | ft -AutoSize
    Set-ExecutionPolicy AllSigned -Scope LocalMachine
    
  6. Test your newly signed script and trust your certificate as needed. (Note this is required even if you have a publicly issued cert.)
    .\somescript.ps1