Americas

  • United States

Chapter 4: Code Signing

Analysis
May 21, 200719 mins
Networking

Sams

In an effort to learn how to sign PowerShell scripts and configuration files, you have searched the Internet, read several blogs about code signing, reviewed the PowerShell documentation, and even browsed through some PowerShell books. Yet the more you read about code signing, the more confused you are. Finally, in frustration, you open your PowerShell console and enter the following command:

set-executionpolicy unrestricted

Before you enter this command, remember what you learned about execution policies in Chapter 3, “PowerShell: A More In-Depth Look.” Using the Unrestricted setting negates an important security layer that was designed to prevent malicious code from running on your system. Code signing is another essential component of PowerShell security, but many people believe it’s too complicated to learn and set their execution policies to Unrestricted to avoid having to use it. In response to an entry on script signing at Scott Hanselman’s blog (https://www.hanselman.com/blog), one person commented that “Handling code signing certificates is way over the head of most users, including average developers and admins.” This statement indicates a real need that should be addressed—hence this chapter devoted to code signing. Code signing seems complicated on the surface, but with some clear instructions, the process is easy to understand. Scripters, developers, and administrators should be familiar with it as an important part of their overall security efforts.

What Is Code Signing?

In short, code signing is the process of digitally signing scripts, executables, DLLs, and so forth to establish a level of trust for the code. The trust granted to digitally signed code is based on two assumptions. One, a signed piece of code ensures that the code hasn’t been altered or corrupted since being signed. Two, the digital signature serves to prove the identity of the code’s author, which helps you determine whether the code is safe for execution.

These two assumptions are a way to ensure the integrity and authenticity of code. However, these assumptions alone are no guarantee that signed code is safe to run. For these two assumptions to be considered valid, you need the digital signature and the infrastructure that establishes a mechanism for identifying the digital signature’s originator.

A digital signature is based on public key cryptography, which has algorithms used for encryption and decryption. These algorithms generate a key pair consisting of a private key and a public key. The private key is kept secret so that only the owner has access to it, but the public key can be distributed to other entities through some form of secure interaction. Depending on the type of interaction, one key is used to lock (encrypt) the communication, and the other key is used unlock (decrypt) the communication. In digital signatures, the private key is used to generate a signature, and the public key is used to validate the generated signature. The process is as follows:

  1. A one-way hash of the content (documents, code, and so forth) being signed is generated by using a cryptographic digest.

  2. The hash is then encrypted with the private key, resulting in the digital signature.

  3. Next, the content is transmitted to the recipient.

  4. The recipient then creates another one-way hash of the content and decrypts the hash by using the sender’s public key.

  5. Finally, the recipient compares the two hashes. If both hashes are the same, the digital signature is valid and the content hasn’t been modified.

A one-way hash (also known as a message digest, fingerprint, or compression function) is a cryptographic algorithm that turns data into a fixed-length binary sequence. The term one-way comes from the fact that it is difficult to derive the original data from the resulting sequence.

To associate an entity, such as an organization, a person, or a computer, with a digital signature, a digital certificate is used. A digital certificate consists of the public key and identifying information about the key pair owner. To ensure a digital certificate’s integrity, it’s also digitally signed. A digital certificate can be signed by its owner or a trustworthy third party called a certificate authority (CA).

The act of associating code with the entity that created and published it removes the anonymity of running code. Furthermore, associating a digital signature with a code-signing certificate is much like using a brand name to establish trust and reliability. Armed with this information, users of PowerShell scripts and configuration files can make informed decisions about running a script or loading a configuration file. This, in a nutshell, is why code signing is an important aspect of the PowerShell security framework.

Obtaining a Code-Signing Certificate

There are two methods for obtaining a code-signing certificate: generating self-signed certificates and using a CA from a valid public key infrastructure (PKI).

Generating a self-signed certificate for signing your PowerShell scripts and configuration files is simpler and quicker and has the advantage of not costing anything. However, no independent third party verifies the certificate’s authenticity, so it doesn’t have the same level of trust that’s expected from code signing. As a result, no other entity would trust your certificate by default. To distribute your PowerShell script or configuration file to other machines, your certificate would have to be added as a trusted root CA and a trusted publisher.

Although changing what an entity trusts is possible, there are two problems. One, entities outside your sphere of control might not choose to trust your certificate because there’s no independent method for verifying who you are. Two, if the private key associated with your self-signed certificate becomes compromised or invalid, there’s no way to manage your certificate’s validity on other entities. Given these problems, limiting the use of self-signed certificates to a local machine or for testing purposes is recommended.

If you plan to digitally sign your scripts and configuration files so that they can be used in an enterprise or even the public realm, you should consider the second method of obtaining a code-signing certificate: a CA from a valid PKI. A valid PKI can mean a well-known and trusted commercial organization, such as https://www.globalsign.net, https://www.thawte.com, or https://www.verisign.com, or an internal PKI owned and operated by your organization. Obtaining a code-signing certificate from an external PKI can be quick and easy, as long as you keep a few caveats in mind.

First, a certificate must be purchased from the owner of the external PKI. Second, because you’re purchasing the certificate from an outside entity, you’re placing a lot of trust in the organization’s integrity. For these reasons, code-signing certificates from commercial PKIs should be limited to certificates used to sign scripts and configuration files for public distribution.

Therefore, an internal PKI should be used for scripts and configuration files not meant for public consumption. Keep in mind that deploying and managing an internal PKI takes planning, effort, and money (Hardware Security Modules (HSMs), security consultants, and so forth can be expensive). Most organizations tend to shy away from the effort required to set up a PKI. Instead, they bring up CAs ad hoc, purchase certificates from commercial PKIs, or ignore PKI requirements. A commercial PKI might not provide the level of trust your organization needs, and the ad hoc approach isn’t recommended because it reduces trust of certificates generated by rogue CAs, which are CAs that have a low level of assurance around their integrity. Having no valid PKI infrastructure could make internal distribution of digitally signed files difficult. Last, organizations that ignore PKI requirements illustrate another drawback of using an internal PKI: time.

If there’s no PKI in your organization, obtaining a code-signing certificate might take an extended period of time. PKIs do not materialize overnight. If you have identified a PKI requirement for your scripts, there are probably additional PKI requirements in your organization. These requirements will need to be identified and considered before a PKI is deployed. Trying to drive a PKI deployment around your needs alone isn’t the best approach for an infrastructure service that needs to meet the needs of an entire organization. After you have presented the PKI requirement to your organization, you might have to wait for the services to be provided. However, after the PKI is in place, you can obtain code-signing certificates knowing that the infrastructure fully supports the distribution of your signed PowerShell scripts and configuration files.

Method One: Self-Signed Certificate

This method of creating a self-signed certificate is based on using the makecert utility, which is part of the .NET Framework Software Development Kit (SDK). Follow these steps:

  1. Download the latest Microsoft .NET Framework SDK from https://msdn2.microsoft.com/en-us/netframework/aa731542.aspx. At the time of this writing, the current .NET Framework SDK version is 2.0.

  2. Install the SDK on the machine where you want to generate the self-signed certificate.

  3. Locate the makecert utility on your system. The default location is C: Program Files Microsoft Visual Studio 8 SDK v2.0 Bin.

  4. Open up a cmd command prompt and change the working directory to the location of the makecert utility using the cd command.

  5. Create a self-signed certificate by using the following command:

    makecert -r -pe -n "CN=CertificateCommonName" -b 01/01/2000 -e 01/01/2099 –eku
    1.3.6.1.5.5.7.3.3 -ss My
    

    You should see output similar to the following:

    C: Program Files Microsoft Visual Studio 8 SDK v2.0 Bin>makecert -r -pe -n "CN= Turtle Code Signing" -b 01/01/2000 -e 01/01/2099 -eku 1.3.6.1.5.5.7.3.3 -ss My
    Succeeded
    
  6. Finally, use the following PowerShell command to verify that the certificate was installed:

    PS C: > get-childitem cert: CurrentUser My -codesign
    
    
     Directory: Microsoft.PowerShell.Security Certificate::CurrentUser My
    
    
    Thumbprint                               Subject
    ----------                               -------
    944E910757A862B53DE3113249E12BCA9C7DD0DE CN=Turtle Code Signing
    
    
    PS C: >
    

Method Two: CA Signed Certificate

This method is based on obtaining a code-signing certificate from a Microsoft Windows CA. These steps assume a PKI has been deployed at your organization. If not, installing Windows Certificate Services to meet your immediate need isn’t recommended. Follow these steps to request a code-signing certificate:

  1. Request that your PKI administrator create and enable a code-signing certificate template for your PowerShell scripts and configuration files.

  2. Use Internet Explorer to access the Certificate Services Web Enrollment site at https://CAServerName/certsrv (replacing CAServerName with the name of your server).

  3. Click the Request a Certificate link.

  4. On the Request a Certificate page, click the Advanced certificate request link.

  5. On the Advanced Certificate Request page, click the Create and submit a request to this CA link.

  6. In the Certificate Template section, click to select the code-signing certificate your PKI administrator created.

  7. Enter the rest of the identifying information and certificate request options according to your organization’s certificate policy. You can use Figure 4.1 as a guideline.

    Figure 4.1

    Example of requesting a code-signing certificate

  8. Click the Submit button.

  9. In the Potential Scripting Violation dialog box that opens (see Figure 4.2), click Yes to continue.

    Figure 4.2

    Potential Scripting Violation message box

  10. Next, if applicable, set the private key security level based on your organization’s certificate policy (see Figure 4.3), and then click OK.

    Figure 4.3

    Figure 4.3

    Creating a new RSA signature key dialog box

  11. If your organization’s certificate policy requires approval from a certificate manager, then ask your certificate manager to approve the certificate request you just submitted. If approval isn’t required, go to step 16.

  12. After the certificate request has been approved, use Internet Explorer to access the Certificate Services Web Enrollment site at https://CAServerName/certsrv (replacing CAServerName with the name of your server).

  13. Click the View the status of a pending certificate request link.

  14. On the next page, click the appropriate certificate request link.

  15. On the Certificate Issued page, click the Install this certificate link.

  16. In the Potential Scripting Violation dialog box that opens (see Figure 4.4), click Yes to continue.

    Figure 4.4

    Potential Scripting Violation message box

  17. Finally, the Certificate Services Web Enrollment site states that the certificate was installed successfully. Use the following PowerShell command to verify the certificate installation status:

    PS C: > get-childitem cert: CurrentUser My -codesign
    
    
     Directory: Microsoft.PowerShell.Security Certificate::CurrentUser My
    
    
    Thumbprint                               Subject
    ----------                               -------
    5CBCE258711676061836BC45C1B4ACA6F6C7D09E E=Richard.Stallman@goodcode.com, C...
    
    
    PS C: >
    

The PVK Digital Certificate Files Importer

When a digital certificate is generated, sometimes the private key is stored in a PVK (private key) file, and the corresponding digital certificate is stored in a Software Publishing Certificate (SPC) file. When a code-signing certificate has been obtained from Verisign or Thawte, for example, the digital certificate is issued to you as a SPC and PVK file combination. If you want to use the code-signing certificate to digitally sign PowerShell scripts or configuration files, you must import the SPC and PVK file combination into your personal certificate store.

A certificate store is a location that resides on a computer or device that is used to store certificate information. In Windows, you can use the Certificates MMC snap-in to display the certificate store for a user, a computer, or a service according. Your personal certificate store is referring to your own “user” certificate store.

To import the SPC+PVK, you use the Microsoft utility called PVK Digital Certificate Files Importer. You can download it from the Microsoft Download Web site at https://www.microsoft.com/downloads/details.aspx?FamilyID=F9992C94-B129-46BC-B240-414BDFF679A7&displaylang=EN.

Next, enter the following command to import the SPC+PVK, substituting your own filenames:

pvkimprt -IMPORT "mycertificate.spc" "myprivatekey.pvk"

Signing PowerShell Scripts

When signing a PowerShell script, you use the Set-AuthenticodeSignature cmdlet, which takes two required parameters. The first parameter, filePath, is the path and filename for the script or file to be digitally signed. The second parameter, certificate, is the X.509 certificate used to sign the script or file. To obtain the X.509 certificate in a format the Set-AuthenticodeSignature cmdlet understands, you retrieve the certificate as an object with the Get-ChildItem cmdlet, as shown in this example:

PS C: > set-authenticodesignature –filePath signed.ps1 -certificate @(get-childitem cert: CurrentUser My -codeSigningCert)[0] -includeChain "All"


 Directory: C: 


SignerCertificate                        Status      Path
-----------------                        ------      ----
5CBCE258711676061836BC45C1B4ACA6F6C7D09E Valid       signed.ps1


PS C: > 

To retrieve the certificate you want from your own “user” certificate store, you use the Get-ChildItem cmdlet with the codeSigningCert SwitchParameter. This SwitchParameter can be used only with the PowerShell Certificate provider and acts as a filter to force the Get-ChildItem cmdlet to retrieve only code-signing certificates. Last, to ensure that the entire certificate chain is included in the digital signature, the includeChain parameter is used.

After the Set-AuthenticodeSignature cmdlet has been executed successfully, the signed file has a valid digital signature block containing the digital signature. A signature block in a PowerShell script or configuration file is always the last item in the file and can be found easily because it’s enclosed between SIG # Begin signature block and SIG # End signature block, as shown here:

write-host ("This is a signed script!") -Foregroundcolor Green
# SIG # Begin signature block
# MIIIHQYJKoZIhvcNAQcCoIIIDjCCCAoCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUOBxWZ+ceVCY8SKcVLl/3iq2F
# w0OgggYVMIIGETCCBPmgAwIBAgIKcsuBWwADAAAAIzANBgkqhkiG9w0BAQUFADBE
...
# KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFG+QcdwH
# dHiuftHilhdyHCeSl0UgMA0GCSqGSIb3DQEBAQUABIGAZxItZJ+uo1E/cVhOCFex
# 9hinxULa3s0urQi362qa+NQ7yV3XczQOAPl0/kBIrEcwFN6YyS7PPm0wkCAPnfib
# 4J3uKxZK+4l9iHTiEVmp1ZO5G+P3KrqUS9ktFs7v9yTgqc8JLznxsRLvMwZpAMBO
# R2792YGWH5Jy4AwDYeljQ6Y=
# SIG # End signature block

This process for digitally signing scripts also applies to PowerShell configuration files. As discussed in Chapter 3, configuration files, depending on the execution policy setting, might also need to be signed before they are loaded into a PowerShell session.

Verifying Digital Signatures

To verify the digital signature of PowerShell scripts and configuration files, you use the Get-AuthentiCodeSignature cmdlet. It returns a valid status or an invalid status, such as HashMismatch, indicating a problem with the file.

Valid status:

PS C: > get-authenticodesignature signed.ps1


 Directory: C: 


SignerCertificate                        Status      Path
-----------------                        ------      ----
5CBCE258711676061836BC45C1B4ACA6F6C7D09E Valid       signed.ps1


PS C: > . signed.ps1
This is a signed script!
PS C: > 

Invalid status:

PS C: > Get-AuthenticodeSignature signed.ps1


 Directory: C: 


SignerCertificate                        Status          Path
-----------------                        ------          ----
5CBCE258711676061836BC45C1B4ACA6F6C7D09E HashMismatch    signed.ps1


PS C: . signed.ps1
File C: signed.ps1 cannot be loaded. The contents of file D: signed.ps1 may have been tampered because the hash of the file does not match the hash stored in the digital signature. The script will not execute on the system. Please see "get-help about_signing" for more details.
At line:1 char:12
+ . signed.ps1 
PS C: > 

Based on the error in the preceding example, the script has been modified or tampered with or is corrupt. If the script has been modified by its owner, it must be signed again before it can be used. If the script has been tampered with or is corrupt, it should be discarded because its validity and authenticity can no longer be trusted.

Signed Code Distribution

Distributing signed PowerShell scripts and configuration files requires the user to determine whether to trust code from a particular publisher. The first step is to validate the publisher’s identity based on a chain of trust. To establish a chain of trust, the user uses the publisher’s code-signing certificate associated with the digital signature to verify that the certificate owner is indeed the publisher. For example, Figure 4.5 shows an unbroken path (or chain) of valid certificates from the publisher’s certificate to a trusted root certificate (or trust anchor).

Figure 4.5

The certificate path

When a well-known trusted public root CA or internally trusted root CA is the trust anchor for the publisher’s certificate, the user explicitly trusts that the publisher’s identity claims are true.

For Windows users, if a root CA is considered trusted, that CA’s certificate resides in the Trusted Root Certification Authorities certificate store (see Figure 4.6).

Figure 4.6

Trusted Root Certification Authorities certificate store

When a root CA is not a valid trust anchor or the certificate is self-signed, the user needs to decide whether to trust a publisher’s identity claim. If the user determines the identity claim to be valid, the root CA’s certificate or the self-signed certificate should be added to the Trusted Root Certification Authorities certificate store to establish a valid chain of trust.

After the publisher’s identity has been verified or trusted, the next step is deciding whether the signed code is safe for execution. If a user has previously decided that code from a publisher is safe for execution, the code (PowerShell script or configuration file) runs without further user action.

For Windows users, if a publisher is considered trusted, their code-signing certificate resides in the Trusted Publishers certificate store (see Figure 4.7).

Figure 4.7

Trusted Publishers certificate store

If a publisher is not trusted, PowerShell prompts the user to decide whether to run signed code from that publisher, as shown in this example:

PS C: > . signed.ps1

Do you want to run software from this untrusted publisher?
File C: signed.ps1 is published by CN=companyabc.com, OU=IT,
O=companyabc.com, L=Oakland, S=California, C=US and is not trusted on your
system. Only run scripts from trusted publishers.
[V] Never run [D] Do not run [R] Run once [A] Always run [?] Help
(default is "D"): 

The following list explains the available options:

  • [V] Never run—This option places the publisher’s certificate in the user’s Untrusted Certificates certificate store. After a publisher’s certificate has been determined to be untrusted, PowerShell never allows code from that publisher to run unless the certificate is removed from the Untrusted Certificates certificate store or the execution policy is set to Unrestricted or RemoteSigned.

  • [D] Do not run—This option, which is the default, halts execution of the untrusted code.

  • [R] Run once—This option allows one-time execution of the untrusted code.

  • [A] Always run—This option places the publisher’s certificate in the user’s Trusted Publishers certificate store. Also, the root CA’s certificate is placed in the Trusted Root Certification Authorities certificate store, if it isn’t already there.

Enterprise Code Distribution

You might be wondering how to control what code is considered trusted in your organization. Obviously, having users or machines decide what to trust defeats the purpose of distributing signed code in a managed environment. If your environment is managed, your PKI deployment should have methods for controlling what’s trusted in an organization. If your organization is a Windows environment, the most common method is through GPO. For example, you can define trusted publishers by using a Certificate Trust List (CTL) or manage them through the Internet Explorer Maintenance extension.

Public Code Distribution

Determining trust in the public realm is entirely different. When establishing trust between two private entities, they are able to define what is and isn’t trusted. When dealing with public entities, you don’t have this level of control. It is up to those public entities to determine what they do or do not trust.

Summary

In summary, this chapter, as its name suggested, was an in-depth exploration into code signing. Based on the information that you have gleaned from this chapter, you should now have an understanding for just how important code signing is to PowerShell security and how to use it. If you haven’t come to this realization, then it is again stressed that code signing be understood and used in conjunction with your script development activities.

In addition to stressing the use of code signing, you should also now have a better understanding for the infrastructure that is required to make code signing a viable method for trusting code within an organization. Granted, while PKI can be difficult to understand, one of the main goals of this chapter was to explain PKI from the perspective that was related to your scripting activities—an approach that was taken in an effort to reduce the amount of bewilderment, on your part, by relating PKI to something that is applicable to how it would be used with PowerShell. With this knowledge, you should now be able to determine, or at least convey, a PKI need and hopefully move a project forward such that the scripts you developed can be trusted at your organization.

Copyright © 2007 Pearson Education. All rights reserved.