PowerShell Code signing
Signing a script attaches a cryptographic signature that proves who published it and that the file has not changed since. Set-AuthenticodeSignature signs, Get-AuthenticodeSignature checks.
Documentation: Microsoft Learn
Overview
You need a code-signing certificate. In an enterprise that comes from your internal CA; for public distribution it comes from a commercial CA. New-SelfSignedCertificate -Type CodeSigningCert makes one for testing, and it is only trusted on machines where you have installed it into the trusted roots, which is fine for a lab and wrong for anything else.
The verification statuses are worth knowing: Valid, NotSigned, HashMismatch when the file changed after signing, UnknownError, and NotTrusted when the signing certificate's chain is not trusted on this machine. NotTrusted is the one you meet with self-signed certificates.
Add a timestamp with -TimestampServer. Without it, the signature stops validating when the certificate expires. With it, the signature remains valid because the timestamp proves the file was signed while the certificate was still good. Skipping this is the mistake that shows up a year later, all at once.
Editing a signed file breaks the signature. Sign as the last step of your release, not while you are still working.
Inspect this example’s structure
Read the command and its supplied arguments. This example requires real PowerShell or its module; this diagram does not execute it.
[pscustomobject]@{
Step1 = 'Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert'
Step2 = 'Set-AuthenticodeSignature -FilePath .\script.ps1 -Certificate $cert'
Step3 = 'Get-AuthenticodeSignature .\script.ps1'
Note = 'timestamp the signature so it outlives the certificate'
} | Format-List- Stage 1[pscustomobject]@{
Positional values or no named parameters - Stage 2Format-List
Positional values or no named parameters
An example to inspect
Preview the example. Sign in for the full lesson and activities.
[pscustomobject]@{
Step1 = 'Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert'
Step2 = 'Set-AuthenticodeSignature -FilePath .\script.ps1 -Certificate $cert'
Step3 = 'Get-AuthenticodeSignature .\script.ps1'
Note = 'timestamp the signature so it outlives the certificate'
} | Format-ListWhat happens
This prints the three commands in order rather than signing anything, since there is no certificate here. Step 1 finds a code-signing certificate in your personal store, step 2 applies it, step 3 verifies the result. The note is the part that gets forgotten until certificates start expiring.
Worth remembering
- Set-AuthenticodeSignature signs; Get-AuthenticodeSignature verifies.
- Self-signed certificates are only trusted where you installed them.
- HashMismatch means the file was edited after signing.
- Use -TimestampServer or signatures die with the certificate.
Primary documentation
Try it
Expected output · prints command text; does not execute these commands
Example reader
Inspect this page's example
Run supported commands against the practice data, or inspect the example without executing it.
Ready to inspect.
Prints the sign-and-verify sequence and the timestamping caveat.
Show reference output
Expected output · prints command text; does not execute these commands
Step1 : Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert Step2 : Set-AuthenticodeSignature -FilePath .\script.ps1 -Certificate $cert Step3 : Get-AuthenticodeSignature .\script.ps1 Note : timestamp the signature so it outlives the certificate
Keep going
Next: PowerShell Least privilege automationCheck your understanding
A signed script changes after signing. What should you check before running it?
Inspect Get-AuthenticodeSignature. A changed file can invalidate its signature. Review the change, then have the approved publisher sign the final file again.