huanld d78ca9b6ac feat: initial commit - Azure Trusted Signing automation script
- 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
2026-05-18 14:54:06 +07:00

🔐 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 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:

    • 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:

$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.


Tham khảo

S
Description
Azure Trusted Signing automation script for Windows executables
Readme 29 KiB
Languages
PowerShell 100%