Files
PowerToys/tools/build/self-sign.ps1
Kai Tao 4f9e829155 [cmdpal] Run cmdpalette from runner locally (#38725)
* empowering users to maximize OOBE to their heart desire (#37823)

empowering users to maximize to their heart desire

* resume main

* Trust selfsign cert in localmachine\root to make msix available

* minor fix

* retry signing

---------

Co-authored-by: Clint Rutkas <clint@rutkas.com>
2025-04-17 11:53:22 +08:00

180 lines
5.9 KiB
PowerShell

#https://learn.microsoft.com/en-us/windows/msix/package/signing-known-issues
# 1. Build the powertoys as usual.
# 2. Call this script to sign the msix package.
# First time run needs admin permission to trust the certificate.
param (
[string]$architecture = "x64", # Default to x64 if not provided
[string]$buildConfiguration = "Debug" # Default to Debug if not provided
)
$signToolPath = $null
$kitsRootPaths = @(
"C:\Program Files (x86)\Windows Kits\10\bin",
"C:\Program Files\Windows Kits\10\bin"
)
$signToolAvailable = Get-Command "signtool" -ErrorAction SilentlyContinue
if ($signToolAvailable) {
Write-Host "SignTool is available in the system PATH."
$signToolPath = "signtool"
}
else {
Write-Host "Searching for latest SignTool matching architecture: $architecture"
foreach ($root in $kitsRootPaths) {
if (Test-Path $root) {
$versions = Get-ChildItem -Path $root -Directory | Where-Object {
$_.Name -match '^\d+\.\d+\.\d+\.\d+$'
} | Sort-Object Name -Descending
foreach ($version in $versions) {
$candidatePath = Join-Path -Path $version.FullName -ChildPath "x86"
$exePath = Join-Path -Path $candidatePath -ChildPath "signtool.exe"
if (Test-Path $exePath) {
Write-Host "Found SignTool at: $exePath"
$signToolPath = $exePath
break
}
}
if ($signToolPath) { break }
}
}
if (!$signToolPath) {
Write-Host "SignTool not found. Please ensure Windows SDK is installed."
exit 1
}
}
Write-Host "`nUsing SignTool: $signToolPath"
# Set the certificate subject and the ECDSA curve
$certSubject = "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
# Check if the certificate already exists in the current user's certificate store
$existingCert = Get-ChildItem -Path Cert:\CurrentUser\My |
Where-Object { $_.Subject -eq $certSubject } |
Sort-Object NotAfter -Descending |
Select-Object -First 1
if ($existingCert) {
# If the certificate exists, use the existing certificate
Write-Host "Certificate already exists, using the existing certificate"
$cert = $existingCert
}
else {
# If the certificate doesn't exist, create a new self-signed certificate
Write-Host "Certificate does not exist, creating a new certificate..."
$cert = New-SelfSignedCertificate -Subject $certSubject `
-CertStoreLocation "Cert:\CurrentUser\My" `
-KeyAlgorithm RSA `
-Type CodeSigningCert `
-HashAlgorithm SHA256
}
function Import-And-VerifyCertificate {
param (
[string]$cerPath,
[string]$storePath
)
$thumbprint = (Get-PfxCertificate -FilePath $cerPath).Thumbprint
# ✅ Step 1: Check if already exists in store
$existingCert = Get-ChildItem -Path $storePath | Where-Object { $_.Thumbprint -eq $thumbprint }
if ($existingCert) {
Write-Host "✅ Certificate already exists in $storePath"
return $true
}
# 🚀 Step 2: Try to import if not already there
try {
$null = Import-Certificate -FilePath $cerPath -CertStoreLocation $storePath -ErrorAction Stop
}
catch {
Write-Warning "❌ Failed to import certificate to $storePath : $_"
return $false
}
# 🔁 Step 3: Verify again
$imported = Get-ChildItem -Path $storePath | Where-Object { $_.Thumbprint -eq $thumbprint }
if ($imported) {
Write-Host "✅ Certificate successfully imported to $storePath"
return $true
}
else {
Write-Warning "❌ Certificate not found in $storePath after import"
return $false
}
}
$cerPath = "$env:TEMP\temp_cert.cer"
Export-Certificate -Cert $cert -FilePath $cerPath -Force
# used for sign code/msix
# CurrentUser\TrustedPeople
if (-not (Import-And-VerifyCertificate -cerPath $cerPath -storePath "Cert:\CurrentUser\TrustedPeople")) {
exit 1
}
# CurrentUser\Root
if (-not (Import-And-VerifyCertificate -cerPath $cerPath -storePath "Cert:\CurrentUser\Root")) {
exit 1
}
# LocalMachine\Root
if (-not (Import-And-VerifyCertificate -cerPath $cerPath -storePath "Cert:\LocalMachine\Root")) {
Write-Warning "⚠️ Failed to import to LocalMachine\Root (admin may be required)"
exit 1
}
# Output the thumbprint of the certificate (to confirm which certificate is being used)
Write-Host "Using certificate with thumbprint: $($cert.Thumbprint)"
$rootDirectory = (Split-Path -Parent(Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)))
# Dynamically build the directory path based on architecture and build configuration
# $directoryPath = Join-Path $rootDirectory "$architecture\$buildConfiguration\WinUI3Apps\CmdPal\"
$directoryPath = Join-Path $rootDirectory "$architecture\$buildConfiguration\WinUI3Apps\CmdPal\"
if (-not (Test-Path $directoryPath)) {
Write-Error "Path to search for msix files does not exist: $directoryPath"
exit 1
}
Write-Host "Directory path to search for .msix and .appx files: $directoryPath"
# Get all .msix and .appx files from the specified directory
$filePaths = Get-ChildItem -Path $directoryPath -Recurse | Where-Object {
($_.Extension -eq ".msix" -or $_.Extension -eq ".appx") -and
($_.Name -like "*$architecture*")
}
if ($filePaths.Count -eq 0) {
Write-Host "No .msix or .appx files found in the directory."
}
else {
# Iterate through each file and sign it
foreach ($file in $filePaths) {
Write-Host "Signing file: $($file.FullName)"
# Use SignTool to sign the file
$signToolCommand = "& `"$signToolPath`" sign /sha1 $($cert.Thumbprint) /fd SHA256 /t http://timestamp.digicert.com `"$($file.FullName)`""
# Execute the sign command
try {
Invoke-Expression $signToolCommand
}
catch {
Write-Host "Error signing file $($file.Name): $_"
}
}
}
Write-Host "Signing process completed."