Skip to main content
    Code Signing

    Code Signing in CI/CD: How to Automate Signing Without Exposing Your Key

    Automate code signing in CI/CD without exposing your key: USB tokens on self-hosted runners, SimplySign cloud sessions, or a pipeline-native service.

    MS
    My-SSL Security Team
    ·
    12 min read
    ·
    Published July 7, 2026
    ·
    Last updated July 7, 2026

    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?

    Why a CI secret can't hold a code signing key anymoreTwo panels. The left panel, before June 2023, shows a .pfx key file stored directly in a pipeline's secrets store — an arrangement that is no longer issued. The right panel, highlighted in gold, shows today's rule: the key is locked inside certified hardware, the pipeline sends only a file digest to it, and the signature comes back. The key itself never enters the pipeline.The key no longer fits in a secrets storeBefore June 2023CI secrets storeholds the .pfx key file itselfAny job with the secret can copy the keyhow signing keys got stolenno longer issued this waySince June 1, 2023Certified hardwarekey generated inside, non-exportabledigest insignature outPipeline sends a digest, gets a signaturethe key never enters the pipelinethe only compliant shape today
    Every working CI/CD signing setup is a variation on the right-hand panel — they differ only in where the hardware sits and who opens the door to it.

    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.

    Three CI/CD signing setups and how each behaves in practiceThree rows mapping setups to their behavior. A USB token on a self-hosted runner works but is fragile: PIN prompts and a single machine. Cloud signing on a dedicated agent, highlighted in gold as where most teams land, keeps a SimplySign session open so release jobs sign unattended. A pipeline-native service like Azure Artifact Signing runs on hosted runners but is limited to certain regions.Three ways a pipeline can reach the keyUSB token + self-hosted runnerthe token must be physically presentWorks, but fragilePIN prompts, one machine, key dies with the tokenCloud signing + dedicated agentSimplySign session held open on one machineWhere most teams landhuman opens the session; releases sign unattendedPipeline-native serviceAzure Artifact Signing, service-principal authRuns on hosted runnersbut only offered in certain regions
    The middle row wins so often because it changes nothing about your tools — SignTool sees a smart card either way and never learns the card is virtual.
     Token + self-hosted runnerCloud signing + dedicated agentPipeline-native service
    Where the key livesOn the token, in your rackIn the CA's HSMIn the provider's HSM
    Works on hosted runnersNo — needs the physical machineNo — the session lives on one machineYes
    Human in the loopPIN handling at signing timeOpens the session after rebootsNone at signing time
    Typical failure modeLocked PIN, lost tokenSession expired, build waitsYou're outside its regions
    Best forLow-volume, on-prem shopsMost teams with releases to signAzure/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.

    Where signing belongs in a release pipelineA left-to-right pipeline flow: build, then test, then a release gate with human approval, then the signing and timestamping step highlighted in gold, then an independent verify step, then shipping. The signing step sits after the gate so only approved release builds are ever signed.Sign once, late, and only what shipsBuildTestRelease gatehuman approvalSign + timestampthe guarded stepVerifyShipEverything left of the gate runs on every commit — nothing left of it can produce a signature.
    The gate is doing the security work here: a signature only exists because a person decided this exact build ships.

    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.

    Frequently Asked Questions

    Get instant answers to common questions about SSL certificates and our services.

    Still Have Questions?

    Our SSL experts are available 24/7 to help with any questions about certificates, installation, or technical issues.

    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.