PowerShell: Script Signing

Signing a script doesn’t mean you’re approving or authorizing it, as you might do with a contract or a credit card slip. In the world of digital security, signing is the process of affixing your identity to something and ensuring that the state or condition of that “something” has not been modified in any way. It’s all based on cryptography, and cryptography (at least in this case) starts with a pair of encryption keys.

These keys are referred to as asymmetric keys because they are different from one another. The pair consists of a private key, which is accessible only to you, and a public key, which is accessible to anyone. I’m simplifying this a bit, but basically anything encrypted with the private key can only be decrypted with the matching public key.

Windows PowerShell itself has a cmdlet that can take the script and the certificate and perform the actual signing. When it does this, Windows PowerShell takes the script and encrypts it using my private key. That encrypted copy appears as gibberish text that is added to the bottom of the script as a series of comments. The identity is also encoded, but not encrypted, into those comments – the identity itself can be read without resorting to cryptography.

When Windows PowerShell looks at the signature, it decodes my identity and uses it to obtain my public key. Since only my public key can decrypt the rest of the signature, Windows PowerShell knows that if it’s able to decrypt the signature, then I must have signed it. If someone else had signed it, my public key wouldn’t be able to decrypt the signature. Once the signature is decrypted, Windows PowerShell compares the script to the copy that had been encrypted into the signature. If they match, then the signature is considered intact. If the two scripts differ, the signature is broken and considered invalid.

While the script itself can be easily changed, the signature can’t be changed to match the modified script without using my private key – and only I have my private key. In reality, signatures can and do contain more information, such as a copy of my public key, information about the certificate authority (CA) that issued the keys, and so on. But my description here certainly outlines the important parts of the process and highlights the fact that a signature actually does protect your script from unauthorised modification.

Creating and Using Self-Signed Certificates

To sign scripts, you will need a specific kind of certificate – a Class III Authenticode Code-Signing Certificate – and there are three main ways to get one. The first is to use your organization’s internal Public Key Infrastructure, or PKI, if it has one. A well-implemented PKI will have its root CA trusted by all of your organization’s computers – this is necessary for the certificates issued by the CA to be usable in the organization.

Windows Server has shipped with its own Certificate Server software since Windows 2000, and you can use that software to create your own PKI. A full-fledged PKI implementation requires a lot of planning, but if you just need a PKI for the sole purpose of issuing code-signing certificates, you don’t need to do a ton of work. You can simply use Group Policy to push your CA’s root certificate out to your computers so that they’ll trust it.

A second option is to use a commercial CA. One benefit of using a commercial CA is that, if you select one of the major CAs, your organization’s computers are probably already configured to trust certificates from that CA. (Note that Windows XP trusts a large number by default, while Windows Vista trusts a far smaller number by default). One popular commercial CA is VeriSign (verisign.com). There are others worth investigating, also, such as CyberTrust (cybertrust.com) and Thawte (thawte.com).

The third option for obtaining a code-signing certificate is to make your own self-signed certificate using a tool like makecert.exe. It ships with the Windows Platform SDK, it installs in some editions of Microsoft Office, and it can be found in many other places. The upside of a self-signed certificate is that it’s free and doesn’t require any infrastructure. The downside, however, is that it’s really only usable on your computer. But if you just need to enable script execution on your computer – for testing or general experimentation with code signing –makecert.exe is a great option. Documentation about this tool is here. Just be aware that many versions of this tool have existed over the years, so it’s possible that your computer is running an older version that doesn’t work exactly as the documentation describes.

Once you have makecert.exe, you can create a self-signed certificate using the same tool makecert.exe. This utility is included in the Microsoft .NET Framework Software Development Kit (SDK) Versions 1.1 and later, and in the platform-specific Windows SDK. After you download and install the appropriate SDK, you must:
1. Open an elevated, administrator command prompt (cmd.exe).
2. Use the command prompt to create a local certificate authority for your computer.
3. Use the command prompt to generate a personal certificate via this certificate authority.
You create the local certificate authority by entering the following command:

makecert -n "CN=PowerShell Local Certificate Root" -a sha1 -eku `
1.3.6.1.5.5.7.3.3 -r -sv root.pvk root.cer -ss Root -sr localMachine

You’re running this from a command prompt with Administrator privileges in the directory where makecert is located. Note that this is a single command, and the parameters are used as follows:

  • The –n parameter sets the certificate name. The name value is preceded by CN=.
  • The –a parameter sets the signature algorithm as SHA1 as opposed to MD5 (the default value).
  • The –eku parameter inserts the enhanced key usage object identifier: 1.3.6.1.5.5.7.3.3.
  • The –r parameter specifies that you want to create a self-signed certificate.
  • The –sv parameter sets the file names for the private key file and the certificate file that makecert will create.
  • The –ss parameter sets the name of the store that stores the output certificate as Root for the Trusted Root Certificate Authorities store.
  • The –sr parameter sets the certificate store location as the local machine, as opposed to the current user (the default value).

You generate a personal certificate via this certificate authority by entering the following command:

makecert -pe -n "CN=PowerShell User" -ss MY -a sha1 -eku `
1.3.6.1.5.5.7.3.3 -iv root.pvk -ic root.cer

Note that this is a single command, and the parameters are used as follows:

  • The –pe parameter marks the certificate’s private key as exportable.
  • The –n parameter sets the certificate name. Again, the name value is preceded by CN=.
  • The –ss parameter sets the name of the store that stores the output certificate as MY, for the Personal store.
  • The –a parameter sets the signature algorithm as SHA1.
  • The –eku parameter inserts the enhanced key usage object identifier: 1.3.6.1.5.5.7.3.3.
  • The –iv parameter specifies the name of the CA’s private key file.
  • The –ic parameter specifies the name of the CA’s certificate file.

The first command generates two temporary files: root.pvk and root.cer. The second command uses these files to create a certificate that is stored in the Personal certificate store on the local computer. MakeCert will prompt you for a private key password. To verify that the certificate was generated correctly, use the following command to search for the certificate in the Personal certificate store on the computer:

get-childitem cert:\CurrentUser\My -codesigningcert

Signing Scripts

To sign scripts, you can use the Set-AuthenticodeSignature cmdlet. This cmdlet creates digital signatures using a digital certificate. Digital certificates can be created by a certificate authority (CA), or you can create your own self-signed certificates. When you use certificates created by a CA, you can use the certificate on any computer that trusts the CA. When you use self-signed certificates, you can use the certificate on your local computer.

In Windows domains, you can use Active Directory Certificate Services to establish a certificate authority (CA) and create digital certificates. As most enterprises have CAs and use digital certificates to enhance security, you may already have been issued a digital certificate that you can use for code signing. To find out, enter the following command:

get-childitem cert:\CurrentUser\My -codesigningcert

Or you can examine the certificates store. The certificates store on a Windows computer stores trust information. In the certificates store, you can view information about the following:

  • Personal Certificates stored on the local computer that are assigned to you for various uses.
  • Other People Certificates stored on the local computer that are assigned to other people for various uses.
  • Trusted Root Certification Authorities Root CAs your computer trusts. Your computer will trust any certificates from these root CAs.
  • Trusted Publishers Publishers whose digitally signed scripts are trusted.
  • Untrusted Publisher Publishers whose digitally signed scripts are not trusted.

You can access the certificates store through the Internet Properties dialog box. In Control Panel, select Network And Internet and then click Internet Options. In the Internet Properties dialog box, on the Content tab, click Certificates to display the Certificates dialog box. Use the Certificates store to examine the various types of trust information and related details.

PowerShell makes certificates available through the Cert provider. The data that a provider exposes appears as a drive that you can browse much like you browse a hard drive. If you enter cd cert: at the PowerShell prompt, you will access the certificates store on your computer. If you then enter dir (which is an alias for Get-ChildItem), you’ll see a list of locations you can browse. Typically, this will include CurrentUser and LocalMachine, which are the certificate stores for the currently logged-on user and the local computer, respectively. While you are working with the Cert provider, if you enter cd currentuser and then type dir again, you’ll see all the substores for the current user. One of these stores is the My Store, where personal certificates are stored. You can see the personal certificates for code signing in the My Store by entering cd my and then entering dir –codesigningcert.

In PowerShell, you can reference individual elements in an array by their index position. The first element in an array has the index position 0, the second 1, and so on. If you have a personal certificate for code signing issued by a CA, you can sign unsigned scripts using the following commands:

$cert = @(Get-ChildItem cert:\CurrentUser\My -codesigningcert)[0]
Set-AuthenticodeSignature ScriptName.ps1 $cert

Conclusion

The fact is security almost always involves some level of hassle. Windows PowerShell is a powerful tool, and like any other tool, there is always the potential for it to be twisted into a security vulnerability by a malicious user. That is why it is important for you to take steps to head off these malicious users. Code signing is the best step you can take, and it doesn’t take much involvement. For example, you can use a script editor that automatically signs your scripts every time you press Save, so code signing becomes pretty much transparent. Another way would be to add the following function to your PowerShell profile:

function sign ($filename)
{
    $cert = @(gci cert:\currentuser\my -codesigning)[0]
    Set-AuthenticodeSignature $filename $cert
}

Now you can sign your script by simply entering:

sign ./scriptfile.ps1

Of course, that profile script should be the first one you sign.

Deploying a one-server PKI for the sole purpose of issuing a code-signing certificate isn’t much work. (Keep in mind, though, that you really do need to research and plan your PKI, including protecting the root CA and providing disaster recovery, especially if it will be used for anything else.) Makecert.exe is always available if you’re the only one who will need to run scripts in your environment.

2 Responses to “PowerShell: Script Signing”

  1. Rex Wilmot August 17, 2012 at 6:49 pm #

    Wow, great blog.Thanks Again. Keep writing.

  2. jespernohr2015 November 22, 2016 at 12:34 pm #

    nice article. thank You!

Leave a Reply

revia in alcoholism