Signing a PowerShell Script
Signing a script is a straightforward process. You use the Set-AuthenticodeSignature cmdlet and specify the script file to sign and the code-signing certificate to use when signing the file. For example, suppose you want to sign the C:\Audit\SecurityAudit.ps1 script file, which callout A in Listing 1 shows. (You can download SecurityAudit.ps1 by clicking the Download the Code Here button at the top of the page.) This script retrieves the most recent 20 events listed in the Security log.
Listing 1: Signing the SecurityAudit.ps1 Script File |
 |
The following statements first specify the script file and certificate, then run the Set-AuthenticodeSignature cmdlet:
$file = "C:\Audit\SecurityAudit.ps1"
$cert = Get-ChildItem cert:\CurrentUser\My `
-CodeSigningCert
Set-AuthenticodeSignature $file $cert
In the first statement, I assign the full filename as a string to the $file variable. In the second statement, I use the Get-ChildItem cmdlet to retrieve the code-signing certificate from the certificate store and assign it to the $cert variable. To retrieve the certificate, I specify as a path cert:\CurrentUser\My. The cert: prefix is the drive used to access the certificate store. This is followed by CurrentUser, which refers to the location within the certificate store. The My refers to the certificates within the Personal folder. When you use the Get-ChildItem cmdlet to retrieve the certificate, you should also include the -CodeSigningCert switch parameter to retrieve only the certificates that have code-signing authority.
If the My certificate store contains more than one code-signing certificate, the $cert variable will contain those certificates, in which case you must specify the desired certificate when you reference the $cert variable. One way to do this is to add the object index after the variable name. For example, you'd use $cert\[0] to call the first code-signing certificate, $cert\[1] to call the second one, and so on. However, if you know that there is only one code-signing certificate, you don't need to include the bracketed index reference.
After you have set the values of the $file and $cert variables, you're ready to sign your code. The third statement in the example uses the Set-AuthenticodeSignature cmdlet to sign the code. Notice that you provide the filename ($file) and certificate ($cert) as the two arguments to the cmdlet. When you run the command, the certificate is used to digitally sign the file. You can verify that a file has been signed by viewing its contents. At the end of the file, you'll find a block of commented code that is the digital signature. Callout B in Listing 1 shows what this signature might look like. After the file has been signed, you can run the script.
When you run a script that's been digitally signed, you'll be prompted to verify whether it's safe to run it. You can choose to never run the file, not run it this time, run it once, or always run it. If you chose to never run the file or always run the file, you won't be prompted again if you try to run the script. Otherwise, you'll be prompted the next time you try to run it.
Using a .pfx File to Sign a Script
If you use a private certificate to sign your files, it's still possible for a malicious program to use the certificate to sign a script, thus allowing an unwanted script to run. A way to help avoid this problem and provide even further protection for your system is to export your code-signing certificate to a .pfx file, then use that file to sign your script.
To export your certificate, open the Certificates snap-in and locate your certificate (refer back to Figure 4). Right-click the code-signing certificate, point to All Tasks, then point to Export. This launches the Certificate Export wizard. Follow the steps in the wizard to export the file. Be sure to export the private key along with the certificate and enable strong protection. You can also choose whether to include all certificates in the certification path, delete the private key, and export extended properties. You'll also need to provide a password and the file location. For these examples, I saved the file to C:\Audit\PS_Cert.pfx. After you export the certificate, be sure to delete it from the certificate store and be sure to store the .pfx file in a secure location.
After you run the wizard, you're ready to sign the file. As before, the first two statements should define the necessary variables, as in
$file = "C:\Audit\\SecurityAudit.ps1"
$cert = Get-PfxCertificate C:\Audit\PS_Cert.pfx
In the first statement, I assign the script file's location to the $file variable. Next, I use the Get-PfxCertificate cmdlet to retrieve the .pfx file and save it in $cert.
When you run the second statement, you'll be prompted for a password. This is the password you specified when you exported the certificate to the file. As before, you use the Set-AuthenticodeSignature cmdlet to sign the file. When you run the cmdlet, specify the script and.pfx files, as in
Set-AuthenticodeSignature $file $cert
That's all there is to signing your file. As you can see, once you've created your certificate and, optionally, exported it to the .pfx file, it's a simple matter to sign the files, yet an effective way to help secure your system. As any administrator knows, you can never be too careful, especially when it comes to protecting your PowerShell scripts.