This project can replace AzureSignTool for Windows signing when built with --features azure-kv-sign. psign-tool portable covers digest checks, verification, and (with --features azure-kv-sign-portable) Key Vault keys/sign on digest files plus PE Authenticode signing through portable sign-pe or the PE subset of --mode portable sign. Windows mode remains the broader native-shaped signing path.
Azure Artifact Signing (Trusted Signing) via Microsoft’s decoupled Azure.CodeSigning.Dlib.dll is not the Key Vault path: use --dlib / --trusted-signing-dlib-root with --dmdf only (never mixed with --azure-key-vault-url). See migration-artifact-signing.md. PowerShell OpenAuthenticode overlap (inspect JSON, REST submit, EKU prefix selection) is summarized in psa-interoperability.md.
Build:
cargo build -p psign --features azure-kv-sign --release --bin psign-tool
Typical Azure-shaped invocation:
psign-tool.exe sign ^
--azure-key-vault-url https://myvault.vault.azure.net ^
--azure-key-vault-certificate my-cert ^
--azure-key-vault-managed-identity ^
--timestamp-url http://timestamp.digicert.com ^
--timestamp-digest sha256 ^
--digest sha256 ^
-ifl files.txt ^
path\to\*.dll ^
other.exe
| AzureSignTool | psign-tool |
|---|---|
-kvu / --azure-key-vault-url |
Same |
-kvc |
--azure-key-vault-certificate |
-kvcv |
--azure-key-vault-certificate-version |
-kvi, -kvs, -kvt |
Client credential trio |
-kva |
--azure-key-vault-accesstoken |
-kvm |
--azure-key-vault-managed-identity |
-au |
--azure-authority |
-tr, -td, -t, -fd, -d, -du, -ph, -nph, -as |
Existing sign flags (--timestamp-url, --legacy-timestamp-url, …) |
-ac (repeatable) |
--ac repeatable (Vec) |
-ifl |
--input-file-list |
-coe |
--continue-on-error |
-mdop |
--max-degree-of-parallelism |
-s (skip signed) in AzureSignTool conflicts with native /s (certificate store name) in this tool. Use --skip-signed instead.
- Managed identity calls the IMDS endpoint (
169.254.169.254) with resourcehttps://vault.azure.net, matching common VM/App Service scenarios. - Client credentials use the v2.0 OAuth endpoint at
{authority}/{tenant}/oauth2/v2.0/tokenwith scopehttps://vault.azure.net/.default. - Access token bypass (
-kva) sends the bearer token directly to Key Vault REST.
Signing uses RSA PKCS#1 v1.5 over the file digest via Key Vault keys/sign (RS256 / RS384 / RS512). SHA-1 file digest is not supported on this path.
AzureSignTool documents HRESULT-style batch exits (README Exit Codes):
| Outcome | Value |
|---|---|
| Success | 0 |
| Partial success | 0x20000001 |
| All failed | 0xA0000002 |
Enable the same behavior directly on psign-tool with:
--exit-codes azuresigntool(aliasazure), or- Environment
PSIGN_EXIT_CODES=azure(orazuresigntool).
The old helper executable name is no longer emitted; use psign-tool sign --exit-codes azure ... as the AzureSignTool replacement invocation, or set PSIGN_EXIT_CODES=azure for scripts that need an environment-level default.
Default signtool exit codes remain 0 / 1 / 2.
For Windows PE artifacts on Linux/macOS, use the portable PE signer. This assembles Authenticode CMS locally, asks Key Vault to sign the CMS authenticated-attribute digest, embeds the resulting PKCS#7 in the PE certificate table, and recomputes the PE checksum:
psign-tool portable sign-pe ./MyApp.exe \
--azure-key-vault-url https://myvault.vault.azure.net \
--azure-key-vault-certificate my-cert \
--azure-key-vault-managed-identity \
--timestamp-url http://timestamp.digicert.com \
--timestamp-digest sha256 \
--digest sha256 \
--output ./MyApp.signed.exeThe native-shaped PE subset also works in-place:
psign-tool --mode portable sign \
--azure-key-vault-url https://myvault.vault.azure.net \
--azure-key-vault-certificate my-cert \
--azure-key-vault-managed-identity \
--timestamp-url http://timestamp.digicert.com \
--timestamp-digest sha256 \
--digest sha256 \
./MyApp.exeThe portable path currently supports PE/WinMD, SHA-2 digests, Key Vault signer certificates, optional --ac / --chain-cert certificates, and RFC3161 sign-time timestamping. Use psign-tool portable timestamp-pe-rfc3161 as a second portable step only when you already have a timestamp token/response.
For lower-level integrations, produce digest.bin with pe-digest / cab-digest (--encoding raw), then:
psign-tool portable azure-key-vault-sign-digest \
--azure-key-vault-url https://myvault.vault.azure.net \
--azure-key-vault-certificate my-cert \
--digest-file digest.bin \
--digest-algorithm sha256 \
--azure-key-vault-managed-identityStdout prints standard base64 signature bytes (no PEM). --signature-output PATH writes raw signature. ECDSA certificates use ES256 / ES384 / ES512 automatically for the digest-only helper. Portable PE signing currently requires an RSA Key Vault certificate because Authenticode CMS emission uses RSA PKCS#1 v1.5.
CMS signer digest vs subject (file layout) digest: pe-digest, cab-digest, and the MSI installer fingerprint path behind verify-msi hash subject layout — not the 32-byte RS256 input over SignerInfo.signedAttrs. AzureSignTool’s CryptMsg path signs authenticated attributes; for RSA SHA-256 the Key Vault RS256 value is SHA-256 over that attribute SET — on PE use pe-signer-rs256-prehash --encoding raw (--index = PKCS#7 row, --signer-index = SignerInfo); on signed .cab use cab-signer-rs256-prehash (or extract-cab-pkcs7 then pkcs7-signer-rs256-prehash); on .msi use msi-signer-rs256-prehash (or extract-msi-pkcs7 then pkcs7-signer-rs256-prehash); on raw PKCS#7 .cat bodies use catalog-signer-rs256-prehash (same bytes as pkcs7-signer-rs256-prehash). If PKCS#7 is already in a file, use pkcs7-signer-rs256-prehash --signer-index N --encoding raw. Library parity is tested in psign-sip-digest (rsa_pkcs1v15_signed_attrs_verify).
Experimental (Linux PE layout only): psign-tool portable append-pe-pkcs7 appends prebuilt PKCS#7 DER as a new WIN_CERTIFICATE row and recomputes CheckSum. Use pe-checksum --strict on the output to gate ImageHlp-style checksum parity.
AzureSignTool does not verify signatures. After signing on Windows, use portable verification on Linux/macOS CI agents where helpful, for example:
psign-tool portable verify-pe -- <artifact>
For trust validation without the OS certificate store, use trust-verify-pe (or format-specific trust-verify-* commands). Portable trust uses the automatic AuthRoot cache when no anchors are supplied; pass --anchor-dir / --authroot-cab for enterprise anchors or pinned CI inputs. Short-lived signing certificates—common with Artifact Signing profiles—need RFC3161 timestamping at sign time so signatures remain verifiable after the leaf expires; combine digest checks with timestamp-aware trust options when applicable:
psign-tool portable trust-verify-pe ./artifact.exe \
--prefer-timestamp-signing-time \
--require-valid-timestamp \
--anchor-dir ./anchors \
--authroot-cab ./authroot.stl.cab
Optional --as-of YYYY-MM-DD fixes the verification instant for reproducible CI. More background and flag mapping: migration-artifact-signing.md.
Use the appropriate portable subcommands for your format (verify-pe, catalog checks, artifact-signing-metadata-check for JSON templates, etc.) as documented in that crate.
Automated CI uses psign-server azure-key-vault-server as a local Key Vault replacement. Non-ignored E2E tests cover both psign-tool portable azure-key-vault-sign-digest and full portable PE signing through portable sign-pe / --mode portable sign with a dummy access token, then verify the embedded Authenticode signature without contacting Azure.