d78ca9b6ac
- 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
284 lines
8.1 KiB
Markdown
284 lines
8.1 KiB
Markdown
# 🔐 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](#tổng-quan)
|
|
- [Yêu cầu](#yêu-cầu)
|
|
- [Cấu hình Azure](#cấu-hình-azure)
|
|
- [Cài đặt](#cài-đặt)
|
|
- [Sử dụng](#sử-dụng)
|
|
- [Tích hợp CI/CD](#tích-hợp-cicd)
|
|
- [Troubleshooting](#troubleshooting)
|
|
|
|
---
|
|
|
|
## Tổng quan
|
|
|
|
Tool này sử dụng:
|
|
- **`signtool.exe`** (Windows SDK) — công cụ ký số chuẩn của Microsoft
|
|
- **`Microsoft.Trusted.Signing.Client`** dlib — 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)
|
|
|
|
1. **Tạo Trusted Signing Account** tại [Azure Portal](https://portal.azure.com):
|
|
- Resource Group: `signcode`
|
|
- Account Name: `huanld`
|
|
- Region: `East US`
|
|
|
|
2. **Tạo Certificate Profile**:
|
|
- Profile Name: `codesign-profile`
|
|
- Profile Type: `Private Trust`
|
|
|
|
3. **Tạo App Registration**:
|
|
```
|
|
Azure Portal → App registrations → New registration
|
|
Name: trusted-signing-sp
|
|
```
|
|
|
|
4. **Tạo Client Secret**:
|
|
```
|
|
App registration → Certificates & secrets → New client secret
|
|
Expiry: 24 months
|
|
```
|
|
|
|
5. **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:
|
|
|
|
```powershell
|
|
$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
|
|
|
|
```powershell
|
|
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
|
|
powershell -ExecutionPolicy Bypass -File sign-app.ps1 -FilePath <đường_dẫn_file>
|
|
```
|
|
|
|
### Ví dụ
|
|
|
|
```powershell
|
|
# 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
|
|
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ý
|
|
|
|
```powershell
|
|
$sig = Get-AuthenticodeSignature "C:\build\MyApp.exe"
|
|
$sig | Format-List Status, SignerCertificate
|
|
```
|
|
|
|
---
|
|
|
|
## Tích hợp CI/CD
|
|
|
|
### PowerShell Build Script
|
|
|
|
```powershell
|
|
# 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
|
|
|
|
```yaml
|
|
- 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`
|
|
|
|
```powershell
|
|
# 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.
|
|
|
|
---
|
|
|
|
## Tham khảo
|
|
|
|
- [Azure Trusted Signing Docs](https://learn.microsoft.com/en-us/azure/trusted-signing/)
|
|
- [Microsoft.Trusted.Signing.Client NuGet](https://www.nuget.org/packages/Microsoft.Trusted.Signing.Client)
|
|
- [signtool.exe Reference](https://learn.microsoft.com/en-us/windows/win32/seccrypto/signtool)
|
|
- [Azure Identity - DefaultAzureCredential](https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential)
|