Learn about Windows PowerShell
Hey Scripting Guy! Will you give us the final steps in the step-by-step guide about how to use an existing Windows PKI installation to sign Windows PowerShell scripts, or were you just kidding yesterday?
Microsoft Scripting Guy Ed Wilson here. Just as we promised yesterday, we continue today with the final steps involved in Windows PKI to sign scripts. We go back to Ragnar Harper.
Step 3: Sign my Windows PowerShell script and run it
In this step we will be inside Windows PowerShell, and we will sign our script. For this purpose I have a simple script named demoscript.ps1.
$yourName=Read-Host "What is your name?"
Write-Host "Hello $yourName"
Just a quick reminder that your requirements for signed scripts are set using the Set-ExecutionPolicy cmdlet (or by Group Policy).
No requirements; all scripts allowed
All local scripts allowed; only signed remote scripts
All scripts need to be signed
No scripts allowed
For this demonstration, my executionpolicy is set to AllSigned. If I just try to run my script, it will fail, as shown in the following image.
We will use the cmdlet Set-AuthenticodeSignature to sign the script. I will start storing the code signing certificate in a variable named $cert.
$cert=(dir cert:currentuser\my\ -CodeSigningCert)
Then I am ready to sign my script with the Set-AuthenticodeSignature cmdlet. This is shown in the following image.
As you see, the status is valid, so the signing was successfully done. Please note that I recommend that you supply the TimeStampServer parameter. This will make sure the script works even though the certificate that signed it is expired. It will tell the system that the code signing certificate was valid at the time of signing. (Okay, I can imagine there are some situations where this might not be correct, but I also guess it will be good enough for most of us.) If you do not use the TimeStampServer parameter, the script will stop to work when the certificate used for signing expires. There are multiple sources for timestamping out there. Use one that suits you.
Let us try to run the scripts again, and see what happens. The results are shown in the following image.
We get a question if we want to run the script or not. The question says that this is a script from an untrusted publisher. In Step 4, I will show you how to make the publisher (code signing certificate) trusted for your domain.
As for this computer, you can now make this publisher trusted by choosing A for Always run. If you choose V for Never run, you will explicitly make this publisher untrusted, and scripts signed by this certificate will not run.
Let’s stop and see what exactly is happening here. If you make any choice persistent (such as Always run or Never run), the code signing certificate is stored as a trusted or untrusted publisher on your computer. You can see this through the GUI if you open mmc.exe and load the Certificates snap-in, as shown in the following image.
Or, you could also do this from Windows PowerShell:
As you will see in Step 4, you can also control this setting through Group Policy. For now, you can just click Run Once, and the script is allowed to execute. If you open the script, you will see that the signature is attached at the bottom.
You can also use validate the signature using the Get-Authenticode cmdlet.
In this step, I showed you how to sign a Windows PowerShell script, and also how to make it trusted or untrusted on your computer. In the next step, we will make the code signing certificate trusted in our domain using group policy.
Step 4: Make the code signing certificate trusted in my domain
If you were to deploy this in your domain, you would probably use Group Policy to make sure the code signing certificate in use is a trusted publisher. To do this there a two steps:
1. Export the code signing certificate.
2. Create a policy and import the code signing certificate into trusted publishers.
Let’s start with exporting the code signing certificate from the client computer where we requested the certificate.
Start the Certificates snap-in as shown in Step 2 yesterday. Open the Personal node, and then Certificates. In the content pane, you will now see your certificate. (The one with Intended Purpose set to Code Signing). Right-click the certificate, click All Tasks, and then click Export. You can see this in the following image.
Click Next in each of the three dialog boxes you see. Make sure that you save the certificate somewhere you can access it from the computer on which you are going to run Group Policy Management. There is no security risk making the public part of this certificate available, so you can store it wherever you want.
This finishes the export part from the client. Now we need to open up the Group Policy Management Console. This is a part of the Server Administration tools and is usually found if you have installed RSAT (Remote Server Administration Tools) on your client or on your domain controller. For this demonstration, I will run this from one of my domain controllers.
When I open the Group Policy Management Console, I start by creating a new policy. I open my domain (harper.labs), right-click it, and click Choose Create a GPO in this domain, and link it here.
Make sure that you create this Group Policy object (GPO) where you want it in your own domain. For this demonstration, I create it at the domain level. I give the policy the name Certificates Policy, and I click OK.
Select the policy (Certificates Policy) in the navigation pane, right-click it, and click Edit, as shown in the following image.
Wait for the Group Policy Editor to start, and then click Computer Configuration, click Policies, click Windows Settings, and then click Public Key Policies. You are now ready to start the import. Right-click Trusted Publishers, and then click Import.
In the dialog box that asks you for the certificate to import, select the certificate you exported earlier. Then click Next.
As shown in the following image, make sure the certificate is placed in the Trusted Publishers store, and click Next.
Now finish the wizard by clicking Finish. You have imported the certificate as a trusted publisher, which is shown in the following image.
You can confirm this by looking inside the Trusted Publishers node in the Group Policy Editor as shown in the following image.
So, the next time the policy is updated on computers in your domain, they will add this certificate as a trusted publisher. You can now run scripts signed by this certificate without being asked if the certificate is trusted or not. You can also do the same with untrusted certificates if you want.
I will test this from my client computer. I will first make sure that the certificate is not in my trusted publishers list. This should be done through the Certificates snap-in on my client.
Then I run gpupdate /force from my Windows PowerShell window. The results are shown in the following image.
When the update is finished successfully, I refresh the Trusted Publishers list in my Certificates snap-in. My certificate should now be listed as trusted, as shown in the following image.
I hope this helps, and have fun with Windows PowerShell!
HR, that is all there is to using Windows PKI to sign Windows PowerShell scripts. This brings Guest Blogger Week to a close. Check with us tomorrow as we delve into the virtual mail bag for Quick-Hits Friday.
If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at firstname.lastname@example.org, or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson and Craig Liebendorfer, Scripting Guys
Another great article - and if I may, I have a small addition to make.
The steps noted in the article do indeed work just fine, so long as you are editing your scripts in notepad and signing them from the console. If you use the ISE and save your scripts, the signing noted above does not work correctly (by default). By default, the ISE saves scripts in Unicode Big Endian - which Set-Authenticode does not like signing. Here is a short transcript of what I see:
Psh[Cookham8]>Set-AuthenticodeSignature .\helloworld.ps1 -cert $cert
SignerCertificate Status Path
----------------- ------ ----
If I then use notepad to save the file as ansii, the results are what you want.
D42B4A6B4DBB8C697E5CA2CDD51A7F1F9325B632 Valid helloworld.ps1
I have written a short blog article about this as it has caught a number of folks out. See: tfl09.blogspot.com/.../signing-powershell-scripts.html
Couldn't read the text in the PS images.
What do yo do if you have to edit the script after it has been signed?
Thanks Ed! Great post.
A very special thanks to Thomas Lee for supplying the final critical piece of the puzzle, it was a sanity saver to say-the-least.
Boom! This article (and its preceding twin, Part 1) are dynamite. Helped immensely. I can now confidently roll out scripts without setting every client in the enterprise to unrestricted.
It seems like a ridiculously complex process to me...
Thanks this is great information. If I want to sign files uses by PowerShell, for example say I have external text files like .SQL files, how could I sign as well? Or should I put them in .ps1 file as well? Thanks,
If you already have your ExecutionPolicy set to RempteSigned, this error will only occur occasionally if you download scripts from other locations.
The simplest thing to do is just create a new script and copy the contents. This whole signing process and certificates for an uncompiled text file is just ludicrous.
Excellent post. I must say that you have provided a great amount of information about digital signatures in this article. You have also posted the steps to create digital signature which helped me to successfully design my own signature. Thanks a lot.
[url="http://www.arx.com"]digital signature FAQ[/url]
Top comment and addition to the walk-through here.
While I appreciate learning something as important as signing scripts, the contrast in workload between signing a script to run it on a non-Unrestricted compared to just copy+pasting it into a new .ps1 file seems oddly diverging.
From a naive, practical point of view I'd wonder why I would want to go through the extra hassle if I could just "new file->copy+paste" any script on the machine in question, heck, I could even write a script that would do that for me if I have a constant coming
and going of different PS scripts that need to be executed.
I had some trouble with the Trusted Publishers GPO and wanted to post my correction to the steps mentioned above. In addition to adding the Trusted Publisher Certificate to the GPO you need to do this:
In the console tree under Computer Configuration\Windows Settings\Security Settings, click Public Key Policies.
Double-click Certificate Path Validation Settings, and then click the Trusted Publishers tab.
Select the Define these policy settings check box, select the policy settings that you want to apply, and then click OK to apply the new settings.
Awesome article. Very helpful.
I can get this to work as described by trusting the direct code signing certificate. But in most cases, clients will trust the enterprise CA. If I import the enterprise CA cert into Trusted Publishers I still get a prompt from PowerShell. How can I get
PowerShell to use a proper PKI chain of trust? Otherwise when I leave the company and they disable my account, my scripts will fail!
/\ Opps, should have signed in to be notified /\