Code signing using PowerShell Scripting

Scenario – You have a requirement to sign the output of your application. The output can be in the form of .dll, .exe, .ocx etc. You need to sign all of them before distributing them to others.

Requirements – To implement the code signing, you will need to things –

  1. Security certificate
  2. Timestamp server URL

You need to install certificate on the server where you want to sign your code from. Insure that the certificate should be non-exportable or else some one will export it and use it.

Timestamp is needed to ensure the validity of the signed code.

Implementation – With the above requirements in place, we can use the below PowerShell Cmdlets to sign the code –

  1. Set-AuthenticodeSignature – This is to add the authenticode signature to the file.
  2. Get-AuthenticodeSignature – This is to get information about the Authenticode signature in the file. With this cmdlet, you can find if the file has valid signature or not. 

I have written below script to sign any .dll, .exe, .ocx files present in the base location where this script is placed, if they don’t have valid signature.

It does below activities –

  1. It is reading certificate from the store on the server.
  2. It is then finding the certificate that we want to use from the output of above command and then storing it into the variable to be used.
  3. There is a command line in the script to get the full path of all the files (.dll, .ocx, .exe) from all the folders and subfolders where the script is kept.
  4. It is then finding if the file (with full path) as found in step 3 above has a valid signature or not. If it has valid signature, it is ignoring this file and going to next file. If the file is not signed, it is calling function “Codesigning()” to sign it.
  5. All the activities are captured in the log file at the same location as well as displaying on the console.

Here is the PowerShell Script –

<#
.Synopsis
   Script is to sign all internal built (.dll, .exe, .ocx) file outputs
#>

function CodeSigning ()
{
    Param (
            [Parameter(Mandatory=$True)]
            [ValidateNotNull()]
            $FileNameWithPath,           
            [Parameter(Mandatory=$True)]
            [ValidateNotNull()]
            $CertInfo
          )

    write-host "———————————————"
    Write-Host "FileName – " $FileNameWithPath
    Write-Host "Code Signing Started–"
    Set-AuthenticodeSignature $FileNameWithPath $CertInfo -TimestampServer
http://TimeStampURL
    Write-Host "Code Signing Finished Successfully"
    write-host "———————————————"
}

Start-Transcript -Path ".\codesigningTrans.log"
$cert= (dir cert:localmachine\my\ -CodeSigningCert)
write-host "————All Certificate Information from the server———-"
write-host $cert
write-host "———————————————"
foreach ($_cert in $cert)
{
    if($_cert.Thumbprint -eq "ReplaceWithACTUAL")
    {
        $CertInfo = $_cert
        Write-Host "————Certificate in Use start————–"
        Write-Host $CertInfo
        Write-Host "————Certificate in Use End————–"
        $FileData = Get-ChildItem -rec | Where {$_.Extension -in ".dll",".exe",".ocx"}  | ForEach-Object -Process {$_.FullName}

        foreach ($_FileData in $FileData)
        {
            $FileNameWithPath = $_FileData           
            $IsValid = Get-AuthenticodeSignature  $FileNameWithPath | where {$_.Status -eq "Valid"}
            if (!$IsValid.Status -eq "Valid")
            {
                CodeSigning $FileNameWithPath $CertInfo
            }
            else
            {
                Write-Host $FileNameWithPath " already has valid signature"
            }
        }  
    }
}
Stop-Transcript
$log = Get-Content ".\codesigningTrans.log"
$log > ".\codesigningTrans.log"

————————-End of Article———————-