PSAzureSignTool.psm1
function Select-FilesToSign() { <# .SYNOPSIS Produces a file that containing a list of files to be code signed. .DESCRIPTION Produces a file that containing a list of files to be code signed. Searches the provided root directory, and selects files based on regex pattern parameters, which do not already have a valid authenticode signature. .PARAMETER Path System.String; Required - The root path to begin searching for files to code sign. .PARAMETER Includes System.String; Optional - Default value is '.*' (matching everything). Each file which has a full path matching this regular expression pattern will be signed, if: 1. The file extension matches an extension Extensions 2. The file full name does not match the optional 'Excludes' pattern .PARAMETER Excludes System.String; Optional - Files with a full name matching this optional pattern will be excluded from code signing. .PARAMETER Extensions System.String; Optional - Comma-separated list of file extensions to include in search for files to sign. .PARAMETER OutputFileList System.String; Required - Full path for text file which will contain list of files that require an authenticode signature. .EXAMPLE # Searches C:\src\bin\Release, using default parameter values; # C:\files-to-sign.txt will be created. Any unsigned file with an extension # of .exe, .dll, .ocx, or .cab. Select-FilesToSign -Path C:\src\bin\Release -OutputFileList C:\files-to-sign.txt .EXAMPLE # Searches C:\src\bin\Release, using default parameter values; # C:\files-to-sign.txt will be created. Any unsigned file with an extension # of .exe, .dll, .ocx, or .cab that contains 'Fabrikam' or 'FabrikamFiber' # in the full file name Select-FilesToSign -Path C:\src\bin\Release -Includes "Fabrikam(Fiber)?" -OutputFileList C:\files-to-sign.txt .EXAMPLE # Searches C:\src\bin\Release, using default parameter values; # C:\files-to-sign.txt will be created. Any unsigned file with an extension # of .exe, .dll, .ocx, or .cab that contains 'Fabrikam' or 'FabrikamFiber' # that does not include 'Test' in the full file name Select-FilesToSign -Path C:\src\bin\Release -Includes "Fabrikam(Fiber)?" -Excludes "Test" -OutputFileList C:\files-to-sign.txt #> [CmdletBinding()] Param( [Alias("pa")] [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [ValidateScript({Test-Path $_})] [string]$Path, [Alias("inf")] [ValidateNotNullOrEmpty()] [string]$Includes = ".*", [Alias("exf")] [ValidateNotNullOrEmpty()] [string]$Excludes = "^$", [Alias("ext")] [ValidateNotNullOrEmpty()] [string]$Extensions = ".exe,.dll,.ocx,.cab", [Alias("ofl")] [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$OutputFileList ) [string[]]$ToSign = @() [string[]]$ArrExtensions = $Extensions -split "," New-Item -Path (Split-Path -Path $OutputFileList -Parent) -ItemType Directory -Force | Out-Null Get-ChildItem -Path $Path -File -Recurse ` | Where-Object {($_.FullName -match $Includes) ` -and ($_.FullName -notmatch $Excludes) ` -and ($_.Extension -in $ArrExtensions)} ` | ForEach-Object { $signStatus = (Get-AuthenticodeSignature -FilePath $_.FullName).Status if($signStatus -ne "Valid") { [string]$ToAdd = Get-Item $_.FullName Write-Host "$signStatus - $ToAdd" $ToSign += $ToAdd } } if($ToSign) { Write-Host "`nList of files to sign saved to $OutputFileList `n" $ToSign | Out-File -FilePath $OutputFileList -Encoding utf8 -Force } else { throw "No files found to sign." } } function Start-CodeSign() { <# .SYNOPSIS Invokes AzureSignTool to sign and timestamp a list of files. For detailed parameter descriptions, please refer to the AzureSignTool documentaiton: https://github.com/vcsjones/AzureSignTool .PARAMETER AzureKeyVaultUrl System.String; Required - Corresponds to the AzureKeyVault '--azure-key-vault-url' parameter. For details see https://github.com/vcsjones/AzureSignTool#parameters .PARAMETER AzureKeyVaultClientId System.String; Required - Corresponds to the AzureKeyVault '--azure-key-vault-client-id' parameter. For details see https://github.com/vcsjones/AzureSignTool#parameters .PARAMETER AzureKeyVaultClientSecret System.String; Required - Corresponds to the AzureKeyVault '--azure-key-vault-client-secret' parameter. For details see https://github.com/vcsjones/AzureSignTool#parameters .PARAMETER AzureKeyVaultCertificate System.String; Required - Corresponds to the AzureKeyVault '--azure-key-vault-certificate' parameter. For details see https://github.com/vcsjones/AzureSignTool#parameters .PARAMETER InputFileList System.String; Required - Corresponds to the AzureKeyVault '--input-file-list' parameter. For details see https://github.com/vcsjones/AzureSignTool#parameters .PARAMETER TimestampUrl System.String; Optional - Corresponds to the AzureKeyVault '--azure-key-vault-client-id' parameter. For details see https://github.com/vcsjones/AzureSignTool#parameters #> [CmdletBinding()] Param( [Alias("kvu")] [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$AzureKeyVaultUrl, [Alias("kvi")] [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$AzureKeyVaultClientId, [Alias("kvs")] [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$AzureKeyVaultClientSecret, [Alias("kvc")] [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$AzureKeyVaultCertificate, [Alias("ifl")] [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$InputFileList, [Alias("tr")] [string]$TimestampUrl ) try { AzureSignTool Write-Host "AzureSignTool is installed." } catch { Write-Host "AzureSignTool is not available, installing..." dotnet tool install --global AzureSignTool } [string[]]$SignArguments = @( 'sign', '-v', '-fd',"sha256", '-kvu',$AzureKeyVaultUrl, '-kvi',$AzureKeyVaultClientId, '-kvs',$AzureKeyVaultClientSecret, '-kvc',$AzureKeyVaultCertificate, '-ifl',$InputFileList ) if($TimestampUrl) { $SignArguments += @('-tr',$TimestampUrl) } else { Write-host "Warning: TimestampUrl is not provided, only digital signature will be applied to assemblies." } $pscore = $false try{ if ((pwsh -version)){ $pscore = $true } } catch{} if($pscore){ pwsh -Command "& AzureSignTool $SignArguments" } else{ powershell -Command "& AzureSignTool $SignArguments" } } function Test-AuthenticodeSignature() { <# .SYNOPSIS Validates the authenticode signature and timestamps of a list of file paths .PARAMETER InputFileList System.String; Required - Text file containing newline-separated list of full paths of files. Each file must have a valid authenticode signature and timestamp or the function will throw an exception. .EXAMPLE # Validates that each file listed in C:\files-to-sign.txt has a valid # authenticode signature and timestamp Test-AuthenticodeSignature -InputFileList C:\files-to-sign.txt #> [CmdletBinding()] Param( [Alias("ifl")] [ValidateNotNullOrEmpty()] [string]$InputFileList ) [string[]]$SigErrors = @() Write-Host "`nStarting validation..." $ToSignArray = ((Get-Content $InputFileList) -join ',') -split ',' Get-AuthenticodeSignature -FilePath $ToSignArray | ForEach-Object { if ($_.Status.ToString() -ne 'Valid') { $ErrMsg = "Signature invalid: $($_.Path)" $SigErrors += $ErrMsg Write-Host $ErrMsg } elseIf ($TimestampUrl -and (!$_.TimeStamperCertificate)) { $ErrMsg = "Timestamp certificate missing: $($_.Path)" $SigErrors += $ErrMsg Write-Host $ErrMsg } else { Write-Host ("Signature/timestamp valid: {0}" -f $_.Path) } } if($SigErrors -gt 0) { throw "`nValidation failed." } else { Write-Host "`nValidation completed successfully!" } } Export-ModuleMember -Function Select-FilesToSign, ` Start-CodeSign, ` Test-AuthenticodeSignature |