1 # This script uses Set-AuthenticodeSignature to sign a file
2 # or exe/dll/msi files in a directory using a pfx file.
3 # If PFX file is not present, script expects a base64 encode certificate
4 # in WINDOWS_CERTIFICATE environment variable.
5 # Password can be passed using WINDOWS_CERTIFICATE_PASSWORD environmnet variable.
6 # The script only signs previously unsigned files
7
8 [CmdletBinding()]
9 param (
10 # A path to the certificate file. If missing, env:WINDOWS_CERTIFICATE will be used.
11 [string]$CertFile,
12 # A password for the certificate. If missing, env:WINDOWS_CERTIFICATE_PASSWORD will be used.
13 [string]$Password,
14 # A file to sign.
15 [string]$File,
16 # A directory to sign.
17 [string]$Directory
18 )
19
20 # Configure the error behavior
21 $ErrorActionPreference = "Stop"
22 $PSDefaultParameterValues['*:ErrorAction']='Stop'
23
24 # Sign a file, if it was previously unsigned
25 # We sign using SHA256, as we only support Windows 7+.
Set-FileSignature()26 function Set-FileSignature {
27 param (
28 [String]$InputFile,
29 $pfxCert
30 )
31 if((Get-AuthenticodeSignature $InputFile).Status -ne [System.Management.Automation.SignatureStatus]::Valid) {
32 Write-Host "Singning file $InputFile"
33
34 Set-AuthenticodeSignature `
35 -FilePath $InputFile `
36 -Certificate $pfxCert `
37 -IncludeChain All `
38 -TimestampServer 'http://timestamp.digicert.com' `
39 -HashAlgorithm 'sha256' `
40 -Force
41 } else {
42 Write-Host "Skipping file $InputFile as it is already signed"
43 }
44 }
45
46 # Sanity checks: only allow File or Directory
47 if ($File -eq "" -and $Directory -eq "") {
48 Write-Host "-File or -Directory should be provided"
49 exit 1
50 } elseif ($File -ne "" -and $Directory -ne "") {
51 Write-Host "Only one of -File or -Directory should be provided"
52 exit 1
53 }
54
55 $cleanupPfx = $false
56
57 # Check, if we need to retrieve PFX from environment
58 if($CertFile -eq "") {
59 Write-Host "Trying to read certificate file from env:WINDOWS_CERTIFICATE"
60
61 if(Test-Path "env:WINDOWS_CERTIFICATE") {
62 $CertFile = "cert.pfx"
63
64 [IO.File]::WriteAllBytes($CertFile, [System.Convert]::FromBase64String($env:WINDOWS_CERTIFICATE))
65
66 $cleanupPfx = $true
67 } else {
68 Write-Host "No certificate is provided"
69 exit 1
70 }
71 }
72
73 # Check, if we need to retrieve password from environment
74 if($Password -eq "") {
75 if (Test-Path "env:WINDOWS_CERTIFICATE_PASSWORD") {
76 $Password = $env:WINDOWS_CERTIFICATE_PASSWORD
77 }
78 }
79
80 $pfx = $null
81
82 # Retrieve the actual certificate
83 if($Password -ne "") {
84 $securePassword = ConvertTo-SecureString -String $Password -AsPlainText -Force
85 $pfx = Get-PfxData -FilePath "$CertFile" -Password $securePassword
86 } else {
87 $pfx = Get-PfxData -FilePath "$CertFile"
88 }
89
90 $pfxCert = $pfx.EndEntityCertificates[0]
91
92 # Perform the code signing.
93 if ($File -ne "") {
94 Set-FileSignature -InputFile $File -pfxCert $pfxCert
95 } else {
96 Get-ChildItem `
97 -Path "$Directory" `
98 -Include *.dll,*.exe,*.msi `
99 -Recurse `
100 -File | ForEach-Object {
101 Set-FileSignature -InputFile $_.FullName -pfxCert $pfxCert
102 }
103 }
104
105 # Remove PFX file if it was created from environment.
106 if($cleanupPfx) {
107 Remove-Item "${CertFile}"
108 }
109