Tuesday, October 20, 2015

Easy PowerShell Code Signing



The other day i started working with custom application detection method scripts in SCCM 2012 and wanted to use PowerShell as my scripting method.  Generally i will pass a temporary Set-ExecutionPolicy bypass, run the script, then reset the execution policy.  This works in SCCM 2012 applications, package, and task sequence command lines, but it does not work for detection method scripts.  These require signed PowerShell scripts.

I figure, if I am going to go down the route of signing scripts I may as well start doing it for all of my scrips.  In order to make my life easier, I created a PowerShell script to automate this process.  This script also allows others who may not be familiar with script signing an easy way to sign their own scripts.

For this to work you will need a Class III Authenticode Code-Signing Certificate.  After you have the code signing certificate installed you are ready to run this PowerShell script to sign any script you have with your code signing certificate.

The script performs the following actions:
  1. Prompt user for the location of the script to be signed
  2. The script will then convert your .ps1 file encoding to UTF-8.  The Set-AuthenticodeSignature cmdlet will throw an error if files are not converted to UTF-8 encoding.  By default PowerShell scripts created with the ISE are saved with a "Unicode big endian" encoding.
  3. The script will then sign your .ps1 file with the code signing certificate from your system.
Script Code:
<# 
# NAME: PSCodeSigning.ps1 
# AUTHOR: Michael Wolf / http://thecomputermanagersden.blogspot.com/
# DATE:19.10.2015
.Synopsis 
  Signs Powershell Scripts with your Code Signing Certificate
.DESCRIPTION 
  This script will prompt you for the path to a script you wish to sign.
  It will then ensure the script is converted to UTF-8 encoding.  This is to resolve an issue with scripts created in ISE with a default encoding of "Unicode big endian"
  Finaly your script will be signed with the code signing certificate available on your system.
.EXAMPLE 
   PSCodeSigning.ps1
.INPUTS 
   -
.OUTPUTS 
   - 
.NOTES 
   Be aware that you will need to have a valid code signing certificate in your User Certificate Store.
   Run the gci command below to confirm you have a code signing cert.
   gci cert:\CurrentUser\My -CodeSigningCert
   
   Function Get-FileName sourced from Scripting Guy
   http://blogs.technet.com/b/heyscriptingguy/archive/2009/09/01/hey-scripting-guy-september-1.aspx
#> 

Function Get-FileName($initialDirectory)
{   
 [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
 Out-Null

 $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
 $OpenFileDialog.initialDirectory = $initialDirectory
 $OpenFileDialog.filter = "All files (*.*)| *.*"
 $OpenFileDialog.ShowDialog() | Out-Null
 $OpenFileDialog.filename
} #end function Get-FileName

#Powershell script you wish to sign.
$CertPath = Get-FileName
#Timestamp Server
$Timestamp = "http://timestamp.globalsign.com/scripts/timestamp.dll"

#Open and convert Powershell Script to UTF-8 encoding so it will work with Set-AuthenticodeSignature Commandlet
(Get-Content $CertPath) | Set-Content -Encoding "utf8" $CertPath -Force -Confirm:$false

#Retrieve the first installed code signing certificate
#If the certificate you wish to use is not the first certificate in the list adjust the [0] to the appropriate value.
$Cert = @(gci cert:\CurrentUser\My -CodeSigningCert)[0]

#Sign 
Set-AuthenticodeSignature $CertPath $Cert -TimestampServer $Timestamp