5d9aa02c07
- windows/win_card_reader.py: Windows-native card reader via the Raw Input API with a low-level keyboard-hook fallback. - windows/sign_exe.ps1: sign the built executable with a .pfx certificate. - windows/create_self_signed_cert.ps1: generate a self-signed cert for local testing (not trusted by Smart App Control). - windows/verify_sendinput_fix.py: verification helper for the SendInput foreground-unlock fix in run_win.py. - documentation/CODE_SIGNING_SMART_APP_CONTROL.md: signing guidance. - working_files/execute_playlist_retrieve.py, working_files/raw_server_playlist.json: playlist retrieval diagnostics. Note: windows/archive_list.txt and windows/build_last.txt are build output and were committed by request rather than by convention.
157 lines
6.8 KiB
PowerShell
157 lines
6.8 KiB
PowerShell
<#
|
|
================================================================================
|
|
Kiwy Signage Player - Code Signing Script
|
|
================================================================================
|
|
Signs the built KiwySignagePlayer.exe with an Authenticode certificate.
|
|
|
|
For PRODUCTION PCs that have Smart App Control (SAC) ENABLED:
|
|
- The cert MUST be issued by a reputable public CA (e.g. Sectigo, SSL.com,
|
|
DigiCert, GlobalSign). Self-signed certs will NOT satisfy SAC.
|
|
- You must use this script with -CertPath pointing at your .pfx/.p12.
|
|
|
|
For DEV/TEST machines where you have admin rights:
|
|
- A self-signed cert trusted in the local Root store + Trusted Publisher
|
|
works (see create_self_signed_cert.ps1), but it does NOT satisfy SAC.
|
|
|
|
Usage:
|
|
.\sign_exe.ps1 -CertPath "C:\certs\mycodesign.pfx" -CertPassword "secret"
|
|
.\sign_exe.ps1 -CertPath "C:\certs\mycodesign.pfx" # prompt for pwd
|
|
.\sign_exe.ps1 -CertThumbprint "A1B2..." # from cert store
|
|
.\sign_exe.ps1 -CertPath "C:\certs\mycodesign.pfx" -SkipTimestamp $false
|
|
|
|
Optional:
|
|
-TimestampUrl RFC3161 timestamp server (default DigiCert).
|
|
Timestamping is REQUIRED for the signature to stay valid
|
|
after the cert expires and to satisfy Smart App Control.
|
|
-ExePath Path to the exe to sign (default dist\KiwySignagePlayer\KiwySignagePlayer.exe)
|
|
-Force Re-sign even if already signed
|
|
================================================================================
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$CertPath,
|
|
[string]$CertPassword,
|
|
[string]$CertThumbprint,
|
|
[string]$ExePath = (Join-Path $PSScriptRoot 'dist\KiwySignagePlayer\KiwySignagePlayer.exe'),
|
|
[string]$TimestampUrl = 'http://timestamp.digicert.com',
|
|
[switch]$SkipTimestamp,
|
|
[switch]$Force
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
Set-Location $PSScriptRoot
|
|
|
|
function Find-Signtool {
|
|
$candidates = @(
|
|
(Get-Command signtool.exe -ErrorAction SilentlyContinue).Source,
|
|
"$env:ProgramFiles(x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe",
|
|
"$env:ProgramFiles(x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe",
|
|
"$env:ProgramFiles(x86)\Windows Kits\10\bin\10.0.22000.0\x64\signtool.exe",
|
|
"$env:ProgramFiles(x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe"
|
|
)
|
|
foreach ($c in $candidates) {
|
|
if ($c -and (Test-Path $c)) { return $c }
|
|
}
|
|
# Fallback: newest SDK on disk
|
|
$sdkBin = "$env:ProgramFiles(x86)\Windows Kits\10\bin"
|
|
if (Test-Path $sdkBin) {
|
|
$found = Get-ChildItem $sdkBin -Recurse -Filter signtool.exe -ErrorAction SilentlyContinue |
|
|
Sort-Object FullName -Descending | Select-Object -First 1 -ExpandProperty FullName
|
|
if ($found) { return $found }
|
|
}
|
|
return $null
|
|
}
|
|
|
|
$signtool = Find-Signtool
|
|
if ($signtool) {
|
|
Write-Host "[INFO ] signtool: $signtool" -ForegroundColor Green
|
|
} else {
|
|
Write-Host "[WARN ] signtool.exe not found - will use PowerShell Set-AuthenticodeSignature fallback." -ForegroundColor Yellow
|
|
Write-Host "[WARN ] NOTE: the fallback cannot apply an RFC3161 timestamp. For production (SAC),"
|
|
Write-Host "[WARN ] install the Windows SDK signtool: winget install Microsoft.WindowsSDK.10.0.26100"
|
|
}
|
|
|
|
if (-not (Test-Path $ExePath)) {
|
|
Write-Host "[ERROR] Exe not found: $ExePath" -ForegroundColor Red
|
|
Write-Host "Run build_win.bat first, or pass -ExePath."
|
|
exit 1
|
|
}
|
|
|
|
# Already signed?
|
|
$sig = Get-AuthenticodeSignature -FilePath $ExePath
|
|
if ($sig.Status -eq 'Valid' -and -not $Force) {
|
|
Write-Host "[INFO ] Exe is already validly signed by: $($sig.SignerCertificate.Subject)" -ForegroundColor Green
|
|
exit 0
|
|
}
|
|
|
|
# ── Load the certificate ────────────────────────────────────────────
|
|
$cert = $null
|
|
if ($CertThumbprint) {
|
|
$cert = Get-ChildItem Cert:\CurrentUser\My, Cert:\LocalMachine\My -Recurse -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.Thumbprint -eq $CertThumbprint } | Select-Object -First 1
|
|
if (-not $cert) {
|
|
Write-Host "[ERROR] No certificate with thumbprint $CertThumbprint in My store." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
} elseif ($CertPath) {
|
|
if (-not (Test-Path $CertPath)) {
|
|
Write-Host "[ERROR] Cert file not found: $CertPath" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
if (-not $CertPassword) {
|
|
$secure = Read-Host "Certificate password for $CertPath" -AsSecureString
|
|
$CertPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
|
|
[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure))
|
|
}
|
|
$securePwd = ConvertTo-SecureString -String $CertPassword -Force -AsPlainText
|
|
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($CertPath, $securePwd)
|
|
} else {
|
|
Write-Host "[ERROR] Provide -CertPath or -CertThumbprint." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
|
|
# ── Sign (signtool preferred, PowerShell fallback) ──────────────────
|
|
if ($signtool) {
|
|
$args = @()
|
|
if ($CertThumbprint) {
|
|
$args = @('sign', '/sha1', $CertThumbprint, '/fd', 'SHA256')
|
|
} else {
|
|
$args = @('sign', '/f', $CertPath, '/p', $CertPassword, '/fd', 'SHA256')
|
|
}
|
|
if (-not $SkipTimestamp) {
|
|
$args += @('/tr', $TimestampUrl, '/td', 'SHA256')
|
|
}
|
|
$args += @('"' + $ExePath + '"')
|
|
$cmd = "& `"$signtool`" " + ($args -join ' ')
|
|
Write-Host "[INFO ] Signing with signtool..." -ForegroundColor Cyan
|
|
Write-Host "[CMD ] $cmd"
|
|
Invoke-Expression $cmd
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Host "[ERROR] signtool failed with exit code $LASTEXITCODE" -ForegroundColor Red
|
|
exit $LASTEXITCODE
|
|
}
|
|
} else {
|
|
Write-Host "[INFO ] Signing with PowerShell Set-AuthenticodeSignature (no timestamp)..." -ForegroundColor Cyan
|
|
if (-not $cert.HasPrivateKey) {
|
|
Write-Host "[ERROR] Certificate has no private key - cannot sign." -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
$sig = Set-AuthenticodeSignature -FilePath $ExePath -Certificate $cert -HashAlgorithm SHA256
|
|
if ($sig.Status -notin @('Valid','UnknownError')) {
|
|
Write-Host "[ERROR] Signing failed: $($sig.StatusMessage)" -ForegroundColor Red
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# Verify
|
|
$sig = Get-AuthenticodeSignature -FilePath $ExePath
|
|
Write-Host ""
|
|
Write-Host "[INFO ] Signature status: $($sig.Status)" -ForegroundColor Green
|
|
Write-Host "[INFO ] Signer: $($sig.SignerCertificate.Subject)" -ForegroundColor Green
|
|
if ($sig.Status -eq 'Valid') {
|
|
Write-Host "[OK ] KiwySignagePlayer.exe is now digitally signed." -ForegroundColor Green
|
|
} else {
|
|
Write-Host "[WARN ] Signature status is '$($sig.Status)' - inspect above." -ForegroundColor Yellow
|
|
Write-Host "[WARN ] If no timestamp was applied, SAC may still block after cert expiry." -ForegroundColor Yellow
|
|
}
|