Have you opened a PowerShell script and seen random gibberish near the bottom? That seemingly useless chunk of code is not for nothing. Instead, that shows a PowerShell digitally-signed script.
A PowerShell digitally signed script is a script that contains the identity of the source that signed it. What are the benefits when you digitally sign a PowerShell script?
For example, before you publish a script to the internet, it is a good idea to digitally sign your scripts so that the original code is marked and can be identified. Any changes to the original code invalidate the existing signature, which would clearly indicate the code has been modified.
Also, PowerShell has a set of Execution Policies. In Windows Servers, the default is Remote Signed, which only permits running scripts obtained from the internet that are digitally signed. In some security-conscious organizations, administrators set the execution policy to All Signed on all client computers, which means you can only run signed scripts and nothing else.
In these cases, if the PowerShell script is not digitally signed, you’re in a pickle. But not all hope is lost. Look at this situation as the opportunity to better protect your scripts by digitally signing them yourself, and this post will teach you how.
Requirements
- A computer running any recent version of the Windows Server or Desktop operating system.
- Windows PowerShell 5.1 or the latest version of PowerShell Core, 7.3.0, as of this writing.
- A sample PowerShell script is not digitally signed. A simple Hello World script should suffice for this demo.
Get a Code Signing Certificate
Before signing a certificate, you must have a code signing certificate (Authenticode). There are three Authenticode certificates you can get depending on how you plan to deploy your scripts.
- A public or global digital certificate — This type of Authenticode certificate is not free, and you must purchase them from a well-known digital certificate provider, like DigiCert.com, Comodo, or SSL.com.
- A certificate issued by an internal certificate authority — This certificate type is usually found in a network setting where the organization issues certificates from the CA server. As such, this certificate has no additional cost as long as the CA infrastructure is already in place.
- A self-signed Authenticode — This certificate is free but the least trusted since no other party verifies its validity and identity. But for this demo, a self-sign certificate should be enough.
Let’s create the self-signed certificate. Open PowerShell as admin and run the below command. This command generates a digital certificate called MyDigitalCertificate, and the type is CodeSigningCert. The certificate store location in this example is Cert:\LocalMachine\My, a system-wide store. The only other valid certificate store location is Cert:\CurrentUser\My.
# Create the authenticode certificate $certSplat = @{ Subject = 'MyDigitalCertificate' CertStoreLocation = 'Cert:\LocalMachine\My' Type = 'CodeSigningCert' } $authenticode = New-SelfSignedCertificate @certSplat $authenticode
Make sure to copy the certificate thumbprint value. You’ll need this later when signing.
Next, open the Certificates management console by running the below command.
certlm.msc
Go to Personal → Certificates and locate the certificate you created. Once you find the certificate, copy it to the following location.
- Trusted Publishers → Personal.
- Trusted Certification Authorities Authorities → Personal.
The above step ensures that the computer trusts the code-signing certificate from end to end.
You now have a valid, trusted code signing certificate.
Sign PowerShell Scripts
So now you have the code signing certificate, let’s use it to sign a PowerShell script. For this, we’ll use a simple PowerShell script to sign. In this example, the PowerShell script is named hello-world.ps1.
First, retrieve the code signing certificate and store it in the $certificate variable. Replace the 8455C4AC6F3D8B5C81AEB61DBCC31E520284B687 with your certificate’s thumbprint.
$certificate = Get-Item Cert:\LocalMachine\My\8455C4AC6F3D8B5C81AEB61DBCC31E520284B687
Next, take the below code and replace the FilePath value with your script’s file path. The TimeStampServer in this example points to the DigiCert timestamp URL.
*A timestamp server adds a verified timestamp to the digital signature. This ensures that the code does not expire even when the code signing certificate expires.
Other timestamp servers exist, one from Comodo, which is http://timestamp.comodoca.com.*
$signSplat = @{ FilePath = ".\hello-world.ps1" Certificate = $certificate TimeStampServer = 'http://timestamp.digicert.com' } Set-AuthenticodeSignature @signSplat
And that’s it! Your script is now digitally signed.
Verify the Script Digital Signature
How do you verify that that PowerShell script has a digital signature? There are a few ways.
First, you can open the script and confirm that the digital signature exists. The signature block starts with # SIG # Begin signature block.
Second, you can open the script’s file properties and go the Digital Signatures tab.
Third, run the Get-AuthenticodeSignature cmdlet against the PowerShell script file.
Get-AuthenticodeSignature -FilePath .\hello-world.ps1
As you can see below, the script’s signature is valid and verified.
Conclusion
If a PowerShell script is not digitally signed, it may be modified and passed on as the original. Digitally signing your scripts helps comply with system policies and ensure that your scripts have verified publisher identities.
- How to find printer IP address? - 20.03.2023
- How to fix ERR_CERT_AUTHORITY_INVALID? - 20.03.2023
- How to Fix Side by side Configuration is Incorrect in Windows 10/11? - 13.03.2023