* feat: bump dependencies (NuGet 8 -> 10, NLog 5 -> 6) * feat: shared IPC data structures for policy state * feat: registry policy reader with WMI watcher * feat: monitor service policy enforcement, maintenance window, deferred install * feat: UI policy state propagation, locks, deferred-install + sentinel UX fix * feat: ADMX/ADML templates, admin guide, helper script, test runbook * chore: build tooling, MSI SHA256, version bump, release notes * more reworking around the adml/admx/md * build and upload ADMX/ADML admin templates zip * updates from looking at / testing GPO UI * missed the prepare-for-tests ps1 * add boms to all ps1's just in case * update test-run for more vebosity to allow humans to understand * more updates to release notes * Fix alignment on "from" label for maintenance window * Remove arrow pointing to task bar * Remove unused selection changed stub * Ensure logical hour value is passed to maintenance window start/end * Fix invalid URL error not showing - ensure maintenance start/end time errors return correctly Change variable names * reworking new-upcoming-releasenotes * publish admin templates zip + sha256 sidecar to GitHub release, update release note * add tatooing words * reword clamping words --------- Co-authored-by: Christopher Britton <christopher.l.britton@outlook.com>
AWSSigner.NET
A custom Authenticode signing tool that uses AWS KMS as the HSM backend. The private key never leaves AWS KMS — only a digest is sent over the wire.
Purpose
Windows Authenticode signing normally requires the private key to be locally accessible (PKCS#12 file or hardware token). This tool implements a "detached digest" pattern so that an asymmetric RSA key stored in AWS KMS can be used for Authenticode without ever exporting it.
Why AWS KMS?
Since June 2023, CA/Browser Forum rules require EV code-signing private keys to be stored in a FIPS 140 Level 2 (or higher) certified module. GitHub Actions secrets do not meet this requirement. AWS KMS is FIPS 140 Level 3 certified — a superset of Level 2 — so it satisfies the requirement while remaining accessible from CI/CD pipelines without ever exporting the key.
AWS prerequisites
Before using this tool you need:
- An asymmetric RSA signing key created (or imported) in AWS KMS
- A code-signing certificate from your CA (the OpenZiti project uses GlobalSign) issued against a
CSR generated from that KMS key — key usage must include Digital Signature, extended key
usage must include Code Signing (
1.3.6.1.5.5.7.3.3) - An IAM user (or role) with the following KMS permissions scoped to that key:
kms:ListKeys,kms:DescribeKey,kms:Sign - AWS CLI credentials for that IAM user configured in the environment
How it is used in the build
Installer/build.ps1 wires everything together:
AWSSigner.NETis built (dotnet build -c Release) and staged toInstaller/AWSSigner.NET/SIGNING_CERTis set to the GlobalSign DV certificate (Installer/GlobalSign-SigningCert-2024-2027.cert)SIGNTOOL_PATHis set to thesigntool.exebundled with Advanced Installer- Advanced Installer builds
ZitiDesktopEdge.aip— the.aipproject registers AWSSigner.NET as its custom signing tool viaCustomToolPath="AWSSigner.NET\AWSSigner.NET.exe". Advanced Installer's signing configuration requires a native executable here; a PowerShell script cannot be used directly, which is the reason this wrapper is a compiled C# project rather than a.ps1. It is invoked once for each file markedDigSign="true"in the project:ZitiDesktopEdge.exe,ZitiUpdateService.exe,ZitiUpgradeSentinel.exe, andziti-edge-tunnel.exe - Optionally, a second (dual) Authenticode signature is appended to the final installer EXE
with
signtool /asusing the localopenziti_2024.p12cert (ssl.com TSA) — only when the environment variableOPENZITI_P12_PASS_2024is set
Signing steps performed by AWSSigner.NET
signtool sign /dg <dir> /fd sha256 /f <cert> <file>
--> produces <file>.dig (base64-encoded digest)
AWS KMS Sign(keyId, digest, RSASSA_PKCS1_V1_5_SHA_256)
--> returns base64 signature, written to <file>.dig.signed
signtool sign /di <dir> <file>
--> injects the KMS signature into the PE Authenticode slot
signtool timestamp /tr http://timestamp.digicert.com /td sha256 <file>
--> countersigns with DigiCert RFC 3161 timestamp
NOTE: without a timestamp the signature is only valid while the signing cert is valid.
The timestamp proves the binary was signed while the cert was current, extending
trust beyond the cert's expiration date.
signtool verify /pa <file>
--> validates the completed signature chain
cleanup: <file>.dig <file>.dig.signed <file>.p7u
NOTE: <file>.p7u is produced by the /dg step and is required as input for the /di step.
All three temp files are removed after successful signing.
The signing algorithm used is RSASSA_PKCS1_V1_5_SHA_256 which maps to the sha256 file-digest
flag (/fd sha256) passed to signtool.
Required environment variables
| Variable | Description |
|---|---|
AWS_KEY_ID |
ARN or alias of the KMS asymmetric signing key |
AWS_ACCESS_KEY_ID |
AWS IAM access key (kms:ListKeys, kms:DescribeKey, kms:Sign) |
AWS_SECRET_ACCESS_KEY |
Corresponding IAM secret key |
AWS_REGION |
AWS region where the KMS key lives (e.g. us-east-1) |
SIGNING_CERT |
Path to the .cert / .cer file for the code-signing certificate |
SIGNTOOL_PATH |
(optional) Full path to signtool.exe if not on PATH |
build.ps1 sets SIGNING_CERT and SIGNTOOL_PATH automatically. The AWS variables must be
provided by the CI/CD environment (or the operator's shell when building locally).
Optional second signature (dual-signing)
If OPENZITI_P12_PASS_2024 is set, build.ps1 appends a second Authenticode signature via:
signtool sign /f openziti_2024.p12 /p $env:OPENZITI_P12_PASS_2024 `
/tr http://ts.ssl.com /fd sha512 /td sha512 /as <installer.exe>
This uses SHA-512 digests and the ssl.com TSA, complementing the SHA-256 KMS signature.
Debugging locally
Set AWSSIGNER_DEBUG=TRUE to enable verbose logging. In a DEBUG build, Program.cs accepts
four positional arguments so you can test without Advanced Installer:
AWSSigner.NET.exe <env-var-file.ps1> <file-to-sign.exe> <cert.cert> <signtool.exe>
The env-var file should contain lines in the form $env:VAR="value".
Logs are written to AWSSigner.log in the working directory, with daily rotation (7-day
retention).
Known limitations / gotchas
- Validation short-circuits incorrectly:
VerifyEnvVaris called for all four required AWS variables but each call overwrites the sameenvVarsExistboolean. Only the last variable checked (SIGNING_CERT) determines whether the process aborts. If the AWS variables are missing butSIGNING_CERTis set, the tool will proceed and fail at the KMS call rather than at startup validation. - Silent return on missing inputs: when validation fails the tool exits with code 0, so
Advanced Installer treats it as success. The resulting binary will be unsigned. Watch for the
ERROR:lines in the console output orAWSSigner.log. AWS_KEY_IDmissing = unsigned build:build.ps1warns but does not abort whenAWS_KEY_IDis unset. A release built without this variable will ship unsigned.- Target framework: The project targets .NET 4.7.2 (
net472).dotnet buildworks but requires the .NET Framework 4.7.2 targeting pack to be installed on the build machine.