Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/actions/spell-check/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ CDeclaration
cdn
CElems
CENTERALIGN
cer
certlm
certmgr
cfp
Expand Down Expand Up @@ -355,6 +356,7 @@ DEVMODE
DEVMODEW
devpal
DIALOGEX
digicert
dimm
DISABLEASACTIONKEY
DISABLENOSCROLL
Expand Down Expand Up @@ -1525,6 +1527,7 @@ SIDs
siex
sigdn
SIGNINGSCENARIO
signtool
Signtool
SINGLEKEY
sipolicy
Expand Down
8 changes: 6 additions & 2 deletions src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,12 +207,16 @@ class CmdPal : public PowertoyModuleIface

try
{
if (!package::GetRegisteredPackage(L"Microsoft.CommandPalette", false).has_value())
std::wstring packageName = L"Microsoft.CommandPalette";
#ifdef _DEBUG
packageName = L"Microsoft.CommandPalette.Dev";
#endif
if (!package::GetRegisteredPackage(packageName, false).has_value())
{
Logger::info(L"CmdPal not installed. Installing...");

std::wstring installationFolder = get_module_folderpath();
#if _DEBUG
#ifdef _DEBUG
std::wstring archSubdir = L"x64";
#ifdef _M_ARM64
archSubdir = L"ARM64";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>

<PropertyGroup Condition="'$(CIBuild)'=='true'">
<PropertyGroup>
<GenerateAppxPackageOnBuild>true</GenerateAppxPackageOnBuild>
</PropertyGroup>

Expand Down
179 changes: 179 additions & 0 deletions tools/build/self-sign.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#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 `
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why RSA? I remember it's not a recommended choice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's not, I meant to use ecc curve but it failed, so I use rsa for now. Thanks for pointing, I can do more investigation later.

-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."
Loading