mirror of
https://github.com/safing/portmaster
synced 2025-04-25 13:29:10 +00:00
[packaging] (Windows) Added script to sign binaries
This commit is contained in:
parent
9324c59f7b
commit
911255e1d2
2 changed files with 224 additions and 4 deletions
packaging/windows
|
@ -1,16 +1,57 @@
|
|||
# Tested with docker image 'abrarov/msvc-2022:latest'
|
||||
# sha256:f49435d194108cd56f173ad5bc6a27c70eed98b7e8cd54488f5acd85efbd51c9
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Portmaster Windows Installer Generator
|
||||
#------------------------------------------------------------------------------
|
||||
# This script creates Windows installers (MSI and NSIS) for Portmaster application
|
||||
# by combining pre-compiled binaries and packaging them with Tauri.
|
||||
#
|
||||
# ## Workflow for creating Portmaster Windows installers:
|
||||
#
|
||||
# 1. Compile Core Binaries (Linux environment)
|
||||
# ```
|
||||
# earthly +release-prep
|
||||
# ```
|
||||
# This compiles and places files into the 'dist' folder with the required structure.
|
||||
# Note: Latest KEXT binaries and Intel data will be downloaded from https://updates.safing.io
|
||||
#
|
||||
# 2. Compile Windows-Specific Binaries (Windows environment)
|
||||
# Some files cannot be compiled by Earthly and require Windows.
|
||||
# - Compile 'portmaster-core.dll' from the /windows_core_dll folder
|
||||
# - Copy the compiled DLL to <project-root>/dist/download/windows_amd64
|
||||
#
|
||||
# 3. Sign All Binaries (Windows environment)
|
||||
# ```
|
||||
# .\sign_binaries_in_dist.ps1 -certSha1 <SHA1_of_the_certificate>
|
||||
# ```
|
||||
# This signs all binary files in the dist directory
|
||||
#
|
||||
# 4. Create Installers (Windows environment)
|
||||
# Note! You can run it from docker container (see example bellow).
|
||||
# ```
|
||||
# .\generate_windows_installers.ps1
|
||||
# ```
|
||||
# Installers will be placed in <project-root>/dist/windows_amd64
|
||||
#
|
||||
# 5. Sign Installers (Windows environment)
|
||||
# ```
|
||||
# .\sign_binaries_in_dist.ps1 -certSha1 <SHA1_of_the_certificate>
|
||||
# ```
|
||||
# This signs the newly created installer files
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
# Running inside Docker container
|
||||
# Tested with docker image 'abrarov/msvc-2022:latest'
|
||||
# sha256:f49435d194108cd56f173ad5bc6a27c70eed98b7e8cd54488f5acd85efbd51c9
|
||||
#
|
||||
# Note! Ensure you switched Docker Desktop to use Windows containers.
|
||||
# Start powershell and cd to the root of the project.
|
||||
# Then run:
|
||||
# $path = Convert-Path . # Get the absolute path of the current directory
|
||||
# docker run -it --rm -v "${path}:C:/app" -w "C:/app" abrarov/msvc-2022 powershell -NoProfile -File C:/app/packaging/windows/generate_windows_installers.ps1
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#
|
||||
# Optional arguments:
|
||||
# -i, --interactive: Can prompt for user input (e.g. when a file is not found in the primary folder but found in the alternate folder)
|
||||
#------------------------------------------------------------------------------
|
||||
param (
|
||||
[Alias('i')]
|
||||
[switch]$interactive
|
||||
|
|
179
packaging/windows/sign_binaries_in_dist.ps1
Normal file
179
packaging/windows/sign_binaries_in_dist.ps1
Normal file
|
@ -0,0 +1,179 @@
|
|||
param (
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$certSha1,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$timestampServer = "http://timestamp.digicert.com"
|
||||
)
|
||||
|
||||
function Show-Help {
|
||||
Write-Host "Usage: sign_binaries_in_dist.ps1 -certSha1 <CERT_SHA1> [-timestampServer <TIMESTAMP_SERVER>]"
|
||||
Write-Host ""
|
||||
Write-Host "This script signs all binary files located under the '<project root>\dist\' directory recursively."
|
||||
Write-Host "Which should be done before creating the Portmaster installer."
|
||||
Write-Host ""
|
||||
Write-Host "Arguments:"
|
||||
Write-Host " -certSha1 The SHA1 hash of the certificate to use for signing (code signing certificate)."
|
||||
Write-Host " -timestampServer The timestamp server URL to use (optional). Default is http://timestamp.digicert.com."
|
||||
Write-Host ""
|
||||
Write-Host "Example:"
|
||||
Write-Host " .\sign_binaries_in_dist.ps1 -certSha1 ABCDEF1234567890ABCDEF1234567890ABCDEF12"
|
||||
}
|
||||
|
||||
# Show help if no certificate SHA1 provided or help flag used
|
||||
if (-not $certSha1 -or ($args -contains "-h") -or ($args -contains "-help") -or ($args -contains "/h")) {
|
||||
Show-Help
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Find signtool.exe - simplified approach
|
||||
function Find-SignTool {
|
||||
# First try the PATH
|
||||
$signtool = Get-Command signtool.exe -ErrorAction SilentlyContinue
|
||||
if ($signtool) { return $signtool }
|
||||
|
||||
Write-Host "[+] signtool.exe not found in PATH. Searching in common locations..."
|
||||
|
||||
# Common locations for signtool
|
||||
$commonLocations = @(
|
||||
# Windows SDK paths
|
||||
"${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x64\signtool.exe",
|
||||
"${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x86\signtool.exe",
|
||||
|
||||
# Visual Studio paths via vswhere
|
||||
(& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.Component.MSBuild -find "**/signtool.exe" -ErrorAction SilentlyContinue)
|
||||
)
|
||||
|
||||
foreach ($location in $commonLocations) {
|
||||
$tools = Get-ChildItem -Path $location -ErrorAction SilentlyContinue |
|
||||
Sort-Object -Property FullName -Descending
|
||||
if ($tools -and $tools.Count -gt 0) {
|
||||
return $tools[0] # Return the first match
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-SignatureInfo {
|
||||
param(
|
||||
[string]$filePath
|
||||
)
|
||||
# Get the raw output from signtool
|
||||
$rawOutput = & $signtool verify /pa /v $filePath 2>&1
|
||||
|
||||
# Filter output to exclude everything after the timestamp line
|
||||
$filteredOutput = @()
|
||||
foreach ($line in $rawOutput) {
|
||||
if ($line -match "The signature is timestamped:") {
|
||||
break
|
||||
}
|
||||
$filteredOutput += $line
|
||||
}
|
||||
# Extract last subject in the signing chain - it's typically the last "Issued to:" entry
|
||||
$lastSubject = ($filteredOutput | Select-String -Pattern "Issued to: (.*)$" | Select-Object -Last 1 | ForEach-Object { $_.Matches.Groups[1].Value })
|
||||
# Create signature info object
|
||||
$signInfo = @{
|
||||
"IsSigned" = $LASTEXITCODE -eq 0
|
||||
"Subject" = ($filteredOutput | Select-String -Pattern "Issued to: (.*)$" | ForEach-Object { $_.Matches.Groups[1].Value }) -join ", "
|
||||
"Issuer" = ($filteredOutput | Select-String -Pattern "Issued by: (.*)$" | ForEach-Object { $_.Matches.Groups[1].Value }) -join ", "
|
||||
"ExpirationDate" = ($filteredOutput | Select-String -Pattern "Expires: (.*)$" | ForEach-Object { $_.Matches.Groups[1].Value }) -join ", "
|
||||
"SubjectLast" = $lastSubject
|
||||
"SignedBySameCert" = $false
|
||||
}
|
||||
|
||||
# Check if signed by our certificate
|
||||
$null = & $signtool verify /pa /sha1 $certSha1 $filePath 2>&1
|
||||
$signInfo.SignedBySameCert = $LASTEXITCODE -eq 0
|
||||
|
||||
return $signInfo
|
||||
}
|
||||
|
||||
# Find dist directory relative to script location
|
||||
$distDir = Join-Path $PSScriptRoot "../../dist"
|
||||
if (-not (Test-Path -Path $distDir)) {
|
||||
Write-Host "The directory '$distDir' does not exist." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
$distDir = Resolve-Path (Join-Path $PSScriptRoot "../../dist") # normalize path
|
||||
|
||||
# Find signtool.exe
|
||||
$signtool = Find-SignTool
|
||||
if (-not $signtool) {
|
||||
Write-Host "signtool.exe not found in any standard location." -ForegroundColor Red
|
||||
Write-Host "Please install one of the following:" -ForegroundColor Yellow
|
||||
Write-Host "- Windows SDK" -ForegroundColor Yellow
|
||||
Write-Host "- Visual Studio with the 'Desktop development with C++' workload" -ForegroundColor Yellow
|
||||
Write-Host "- Visual Studio Build Tools with the 'Desktop development with C++' workload" -ForegroundColor Yellow
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "[i] Using signtool: $($signtool)"
|
||||
|
||||
# Sign all binary files in the dist directory
|
||||
try {
|
||||
# Define extensions for files that should be signed
|
||||
$binaryExtensions = @('.exe', '.dll', '.sys', '.msi')
|
||||
|
||||
# Get all files with binary extensions
|
||||
$files = Get-ChildItem -Path $distDir -Recurse -File | Where-Object {
|
||||
$extension = [System.IO.Path]::GetExtension($_.Name).ToLower()
|
||||
$binaryExtensions -contains $extension
|
||||
}
|
||||
|
||||
$totalFiles = $files.Count
|
||||
$signedFiles = 0
|
||||
$alreadySignedFiles = 0
|
||||
$wrongCertFiles = 0
|
||||
$filesToSign = @()
|
||||
|
||||
Write-Host "[+] Found $totalFiles binary files to process" -ForegroundColor Green
|
||||
foreach ($file in $files) {
|
||||
$relativeFileName = $file.FullName.Replace("$distDir\", "")
|
||||
# Get signature information
|
||||
$signInfo = Get-SignatureInfo -filePath $file.FullName
|
||||
|
||||
if ($signInfo.IsSigned) {
|
||||
if ($signInfo.SignedBySameCert) {
|
||||
Write-Host -NoNewline " [signed OK ]" -ForegroundColor Green
|
||||
Write-Host -NoNewline " $($relativeFileName)" -ForegroundColor Blue
|
||||
Write-Host "`t: signed by our certificate"
|
||||
$alreadySignedFiles++
|
||||
} else {
|
||||
Write-Host -NoNewline " [different ]" -ForegroundColor Yellow
|
||||
Write-Host -NoNewline " $($relativeFileName)" -ForegroundColor Blue
|
||||
Write-Host "`t: signed by different certificate [$($signInfo.SubjectLast)]"
|
||||
$wrongCertFiles++
|
||||
}
|
||||
} else {
|
||||
Write-Host -NoNewline " [NOT signed]" -ForegroundColor Red
|
||||
Write-Host -NoNewline " $($relativeFileName)" -ForegroundColor Blue
|
||||
Write-Host "`t: not signed"
|
||||
$filesToSign += $file.FullName
|
||||
}
|
||||
}
|
||||
|
||||
# Batch sign files
|
||||
if ($filesToSign.Count -gt 0) {
|
||||
Write-Host "`n[+] Signing $($filesToSign.Count) files in batch..." -ForegroundColor Green
|
||||
|
||||
& $signtool sign /tr $timestampServer /td sha256 /fd sha256 /sha1 $certSha1 /v $filesToSign
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Failed to sign files!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$signedFiles = $filesToSign.Count
|
||||
} else {
|
||||
Write-Host "`n[+] No files need signing." -ForegroundColor Green
|
||||
}
|
||||
|
||||
Write-Host "`n[+] Summary:" -ForegroundColor Green
|
||||
Write-Host " - Total binary files found: $totalFiles"
|
||||
Write-Host " - Files already signed with our certificate: $alreadySignedFiles"
|
||||
Write-Host " - Files signed with different certificate: $wrongCertFiles"
|
||||
Write-Host " - Files newly signed: $signedFiles"
|
||||
} catch {
|
||||
Write-Host "An error occurred: $_" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
Loading…
Add table
Reference in a new issue