Secure Boot and Firmware Signing Automation in CI/CD: A Practical Playbook
How to get signing keys off laptops and into a pipeline you can audit, without slowing releases or bricking a fleet.
Executive brief
- Secure boot firmware signing automation means your CI/CD pipeline signs every boot artifact (bootloader, kernel, device tree, root filesystem integrity data) using keys that no engineer and no build runner can read, and the device verifies each signature before it runs the next stage.
- Build and sign must be separate stages. Build runners produce unsigned artifacts and an SBOM. A locked-down signing service holding keys in an HSM or cloud KMS signs only what came from a protected branch or release tag.
- Design the key hierarchy, rotation and revocation before the first fuse is burned. OTP fuses can't be undone, and a bad key plan on shipped hardware is a recall.
- Test the failure case in CI. A tampered or wrongly signed image has to be rejected on a QEMU or hardware-in-the-loop target, every release.
- A signature proves where an image came from, not that the code inside is free of vulnerabilities. Pair signing with SBOM-driven CVE tracking and anti-rollback OTA.
- For products sold in the EU, this is the practical route to meeting integrity and vulnerability-handling expectations under the Cyber Resilience Act.
Why manual signing breaks down
Most embedded teams start the same way. Someone generates a signing key during bring-up, it lands on a laptop or a shared build server, and a release means "ask Priya to sign it." It works until Priya goes on leave, leaves the company, or has her laptop stolen from a train.
The failure isn't only about theft. A key that humans handle can't be audited. You can't say which commit produced the image that got signed on a Thursday night before a customer demo, and you can't prove nobody swapped a binary between the build and the signing step.
Automation fixes this, but only if you automate the right way. Dropping the private key into a CI secret and calling it done is the most common mistake. It moves the key from one laptop to every job that can read the variable, including a compromised dependency or a pull request workflow someone misconfigured.
What you want instead is a pipeline where signing is a controlled service call, tied to a specific commit, with a log entry nobody can quietly delete.
Mapping the chain of trust
Secure boot is a chain, and a chain is only as good as its weakest link. Before you automate anything, write down every stage from the SoC's immutable boot ROM to the point where your application starts, and note what verifies what.
On a typical Linux-based device the chain looks like this:
| Stage | Verified by | What gets signed | Common tooling |
|---|---|---|---|
| Boot ROM to first-stage loader | Hash of root public key in fuses or OTP | SPL or first-stage loader, often with a vendor wrapper | Vendor tools (NXP CST, TI K3 signing scripts, STM32 signing tool) |
| First stage to U-Boot | Key embedded in the previous stage | U-Boot proper | mkimage, vendor tools |
| U-Boot to kernel | Public key in U-Boot's control device tree | FIT image with kernel, device tree, optional initramfs | mkimage, fit_check_sign |
| Kernel to root filesystem | Root hash passed in a signed image or signed command line | dm-verity root hash, or a signed update bundle | veritysetup, RAUC or SWUpdate signing |
| UEFI-based x86 or Arm systems | db, KEK and PK variables | Shim, bootloader, kernel, modules | sbsign, sbsigntools, sign-file for modules |
Two things trip people up here. First, kernel modules and the initramfs are easy to forget, and an unsigned initramfs is a free injection point into a system whose kernel is perfectly signed. Second, a signed kernel that mounts an unverified root filesystem has only moved the problem down a layer.
The vendor sections differ a lot between NXP, TI, STMicroelectronics and the rest, so read your SoC's security reference manual before you design the pipeline. The first stages often use a vendor-specific signing format, while U-Boot's verified boot and UEFI Secure Boot are better documented. The U-Boot FIT signature documentation is a good starting point for the middle of the chain.
Key hierarchy, rotation and revocation
Do this part on paper, before any code. The hash of your root public key will likely end up in one-time-programmable fuses, and what you burn there is permanent for the life of that board.
A workable hierarchy has three tiers. The root key stays offline, on an HSM or hardware token in a physically controlled place, and it signs almost nothing. It certifies subordinate keys. The online signing keys live in an HSM or cloud KMS and sign real releases. Where the SoC supports it, you keep a spare slot or two so you can move to a new key without touching hardware.
| Key tier | Where it lives | Who or what can use it | Rotation |
|---|---|---|---|
| Root | Offline HSM or token, split custody | Two people, a few times a year at most | Effectively never; plan for it, don't count on it |
| Product signing key | HSM or KMS, non-exportable | The signing service, release branches only | On a schedule (annual is common) or after any suspected exposure |
| Development key | Developer machines or lab CI | Anyone, on dev hardware only | Whenever convenient |
That last row matters more than people think. Development boards should boot with a development key that production hardware refuses. Without that split, engineers will "just sign it locally" to unblock themselves, and your carefully protected production key becomes irrelevant.
Revocation is where hardware reality bites. UEFI has the dbx revocation list. Most embedded SoCs have a fuse-based revocation mask or a monotonic counter instead, with a limited number of slots, so you get a handful of rotations before you run out. Count them. If your part gives you four slots and you plan annual rotation, that's a four-year ceiling on the product's secure life unless you plan something else.
Pipeline architecture for automated signing
The principle is separation. The system that builds untrusted-ish code (it pulls in hundreds of upstream packages) should never be the system that can sign.
In practice that gives you five stages, each with a different trust level:
| Stage | Runs on | Has key access? | Output |
|---|---|---|---|
| Build | Ephemeral runners, network-limited where possible | No | Unsigned artifacts, SBOM, build provenance |
| Scan and test | Standard runners | No | CVE report, unit and integration results |
| Sign | Isolated signing service or dedicated runner | Indirect, through HSM or KMS API | Signed artifacts, signing log entry |
| Verify | QEMU or hardware-in-the-loop farm | Public keys only | Pass or fail, including tamper tests |
| Publish | Release job | No | Artifacts plus attestation to the OTA backend |
What the signing service actually checks
A signing service that signs anything it's handed is just a more expensive way to leak your key. It should accept an artifact digest, not a file upload, and it should refuse unless several things line up: the request comes from a pipeline run on a protected branch or a signed tag, the digest matches an artifact that the build stage recorded, and for a production key, a second person has approved the release in your CI system.
HSMs and KMS products expose this through PKCS#11 or a cloud signing API. mkimage and OpenSSL can work with PKCS#11 through an engine or provider, so you can keep U-Boot's tooling and still keep the private key non-exportable. Sigstore and its keyless signing flow are worth a look for the artifact-publishing end, but boot-chain keys that SoC hardware trusts still need a traditional key hierarchy; a short-lived certificate isn't something a ROM can check.
Log every signing call with the digest, the requester identity, the commit, and the approval. If you ever need to answer an incident question, this log is the whole answer.
Wiring it into a Yocto build
If you build with Yocto, the pieces are already there. The relevant variables live in your machine or distro configuration, and U-Boot signing plus FIT image signing are controlled by a handful of settings:
# local signing config, used ONLY by the signing job
UBOOT_SIGN_ENABLE = "1"
UBOOT_SIGN_KEYDIR = "/run/signing/keys"
UBOOT_SIGN_KEYNAME = "fw-fit-2026a"
FIT_HASH_ALG = "sha256"
FIT_SIGN_ALG = "rsa4096"The catch is that Yocto wants to see the key while it assembles the FIT image. That clashes with the rule that build runners never hold keys. The cleanest pattern we've used is a two-pass approach. The normal build runs with signing off and fills the shared sstate cache. A second job on the isolated signer then re-runs only the image assembly tasks with signing on, pulling everything else from the cache. The signer's key directory is a mount backed by an HSM-aware PKCS#11 setup rather than plain files on disk.
For UEFI targets, meta-secure-core provides recipes for Secure Boot key handling, and the same split applies: keep the generated keys out of the build workspace and sign in the isolated stage.
Whatever you choose, pin your build inputs. A signature on an image you can't rebuild is a signature on a mystery. Reproducible or at least repeatable builds let a second party confirm that the signed binary matches the source.
Verifying secure boot in CI, including the failures
Booting the signed image and seeing a login prompt tells you almost nothing. Plenty of devices boot a signed image fine because verification was never switched on.
The test that proves it is the negative one. In the verify stage, take the signed image and flip a single byte in the kernel inside the FIT. Boot it. The boot must halt with a signature error. Then sign a good image with a throwaway key the device has never seen. Boot it. Same result. Then boot an older, correctly signed image with a lower version number and confirm the anti-rollback check rejects it.
QEMU covers a lot of this for UEFI and for U-Boot-based machines, and it's cheap enough to run on every pull request. For anything that depends on real fuses, ROM behavior, or vendor signing, you need actual boards on a test rack. Use dev-fused boards for this, never production-fused ones, and keep a procedure for recovering them, because you'll lock one up eventually.
On the host side, run fit_check_sign or the equivalent against the final artifacts before they leave the pipeline. It's a fast gate and catches the "signed with the wrong key name" mistake long before a device does.
The seven-step playbook
- 1Define the chain of trustList every stage from boot ROM to application start. For each, write down what verifies it and what format the signature takes. Include the initramfs, modules and root filesystem integrity data.
- 2Design the key hierarchy and rotation planSet up an offline root, online product signing keys, and separate development keys. Count the revocation slots your SoC gives you and decide the rotation schedule against that number.
- 3Stand up the signing servicePut production keys in an HSM or KMS with non-exportable keys. Wrap it in a small service that signs digests, authenticates the caller, and writes an append-only audit log.
- 4Split build from signBuild runners produce unsigned artifacts, an SBOM and provenance data, and never see a key. The sign stage fetches artifacts by digest and works from what the build recorded.
- 5Gate signing on branch, tag and approvalAllow production signing only from protected branches or signed tags, with a human approval for release keys. Development keys sign everything else, on development hardware only.
- 6Verify with positive and negative boot testsBoot the good image, a byte-flipped image, an image signed with the wrong key, and a rolled-back image. Fail the pipeline unless the three bad ones are rejected.
- 7Publish signed artifacts with anti-rollback into OTAHand the signed bundle and its attestation to your update system, with version counters that the device enforces. Keep the audit log with the release record.
Anti-patterns we keep finding
| What we see | Why it hurts | Fix |
|---|---|---|
| Production key stored as a CI/CD variable | Any job or compromised dependency that can read it can sign a malicious image | HSM or KMS behind a signing service |
| One key for dev, test and production | Developers sign locally and the key spreads | Separate dev keys that production hardware rejects |
| Signing done on the build runner | The most exposed machine in your pipeline holds the most valuable secret | Isolated sign stage using digests from the build |
| Secure boot enabled but never tested to fail | Verification may be silently off | Negative boot tests in every release pipeline |
| Root key hash burned before a rotation plan existed | First compromise means a hardware change or a recall | Key plan reviewed before any fuse is blown |
| No anti-rollback counter | An attacker can reinstall an old, signed, vulnerable image | Monotonic version enforced in bootloader and OTA |
| Unsigned initramfs or modules | Open door past a signed kernel | Include them in the signed FIT or enforce module signatures |
The Cyber Resilience Act angle
If you sell connected products into the EU, the Cyber Resilience Act turns much of this from good practice into a legal expectation. The regulation asks manufacturers to protect product integrity against unauthorized modification, to handle vulnerabilities for the support period, and to be able to deliver security updates. The reporting obligations for actively exploited vulnerabilities began applying in September 2026, and the main obligations follow in December 2027.
A signed boot chain with an audit trail is a direct, demonstrable answer to the integrity expectation. It also gives you the evidence trail an assessor will ask for: which key signed what, when, and on whose approval. We cover the vulnerability and component side in our guide to Yocto SBOM and CVE checks for the Cyber Resilience Act, and the delivery side in the zero-downtime OTA deployments playbook. Signing sits between the two: the SBOM says what is in the image, signing says who built it, and OTA gets it onto the device without breaking the chain.
The NIST SP 800-193 guidelines on platform firmware resiliency are a useful reference for the protect, detect and recover framing, even if you're not building to a US standard.
FAQ
What is secure boot firmware signing automation?
It's the practice of having your CI/CD pipeline sign bootloaders, kernels, and other boot artifacts automatically, using keys held in an HSM or cloud KMS, so no engineer ever handles a private key. The device checks those signatures at every stage of boot and refuses to run anything unsigned or altered.
Can I store signing keys as CI secrets?
You can, but you shouldn't for production keys. A CI secret is readable by any job that can reach it, including a compromised dependency or a careless pull request workflow. Production keys belong in an HSM or KMS behind a signing service that only accepts requests from protected branches and tags.
Do I need an HSM or is a cloud KMS enough?
For most teams a cloud KMS or a network HSM is enough, as long as the key can't be exported and every signing call is logged. Your offline root key is the exception, and it should live on a hardware token or HSM in a physically controlled location. Regulated products or customer contracts may require a certified HSM, so check before you choose.
How do I rotate a key when the public key hash is burned into fuses?
Only if you planned for it before shipping. Many SoCs let you provision several key slots or a key revocation mask, so you can retire one key and move to the next. If your chip has a single slot, rotation means a hardware change, which is why the key plan comes before the first fuse is blown.
Does a signed image mean the firmware is secure?
No. A signature proves the image came from your pipeline and wasn't modified afterward. It says nothing about vulnerabilities in the code inside. You still need an SBOM, CVE tracking, and a patch process, which is what keeps the signed image worth trusting.
How do I test that secure boot actually rejects bad images?
Add a negative test to the pipeline. Flip one byte in a signed artifact, or sign it with a throwaway key, then boot it on QEMU or a hardware-in-the-loop board and assert that boot halts. A test that only checks the good image booting proves nothing about verification.
Signing keys still living on someone's laptop?
We design and build signing pipelines for embedded Linux products: key hierarchy, HSM or KMS integration, Yocto changes, and the boot tests that prove it works. If you're weighing whether to build this in-house or bring in people who've done it before, talk to us before the first fuse gets burned.
Talk to Stonetusker about your signing pipeline
