The short answer
RPM and DEB packages are signed with an OpenPGP key, not with an X.509 code signing certificate. As of September 2026 there is still no way to sign an RPM package directly with a certificate from a public CA, and dpkg ships with embedded .deb signature checking switched off. For RPM you name a key in %_gpg_name and run rpm --addsign. For Debian and Ubuntu the signature that actually protects users sits on the repository's InRelease file, which carries the hashes of the Packages index, which carries the hash of every .deb. Your users trust that key because you handed it to them, not because anybody vouched for it — so the whole scheme rests on where the private key lives.
On this page
- What actually signs a Linux package?
- Can I use my code signing certificate on a .rpm or .deb?
- How to sign an RPM package
- How to sign a .deb, and who checks it
- Sign the repository: InRelease and signed-by
- What changed: RPM 6.0 and the end of apt-key
- Where the signing key should live
- Where a code signing certificate does apply on Linux
- FAQ
What actually signs a Linux package?
An OpenPGP key. RPM stores an OpenPGP signature inside the package header; Debian and Ubuntu put the signature on the repository's index files instead of on the packages. Neither format consumes an X.509 certificate the way Windows consumes one through Authenticode. There is no root program behind a Linux package signature, so the trust anchor is the public key you publish and your users install.
That difference is the source of nearly every wrong assumption in this area. On Windows, the certificate is doing two jobs at once: it proves a key belongs to a named legal entity, and it chains to a root the operating system already trusts. A package signing key does the first job only, and only for people who already got the key from you. Nothing about it is weaker cryptographically — RSA and ECC behave the same in both worlds — but the question "who decided to trust this key?" has a completely different answer.
Can I use my code signing certificate on a .rpm or .deb?
No — not for the package signature. RPM accepts OpenPGP keys and nothing else, and Red Hat states the position plainly: there is currently no way to sign an RPM using an X.509 certificate. You can sign the .rpm file as an opaque blob with openssl and ship the detached signature and certificate next to it, but no package manager will look at that, so you have built a side channel rather than a package signature. Debian gives the same answer from the other direction: debsigs and the repository tooling both speak OpenPGP.
This is the single most common mismatch we see on the support side. Someone buys an OV or EV code signing certificate because the product page mentions Linux, receives a token or a cloud key, and then discovers the certificate signs their Windows installer and their .jar perfectly while their .rpm will not take it at all. Both halves of that are true at once, which is why the table below is worth reading before you buy, not after:
| What you are signing | Mechanism | Does a public CA certificate apply? |
|---|---|---|
| .exe, .dll, .msi, .msix | Authenticode (signtool, osslsigncode, Jsign) | Yes — and it is required for a trusted result |
| .jar | jarsigner | Yes |
| .rpm package signature | OpenPGP, in the package header | No |
| .deb embedded signature | OpenPGP via debsigs, off by default | No |
| apt or dnf repository metadata | OpenPGP, on InRelease or repomd.xml | No |
| RPM IMA file signatures | X.509 key via rpmsign --signfiles | X.509, yes — but the kernel trusts the key because you enrolled it, not because a CA issued it |
| Container images | cosign / Sigstore | No — an OIDC identity or your own key |
The highlighted row is the one that trips up careful readers, because it does involve a certificate. RPM can attach per-file signatures for the kernel's Integrity Measurement Architecture, and rpmsign --signfiles takes an RSA key with --fskpath or %_file_signing_key, including a PKCS#11 URI where OpenSSL has the provider loaded. The key ID is normally the Subject Key Identifier of the matching X.509 certificate. But nothing about that path goes through a public root program: the key is trusted because it sits in the kernel keyring on the machines you control.
How to sign an RPM package
Three steps: create an OpenPGP key, name it in ~/.rpmmacros, then run rpm --addsign against the built package. The signing tool lives in the rpm-sign package on Red Hat-family distributions, which is not installed by default, so that is usually the first error you hit. Verification is the same command a user would run, which makes it worth doing yourself before you publish anything.
# 0. the signing tool is a separate package
sudo dnf install rpm-sign
# 1. create the key. rpm wants an OpenPGP key, not a CSR.
gpg --full-generate-key
# 2. tell rpm which key to use
cat >> ~/.rpmmacros <<'EOF'
%_signature gpg
%_gpg_name Example Ltd (package signing) <[email protected]>
%_gpgbin /usr/bin/gpg2
EOF
# 3. sign — add a signature, keeping any that are already there
rpm --addsign dist/example-2.4.1-1.el9.x86_64.rpm
# or replace the existing signature instead of adding to it
rpm --resign dist/example-2.4.1-1.el9.x86_64.rpm
# 4. verify the way a user's machine will
rpm --checksig dist/example-2.4.1-1.el9.x86_64.rpmRead the verification output carefully. RPM reports digests and signatures as separate results, and an unsigned package still reports its digests as fine — the file simply arrived intact. If the word signatures is missing from the line, the package is unsigned no matter how healthy the rest of the output looks. This is where most broken pipelines are found weeks late: the macro name had a typo, rpm --addsign silently did nothing useful, and every release since has shipped bare.
On the consumer side, the public key has to be in RPM's own keyring, which is separate from the user's GnuPG keyring:
# import the publisher's key into the rpm database
sudo rpm --import https://packages.example.com/RPM-GPG-KEY-example
# list what rpm currently trusts
rpm -q gpg-pubkey --qf '%{NAME}-%{VERSION}-%{RELEASE} %{SUMMARY}\n'
# and in a .repo file, point dnf at the same key
# gpgcheck=1
# gpgkey=https://packages.example.com/RPM-GPG-KEY-exampleServe that key file over HTTPS and publish its full fingerprint somewhere a human can compare it against — a documentation page, not a blog post that scrolls away. The key is the whole trust anchor, so the channel you distribute it over is the channel an attacker would target.
How to sign a .deb, and who checks it
You can embed a signature in a .deb with debsigs, and on a default Debian or Ubuntu system nobody will check it. dpkg ships with no-debsig in /etc/dpkg/dpkg.cfg, and a client that wants verification needs debsig-verify installed plus an XML policy document for each key it will accept. Debian-based distributions made that choice deliberately: they verify repository metadata and source packages instead.
# embedded signature, role-based (origin, maint, archive)
debsigs --sign=origin -k 0xE732A79A dist/example_2.4.1_amd64.deb
# source packages and .changes files take a different, text-based signature
debsign -k 0xE732A79A dist/example_2.4.1-1.dsc
# what a client needs before any of the above is checked:
# apt install debsig-verify
# a policy XML under /etc/debsig/policies/<fingerprint>/
# the key under /usr/share/debsig/keyrings/<fingerprint>/Embedded signatures are worth the effort in one situation: a fleet you control, where you can ship the policy and keyring along with the machines, and you want a package that is still verifiable after it has been copied off the repository onto a USB stick. For public distribution, spend the same effort on the repository instead. Nothing you do inside the .deb reaches a stock apt installation.
Sign the repository: InRelease and signed-by
apt verifies a chain, not a package. Your key signs the Release file; that file holds the SHA-256 hashes of every Packages index; each entry in Packages holds the hash of one .deb. Break any link and apt refuses the update. Since Debian 10 the signed artefact is InRelease, an inline-signed copy of Release; Release.gpg is the older detached form, still published for compatibility.
In practice a repository tool such as reprepro or aptly produces both files for you from a SignWith setting. It is still worth knowing the two commands underneath, because this is what you end up running by hand the first time a CI job produces an unsigned index:
# InRelease — the signature and the content in one file
gpg --clearsign --local-user 0xE732A79A -o dist/InRelease dist/Release
# Release.gpg — the same signature, detached, for older clients
gpg -abs --local-user 0xE732A79A -o dist/Release.gpg dist/ReleaseThe client half is where the guidance has changed. Publish your key as a file and have users scope it to your repository with signed-by, instead of adding it to a global keyring:
# one-line format
echo "deb [signed-by=/etc/apt/keyrings/example-archive-keyring.gpg] \
https://packages.example.com/debian stable main" \
| sudo tee /etc/apt/sources.list.d/example.list
# deb822 format — /etc/apt/sources.list.d/example.sources
Types: deb
URIs: https://packages.example.com/debian
Suites: stable
Components: main
Signed-By: /etc/apt/keyrings/example-archive-keyring.gpgThe security argument for this has nothing to do with the deprecation warning people are trying to silence. A key added with apt-key add lands in a keyring that apt trusts for every repository on the machine, so a small third-party repository ends up able to vouch for a package claiming to be from the distribution itself. signed-by binds one key to one source, which is what you wanted in the first place. If you publish installation instructions, this is the line to fix in them.
Ship the key as a proper keyring package too, if you can — example-archive-keyring containing the file under /usr/share/keyrings/. Then key rotation is a package update rather than a support article and a thousand people running curl | gpg --dearmor from memory.
What changed: RPM 6.0 and the end of apt-key
Two shifts matter if your signing setup predates them. RPM 6.0.0 was released on 22 September 2025 and enforces signature checking by default, accepts multiple OpenPGP signatures on one package, supports OpenPGP v6 and post-quantum keys, and makes the new v6 package format rpmbuild's default while still building v4. On the Debian side, apt-key is deprecated and its manual page describes it as scheduled for removal after Debian 12 and Ubuntu 24.04, with /etc/apt/keyrings the recommended home for keys APT does not manage since APT 2.4.
| Change | As of | What it means for a publisher |
|---|---|---|
| RPM enforces signature checking by default | RPM 6.0, 22 Sep 2025 | An unsigned internal repository stops being merely untidy |
| Multiple OpenPGP signatures per package | RPM 6.0 | Rotate keys, or add a post-quantum signature, without invalidating the first one |
| v6 format drops MD5 and SHA-1 | RPM 6.0 | Old tooling that parses packages needs checking; v4 output is still available |
| Installing RPM v3 packages removed | RPM 6.0 | Very old archives can still be queried and unpacked with rpm2cpio |
| apt-key deprecated, removal scheduled | after Debian 12 / Ubuntu 24.04 | Rewrite your published install instructions to use signed-by |
The multiple-signature change is the one worth planning around. Until RPM 6.0, adding a signature with a new key meant deciding what happened to the old one, which made rotation a coordination problem with every consumer. Now a package can carry both, so the sequence becomes: publish the new key, sign with both for a release cycle, then drop the old one. That is the same "add, do not replace" discipline that certificate rotation needs on the Windows side, and for the same reason.
Where the signing key should live
Nothing in RPM or APT requires the private key to be in hardware. There is no CA/Browser Forum equivalent for package signing, no audit, and no issuer to refuse you — so a key sitting in ~/.gnupg on a shared build agent is entirely permitted and entirely exposed. Anyone who can read that directory can sign packages as you, and your users' machines will install them without a murmur, because the signature will be perfectly valid.
Pick a step on that staircase deliberately. The cheapest real improvement is structural rather than physical: keep the primary key offline and give the build agent a signing subkey, so a compromised agent costs you a subkey rotation instead of your published identity. Above that, an OpenPGP smartcard or an HSM reached through gpg-agent's smartcard daemon means the key cannot be copied off at all, and rpmsign will take a PKCS#11 URI for the IMA file signing path. A hosted signing service adds the thing hardware alone does not give you: a log of who asked for each signature.
Set an expiry date on the signing key and put the rollover in the calendar. OpenPGP has no equivalent of the RFC 3161 timestamp that keeps an Authenticode signature valid after its certificate lapses, so tooling that checks key validity will start complaining the day after expiry. Extend the existing key or publish the replacement well before that date, and use RPM 6.0's multiple-signature support to overlap the two rather than cutting over in one release.
Where a code signing certificate does apply on Linux
On the artifacts that leave the Linux world. A Linux build agent can produce a perfectly valid Authenticode signature for a Windows .exe, .dll, .msi or .msix using osslsigncode or Jsign, and can sign a .jar with jarsigner. Those signatures are checked against a public root program, which is exactly why they need a publicly trusted certificate — and, since 1 June 2023, a key generated inside certified hardware.
So a release pipeline that ships both a .deb and a Windows installer runs two independent signing systems side by side, with two key custody stories. The mechanics of the Authenticode half on a Linux runner are covered in how to sign a Windows EXE on Linux or macOS, down to the PKCS#11 configuration for a token or a cloud key. If the certificate side is new to you, the OV and EV code signing certificates we resell from Certum list which delivery model — shipped token, or a cloud key that works unattended — fits a pipeline that also builds Linux packages. Certificates issued on or after 1 March 2026 are capped at 460 days under CA/Browser Forum ballot CSC-31, so the renewal cadence is now close to annual.
For container images the answer is different again: neither an OpenPGP key nor a public CA certificate is the default path, and Sigstore compared with code signing certificates covers where each model actually fits. A certificate can be made to work on an OCI image, though whether a code signing certificate is worth buying for Docker images turns out to be a narrower question than it first looks. Three ecosystems, three trust anchors, one phrase — which is why "do we sign our releases?" is never a single yes or no.
Frequently Asked Questions
Answers to common questions about certificates and our services.
Can I sign an RPM package with a code signing certificate?
Not the package signature itself. RPM consumes OpenPGP keys, and as of September 2026 there is no facility for signing a package with an X.509 certificate from a public CA. Red Hat's own position is that if you insist on a certificate you can sign the whole .rpm as an opaque file with openssl and distribute the detached signature and certificate alongside it — but no package manager will check that for you, so you have built a side channel rather than a package signature.
Do I need to sign individual .deb files, or is signing the repository enough?
For almost every publisher, signing the repository is the part that matters. dpkg ships with embedded .deb signature verification switched off, and a client that wants it needs debsig-verify installed plus an XML policy document per key. The signature apt does check on every update is the one on the repository's InRelease file, which carries the hashes of the Packages index, which carries the hash of each .deb. Sign the repository first; treat debsigs as an extra for controlled fleets.
How do I fix a NO_PUBKEY error from apt?
NO_PUBKEY means apt fetched a signed InRelease file and has no key that matches the signature. Get the repository's public key from the publisher over HTTPS, put it in its own file under /etc/apt/keyrings, and reference that file with signed-by= in the matching source entry. Do not pipe it into apt-key add: that puts the key in a keyring trusted for every repository on the machine, which is the behaviour the signed-by pattern exists to remove.
Is apt-key still safe to use?
It still runs on current releases, and it is on the way out. The manual page describes apt-key as deprecated and scheduled for removal after Debian 12 and Ubuntu 24.04, with apt-key del in maintainer scripts as the one remaining sanctioned use. Since APT 2.4, /etc/apt/keyrings is the recommended home for keys APT does not manage. The reason to move is scoping rather than the warning message: a key added with apt-key is trusted for all repositories, not just yours.
Does a Linux package signature expire the way a certificate does?
There is no equivalent of the RFC 3161 timestamp that keeps an Authenticode signature valid after its certificate expires. An OpenPGP signature has no certificate lifetime behind it at all — but if you set an expiry date on your signing key, which is good practice, tooling that checks key validity will start to complain once that date passes. Plan the rollover: extend the expiry on the existing key or publish the replacement well before the old one lapses.
Do I need to sign packages in an internal-only repository?
Yes, and the tooling is steadily making it non-optional. RPM 6.0 enforces signature checking by default, and apt has refused unsigned repositories without an explicit override for years. An internal repository is also the one most likely to be served over plain HTTP from a box nobody patches, which is exactly the case a signature covers. The same OpenPGP key can sign both your RPM packages and your APT repository metadata.
Shipping Windows or Java builds from the same pipeline?
Your OpenPGP key handles the packages. The Windows installer and the signed .jar need a publicly trusted certificate, with the key generated in certified hardware because the rules have required that since June 2023. Compare the OV and EV code signing options and what each one verifies before issuing.
Related reading
- How to sign a Windows EXE on Linux or macOS — the other half of a cross-platform release: Authenticode from a Linux runner, with the key on a token or in a cloud HSM.
- Sigstore vs code signing certificates — the third trust model, and where keyless signing makes sense for containers and open-source releases.
- Code signing certificates explained — what OV and EV actually verify, and what the hardware key requirement changed.