Sign Windows binaries with a Certum SimplySign cloud certificate — from one command, on any OS. · Signez des binaires Windows avec un certificat cloud Certum SimplySign — en une commande, sur n'importe quel OS.
Authenticode signing is just data — hashing the file and wrapping the cloud's
RSA signature into a PKCS#7 blob. ssign does it all over plain HTTPS, so you
can sign a Windows binary from Linux, macOS or Windows — no GUI, no
SimplySign Desktop, no PKCS#11 stack, no container.
flowchart TD
A["ssign app.exe -e you@example.com -O/-T …"] --> B
subgraph cloud["Certum cloud · cloudsign.webnotarius.pl"]
B["1 · OAuth login<br/>email + 6-digit OTP → bearer token"]
C["2 · fetch card<br/>serial + signing certificate"]
E["4 · POST digest + cert<br/>→ async task"]
F["5 · poll → RSA-4096 signature"]
end
subgraph local["Your machine · any OS"]
D["3 · Authenticode SHA-256 of each file"]
G["6 · PKCS#7 + chain + RFC3161 timestamp"]
H["embed into the file → signed ✔"]
end
B --> C --> D --> E --> F --> G --> H
Not affiliated with or endorsed by Certum / Asseco. "Certum" and "SimplySign" are their trademarks;
ssignis an independent client for their public API.
- What it does — and doesn't
- Install
- Usage
⚠️ Security: the OTP is a long-lived secret- GitHub Actions
- Secure signing with owner approval
- Status
- Sign any format — the PKCS#11 module
- Contributing — native formats wanted
- Acknowledgements
How the cloud protocol works:
docs/simplysign-protocol.md.
| ✅ Does | ❌ Does not (yet) |
|---|---|
Authenticode-sign PE files: .exe, .dll, .sys, .ocx, .cpl |
MSI / MSP / MSM installers (work in progress) |
| Embed the full certificate chain (leaf + Certum intermediate) | CAB, catalog (.cat), PowerShell scripts, APPX/MSIX |
Add an RFC3161 timestamp (time.certum.pl) |
Sign with anything other than a Certum SimplySign cloud cert |
| Run on Linux, macOS, Windows; batch many files in one login | Verify signatures (use signtool / osslsigncode) |
cargo install ssign
# or grab a prebuilt binary from the Releases page (Linux / macOS / Windows)Two ways to authenticate — pick one:
| Mode | Flag | When |
|---|---|---|
| Automatic | -O, --otp <SEED> |
CI/scripts: give your TOTP seed once and ssign computes the 6-digit code every run. |
| Manual | -T, --token <CODE> |
On your own machine: read the current code from your authenticator and pass it. |
Every flag also reads an environment variable, so nothing sensitive has to touch your shell history or the process list.
# Manual, local — paste the current code from your app:
ssign -e you@example.com -T 123456 app.exe
# Automation — seed once, then hands-off:
export CERTUM_EMAIL=you@example.com
export CERTUM_OTP=YOUR_BASE32_TOTP_SEED # or the full otpauth:// URI
ssign app.exe installer.dll driver.sysFiles are signed in place by default (--backup keeps a .orig; -o <DIR>
writes elsewhere). See ssign --help for every option.
-O/--otp (env CERTUM_OTP) is your TOTP seed — the base32 secret behind
your authenticator. It is long-lived: anyone who has it plus your e-mail
can sign code as you, indefinitely, until you re-issue the SimplySign QR
code. Treat it like a private key.
- Never pass it as a command-line argument — it lands in your shell history and the process list. Use the environment variable.
- Prefer
-T/--token(a one-shot 6-digit code) for local/manual signing, so the seed never leaves your authenticator app. - In CI, store it as a protected environment secret with required reviewers (see below) — not a plain repo secret.
- If it ever leaks, rotate it: re-issue the QR code from Certum.
The account e-mail is not sensitive, but the seed is. The 6-digit code itself expires in ~30 s and is low-risk.
name: Sign
on:
workflow_dispatch: # manual only — each run performs a real cloud signature
jobs:
sign:
runs-on: ubuntu-latest
environment: signing # ← protected environment; see next section
steps:
- uses: actions/checkout@v4
- run: cargo install ssign
- run: ssign dist/*.exe
env:
CERTUM_EMAIL: ${{ secrets.CERTUM_EMAIL }}
CERTUM_OTP: ${{ secrets.CERTUM_OTP }}
- uses: actions/upload-artifact@v4
with: { name: signed, path: dist/ }Signing can spend your certificate and, if mishandled, expose the OTP seed. Gate it behind a GitHub Environment so the repository owner must approve every signing run before the job can even read the secrets:
- Settings → Environments → New environment, name it
signing. - Add a Deployment protection rule → Required reviewers, and add yourself (the owner). Now any job that targets this environment pauses for your approval.
- Store
CERTUM_EMAILandCERTUM_OTPas environment secrets ofsigning(not repository secrets). They become readable only inside an approved run of a job that declaresenvironment: signing. - In the workflow, the signing job declares
environment: signing(as above).
Result: a pull request — even a malicious one — cannot trigger a signature
or read the seed; every run waits for the owner's click. Also keep the signing
job on workflow_dispatch or protected-branch pushes, never on
pull_request from forks.
In a release pipeline, make signing a job in the same workflow as the
build/release (gated by environment: signing, with the release job set to
needs: it) — so a tag becomes build → approve → signed release. Don't rely on
a separate workflow triggered by release: published: a release created by the
built-in GITHUB_TOKEN does not trigger other workflows.
Working end to end for PE files, proven in CI: ssign produces a valid
Authenticode signature with the real Certum cloud certificate, the full chain,
and an RFC3161 timestamp. MSI signing is in progress and not yet enabled.
Pipeline: otp → auth (OAuth) → card → sign (SCS1_ATOM) → authenticode
(hash + PKCS#7) → timestamp (RFC3161).
ssign's own signer covers PE (.exe/.dll/.sys/…). For every other
Authenticode format — MSI, CAB, catalog, APPX, PowerShell — ssign also ships a
PKCS#11 module (ssign-pkcs11) that hands your Certum cloud key to
osslsigncode, which already knows
how to hash and package them all. Still no SimplySign Desktop, no p11-kit, no
smart card — the module talks to the cloud over HTTPS like the rest of ssign.
cargo build -p ssign-pkcs11 --release # → target/release/libssign_pkcs11.so
export CERTUM_EMAIL=you@example.com CERTUM_OTP=BASE32SEED
osslsigncode sign \
-pkcs11module ./target/release/libssign_pkcs11.so \
-pkcs11cert 'pkcs11:type=cert' -key 'pkcs11:type=private' \
-ac certum-code-signing-2021-ca.pem \
-h sha256 -t http://time.certum.pl/ \
-in installer.msi -out installer-signed.msiIt reads the same CERTUM_EMAIL / CERTUM_OTP (or CERTUM_TOKEN) as the CLI
and caches the cloud session, so signing many files in a row needs only one
code. -ac embeds the Certum "Code Signing 2021 CA" intermediate so the chain
verifies. ssign-pkcs11/tests/sign-all-formats.sh signs one file of each format
end to end.
The module covers every format today via osslsigncode. Native, dependency-free support in ssign itself (so you need neither osslsigncode nor a PKCS#11 engine) is still welcome:
- MSI / MSP / MSM — in progress; the CFB (OLE2) pre-hash doesn't yet match
osslsigncode. Notes or a fix here would be gold. - CAB cabinet archives, CAT security catalogs, APPX / MSIX packages
- PowerShell / script signing (
.ps1,.psm1,.vbs,.js) via SIP
Each is independent of the cloud plumbing — login, card and signing already work, so the task is "hash this container format the Authenticode way, then hand the digest to the existing sign path." Open an issue or a PR; happy to point you at the relevant module.
- hpvb/certum-container — its Docker + Xvnc + SimplySign + p11-kit setup, and especially the "you must press Close or the token won't work" note, are what made reverse-engineering the SimplySign cloud protocol tractable. ssign drops the container entirely, but it stood on that groundwork.
- osslsigncode — the reason the PKCS#11 module reaches every format: it hashes and packages PE, MSI, CAB, CAT, APPX and scripts, so ssign only has to supply the cloud key.
- Ce qu'il fait — et ne fait pas
- Installation
- Utilisation
⚠️ Sécurité : l'OTP est un secret à longue durée- GitHub Actions (FR)
- Signature sécurisée avec accord du propriétaire
- État
- Signer tous les formats — le module PKCS#11
- Contribuer — formats natifs recherchés
- Remerciements
La signature Authenticode, ce ne sont que des données : hacher le fichier et
emballer la signature RSA du cloud dans un blob PKCS#7. ssign fait tout en
HTTPS pur — donc on signe un binaire Windows depuis Linux, macOS ou Windows,
sans GUI, sans SimplySign Desktop, sans pile PKCS#11, sans conteneur. (Voir le
schéma en haut.)
| ✅ Fait | ❌ Ne fait pas (encore) |
|---|---|
Signe en Authenticode les PE : .exe, .dll, .sys, .ocx, .cpl |
Les installeurs MSI / MSP / MSM (en cours) |
| Embarque la chaîne complète (feuille + intermédiaire Certum) | CAB, catalogues (.cat), scripts PowerShell, APPX/MSIX |
Ajoute un horodatage RFC3161 (time.certum.pl) |
Signer avec autre chose qu'un cert cloud Certum SimplySign |
| Tourne sur Linux, macOS, Windows ; lot de fichiers en un seul login | Vérifier les signatures (utilisez signtool / osslsigncode) |
cargo install ssign
# ou récupérez un binaire prêt à l'emploi depuis la page Releases (Linux / macOS / Windows)Deux façons de s'authentifier — au choix :
| Mode | Option | Quand |
|---|---|---|
| Automatique | -O, --otp <SEED> |
CI/scripts : fournissez votre seed TOTP une fois, ssign calcule le code à 6 chiffres à chaque exécution. |
| Manuel | -T, --token <CODE> |
Sur votre machine : lisez le code courant de votre appli d'authentification et passez-le. |
Chaque option lit aussi une variable d'environnement — rien de sensible ne touche votre historique shell ni la liste des processus.
# Manuel, en local — collez le code courant de votre appli :
ssign -e vous@example.com -T 123456 app.exe
# Automatisation — le seed une fois, puis sans intervention :
export CERTUM_EMAIL=vous@example.com
export CERTUM_OTP=VOTRE_SEED_TOTP_BASE32 # ou l'URI otpauth:// complète
ssign app.exe installeur.dll pilote.sysLes fichiers sont signés sur place par défaut (--backup garde un .orig ;
-o <DIR> écrit ailleurs). Voir ssign --help pour toutes les options.
-O/--otp (env CERTUM_OTP), c'est votre seed TOTP — le secret base32
derrière votre authentificateur. Il est à longue durée : quiconque l'a avec
votre e-mail peut signer du code en votre nom, indéfiniment, jusqu'à ce que
vous régénériez le QR code SimplySign. Traitez-le comme une clé privée.
- Jamais en argument de ligne de commande — il finit dans l'historique shell et la liste des processus. Utilisez la variable d'environnement.
- Préférez
-T/--token(un code à usage unique) pour le signage local, afin que le seed ne quitte jamais votre appli d'authentification. - En CI, stockez-le comme secret d'environnement protégé avec relecteurs requis (voir plus bas) — pas un simple secret de dépôt.
- En cas de fuite, régénérez-le : refaites le QR code chez Certum.
L'e-mail du compte n'est pas sensible ; le seed l'est. Le code à 6 chiffres, lui, expire en ~30 s et présente peu de risque.
name: Sign
on:
workflow_dispatch: # manuel uniquement — chaque exécution signe réellement
jobs:
sign:
runs-on: ubuntu-latest
environment: signing # ← environnement protégé ; voir la section suivante
steps:
- uses: actions/checkout@v4
- run: cargo install ssign
- run: ssign dist/*.exe
env:
CERTUM_EMAIL: ${{ secrets.CERTUM_EMAIL }}
CERTUM_OTP: ${{ secrets.CERTUM_OTP }}
- uses: actions/upload-artifact@v4
with: { name: signed, path: dist/ }Signer dépense votre certificat et, mal géré, peut exposer le seed OTP. Verrouillez ça derrière un Environment GitHub pour que le propriétaire du dépôt approuve chaque signature avant même que le job ne lise les secrets :
- Settings → Environments → New environment, nommez-le
signing. - Ajoutez une règle Deployment protection rule → Required reviewers, et ajoutez-vous (le propriétaire). Tout job visant cet environnement se met en pause en attendant votre approbation.
- Stockez
CERTUM_EMAILetCERTUM_OTPcomme secrets d'environnement designing(pas des secrets de dépôt). Ils ne sont lisibles que dans une exécution approuvée d'un job déclarantenvironment: signing. - Dans le workflow, le job de signature déclare
environment: signing(ci-dessus).
Résultat : une pull request — même malveillante — ne peut pas déclencher une
signature ni lire le seed ; chaque exécution attend le clic du propriétaire.
Gardez aussi le job de signature sur workflow_dispatch ou des pushes de
branches protégées, jamais sur pull_request depuis un fork.
Dans un pipeline de release, mettez la signature en job du même workflow
que le build/release (gardé par environment: signing, et le job release le
met en needs:) — un tag devient alors build → approbation → release signée.
Ne comptez pas sur un workflow séparé déclenché par release: published : une
Release créée par le GITHUB_TOKEN intégré ne déclenche pas d'autre workflow.
Fonctionne de bout en bout pour les PE, prouvé en CI : ssign produit une
signature Authenticode valide avec le vrai certificat cloud Certum, la chaîne
complète et un horodatage RFC3161. La signature MSI est en cours et pas
encore activée.
Pipeline : otp → auth (OAuth) → card → sign (SCS1_ATOM) → authenticode
(hash + PKCS#7) → timestamp (RFC3161).
Le signeur natif de ssign couvre les PE (.exe/.dll/.sys/…). Pour
tous les autres formats Authenticode — MSI, CAB, catalogue, APPX, PowerShell
— ssign fournit aussi un module PKCS#11 (ssign-pkcs11) qui présente votre
clé cloud Certum à osslsigncode,
qui sait déjà tous les hasher et les emballer. Toujours sans SimplySign Desktop,
sans p11-kit, sans carte à puce — le module parle au cloud en HTTPS comme le
reste de ssign.
cargo build -p ssign-pkcs11 --release # → target/release/libssign_pkcs11.so
export CERTUM_EMAIL=vous@example.com CERTUM_OTP=GRAINE_BASE32
osslsigncode sign \
-pkcs11module ./target/release/libssign_pkcs11.so \
-pkcs11cert 'pkcs11:type=cert' -key 'pkcs11:type=private' \
-ac certum-code-signing-2021-ca.pem \
-h sha256 -t http://time.certum.pl/ \
-in installeur.msi -out installeur-signe.msiIl lit les mêmes CERTUM_EMAIL / CERTUM_OTP (ou CERTUM_TOKEN) que la CLI et
met en cache la session cloud, donc signer plusieurs fichiers d'affilée ne
demande qu'un seul code. -ac embarque l'intermédiaire Certum « Code Signing
2021 CA » pour que la chaîne se vérifie. ssign-pkcs11/tests/sign-all-formats.sh
signe un fichier de chaque format de bout en bout.
Le module couvre déjà tous les formats aujourd'hui via osslsigncode. Un support natif, sans dépendance, dans ssign lui-même (pour n'avoir besoin ni d'osslsigncode ni d'un moteur PKCS#11) reste le bienvenu :
- MSI / MSP / MSM — en cours ; le pré-hash CFB (OLE2) ne correspond pas
encore à
osslsigncode. Des notes ou un correctif ici vaudraient de l'or. - Archives CAB, catalogues CAT, packages APPX / MSIX
- Signature PowerShell / scripts (
.ps1,.psm1,.vbs,.js) via SIP
Chacun est indépendant de la plomberie cloud — login, carte et signature fonctionnent déjà, donc la tâche est « hasher ce format conteneur à la mode Authenticode, puis passer l'empreinte au chemin de signature existant ». Ouvrez une issue ou une PR ; je vous oriente volontiers vers le bon module.
- hpvb/certum-container — son montage Docker + Xvnc + SimplySign + p11-kit, et surtout la note « il faut cliquer sur Close sinon le token ne fonctionne pas », ont rendu la rétro-ingénierie du protocole cloud SimplySign abordable. ssign supprime le conteneur, mais s'appuie sur ce travail de défrichage.
- osslsigncode — la raison pour laquelle le module PKCS#11 atteint tous les formats : il hashe et emballe PE, MSI, CAB, CAT, APPX et scripts, si bien que ssign n'a qu'à fournir la clé cloud.
MIT