In short
You can't paste a code signing key into a CI secret — since June 1, 2023 every publicly trusted key must live on certified hardware, non-exportable, so the file you'd paste doesn't exist. Automating signatures means connecting the pipeline to that hardware in one of three ways: a USB token plugged into a self-hosted runner, a cloud signing session held open on a dedicated build agent, or a pipeline-native service like Azure Artifact Signing. Most teams land on the middle option, with a signing step that runs only for releases.
A scoping note before the architecture: everything below assumes a certificate that already exists. If you're still choosing one, the Certum code signing certificates My-SSL sells (from $99/year) are delivered through SimplySign cloud signing — the setup this guide's second pattern is built around — so the automation story here is the one you'd actually be living with.
Signing one installer by hand is a solved problem. The trouble starts when the signing has to happen at 2 a.m., on a build agent, with nobody at a keyboard — because the thing that makes code signing trustworthy is precisely that the key isn't lying around where a script can grab it. Every team that automates releases eventually meets this tension, usually as a cryptic SignTool error in a pipeline log. This guide lays out the three arrangements that actually work, what each one costs in operational fuss, and the pipeline habits that keep an unattended signature from becoming a liability.
Why you can't put the key in a CI secret
Publicly trusted code signing keys have had to live on certified hardware since June 1, 2023, generated non-exportable — so there is no .pfx file to paste into a secrets store. That's the CA/Browser Forum's Baseline Requirements at work, and it makes every CI/CD signing setup an answer to the same question: how does an unattended pipeline reach hardware it isn't allowed to contain?
It's worth knowing why the rule reads the way it does, because CI is the environment it was written about. Before 2023, the standard advice was to base64-encode the .pfx into a pipeline variable — which meant the key existed, in copyable form, on every runner that ever decrypted it. Build infrastructure is exactly where signing keys kept getting stolen, and a stolen key signs malware with your name on it until someone notices. The fix was blunt: make the key physically unable to leave its hardware. Our code signing certificates explainer covers the 2023 change in full; what matters for pipeline design is the consequence. The private-key operation now always happens on the hardware. Your pipeline's job shrinks to delivering a file digest and collecting a signature — and the three setups below are just three answers to where that hardware sits.
The three CI/CD signing setups at a glance
Three arrangements work in practice: plug a USB token into a self-hosted runner and sign locally; keep a cloud signing session — Certum's SimplySign, for certificates bought through My-SSL — open on a dedicated build agent, where SignTool signs as if a card were present; or use a pipeline-native service such as Azure Artifact Signing, which authenticates with a service credential instead of a person.
| Token + self-hosted runner | Cloud signing + dedicated agent | Pipeline-native service | |
|---|---|---|---|
| Where the key lives | On the token, in your rack | In the CA's HSM | In the provider's HSM |
| Works on hosted runners | No — needs the physical machine | No — the session lives on one machine | Yes |
| Human in the loop | PIN handling at signing time | Opens the session after reboots | None at signing time |
| Typical failure mode | Locked PIN, lost token | Session expired, build waits | You're outside its regions |
| Best for | Low-volume, on-prem shops | Most teams with releases to sign | Azure/GitHub-centric teams in eligible regions |
The next three sections take the setups one at a time, including the failure modes you only discover at release time.
Setup 1: a USB token on a self-hosted runner
A hardware token only signs on a machine that can physically see it, which rules out GitHub-hosted and cloud-hosted runners — those are ephemeral VMs in someone else's data center, with no USB port of yours attached. Teams that sign with a token register a self-hosted runner on a machine where the token stays plugged in, and route the signing job to it with runner labels.
The part nobody warns you about is the PIN. Token client software is built around a human typing a PIN at signing time, and an unattended job has no human. Vendors offer PIN-caching or single-logon settings that keep the token unlocked between operations, and teams script around the prompts — but every one of those workarounds moves the PIN into a config file or environment variable on the runner, which is a quiet retreat from what the hardware was protecting against. Get the PIN handling wrong in the other direction and repeated failures lock the token entirely, which stops releases until it's unblocked.
The deeper issue is concentration: one machine, one small object, one key that ceases to exist if the token is lost or damaged. For a shop that signs a handful of releases a year from its own server room, this setup is workable and cheap. It ages badly as release cadence grows, and it's the reason the second pattern exists.
Setup 2: cloud signing through a persistent session
With a cloud certificate the key sits in the CA's HSM, and the CI problem turns into session management. Certum's SimplySign opens a session with a one-time code from its mobile app; once open, the session persists on that machine. So teams dedicate a build agent to signing: a person opens the session after each reboot, and every release that follows signs unattended.
Concretely: SimplySign Desktop runs on the agent and presents the cloud certificate to Windows as a virtual smart card. SignTool selects it by thumbprint or subject name and signs exactly as it would against local hardware — the commands from our SignTool walkthrough run unchanged, timestamping against time.certum.pl included, and Certum publishes the same flow for jarsigner on the Java side. This is the setup you get with the Certum certificates My-SSL sells, and it's the reason the pattern is worth designing around rather than treating as a workaround.
Two honest caveats. First, the human step is load-bearing: the session needs a person with the paired phone after a reboot or session expiry, so releases can stall until someone's awake — teams handle it by keeping the agent long-running and checking session health before the release job, and it's possible to generate the one-time codes programmatically from the enrollment secret, though doing so parks both authentication factors on the same machine and deserves real thought. Second, capacity: as of July 2026, Certum's cloud signing allows 5,000 signatures a month. That's ample for signing releases and irrelevant-sounding until a pipeline starts signing every nightly artifact, which is one more reason the release gate in the last section matters.
Setup 3: pipeline-native signing services
A pipeline-native service moves the whole problem server-side: the key lives in the provider's HSM and the pipeline authenticates with a service principal instead of a person with a phone, so it runs on ordinary hosted runners. Microsoft's Azure Artifact Signing — renamed from Trusted Signing in January 2026 — is the main example, with first-party GitHub Actions and Azure DevOps integrations.
It's a genuinely different model, not just a different vendor. You don't bring a certificate; you subscribe to a signing account, and the service issues short-lived certificates under its own CA at signing time. That eliminates session babysitting and makes the GitHub Actions story the cleanest of the three setups. The catch is eligibility: as of July 2026, public-trust signing is available to organizations in the USA, Canada, the EU, and the UK, and to individual developers only in the USA and Canada. Publishers outside those regions — or teams that want a certificate in their own name, accumulating SmartScreen reputation they own and can carry between build systems — are back to the first two setups, which is where a CA-issued certificate keeps its edge.
Pipeline rules that keep automated signing safe
Whichever setup you pick, the same four rules apply: sign once, at the end, only for release builds; timestamp every signature; verify the signature in a separate step; and gate the signing job as strictly as a production deploy. Break one and you either weaken the protection the hardware bought you or ship signatures that stop validating.
Signing late and rarely is what makes the rest tractable. A pipeline that signs every commit turns "who can open a pull request" into "who can produce a binary signed in your name" — and it spends quota and reputation on builds nobody ships. Timestamping is non-negotiable because certificates now outlive their own validity in the field: a timestamped signature stays valid after the certificate expires, and an untimestamped one takes every shipped build down with it on expiry day. The verify step (signtool verify /pa, or jarsigner -verify) belongs in the pipeline as its own stage, because a signing step that half-fails — signed but not timestamped is the classic — will otherwise sail through to publish. And access control is the one rule that's about people: restrict the signing job to protected branches, put an approval in front of it, and keep the signing machine's credentials out of the general CI pool. The hardware rule took key theft mostly off the table; what's left to defend is the ability to ask your own hardware to sign the wrong thing.
The bottom line
CI/CD signing stopped being a secrets-management problem in 2023 and became an architecture choice: token on a self-hosted runner if you sign rarely and on-prem, a pipeline-native service if you're in its regions and all-in on Azure or GitHub, and a cloud signing session on a dedicated agent for most teams in between. If that middle path is yours, the Certum code signing certificates at My-SSL sign through SimplySign from the day validation clears — $99/year for Standard, $299/year for EV, with no token to courier to your build room.