Windows PowerShell Sign Here, Please

Don Jones

In the January 2008 Windows PowerShell column, I stressed the importance of digitally signing your Windows PowerShell scripts. And I described some scenarios where any execution policy other than AllSigned could create significant security vulnerabilities in your environment.

One thing I didn't cover in much detail is how to actually sign your scripts. You can take a look at that column at technetmagazine.com/issues/2008/01/PowerShell. Guess what I'm covering this month?

More than Just a Signature

The word signature is the correct technical term for what I'm talking about, though the word itself isn't really a good description of what is actually happening here. 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.

Here's how it works. Again, I'm using some simplification just to keep the discussion moving along. I have a Windows PowerShell® script that I want to sign and I have a certificate containing my private key that I want to use to sign the script. Windows PowerShell itself has a cmdlet, which I'll discuss in a moment, 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 (see Figure 1). My identity is also encoded, but not encrypted, into those comments—the identity itself can be read without resorting to cryptography.

Figure 1 Signature block stored at the bottom of your script

Figure 1 Signature block stored at the bottom of your script (Click the image for a larger view)

Later, 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.

This is how a signature protects a clear-text script from being modified without someone noticing. Remember, 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.

This is definitely a simplification of the underlying technical process. 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 unauthorized modification.

A critical security factor is the absolute protection of your private key—you don't want this falling into someone else's hands. When you install your key in Windows, you should immediately password-protect it so that a malicious process can't use your key without your knowledge. Smart cards provide even better security since they contain your private key. With a smart card, your private key never leaves the card. Instead, the data that you want to encrypt is transmitted to the card through its reader hardware, and the circuitry of the card itself performs the encryption and sends the result back.

Getting a Certificate

Cmdlet of the Month: Export-CSV

The folks in the forums at PowerShellCommunity.org often ask if Windows PowerShell can export to Microsoft Excel®. Of course it can! The Export-CSV cmdlet does the trick, since Excel can natively open, edit, and save CSV files. For example, if you want to export a list of services, just run Get-Service | Export-CSV MyServices.csv. In fact, you can tack Export-CSV onto the end of any pipeline and all the objects in that pipeline will wind up in the CSV file you specify. By default, Export-CSV adds a header line to the top of the file, which it uses to specify the type of object that was exported. This line is commented so it won't prevent Excel from opening the file. If you don't want that type information exported, you can just add the -notype parameter to Export-CSV. In addition, you can add the -force parameter to force overwriting an existing CSV file of the same name without prompting (the default is to prompt), or you can add the aptly named -noClobber parameter to prevent overwriting an existing file.

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 at go.microsoft.com/fwlink/?LinkId=108538. 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 then create a self-signed certificate by running something like the following from the Windows PowerShell command line (and don't let me catch anyone using cmd.exe when you're reading this column):

makecert -n "CN=MyRoot" -a sha1 –eku
1.3.6.1.5.5.7.3.3 -r -sv root.pvk root.cer 
–ss Root -sr localMachine

And then run the following:

makecert -pe -n "CN=MyCertificate" -ss MY 
–a sh1 -eku 1.3.6.1.5.5.7.3.3 -iv root.pvk 
–c root.cer

Makecert.exe will prompt you for a password to protect the key, and then it will create the certificate. You can verify the installation by running this code in Windows PowerShell:

gci cert:\CurrentUser\My -codesigning

This will display a list of all the items in the CERT: drive (that's your Windows certificate store) that are code-signing certificates. By the way, all of this is also documented in Windows PowerShell itself. To find that documentation, just run help About_Signing and read down a few screens.

Signing a Script

Now that you've got a certificate and you've got a script (for testing purposes, you can just make one up quickly, if you need to), you're ready to sign the script. It's shockingly easy with Windows PowerShell—just enter:

$cert = @(gci cert:\currentuser\my
-codesigning)[0]
Set-AuthenticodeSignature myscript.ps1 $cert

The first part retrieves the first installed code signing certificate (if you have multiple certificates installed and want to use one other than the first, just change the "0" to the appropriate number). The second part signs the file (of course, you'll need to change the filename to match yours). It's that simple! To be totally honest, though, I find the Set-AuthenticodeSignature cmdlet name to be a bit longer than necessary, so that's why I created an alias called Sign. Now, I can just run:

Sign myscript.ps1 $cert

Now open that script and you'll see the signature block inserted at the bottom. Try running the script with your execution policy set to AllSigned (Set-ExecutionPolicy AllSigned) and it should work fine. Now you'll want to modify the script and save it, but make sure you don't sign it again. Windows PowerShell should now simply refuse to run the modified version because the signature has been broken.

Worth the Effort

Isn't this really more hassle than it's worth? Is it asking too much to be able to script a few administrative tasks without having to drop $500 on a certificate or implement a whole new infrastructure element? Look, I'm an admin, too and I understand your concerns. But the simple answer is that this is definitely worth every bit of the effort it takes.

The fact is security almost always involves some level of hassle. Antivirus software can be a pain, but you aren't going to run without it. Firewall software is definitely a hassle, but as you know, it isn't safe to run without a firewall. And Windows Vista User Account Control (UAC) can also be a hassle, but it makes your computer safer. And isn't it this improved security that we're all trying to achieve?

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, I use a graphical script editor that automatically signs my scripts every time I press Save, so code signing is pretty much transparent for me.

You can make it more transparent even without a fancy editor. For example, you can create a Windows PowerShell profile for yourself (create the file Microsoft.PowerShell_profile.ps1 in a folder named WindowsPowerShell, located under your documents folder), and add this function to it:

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

Now you can sign your script by simply entering:

Sign c:\scripts\myscript.ps1

Of course, that profile script should be the first one you sign! The idea is to take a few steps to make script signing as convenient as possible, so that it's not a hassle.

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. Note that the About_Signing Windows PowerShell help topic also includes information on securing the certificate produced by Makecert.exe.

Don Jones is the author of Windows PowerShell: TFM and VBScript, WMI, and ADSI Unleashed. You can contact him through the PowerShellCommunity.org Web site.

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.