TrustedSigning.psm1
using namespace System.Collections.Generic using namespace System.IO function Invoke-TrustedSigning { param ( [Parameter(Mandatory)] [string]$Endpoint, [Parameter(Mandatory)] [string]$CodeSigningAccountName, [Parameter(Mandatory)] [string]$CertificateProfileName, [Parameter()] [AllowEmptyString()] [string]$Files, [Parameter()] [AllowEmptyString()] [string]$FilesFolder, [Parameter()] [AllowEmptyString()] [string]$FilesFolderFilter, [Parameter()] [switch]$FilesFolderRecurse = $false, [Parameter()] [AllowNull()] [int]$FilesFolderDepth, [Parameter()] [AllowEmptyString()] [string]$FilesCatalog, [Parameter(Mandatory)] [string]$FileDigest, [Parameter()] [AllowEmptyString()] [string]$TimestampRfc3161, [Parameter()] [AllowEmptyString()] [string]$TimestampDigest, [Parameter()] [switch]$AppendSignature = $false, [Parameter()] [AllowEmptyString()] [string]$Description, [Parameter()] [AllowEmptyString()] [string]$DescriptionUrl, [Parameter()] [AllowEmptyString()] [string]$GenerateDigestPath, [Parameter()] [switch]$GenerateDigestXml = $false, [Parameter()] [AllowEmptyString()] [string]$IngestDigestPath, [Parameter()] [switch]$SignDigest = $false, [Parameter()] [switch]$GeneratePageHashes = $false, [Parameter()] [switch]$SuppressPageHashes = $false, [Parameter()] [switch]$GeneratePkcs7 = $false, [Parameter()] [AllowEmptyString()] [string]$Pkcs7Options, [Parameter()] [AllowEmptyString()] [string]$Pkcs7Oid, [Parameter()] [AllowEmptyString()] [string]$EnhancedKeyUsage, [Parameter()] [switch]$ExcludeEnvironmentCredential = $false, [Parameter()] [switch]$ExcludeWorkloadIdentityCredential = $false, [Parameter()] [switch]$ExcludeManagedIdentityCredential = $false, [Parameter()] [switch]$ExcludeSharedTokenCacheCredential = $false, [Parameter()] [switch]$ExcludeVisualStudioCredential = $false, [Parameter()] [switch]$ExcludeVisualStudioCodeCredential = $false, [Parameter()] [switch]$ExcludeAzureCliCredential = $false, [Parameter()] [switch]$ExcludeAzurePowerShellCredential = $false, [Parameter()] [switch]$ExcludeAzureDeveloperCliCredential = $false, [Parameter()] [switch]$ExcludeInteractiveBrowserCredential = $false, [Parameter()] [int]$Timeout = 300, [Parameter()] [ValidateRange(0, 30000)] [int]$BatchSize = 10000, [Parameter()] [AllowEmptyString()] [string]$ClickOnceApplicationName, [Parameter()] [AllowEmptyString()] [string]$ClickOncePublisherName ) # Install required packages unless they already exist. Write-Information -MessageData "Checking for required dependencies." -InformationAction Continue $dependencies = Get-EveryDependency -Verbose $dlibFolderPath = $dependencies.DlibFolderPath $signToolFolderPath = $dependencies.SignToolFolderPath $signCliFolderPath = $dependencies.SignCliFolderPath # Create the Trusted Signing metadata.json file that is passed to signtool.exe. $metadataFilePath = Join-Path -Path $dlibFolderPath -ChildPath "metadata.json" Write-Information -MessageData "`nCreating metadata file: $metadataFilePath" -InformationAction Continue $convertToMetadataJsonParams = @{ Endpoint = $Endpoint CodeSigningAccountName = $CodeSigningAccountName CertificateProfileName = $CertificateProfileName ExcludeEnvironmentCredential = $ExcludeEnvironmentCredential ExcludeWorkloadIdentityCredential = $ExcludeWorkloadIdentityCredential ExcludeManagedIdentityCredential = $ExcludeManagedIdentityCredential ExcludeSharedTokenCacheCredential = $ExcludeSharedTokenCacheCredential ExcludeVisualStudioCredential = $ExcludeVisualStudioCredential ExcludeVisualStudioCodeCredential = $ExcludeVisualStudioCodeCredential ExcludeAzureCliCredential = $ExcludeAzureCliCredential ExcludeAzurePowerShellCredential = $ExcludeAzurePowerShellCredential ExcludeAzureDeveloperCliCredential = $ExcludeAzureDeveloperCliCredential ExcludeInteractiveBrowserCredential = $ExcludeInteractiveBrowserCredential } $metadataJson = ConvertTo-MetadataJson @convertToMetadataJsonParams [File]::WriteAllLines($metadataFilePath, $metadataJson) # Get the list of files to be signed from the files list, files folder and the # catalog file. # Files list. Write-Information -MessageData "`nGetting the list of files to be signed." -InformationAction Continue if ([string]::IsNullOrWhiteSpace($Files)) { Write-Information -MessageData "`tNo files list was provided." -InformationAction Continue $listedFiles = [List[string]]::new() } else { Write-Information -MessageData "`tGetting files from list." -InformationAction Continue $listedFiles = Get-FileList -Files $Files } Write-Information -MessageData "`tListed files: $($listedFiles.Count)" -InformationAction Continue foreach ($file in $listedFiles) { Write-Information -MessageData "`t`t$file" -InformationAction Continue } # Files folder. if ([string]::IsNullOrWhiteSpace($FilesFolder)) { Write-Information -MessageData "`n`tNo files folder was provided." -InformationAction Continue $filteredFiles = [List[string]]::new() } else { Write-Information -MessageData "`n`tGetting files from folder: $FilesFolder" -InformationAction Continue Write-Information -MessageData "`tFilter: $FilesFolderFilter" -InformationAction Continue $getFilteredFileListParams = @{ Path = $FilesFolder Filter = $FilesFolderFilter Recurse = $FilesFolderRecurse Depth = $FilesFolderDepth } $filteredFiles = Get-FilteredFileList @getFilteredFileListParams } Write-Information -MessageData "`tFiltered files: $($filteredFiles.Count)" -InformationAction Continue foreach ($file in $filteredFiles) { Write-Information -MessageData "`t`t$file" -InformationAction Continue } # Catalog file. if ([string]::IsNullOrWhiteSpace($FilesCatalog)) { Write-Information -MessageData "`n`tNo catalog file was provided." -InformationAction Continue $catalogFiles = [List[string]]::new() } else { Write-Information -MessageData "`n`tGetting files from catalog file: $FilesCatalog" -InformationAction Continue $catalogFiles = Get-CatalogFileList -Path $FilesCatalog } Write-Information -MessageData "`tCatalog files: $($catalogFiles.Count)" -InformationAction Continue foreach ($file in $catalogFiles) { Write-Information -MessageData "`t`t$file" -InformationAction Continue } # Combine the list of files to be signed. Write-Information -MessageData "`nFormatting the list of files to be signed." -InformationAction Continue $formatFileListParams = @{ ListedFiles = $listedFiles FilteredFiles = $filteredFiles CatalogFiles = $catalogFiles } $formattedFileLists = Format-FileList @formatFileListParams $signToolFiles = $formattedFileLists.SignToolFiles $signCliFiles = $formattedFileLists.SignCliFiles # Batch the list of files to be signed by SignTool. Write-Information -MessageData "`nBatching the list of files to be signed by SignTool." -InformationAction Continue $batchedSignToolFileLists = Split-FileList -FormattedFiles $signToolFiles -BatchSize $BatchSize if ($batchedSignToolFileLists.Count -eq 0) { Write-Information -MessageData "`tNo SignTool files were found to sign." -InformationAction Continue } else { Write-Information -MessageData "`tBatched SignTool files: $($batchedSignToolFileLists.Count)" -InformationAction Continue } # Batch the list of files to be signed by SignCli. Write-Information -MessageData "`nBatching the list of files to be signed by SignCli." -InformationAction Continue $batchedSignCliFileLists = Split-FileList -FormattedFiles $signCliFiles -BatchSize $BatchSize if ($batchedSignCliFileLists.Count -eq 0) { Write-Information -MessageData "`tNo SignCli files were found to sign." -InformationAction Continue } else { Write-Information -MessageData "`tBatched SignCli files: $($batchedSignCliFileLists.Count)" -InformationAction Continue } if ($batchedSignCliFileLists.Count -eq 0 -and $batchedSignToolFileLists.Count -eq 0) { throw "No files were found to sign" } # Sign the files in each batch. $batchCounter = 1 foreach ($batchedFileList in $batchedSignToolFileLists) { Write-Information -MessageData "`nSigning SignTool file batch $($batchCounter) of $($batchedSignToolFileLists.Count)." -InformationAction Continue # Format the arguments that will be passed to signtool.exe. $dlibFilePath = Join-Path -Path $dlibFolderPath -ChildPath "Azure.CodeSigning.Dlib.dll" $formatSignToolArgumentListParams = @{ FileList = $batchedFileList FileDigest = $FileDigest TimestampRfc3161 = $TimestampRfc3161 TimestampDigest = $TimestampDigest DlibFilePath = $dlibFilePath MetadataFilePath = $metadataFilePath AppendSignature = $AppendSignature Description = $Description DescriptionUrl = $DescriptionUrl GenerateDigestPath = $GenerateDigestPath GenerateDigestXml = $GenerateDigestXml IngestDigestPath = $IngestDigestPath SignDigest = $SignDigest GeneratePageHashes = $GeneratePageHashes SuppressPageHashes = $SuppressPageHashes GeneratePkcs7 = $GeneratePkcs7 Pkcs7Options = $Pkcs7Options Pkcs7Oid = $Pkcs7Oid EnhancedKeyUsage = $EnhancedKeyUsage } $signToolArguments = Format-SignToolArgumentList @formatSignToolArgumentListParams # Run signtool.exe. $invokeSignToolParams = @{ SignToolFolderPath = $signToolFolderPath SignToolArguments = $signToolArguments Timeout = $Timeout } $result = Invoke-SignTool @invokeSignToolParams # Throw an error if signtool.exe failed. # Exit code 0 means that signtool.exe completed successfully. # Exit code 2 means that signtool.exe completed with warnings. if ($result -ne 0 -and $result -ne 2) { throw "SignTool failed with exit code $($result)" } $batchCounter++ } # Sign the files in each batch. $batchCounter = 1 foreach ($batchedFileList in $batchedSignCliFileLists) { Write-Information -MessageData "`nSigning SignCli file batch $($batchCounter) of $($batchedSignCliFileLists.Count)." -InformationAction Continue $signCliPath = Join-Path -Path $signCliFolderPath -ChildPath "sign.dll" # Format the arguments that will be passed to sign.dll. $formatSignCliArgumentListParams = @{ SignCliPath = $signCliPath TrustedSigningEndpoint = $Endpoint TrustedSigningAccount = $CodeSigningAccountName TrustedSigningCertificateProfile = $CertificateProfileName ClickOnceApplicationName = $ClickOnceApplicationName ClickOncePublisherName = $ClickOncePublisherName FileList = $batchedFileList FileDigest = $FileDigest TimestampRfc3161 = $TimestampRfc3161 TimestampDigest = $TimestampDigest Description = $Description DescriptionUrl = $DescriptionUrl } $signCliArguments = Format-SignCliArgumentList @formatSignCliArgumentListParams # Run sign.dll. $invokeSignCliParams = @{ SignCliPath = $signCliPath SignCliArguments = $signCliArguments Timeout = $Timeout } $result = Invoke-SignCli @invokeSignCliParams # Throw an error if sign.dll failed. # Exit code 0 means that sign.dll completed successfully. # Exit code 1 means that sign.dll options were invalid. # Exit code 2 means that sign.dll failed. # Exit code 3 means that sign.dll inputs were not found. if ($result -ne 0) { throw "SignCli failed with exit code $($result)" } $batchCounter++ } Write-Information -MessageData "`nTrusted Signing completed successfully" -InformationAction Continue return "Trusted Signing completed successfully" } |