The fastest way to lose a code signing key is to put it in a CI/CD pipeline. A long-lived signing key stored as a pipeline secret is reachable by every job that runs, every maintainer with pipeline access, and every dependency that executes during a build. It sits in an environment designed to run arbitrary code at scale, which is exactly the environment you least want holding a credential that can vouch for your software. When that key leaks, the damage persists in every artifact an attacker can sign with it.
The better model is to not store a signing key in the pipeline at all. Keyless signing replaces the durable secret with a short-lived credential minted on demand from the pipeline’s verified identity. The build proves who it is through OIDC, receives a certificate valid for minutes, signs the artifact, records the event in a transparency log, and the credential expires before the job even finishes. There is nothing left to steal.
This post explains why pipeline-resident keys are a structural liability, how keyless signing works, the Sigstore and cosign pattern that popularized it, and how to combine it with HSM-backed signing and approval gates for the artifacts that need stronger controls.
Reactive vs. Proactive Certificate Management
Score comparison across key operational metrics (higher = better)
🔥 Reactive
4-24hr MTTD · $134K/year · 6+ outages · Burnout
✓ Proactive
<15min MTTD · $15K/year · 0 outages · Planned
Why Long-Lived Keys in CI Are a Liability
The problem is not that pipelines are insecure. It is that a long-lived signing key has properties that fit poorly with how pipelines work.
A pipeline secret is reachable by too many things
A signing key stored as a CI secret is available to the jobs that need it, and in practice to far more than that. Pipeline configuration changes, a malicious or compromised dependency pulled during the build, an over-broad secret scope, or a maintainer with write access can all reach it. The pipeline runs untrusted and semi-trusted code constantly, and a durable signing key sitting in that environment is one misconfiguration away from exfiltration.
The key outlives every build
A long-lived key signs today’s release and next year’s. If it leaks during any single build, the exposure is not scoped to that build; it covers everything the key can sign for as long as the key is trusted. The mismatch is between a credential meant to last years and an environment that should hold nothing durable.
Rotation is painful, so it does not happen
Rotating a signing key embedded in CI means re-issuing the key, updating every pipeline that uses it, and coordinating trust for the new certificate. Because that is painful, teams avoid it, and the key stays in place far longer than any sound rotation policy would allow. The absence of a forcing function produces the same decade-old-credential problem that plagues SSH keys.
How Keyless Signing Works
Keyless signing removes the durable key by binding each signing operation to a verified identity and a short-lived certificate. The identity does the work the stored key used to do, and nothing persists.
The flow, step by step
- The CI job authenticates and obtains an OIDC identity token asserting what it is, for example a specific repository, workflow, and trigger.
- The job generates an ephemeral keypair in memory for this signing operation only.
- It presents the OIDC token and the ephemeral public key to a certificate authority, which verifies the identity and issues a short-lived signing certificate binding the ephemeral key to that identity.
- The job signs the artifact with the ephemeral private key.
- The signature, certificate, and identity are recorded in a public transparency log, producing a tamper-evident record of who signed what and when.
- The ephemeral key is discarded. The certificate expires within minutes.
The result is a verifiable signature with no long-lived secret anywhere in the pipeline. Verification later checks that the signature is valid, that the certificate came from the expected CA, that the identity matches the expected build identity, and that the transparency log holds a matching entry.
What replaces the stored key as the trust anchor
In the old model, trust flowed from possession of the key: whoever held it could sign. In the keyless model, trust flows from identity plus the transparency log. A verifier does not ask “was this signed by the key”; it asks “was this signed by the identity we expect, with a certificate from the CA we trust, and is that recorded in the log.” Possession of a secret is replaced by proof of identity, which is exactly the thing a CI pipeline can prove without storing anything.
The Sigstore and cosign Pattern
The keyless model was made practical for mainstream pipelines by the Sigstore project and its signing tool cosign, which wired together the identity, the short-lived CA, and the transparency log into a workflow teams can adopt without building the infrastructure themselves.
Conceptually, what cosign does
When you sign with cosign in keyless mode, it obtains the OIDC identity from the CI environment, requests a short-lived certificate from the signing CA, signs the artifact, and uploads the signing record to the transparency log. The person or pipeline running it never handles a long-lived key. Verification with cosign checks the signature against the expected identity and confirms the transparency log entry.
# GitHub Actions job signing a container image keyless with cosign
permissions:
id-token: write # allows the job to obtain an OIDC identity token
contents: read
jobs:
sign:
runs-on: ubuntu-latest
steps:
- name: Install cosign
uses: sigstore/cosign-installer@v3
- name: Sign the image keyless
env:
COSIGN_EXPERIMENTAL: "1"
run: cosign sign --yes ghcr.io/example/app@${IMAGE_DIGEST}
The important line is the id-token permission. It grants the job the ability to obtain an OIDC identity token, which is the credential the signing CA verifies. No signing key is stored anywhere in that configuration, because there is no signing key to store.
Verifying against an expected identity
Verification is where the policy lives. You verify not just that a signature is valid, but that it was produced by the exact identity you expect, so that a signature from some other repository or workflow is rejected even if it is otherwise well-formed.
# Verify the image was signed by the expected workflow identity
cosign verify \
--certificate-identity "https://github.com/example/app/.github/workflows/release.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/example/app@${IMAGE_DIGEST}
Pinning the identity and the issuer is what makes the signature meaningful. A signature without an identity policy only tells you something signed the artifact; pinning tells you the right thing signed it.
HSM-Backed Signing with Approval Gates
Keyless signing is the right default for most CI artifacts, but some artifacts warrant a stronger control: a signing operation that requires hardware-protected keys and explicit human approval. These models are complementary, not in competition.
When to reach for HSM plus approval
For the highest-trust artifacts, such as release installers that reach millions of machines or firmware with a long field life, you may want signing to pass through a hardware-protected key and an approval gate rather than being fully automated. The point is to make producing a trusted signature for these artifacts a deliberate, authorized act rather than an automatic pipeline step.
Keeping the key out of the pipeline even with an HSM
The key insight carries over: even when the signing key lives in an HSM, the pipeline should never hold it or a credential that silently authorizes it. Instead, the pipeline submits a signing request to a signing service, the service enforces an approval gate, and only on approval does it invoke the HSM to sign. The pipeline receives the signature back. It never touches the key or an unattended credential that can command the key.
# Pipeline requests a signature from a signing service; it holds no key
jobs:
request-signature:
runs-on: ubuntu-latest
steps:
- name: Submit artifact for signing
run: |
signctl submit \
--artifact dist/app-release.bin \
--profile firmware-release
# the signing service requires an approval before the HSM signs;
# the pipeline polls for the completed signature
The pipeline’s role shrinks to “ask for a signature and receive one.” The authority to actually sign lives in the signing service and its approval policy, outside the reach of a compromised build.
Keeping Keys Out of the Pipeline: The Rules That Matter
Whether keyless or HSM-backed, the same principles decide whether your pipeline is a liability.
No durable signing secret in the pipeline
The pipeline should never store a long-lived signing key or a credential that unconditionally authorizes signing. If a leaked pipeline secret can produce trusted signatures indefinitely, you have reproduced the original problem under a new name.
Bind signing to verified identity
Every signature should be traceable to a verified identity, the OIDC identity of the build in the keyless model or the authenticated requester plus approver in the HSM model. “Something in CI signed this” is not an acceptable answer; “this specific workflow, verified by the CA, recorded in the log” is.
Pin verification to expected identities
Signing is only half the control; verification policy is the other half. Pin verification to the exact identities and issuers you expect, so a valid-but-wrong signature is rejected. Without identity pinning, keyless signing degrades into “anyone with the CA can sign and be trusted,” which is not the guarantee you want.
Scope and separate by trust lifetime
Use keyless, fully automated signing for the broad, short-lived population of artifacts, and reserve HSM-backed, approval-gated signing for the small set with long trust lifetimes. Matching the control to the artifact’s exposure keeps the common path fast while giving the high-stakes artifacts the deliberation they deserve.
FAQ
Q: Is keyless signing actually keyless? There is still a keypair involved, but it is ephemeral: generated in memory for a single signing operation and discarded immediately after, with a certificate that lives for minutes. “Keyless” means there is no long-lived key for you to store, manage, rotate, or lose. The durable trust anchor becomes the verified identity and the transparency log entry rather than a persistent secret.
Q: What is the transparency log for? It is a tamper-evident, append-only record of signing events: who signed, what identity was used, and when. It lets anyone later verify that a signature was genuinely produced by the claimed identity and detect if the signing history was altered. In the keyless model it replaces the long-term accountability that a stored key’s usage logs would otherwise provide, and it does so publicly and verifiably.
Q: Does keyless signing work in air-gapped or offline environments? It is harder, because the model depends on reaching an OIDC issuer, a signing CA, and a transparency log at signing time. For offline or air-gapped builds, HSM-backed signing with a local signing service is usually the more practical choice. Keyless signing shines in connected CI environments where the identity and log services are reachable; it is not the universal answer for every deployment model.
Q: Why not just store the key in an HSM and call it done? An HSM protects the key from extraction, which is a major improvement, but if the pipeline holds a credential that can command the HSM to sign anything at any time, a compromised pipeline can still produce trusted signatures. The additional controls, binding signing to verified identity and requiring approval for high-trust artifacts, close that gap. The HSM protects the key; the identity and approval controls protect the act of signing.
Q: How does identity pinning during verification prevent abuse? Pinning means a verifier accepts a signature only if it came from the exact identity and issuer you specify, for example a particular repository and workflow. Without it, any signature from any identity the CA will certify would pass, so an attacker who could sign under a different identity would be trusted. Pinning turns “something valid signed this” into “the specific, expected thing signed this,” which is the guarantee that makes the signature worth checking.
About QCecuring
QCecuring helps engineering teams take long-lived signing keys out of their CI/CD pipelines and move to identity-bound signing. Our platform supports keyless signing for the broad artifact population and HSM-backed, approval-gated signing for high-trust releases, with verification policies that pin signatures to the identities you actually expect.
Get signing keys out of your pipeline
Tags: Code Signing, DevOps, CI/CD, Keyless Signing, Ephemeral Keys, OIDC, Sigstore, cosign, Transparency Log, HSM, Approval Workflow, Supply Chain Security, Identity