How to sign a powershell script

How to sign a powershell script

$cert=(dir cert:currentuser\my\ -CodeSigningCert)

Set-AuthenticodeSignature demoscript2.ps1 $cert -TimestampServer

My PKI root is called pki.harper.labs, and it is already trusted by my domain members, as shown in the following image.

I will follow these steps:

  1. Make the code signing certificate template available on my issuing certificate server.

  2. Request a code signing certificate for my user.

  3. Sign my Windows PowerShell script and run it.

  4. Deploy the code signing certificate as a trusted publisher through Active Directory.

Step 1: Make the code signing certificate template available on my issuing certificate server

Let’s start with making the code signing certificate available on the issuing certificate server so that our certificate server will issue code signing certificates. I do this at the issuing certificate server, and I start the Server Manager console and open the Active Directory Certificate Services node.

We will start with a look at the code signing certificate template. Find the template in the Certificate Templates node right under the Enterprise PKI node. This is called the Certificate Templates snap-in (and if you want you can open it up as a standalone snap-in in the Microsoft Management Console [mmc.exe]). This is shown in the following image.

I will not discuss how to create copies of the template here, so I will just use the existing certificate template. If you double-click the code signing template, you will get a property sheet with a few tabs, as shown in the following image.

Because we are not creating a duplicate copy, we cannot change any of the values on the General tab. If we created a duplicate, we could change those. For example, how long should the certificate be valid? The same goes for Request HandlingSubject Name, and Extensions. If we wanted to change those, we would have to create a duplicate.

What we will look at is the Security tab. We are interested in the permission to enroll---who should be able to enroll for a code signing certificate? I create a group in Active Directory called Codesigners, and I grant the Read and Enroll permissions shown in the following image.

Then I make members of this group the users who should be able to get a code signing certificate. I click OK, and continue to the make the certificate template available on my issuing certificate server.

Next, I open the Certificate Authority console (the node is named pki.harper.labs in my environment, and is found under the Certificate Templates node in Server Manager, as shown in the next image). In the Certificate Authority console, you also see a Certificate Templates node. If you want to check if the code signing certificate template is available for enrollment, see if it is shown in the list. This is shown in the following image.

If the code signing template is not shown, we will add it. Right-click the Certificate Templates node, point to New, and then click Certificate Template to Issue, as shown in the following image.


From the list that appears, such as is shown in the following image, select the code signing template, and then click OK. This list is read from Active Directory, and if you just created the template, you might have to wait until it is replicated to all domain controllers.

We are now able to request a code signing certificate, and enroll the users we gave Enroll permission on the template.

Step 2: Request a code signing certificate for my user

This step is done from my client computer, as a user that is member of the Codesigning group. I open the certificates snap-in through the Microsoft Management Console (mmc.exe). Then I add the Certificates snap-in by clicking File, and then clicking Add/Remove Snap-in. This is shown in the following image.

Click Certificates in the left pane, as shown in the following image. Click Add, and then click OK.

You want the snap-in to manage your user account, so click My user account. Now that you have loaded the snap-in, let’s request a code signing certificate. Right-click Personal, point to All Tasks, and then click Request New Certificate.

Just click Next in the first dialog box. Because we are requesting a certificate from our enterprise PKI, in the next dialog box, select the Active Directory Enrollment Policy, and then click Next, as is shown in the following image.

Because we made the code signing template available in step 1, you should see the template for code signing available for enrollment. You only see the certificates you have permissions for in the list, so if the code signing template does not show up, have a closer look at the permissions. Click the Code Signing certificate. If you look at the details, you will see the validity period of the certificate (the default template is one year or 365 days, as the details say).

All the information that is needed to create the certificate is automatically configured, but if you want, you can change some of it if you click Properties. For example, if you want to make the private key exportable so that you can export/import the private keys to other computers, you can configure this by clicking Properties, and then clicking the Private Key tab, as shown in the following image. This is necessary if you want to use the same certificate on multiple computers.

When you are ready, click Enroll. Wait while the certificate is being generated and issued. Click Finish. You have now created a certificate for code signing!

Just a quick reminder that your requirements for signed scripts are set using the Set-ExecutionPolicy cmdlet (or by Group Policy).

UnrestrictedNo requirements; all scripts allowed
RemoteSignedAll local scripts allowed; only signed remote scripts
AllSignedAll scripts need to be signed
RestrictedNo 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 theTimeStampServer 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 theTimeStampServer 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:

dir cert:\CurrentUser\TrustedPublisher

dir cert:\CurrentUser\Disallowed

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.
Export the code signing certificate

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.
Create a policy and import the code signing certificate into trusted publishers

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 clickImport.

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.

Psh[Cookham8]>Set-AuthenticodeSignature .\helloworld.ps1 -cert $cert

   Directory: C:\foo

SignerCertificate                         Status                                       Path                                        

-----------------                         –----                                       –--                                        

                                         UnknownError                                 helloworld.ps1      

If I then use notepad to save the file as ansii, the results are what you want.

Psh[Cookham8]>Set-AuthenticodeSignature .\helloworld.ps1 -cert $cert

   Directory: C:\foo

SignerCertificate                         Status                                       Path                                        

-----------------                         –----                                       –--                                        

D42B4A6B4DBB8C697E5CA2CDD51A7F1F9325B632  Valid                                        helloworld.ps1