# sign_release.ps1 # Signiert alle relevanten Artefakte fuer eine Kundenauslieferung. # # VORAUSSETZUNGEN: # - signtool.exe im PATH (Windows SDK) oder AZA_SIGNTOOL_PATH gesetzt # - Zertifikat: AZA_SIGN_CERT_THUMBPRINT (SHA-1 Thumbprint) ODER # AZA_SIGN_PFX_PATH + AZA_SIGN_PFX_PASSWORD # - Optional: AZA_SIGN_TIMESTAMP_URL (Standard: http://timestamp.digicert.com) # # AUFRUF: # .\sign_release.ps1 (signiert alle Artefakte) # .\sign_release.ps1 -DryRun (nur pruefen, nichts signieren) # .\sign_release.ps1 -SkipDllSigning (nur EXE + Installer signieren) param( [switch]$DryRun, [switch]$SkipDllSigning ) $ErrorActionPreference = "Stop" $projectRoot = $PSScriptRoot # --- Konfiguration --- $timestampUrl = if ($env:AZA_SIGN_TIMESTAMP_URL) { $env:AZA_SIGN_TIMESTAMP_URL } else { "http://timestamp.digicert.com" } $signToolPath = if ($env:AZA_SIGNTOOL_PATH) { $env:AZA_SIGNTOOL_PATH } else { $found = Get-Command "signtool.exe" -ErrorAction SilentlyContinue if ($found) { $found.Source } else { $null } } # --- Artefakt-Pfade --- $desktopExe = Join-Path $projectRoot "dist\aza_desktop\aza_desktop.exe" $installerExe = Join-Path $projectRoot "dist\installer\aza_desktop_setup.exe" $internalDir = Join-Path $projectRoot "dist\aza_desktop\_internal" # --- Hilfsfunktionen --- function Get-SignableFiles { param([string]$Directory) $extensions = @("*.exe", "*.dll", "*.pyd") $files = @() foreach ($ext in $extensions) { $files += Get-ChildItem -Path $Directory -Filter $ext -Recurse -File } return $files | Sort-Object FullName } function Test-FileSigned { param([string]$FilePath) try { $sig = Get-AuthenticodeSignature -FilePath $FilePath return ($sig.Status -eq "Valid") } catch { return $false } } function Invoke-SignTool { param([string]$FilePath) if ($DryRun) { Write-Host "[DRY RUN] Wuerde signieren: $FilePath" return $true } $args = @("sign") if ($env:AZA_SIGN_CERT_THUMBPRINT) { $args += "/sha1", $env:AZA_SIGN_CERT_THUMBPRINT } elseif ($env:AZA_SIGN_PFX_PATH) { $args += "/f", $env:AZA_SIGN_PFX_PATH if ($env:AZA_SIGN_PFX_PASSWORD) { $args += "/p", $env:AZA_SIGN_PFX_PASSWORD } } else { Write-Error "Kein Zertifikat konfiguriert. AZA_SIGN_CERT_THUMBPRINT oder AZA_SIGN_PFX_PATH setzen." return $false } $args += "/fd", "sha256" $args += "/tr", $timestampUrl $args += "/td", "sha256" $args += "/v" $args += $FilePath & $signToolPath @args return ($LASTEXITCODE -eq 0) } # --- Hauptlogik --- Write-Host "=== AZA Release Signing ===" Write-Host "" # 1. Voraussetzungen pruefen if (-not $signToolPath) { Write-Error @" signtool.exe nicht gefunden. Bitte Windows SDK installieren oder AZA_SIGNTOOL_PATH setzen. Download: https://developer.microsoft.com/windows/downloads/windows-sdk/ "@ exit 1 } Write-Host "SignTool: $signToolPath" Write-Host "Timestamp: $timestampUrl" if (-not $env:AZA_SIGN_CERT_THUMBPRINT -and -not $env:AZA_SIGN_PFX_PATH) { Write-Error @" Kein Signing-Zertifikat konfiguriert. Setze eine der folgenden Umgebungsvariablen: AZA_SIGN_CERT_THUMBPRINT (SHA-1 Thumbprint des Zertifikats im Windows Store) AZA_SIGN_PFX_PATH (Pfad zur .pfx-Datei, optional mit AZA_SIGN_PFX_PASSWORD) "@ exit 1 } if (-not (Test-Path $desktopExe)) { Write-Error "Desktop-EXE nicht gefunden: $desktopExe — Bitte zuerst build_exe.ps1 ausfuehren." exit 1 } # 2. DLLs / PYDs in _internal signieren (optional) $signedCount = 0 $failedCount = 0 if (-not $SkipDllSigning -and (Test-Path $internalDir)) { Write-Host "" Write-Host "--- Schritt 1/3: DLLs und PYDs in _internal signieren ---" $internalFiles = Get-SignableFiles -Directory $internalDir foreach ($file in $internalFiles) { if (Test-FileSigned -FilePath $file.FullName) { Write-Host "[SKIP] Bereits signiert: $($file.Name)" continue } $ok = Invoke-SignTool -FilePath $file.FullName if ($ok) { $signedCount++ } else { $failedCount++ Write-Warning "Signierung fehlgeschlagen: $($file.FullName)" } } Write-Host "DLL/PYD-Signierung: $signedCount signiert, $failedCount fehlgeschlagen" } else { Write-Host "" Write-Host "--- Schritt 1/3: DLL-Signierung uebersprungen ---" } # 3. Haupt-EXE signieren Write-Host "" Write-Host "--- Schritt 2/3: Haupt-EXE signieren ---" $ok = Invoke-SignTool -FilePath $desktopExe if (-not $ok) { Write-Error "Signierung der Haupt-EXE fehlgeschlagen: $desktopExe" exit 1 } $signedCount++ Write-Host "Haupt-EXE signiert: $desktopExe" # 4. Installer signieren (muss nach Inno-Build, aber vor Publish erfolgen) if (Test-Path $installerExe) { Write-Host "" Write-Host "--- Schritt 3/3: Installer signieren ---" $ok = Invoke-SignTool -FilePath $installerExe if (-not $ok) { Write-Error "Signierung des Installers fehlgeschlagen: $installerExe" exit 1 } $signedCount++ Write-Host "Installer signiert: $installerExe" } else { Write-Host "" Write-Host "[WARNUNG] Installer nicht gefunden: $installerExe" Write-Host "Bitte build_installer.ps1 ausfuehren und dann erneut signieren." } # 5. Zusammenfassung Write-Host "" Write-Host "=== Signierung abgeschlossen ===" Write-Host "Signiert: $signedCount Dateien" if ($failedCount -gt 0) { Write-Warning "Fehlgeschlagen: $failedCount Dateien" exit 1 } # 6. Verifikation Write-Host "" Write-Host "--- Verifikation ---" foreach ($path in @($desktopExe, $installerExe)) { if (Test-Path $path) { $sig = Get-AuthenticodeSignature -FilePath $path $status = if ($DryRun) { "(DRY RUN)" } else { $sig.Status } Write-Host "$([System.IO.Path]::GetFileName($path)): $status" } } Write-Host "" Write-Host "Naechster Schritt: build_release_artifacts.ps1 ausfuehren (aktualisiert SHA256-Hashes)."