How-To: Sign Windows Executables (Authenticode) Using the Keyserver¶
This guide explains how to sign Windows binaries (EXE, DLL, MSI, and other PE files) using Authenticode with the Cryptera Keyserver.
Authenticode requires RSA keys and uses either:
- JSign (cross-platform, recommended),
- osslsigncode
This how-to demonstrates the recommended approach using JSign with PKCS#11, which is platform-agnostic and integrates cleanly with CI/CD systems.
+!!! note "Production Authentication Required" Authenticode signing in production environments requires authentication through an external
OAuth2/OIDC identity provider such as Azure Entra ID, GitLab OIDC, etc.
Do not use the internal Authserver when performing production signing operations.
Internal Authserver for Testing Only
If you are following this guide on the demo Keyserver, the internal Authserver may be used for
simplified testing.
In production, enterprise identity controls — including MFA, account lifecycle management, and
central auditing — must be enforced by your external identity provider.
Prerequisites¶
While most Linux distributions should work, this tutorial is based on Ubuntu 24.04 with the following prerequisites:
- A Cryptera Keyserver deployment
- A signing key of type:
- RSA, with an appropriate hash algorithm (typically SHA256)
- For full Authenticode, without manual certificate injection on windows hosts, the key must be signed by Digicert or similar
- The Keyserver PKCS#11 library (
libpkcs11ks.so) (See OpenSSL How to) - OAuth2 client credentials (or CI-issued JWT) for a user with appropriate privileges (like fw-admin)
- The following tools installed:
- cURL, for HTTP requests
- openssl, for command line cryptographic operations
- pkcs11-tool (opensc), for interfacing with Hardware Security Module.
- jq
- libengine-pkcs11-openssl
- Java Runtime environment
Download JSign (7.4): JSign project page
Note on Digicert rooted certificates¶
If you currently have a Digicert signed key pair, the certificate and key can be imported into keyserver and made available to JSign or OSSLSigncode.
1. Authenticate and Obtain an OAuth Token¶
Encode credentials:
Request a token:
export KSC_ID_TOKEN=$(
curl -sS --tlsv1.3 \
-X POST \
-H "Authorization: Basic $AUTH_CREDS" \
-d "grant_type=client_credentials" \
https://authserver.<UNIQUE-ID>.cryptera.com:8300/oauth2/token \
| jq -r .access_token
)
Verify:
2. Create an Operation¶
Authenticode signing uses the same operation workflow as firmware signing / Code signing.
Create an operation allowing a single signature:
export KSC_OPERATION_ID=$(
curl -sS --tlsv1.3 \
-X POST "https://keyserver.<UNIQUE-ID>.cryptera-security.io/api/operations" \
-H "Authorization: Bearer $KSC_ID_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"validms\": 60000,
\"description\": \"Authenticode signing\",
\"new-keyusagerestrictions\": [
{ \"keyid\": \"key4\", \"maxusagecount\": 1 }
]
}" | jq -r .id
)
Check:
Note: Replace
key4with the RSA key you intend to use.
3. Approve the Operation (If Required)¶
If the key requires approvers:
- Navigate to
- Log in as an approver
- Approve the operation
If required approvers = 0, skip this step.
4. Export the Certificate for JSign¶
Authenticode requires a public certificate corresponding to the RSA key.
Export it:
curl -sS --tlsv1.3 \
-H "Authorization: Bearer $KSC_ID_TOKEN" \
"https://keyserver.<UNIQUE-ID>.cryptera-security.io/api/keys/key4" \
| jq -r .certificate > cert.pem
¶
curl -sS --tlsv1.3 \
-H "Authorization: Bearer $KSC_ID_TOKEN" \
"https://keyserver.<UNIQUE-ID>.cryptera-security.io/api/keys/key4" \
| jq -r .certificate > cert.pem
Configuration of PKCS11 Library (libpkcs11ks.so)¶
The PKCS11 library is inspecting the following variables, and require them to be updated for the specific operation being carried out:
export KSC_API_SERVER="https://keyserver.<UNIQUE_ID>.cryptera-security.io"
export KSC_OPERATION_ID=$operation_id
export KSC_OPERATION_DESCRIPTION="Authenticode signing or artifact XYZ"
- KSC_API_SERVER - Reference to the specific customer KeyServer
- KSC_OPERATION_ID - The resulting Operation ID given as response to pipeline initiation
- KSC_OPERATION_DESCRIPTION - Description of changes, presented to the Approver for context
- KSC_ID_TOKEN - For example the token manually fetched above, GitLab JWT, etc.
7. Sign Using JSign (Recommended)¶
Assume you want to sign app.exe.
Use JSign with the Keyserver PKCS#11 library:
java -jar jsign-7.4.jar \
--keystore pkcs11.cfg \
--storetype PKCS11 \
--tsa http://timestamp.digicert.com \
--alias key4 \
--certfile cert.pem
app.exe
Where pkcs11.cfg contains:
8. Sign Using osslsigncode (Alternative)¶
Alternatively OSSLSignCode can be used for signing:
osslsigncode sign \
-pkcs11module libpkcs11kks.so \
-key "pkcs11:token=KEYSERVER;object=<KEYLABEL>;type=private" \
-certs <CERT_CHAIN> \
-in <EXEFILE> \
-out <SIGNED_EXEFILE> \
-t http://timestamp.digicert.com \
-h sha256
9. Verify the Signature using OSSLSigncode¶
Summary¶
In this guide you:
- Obtained an OAuth2 access token
- Created and approved a signing operation
- Fetched the RSA certificate
- Configured the Keyserver PKCS#11 environment
- Identified the RSA key label
- Signed Windows executables using JSign or osslsigncode
- Verified the resulting Authenticode signature
This allows secure, HSM-backed Authenticode signing in CI/CD environments without ever exposing private key material.