PowerSign.psm1
$timeStampers = $null $thumbprint = $null [int]$stampIdx = 0 function GetTsUrl() { [string]$retVal = $script:timeStampers[$script:stampIdx] $script:stampIdx = ($script:stampIdx + 1) % $timeStampers.Length return $retVal } function SignFile([string]$File) { $tsUrl = GetTsUrl [void] (signtool sign /sha1 $thumbprint /fd SHA256 /tr $tsUrl ""$File"") return $LASTEXITCODE } function GetConfigDir() { Join-Path -Path $env:LOCALAPPDATA -ChildPath "PowerSign" } function InitConfigDir() { $configDir = GetConfigDir if (!(Test-Path $configDir -PathType Container)) { New-Item -Path $configDir -ItemType Directory } return $configDir } function LoadConfig([string]$ConfigName) { $config = Import-PowerShellDataFile -Path "$(GetConfigDir)\$($ConfigName).psd1" $script:timeStampers = $config.timeStampers $script:thumbprint = $config.thumbprint } <# .SYNOPSIS Addes a config file to be used for code signing. .PARAMETER File The PowerShell data file containing the configuration. .PARAMETER Name A logical name for the configration, it does not need to mach the file name. .EXAMPLE Add-Config -File MyConfig.psd1 -Name ForQA .NOTES This will copy the config specified by -File into the user's %LOCALAPPDADA% directory, changing the name to the value of the -Name paramter followed by .psd1 . A sambple config is show below: @{ # list as many rfc3161 timestamping urls as designed. timeStampers = @( "http://timestamp.globalsign.com/scripts/timstamp.dll", "http://tsa.startssl.com/rfc3161", "http://timestamp.comodoca.com/?td=sha256", "http://sha256timestamp.ws.symantec.com/sha256/timestamp" ) # Set this to a thumbprint of you code signing certificate. thumbprint = "b6b248ff8ab1bf545d3f8db6c2695a6863f4bb3c" } #> function Add-Config { [CmdletBinding()] param ( [parameter(Mandatory=$true)] [string]$File, [parameter(Mandatory=$true)] [string]$Name ) BEGIN { $configDir = InitConfigDir } PROCESS { Copy-Item -Path $File -Destination (Join-Path -Path $configDir -ChildPath "$($Name).psd1") } } <# .SYNOPSIS Calls Microsoft's signtool.exe code signing utility repeatedly until it succeeded, cycling through a list of timestamper URLs for each attempt. .DESCRIPTION Most often, timespamping succeeds, but at times code siging fails in builds because the call for timestamping fails and the failure is not on the part of the caller, but the timestamper. Fow whatever reason, it may be busy or other error occurs. Set-Sig will attemplt, upto the number specified by Attempts, to sign a particular file, cycling through a list of timespamping URLs each signing attempt. .PARAMETER File File to sign. Can be a full or relative path. .PARAMETER ConfigName Name of saved configuration, see Add-Config, that contains a list of timestamp URLs and certificate hash. .PARAMETER Attempts Max number of attempts that will be made. #> function Set-Sig { [CmdletBinding()] param ( [parameter(Mandatory=$true)] [string]$File, [parameter(Mandatory=$true)] [string]$ConfigName, [int]$Attempts = 300 ) BEGIN { [void] (Get-Command "signtool.exe") [void] (LoadConfig $ConfigName) } PROCESS { [int]$lastExit = -1 [int]$thisAttempt = 1 while ($lastExit -ne 0) { if ($thisAttempt -gt $Attempts) { break } $lastExit = SignFile $File $thisAttempt++ } $lastExit } } Export-ModuleMember -Function Add-Config Export-ModuleMember -Function Set-Sig |