mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-15 19:27:56 +01:00
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Not maintained since wix5 upgrade, so make it build locally for an installer 1. Do elevation when dev cert is not added to root store 2. Set up version to build arg to build, and build cmdpal version same with CI 3. cmdpal AOT local build 4. Make sure every msix file is signed successfully <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [ ] Closes: #xxx <!-- - [ ] Closes: #yyy (add separate lines for additional resolved issues) --> - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed Verify the script can build an installer on a new devbox, and cmdpal, filelocksmith etc can be run without problem <img width="872" height="275" alt="image" src="https://github.com/user-attachments/assets/cf4cff0d-0d90-4496-a7f8-50c582d9c340" /> <img width="1251" height="555" alt="image" src="https://github.com/user-attachments/assets/6529c1a8-a532-4dfc-9f74-2c2fd37e28e6" /> Output for msix packages: PackageFullName Version --------------- ------- Microsoft.PowerToys.SparseApp_0.96.2.0_neutral__8wekyb3d8bbwe 0.96.2.0 Microsoft.PowerToys.FileLocksmithContextMenu_0.96.2.0_neutral__8wekyb3d8bbwe 0.96.2.0 Microsoft.PowerToys.ImageResizerContextMenu_0.96.2.0_neutral__8wekyb3d8bbwe 0.96.2.0 Microsoft.PowerToys.NewPlusContextMenu_0.96.2.0_neutral__8wekyb3d8bbwe 0.96.2.0 Microsoft.PowerToys.PowerRenameContextMenu_0.96.2.0_neutral__8wekyb3d8bbwe 0.96.2.0 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
198 lines
7.3 KiB
PowerShell
198 lines
7.3 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Ensures a code signing certificate exists and is trusted in all necessary certificate stores.
|
|
|
|
.DESCRIPTION
|
|
This script provides two functions:
|
|
|
|
1. EnsureCertificate:
|
|
- Searches for an existing code signing certificate by subject name.
|
|
- If not found, creates a new self-signed certificate.
|
|
- Exports the certificate and attempts to import it into:
|
|
- CurrentUser\TrustedPeople
|
|
- CurrentUser\Root
|
|
- LocalMachine\Root (admin privileges may be required)
|
|
|
|
2. ImportAndVerifyCertificate:
|
|
- Imports a `.cer` file into the specified certificate store if not already present.
|
|
- Verifies the certificate is successfully imported by checking thumbprint.
|
|
|
|
This is useful in build or signing pipelines to ensure a valid and trusted certificate is available before signing MSIX or executable files.
|
|
|
|
.PARAMETER certSubject
|
|
The subject name of the certificate to search for or create. Default is:
|
|
"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
|
|
|
|
.PARAMETER cerPath
|
|
(ImportAndVerifyCertificate only) The file path to a `.cer` certificate file to import.
|
|
|
|
.PARAMETER storePath
|
|
(ImportAndVerifyCertificate only) The destination certificate store path (e.g. Cert:\CurrentUser\Root).
|
|
|
|
.EXAMPLE
|
|
$cert = EnsureCertificate
|
|
|
|
Ensures the default certificate exists and is trusted, and returns the certificate object.
|
|
|
|
.EXAMPLE
|
|
ImportAndVerifyCertificate -cerPath "$env:TEMP\temp_cert.cer" -storePath "Cert:\CurrentUser\Root"
|
|
|
|
Imports a certificate into the CurrentUser Root store and verifies its presence.
|
|
|
|
.NOTES
|
|
- For full trust, administrative privileges may be needed to import into LocalMachine\Root.
|
|
- Certificates are created using RSA and SHA256 and marked as CodeSigningCert.
|
|
#>
|
|
|
|
function ImportAndVerifyCertificate {
|
|
param (
|
|
[string]$cerPath,
|
|
[string]$storePath
|
|
)
|
|
|
|
$thumbprint = (Get-PfxCertificate -FilePath $cerPath).Thumbprint
|
|
|
|
$existingCert = Get-ChildItem -Path $storePath | Where-Object { $_.Thumbprint -eq $thumbprint }
|
|
if ($existingCert) {
|
|
Write-Host "Certificate already exists in $storePath"
|
|
return $true
|
|
}
|
|
|
|
try {
|
|
$null = Import-Certificate -FilePath $cerPath -CertStoreLocation $storePath -ErrorAction Stop
|
|
} catch {
|
|
if ($_.Exception.Message -match "Access is denied" -or $_.Exception.InnerException.Message -match "Access is denied") {
|
|
Write-Warning "Access denied to $storePath. Attempting to import with admin privileges..."
|
|
try {
|
|
Start-Process powershell -ArgumentList "-NoProfile", "-Command", "& { Import-Certificate -FilePath '$cerPath' -CertStoreLocation '$storePath' }" -Verb RunAs -Wait
|
|
} catch {
|
|
Write-Warning "Failed to request admin privileges: $_"
|
|
return $false
|
|
}
|
|
} else {
|
|
Write-Warning "Failed to import certificate to $storePath : $_"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
$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
|
|
}
|
|
}
|
|
|
|
function EnsureCertificate {
|
|
param (
|
|
[string]$certSubject = "CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
|
|
)
|
|
|
|
$cert = Get-ChildItem -Path Cert:\CurrentUser\My |
|
|
Where-Object { $_.Subject -eq $certSubject } |
|
|
Sort-Object NotAfter -Descending |
|
|
Select-Object -First 1
|
|
|
|
if (-not $cert) {
|
|
Write-Host "Certificate not found. Creating a new one..."
|
|
|
|
$cert = New-SelfSignedCertificate -Subject $certSubject `
|
|
-CertStoreLocation "Cert:\CurrentUser\My" `
|
|
-KeyAlgorithm RSA `
|
|
-Type CodeSigningCert `
|
|
-HashAlgorithm SHA256
|
|
|
|
if (-not $cert) {
|
|
Write-Error "Failed to create a new certificate."
|
|
return $null
|
|
}
|
|
|
|
Write-Host "New certificate created with thumbprint: $($cert.Thumbprint)"
|
|
}
|
|
else {
|
|
Write-Host "Using existing certificate with thumbprint: $($cert.Thumbprint)"
|
|
}
|
|
|
|
$cerPath = "$env:TEMP\temp_cert.cer"
|
|
[void](Export-Certificate -Cert $cert -FilePath $cerPath -Force)
|
|
|
|
if (-not (ImportAndVerifyCertificate -cerPath $cerPath -storePath "Cert:\CurrentUser\TrustedPeople")) { return $null }
|
|
if (-not (ImportAndVerifyCertificate -cerPath $cerPath -storePath "Cert:\CurrentUser\Root")) { return $null }
|
|
if (-not (ImportAndVerifyCertificate -cerPath $cerPath -storePath "Cert:\LocalMachine\Root")) {
|
|
Write-Warning "Failed to import to LocalMachine\Root (admin may be required)"
|
|
return $null
|
|
}
|
|
|
|
return $cert
|
|
}
|
|
|
|
function Export-CertificateFiles {
|
|
param (
|
|
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
|
|
[string]$CerPath,
|
|
[string]$PfxPath,
|
|
[securestring]$PfxPassword
|
|
)
|
|
|
|
if (-not $Certificate) {
|
|
Write-Error "No certificate provided to export."
|
|
return
|
|
}
|
|
|
|
if ($CerPath) {
|
|
try {
|
|
Export-Certificate -Cert $Certificate -FilePath $CerPath -Force | Out-Null
|
|
Write-Host "Exported CER to: $CerPath"
|
|
} catch {
|
|
Write-Warning "Failed to export CER file: $_"
|
|
}
|
|
}
|
|
|
|
if ($PfxPath -and $PfxPassword) {
|
|
try {
|
|
Export-PfxCertificate -Cert $Certificate -FilePath $PfxPath -Password $PfxPassword -Force | Out-Null
|
|
Write-Host "Exported PFX to: $PfxPath"
|
|
} catch {
|
|
Write-Warning "Failed to export PFX file: $_"
|
|
}
|
|
}
|
|
|
|
if (-not $CerPath -and -not $PfxPath) {
|
|
Write-Warning "No output path specified. Nothing was exported."
|
|
}
|
|
}
|
|
|
|
# Main execution when script is run directly
|
|
if ($MyInvocation.InvocationName -ne '.') {
|
|
Write-Host "=== PowerToys Certificate Management ===" -ForegroundColor Green
|
|
Write-Host ""
|
|
|
|
# Ensure certificate exists and is trusted
|
|
Write-Host "Checking for existing certificate or creating new one..." -ForegroundColor Yellow
|
|
$cert = EnsureCertificate
|
|
|
|
if ($cert) {
|
|
# Export the certificate to a .cer file
|
|
$exportPath = Join-Path (Get-Location) "PowerToys-CodeSigning.cer"
|
|
Write-Host ""
|
|
Write-Host "Exporting certificate..." -ForegroundColor Yellow
|
|
Export-CertificateFiles -Certificate $cert -CerPath $exportPath
|
|
|
|
Write-Host ""
|
|
Write-Host "=== IMPORTANT NOTES ===" -ForegroundColor Red
|
|
Write-Host "The certificate has been exported to: $exportPath" -ForegroundColor White
|
|
Write-Host ""
|
|
Write-Host "To use this certificate for code signing, you need to:" -ForegroundColor Yellow
|
|
Write-Host "1. Import this certificate into 'Trusted People' store" -ForegroundColor White
|
|
Write-Host "2. Import this certificate into 'Trusted Root Certification Authorities' store" -ForegroundColor White
|
|
Write-Host "Certificate Details:" -ForegroundColor Green
|
|
Write-Host "Subject: $($cert.Subject)" -ForegroundColor White
|
|
Write-Host "Thumbprint: $($cert.Thumbprint)" -ForegroundColor White
|
|
Write-Host "Valid Until: $($cert.NotAfter)" -ForegroundColor White
|
|
} else {
|
|
Write-Error "Failed to create or find certificate. Please check the error messages above."
|
|
exit 1
|
|
}
|
|
} |