- sign-app.ps1: PowerShell signing script using signtool.exe + Trusted Signing dlib - README.md: comprehensive setup and usage documentation - metadata.template.json: signing metadata template (no secrets) - Service Principal auth: AZURE_CLIENT_ID/SECRET/TENANT_ID via EnvironmentCredential
🔐 Azure Trusted Signing Tool
Script tự động ký số (code signing) Windows executables bằng Azure Trusted Signing — không cần Azure CLI, không cần browser login.
Mục lục
Tổng quan
Tool này sử dụng:
signtool.exe(Windows SDK) — công cụ ký số chuẩn của MicrosoftMicrosoft.Trusted.Signing.Clientdlib — thư viện Azure Trusted Signing- Service Principal (App Registration) — xác thực tự động, không cần tương tác
Kiến trúc
sign-app.ps1
│
├── Đọc AZURE_CLIENT_ID / SECRET / TENANT_ID
├── Tạo metadata.json (BOM-free)
│
└── signtool.exe sign /dlib Azure.CodeSigning.Dlib.dll /dmdf metadata.json
│
└── Azure Trusted Signing API (eus.codesigning.azure.net)
└── Ký hash → trả về signature → nhúng vào .exe
Yêu cầu
| Thành phần | Version | Ghi chú |
|---|---|---|
| Windows | 10/11 hoặc Server 2019+ | Bắt buộc |
| PowerShell | 5.1+ | Có sẵn trên Windows |
| Windows SDK | 10.0.26100+ | Chứa signtool.exe |
| .NET SDK | 8.0+ | Để build project |
Cấu hình Azure
Tài nguyên đã cấu hình
| Thông số | Giá trị |
|---|---|
| Tenant ID | c4f27370-f4da-4e92-b944-6adc120b3683 |
| Trusted Signing Endpoint | https://eus.codesigning.azure.net/ |
| Account Name | huanld |
| Certificate Profile | codesign-profile |
| Trust Type | Private Trust |
| App Registration | trusted-signing-sp |
| Client ID | 0a2888b4-baa5-48da-abea-24f2e9a1f92f |
| Secret Expires | 2028-05-17 |
Role Assignment
Service Principal trusted-signing-sp có role:
- Trusted Signing Certificate Profile Signer trên account
huanld
Setup Azure (lần đầu tiên)
-
Tạo Trusted Signing Account tại Azure Portal:
- Resource Group:
signcode - Account Name:
huanld - Region:
East US
- Resource Group:
-
Tạo Certificate Profile:
- Profile Name:
codesign-profile - Profile Type:
Private Trust
- Profile Name:
-
Tạo App Registration:
Azure Portal → App registrations → New registration Name: trusted-signing-sp -
Tạo Client Secret:
App registration → Certificates & secrets → New client secret Expiry: 24 months -
Gán Role:
Trusted Signing Account → Access control (IAM) → Add role assignment Role: Trusted Signing Certificate Profile Signer Member: trusted-signing-sp
Cài đặt
1. Tải Microsoft.Trusted.Signing.Client
Script tự động tải nếu chưa có. Hoặc tải thủ công:
$dir = "$env:TEMP\TrustedSigningClient"
New-Item -ItemType Directory -Force -Path $dir | Out-Null
Invoke-WebRequest "https://www.nuget.org/api/v2/package/Microsoft.Trusted.Signing.Client" `
-OutFile "$dir\TrustedSigningClient.zip" -UseBasicParsing
Expand-Archive "$dir\TrustedSigningClient.zip" -DestinationPath $dir -Force
2. Xác nhận signtool.exe
Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin\*\x86\signtool.exe" |
Sort-Object FullName -Descending | Select-Object -First 1
Kết quả mong đợi:
C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\signtool.exe
Sử dụng
Cú pháp
powershell -ExecutionPolicy Bypass -File sign-app.ps1 -FilePath <đường_dẫn_file>
Ví dụ
# Ký file exe đơn
powershell -ExecutionPolicy Bypass -File sign-app.ps1 -FilePath "C:\build\MyApp.exe"
# Ký file trong thư mục publish
powershell -ExecutionPolicy Bypass -File sign-app.ps1 `
-FilePath "D:\Code\MyProject\bin\Release\net8.0\publish\MyApp.exe"
# Ký nhiều file (loop)
Get-ChildItem "D:\build\publish" -Filter "*.exe" | ForEach-Object {
powershell -ExecutionPolicy Bypass -File sign-app.ps1 -FilePath $_.FullName
}
Tham số tùy chỉnh
powershell -ExecutionPolicy Bypass -File sign-app.ps1 `
-FilePath "C:\build\MyApp.exe" `
-AccountUrl "https://eus.codesigning.azure.net/" `
-AccountName "huanld" `
-ProfileName "codesign-profile" `
-TimestampUrl "http://timestamp.acs.microsoft.com" `
-SigntoolPath "C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\signtool.exe"
Output
[INFO] Signing: C:\build\MyApp.exe
Account : huanld / codesign-profile
Trusted Signing
Version: 1.0.95
Submitting digest for signing...
OperationId abc123: InProgress
Signing completed with status 'Succeeded' in 2.3s
Successfully signed: C:\build\MyApp.exe
[OK] Signed successfully!
Status : Valid
Signer : CN=huanleducoutlook.onmicrosoft.com, ...
Expires : 2026-06-18
Xác nhận chữ ký
$sig = Get-AuthenticodeSignature "C:\build\MyApp.exe"
$sig | Format-List Status, SignerCertificate
Tích hợp CI/CD
PowerShell Build Script
# build-and-sign.ps1
param([string]$Configuration = "Release")
$projectPath = "D:\Code\MyProject"
$signScript = "$PSScriptRoot\sign-app.ps1"
# Build
dotnet publish $projectPath -c $Configuration -o "$projectPath\publish"
# Sign all executables
Get-ChildItem "$projectPath\publish" -Filter "*.exe" | ForEach-Object {
Write-Host "Signing: $($_.Name)"
& powershell -ExecutionPolicy Bypass -File $signScript -FilePath $_.FullName
if ($LASTEXITCODE -ne 0) { throw "Signing failed for $($_.Name)" }
}
Write-Host "All files signed successfully!"
GitHub Actions / Gitea Actions
- name: Sign Executables
shell: pwsh
env:
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
run: |
powershell -ExecutionPolicy Bypass -File sign-app.ps1 `
-FilePath "${{ github.workspace }}\publish\MyApp.exe"
⚠️ Trong CI/CD, đặt credentials vào Secrets thay vì hardcode trong script.
Troubleshooting
❌ SignerSign() failed / BOM error
Nguyên nhân: metadata.json có BOM (Byte Order Mark).
Giải pháp: Script đã xử lý tự động bằng UTF8Encoding(false).
❌ Selected user account does not exist in tenant 'Microsoft Services'
Nguyên nhân: Dùng sai client ID (Azure CLI app 04b07795...).
Giải pháp: Dùng App Registration riêng trong tenant của bạn.
❌ The request body must contain 'code' (AADSTS900144)
Nguyên nhân: Dùng OAuth2 v1 endpoint với sai tên parameter.
Giải pháp: Dùng v2.0 endpoint + hashtable body (đã sửa trong script hiện tại).
❌ Signing failed với status UnknownError
Nguyên nhân: Certificate Profile là Private Trust — chỉ verify được trong môi trường tin tưởng nội bộ.
Giải pháp: Upgrade lên Public Trust để distribute ra ngoài (yêu cầu địa chỉ tổ chức tại Mỹ/EU và Microsoft vetting).
❌ signtool.exe not found
# Tìm signtool.exe
Get-ChildItem "C:\Program Files (x86)\Windows Kits" -Filter "signtool.exe" -Recurse |
Sort-Object FullName -Descending | Select-Object -First 1 FullName
Nếu không có, cài Windows SDK từ:
https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/
❌ Role assignment chưa có hiệu lực
Role mới gán cần 5-10 phút để propagate. Chờ rồi thử lại.