| |

LFCS 53 🐧 Package Verification and Signing

⏱️ Estimated read: 14 min | |

Package verification and signing are the trust mechanisms of a Linux system. Without them, every dnf install or apt install is an act of faith: you are trusting that the package you receive came from the repository you configured, that it has not been modified in transit, and that it has not been tampered with on the mirror. GPG signatures turn that faith into a cryptographic proof. The repository signs its metadata, the package manager verifies the signature against a public key it already trusts, and the installation proceeds only if the math checks out.

The two families of distributions implement this differently, and the LFCS exam expects you to know both. On RHEL-based systems, the rpm tool verifies individual package signatures with rpm --checksig (or rpm -K), and rpm --import adds a public key to the RPM keyring . On Debian-based systems, apt verifies the repository’s Release file signature rather than each package’s signature, and the signed-by option in the source line points to the keyring that performs the verification . The package-level verification on Debian is a separate, less commonly used step, performed with tools like dpkg-sig or debsig-verify .

Key point: On RHEL, rpm --import adds a public key and rpm -K verifies a package’s signature against it. On Debian, APT verifies the repository’s Release file signature using the keyring referenced by signed-by= in the source line; per-package verification is a separate step with dpkg-sig. A repository with gpgcheck=0 (RHEL) or without a signed Release file (Debian) cannot provide this proof.


Why package verification is the foundation of trust

The mirror problem. A repository is rarely a single server. It is a set of mirrors, often in different countries, operated by different organizations. Any one of them could be compromised, or could be serving a modified copy of the package. The signature is what makes the mirror’s identity irrelevant: the package is either signed by the key you trust or it is not. A compromised mirror can serve a modified package, but the signature verification fails, and the installation is blocked .

The transport problem. HTTPS protects the package in transit, but it does not protect the package on the mirror’s disk. If the mirror is compromised and the attacker replaces the package file with a malicious one, HTTPS will deliver the malicious file faithfully. The signature is the only mechanism that detects the substitution, because it verifies the content against the key, not against the transport .

The key distribution problem. The signature is only as trustworthy as the public key. If the attacker can replace the public key on your system, they can sign their own packages and your system will accept them. This is why the public key must come from a trusted source—the distribution’s installation media, the vendor’s website over HTTPS, or a key server whose identity you can verify . The rpm --import command adds the key to the RPM database, and from that point on, every package signed by the corresponding private key is accepted.

The repository metadata problem. On Debian, APT does not verify every package signature. It verifies the Release file, which contains checksums for every package in the repository. If the Release file is signed by a trusted key and the checksum matches the package, the package is considered authentic . This is more efficient than per-package verification because the signature is checked once per repository refresh, not once per package. On RHEL, the gpgcheck=1 directive in the .repo file tells DNF to verify each package’s signature against the RPM keyring, and the gpgkey= directive tells DNF where to fetch the key if it is not already imported.


a. RHEL: importing keys and verifying RPM signatures

The RPM keyring is the set of public keys that the system trusts for package verification. rpm --import adds a key to this keyring. The key can come from a file, a URL, or the distribution’s installation media. The RHEL installation media includes the Red Hat release key at /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release, and the key is imported automatically during installation .

# Import a public key from a file
# The key is added to the RPM database and trusted for future verifications
sudo rpm --import /path/to/RPM-GPG-KEY-custom

# Import a key from a URL
sudo rpm --import https://example.com/RPM-GPG-KEY-custom

# List the keys currently in the RPM keyring
# The output shows the key IDs and the user IDs
rpm -qa gpg-pubkey*

The rpm --checksig (or rpm -K) command verifies a package’s signature. The output shows whether the signature is valid, which key signed it, and whether the key is present in the keyring. If the key is not present, the output shows NOKEY, which means the signature cannot be verified .

# Verify a package's signature
# -K is the short form of --checksig
# -v adds verbose output
rpm -Kv package.rpm

# Expected output when the key is imported:
# package.rpm:
#     Header V4 RSA/SHA256 Signature, key ID abc123: OK
#     Header SHA256 digest: OK
#     Payload SHA256 digest: OK

# Expected output when the key is missing:
# package.rpm:
#     Header V4 RSA/SHA256 Signature, key ID abc123: NOKEY
#     Header SHA256 digest: OK
#     Payload SHA256 digest: OK

The NOKEY output is not a failure of the package’s integrity—the checksums are still OK. It means the signature cannot be verified because the public key is not in the keyring. Importing the key and running rpm -K again will show OK if the package is authentic .

The gpgcheck=1 directive in the .repo file makes DNF perform this verification automatically for every package it installs. The gpgkey= directive points to the URL or file that contains the public key, so DNF can import it if it is not already present. A repository with gpgcheck=0 skips the verification entirely, which is a security risk.


b. Debian: APT’s Release file verification

Debian’s APT verifies the repository’s Release file, not each package. The Release file contains the checksums of every package in the repository, and the Release.gpg file (or the inline signature in the InRelease file) contains the GPG signature of the Release file . If the Release file is signed by a trusted key and the package’s checksum matches the one in the Release file, APT considers the package authentic.

The keyring that APT trusts is configured through the signed-by= option in the source line. The keyring can be a file in /etc/apt/keyrings/ or /usr/share/keyrings/, and it must be referenced explicitly in the .list or .sources file .

# /etc/apt/sources.list.d/custom.list
# The signed-by option points to the keyring that verifies this repository
# The keyring file contains the GPG public key in binary or ASCII-armored form
deb [signed-by=/etc/apt/keyrings/custom-archive-keyring.gpg] https://example.com/repo stable main

The signed-by option is the modern replacement for the deprecated apt-key command. apt-key added keys to a global keyring (/etc/apt/trusted.gpg), which meant that any repository could be verified by any key in the global keyring. This was a security weakness: a key trusted for one repository was implicitly trusted for all repositories. The signed-by option scopes the trust to the specific repository .

When a repository’s Release file is not signed, APT refuses to use the repository by default. The error message indicates that the repository is not signed, and the apt update command fails for that repository. The allow-insecure=yes option in the source line or the Acquire::AllowInsecureRepositories configuration can override this, but the Debian documentation explicitly discourages it .


c. Signing packages: the other side

Verification is only half of the mechanism. The other half is signing, which is what the repository maintainer does. On RHEL, the rpmsign tool adds a GPG signature to an RPM package. On Debian, the debsign tool signs the .changes and .dsc files that are uploaded to the repository, and the repository’s Release file is signed during the archive update process .

# RHEL: sign an RPM package
# The signing key must be configured in ~/.rpmmacros or /etc/rpm/macros
# %_gpg_name must be set to the key's user ID
rpmsign --addsign package.rpm

# Verify the signature was added
rpm -Kv package.rpm
# The output shows the signature with the key ID

The rpmsign tool requires the rpm-sign package and a configured GPG key. The %_gpg_name macro in ~/.rpmmacros specifies which key to use. The --addsign flag adds the signature, and the --delsign flag removes an existing signature .

On Debian, the debsign tool is part of the devscripts package. It signs the .changes file and the .dsc file, not the .deb package itself. The signature on these files is what the repository verifies when the package is uploaded. The .deb package’s integrity is protected by the checksums in the .changes file, which is itself signed .

# Debian: sign a .changes file
# The GPG key is selected by the maintainer field or the -k option
debsign package.changes

# Sign with a specific key
debsign -k KEYID package.changes

The distinction between signing the .changes file and signing the .deb file is important. The .deb file is not individually signed in the standard Debian upload process. The repository’s Release file contains the checksum of the .deb, and the Release file is signed. The chain of trust is: GPG key → Release file → package checksum → package content. If any link in the chain is broken, APT refuses to install .


Complete Example Session

The following session imports a custom RPM key, verifies a package, and then configures a Debian repository with a signed-by keyring.

# ============================================
# PART 1: RHEL — IMPORT THE KEY
# ============================================
# Import the public key for the custom repository
# The key is added to the RPM keyring
sudo rpm --import https://example.com/RPM-GPG-KEY-custom

# Verify the key is in the keyring
rpm -qa gpg-pubkey* | grep -i custom

# ============================================
# PART 2: RHEL — VERIFY THE PACKAGE
# ============================================
# Verify the package signature
# The output should show OK for the signature and the digests
rpm -Kv package.rpm

# If the key is missing, the output shows NOKEY
# Import the key and re-run the verification

# ============================================
# PART 3: DEBIAN — CREATE THE KEYRING
# ============================================
# Create the keyrings directory if it does not exist
sudo mkdir -p /etc/apt/keyrings

# Download the key and convert it to the binary format APT expects
# The .gpg extension is for binary keys, .asc for ASCII-armored
curl -fsSL https://example.com/repo-key.asc | sudo gpg --dearmor -o /etc/apt/keyrings/custom-archive-keyring.gpg

# ============================================
# PART 4: DEBIAN — CONFIGURE THE REPOSITORY
# ============================================
# Add the repository with the signed-by option
echo "deb [signed-by=/etc/apt/keyrings/custom-archive-keyring.gpg] https://example.com/repo stable main" | sudo tee /etc/apt/sources.list.d/custom.list

# Refresh the package index
sudo apt update

# ============================================
# PART 5: DEBIAN — VERIFY THE CHAIN
# ============================================
# If the Release file is signed and the keyring is correct, apt update succeeds
# If the key is wrong or missing, apt update fails with a GPG error
# The error message indicates which key is missing or which signature failed

# ============================================
# PART 6: RHEL — SIGN A PACKAGE
# ============================================
# Configure the signing key in ~/.rpmmacros
echo "%_gpg_name Custom Signing Key <sign@example.com>" >> ~/.rpmmacros

# Sign the package
rpmsign --addsign package.rpm

# Verify the signature
rpm -Kv package.rpm

The six parts cover the RHEL key import and verification, the Debian keyring creation and repository configuration, the verification of the APT chain, and the RPM signing process. What the session demonstrates is that the trust chain is different in each family: RHEL verifies each package against the RPM keyring, and Debian verifies the Release file against the keyring referenced by signed-by.


Quick Reference

RHEL Verification

CommandEffect
rpm --import KEYAdd a public key to the RPM keyring
rpm -Kv pkg.rpmVerify package signature and digests
rpm -qa gpg-pubkey*List keys in the RPM keyring
gpgcheck=1DNF verifies each package
gpgkey=URLDNF imports the key from URL

Debian Verification

CommandEffect
signed-by=/path/key.gpgScope the keyring to one repository
gpg --dearmorConvert ASCII key to binary keyring
apt updateVerifies the Release file signature
dpkg-sig --verify pkg.debVerify a single package (optional)

Signing

ToolPlatformTarget
rpmsign --addsignRHEL.rpm package
debsignDebian.changes / .dsc files

Key Locations

PlatformKey Location
RHEL/etc/pki/rpm-gpg/
Debian/etc/apt/keyrings/
Debian (legacy)/etc/apt/trusted.gpg.d/

Best Practices

✅ Do This:

# RHEL: import the key and verify
sudo rpm --import https://example.com/RPM-GPG-KEY-custom
rpm -Kv package.rpm

# Debian: scope the key to the repository
deb [signed-by=/etc/apt/keyrings/custom.gpg] https://example.com/repo stable main

# RHEL: keep gpgcheck=1 in the .repo file
gpgcheck=1
gpgkey=https://example.com/RPM-GPG-KEY-custom

❌ Don’t Do This:

# Disable signature verification
gpgcheck=0 # accepts unsigned packages

# Use apt-key (deprecated)
sudo apt-key adv --recv-keys KEYID # global trust

# Ignore NOKEY output
rpm -Kv package.rpm # NOKEY means the signature was not verified

Common Pitfalls

PitfallWhy It HappensFix
NOKEY on rpm -KPublic key not importedrpm --import
APT GPG error on updateKeyring missing or wrong keyCheck signed-by path
gpgcheck=0 in repoSignature verification disabledSet to 1 and add gpgkey
apt-key warningDeprecated commandUse signed-by keyring
Signature fails after re-signFile modified after signingRe-sign the package

Real-World Examples

1. Import RPM key

sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

2. Verify RPM

rpm -Kv package.rpm

3. List RPM keys

rpm -qa gpg-pubkey*

4. Create Debian keyring

curl -fsSL https://example.com/key.asc | sudo gpg --dearmor -o /etc/apt/keyrings/custom.gpg

5. Debian signed-by

deb [signed-by=/etc/apt/keyrings/custom.gpg] https://example.com/repo stable main

6. Verify Debian package

dpkg-sig --verify package.deb

7. Sign RPM

rpmsign --addsign package.rpm

8. Sign Debian changes

debsign package.changes

9. Check gpgcheck in repo

grep gpgcheck /etc/yum.repos.d/*.repo

10. Check Release signature

apt update # verifies Release.gpg or InRelease

Visual

RHEL Trust Chain

┌──────────────────────────────────────────────┐
│  GPG PRIVATE KEY (repository)                │
│         │                                    │
│         ▼                                    │
│  package.rpm (signed)                        │
│         │                                    │
│         ▼                                    │
│  rpm -Kv verifies against RPM keyring        │
│         │                                    │
│    ┌────┴────┐                               │
│    │         │                               │
│  OK        NOKEY                             │
│    │         │                               │
│    ▼         ▼                               │
│  install   import key                        │
└──────────────────────────────────────────────┘

Debian Trust Chain

┌──────────────────────────────────────────────┐
│  GPG PRIVATE KEY (archive)                   │
│         │                                    │
│         ▼                                    │
│  Release file (signed)                       │
│         │                                    │
│         ▼                                    │
│  contains checksums of all packages          │
│         │                                    │
│         ▼                                    │
│  apt update verifies Release signature       │
│         │                                    │
│         ▼                                    │
│  package checksum matches → install          │
└──────────────────────────────────────────────┘

signed-by vs apt-key

┌──────────────────────────────────────────────┐
│  apt-key (deprecated)                        │
│  key added to /etc/apt/trusted.gpg           │
│  → trusted for ALL repositories              │
│                                              │
│  signed-by (modern)                          │
│  key referenced per repository               │
│  → trusted only for that repository          │
└──────────────────────────────────────────────┘

Summary

ItemValue
RHEL verify commandrpm -Kv package.rpm
RHEL import commandrpm --import KEY
RHEL key location/etc/pki/rpm-gpg/
RHEL repo directivegpgcheck=1 + gpgkey=
Debian verify chainRelease file signature
Debian keyring optionsigned-by=
Debian key location/etc/apt/keyrings/
RPM signingrpmsign --addsign
Debian signingdebsign package.changes

Key takeaways:

  • RHEL verifies each package against the RPM keyring. rpm --import adds the public key, and rpm -Kv verifies the signature and the digests. A NOKEY output means the key is missing, not that the package is corrupt .
  • Debian verifies the repository’s Release file, not each package. The signed-by= option scopes the keyring to a specific repository, and apt update performs the verification. A repository without a signed Release file is rejected by default .
  • apt-key is deprecated. The modern approach is a keyring file in /etc/apt/keyrings/ referenced by signed-by= in the source line. The global keyring it replaced allowed any trusted key to verify any repository .
  • The signing tools are different. RHEL uses rpmsign --addsign to sign the .rpm package. Debian uses debsign to sign the .changes and .dsc files, which contain the checksums that protect the .deb .
  • gpgcheck=0 is a security failure. It disables signature verification for the repository, which means a compromised mirror can serve a modified package without detection. The directive should be gpgcheck=1 with a valid gpgkey= .
  • The trust chain is only as strong as the key distribution. The public key must come from a trusted source. An attacker who can replace the key can sign their own packages and the verification will succeed.

Remember: Package verification is the mechanism that turns a repository from a source of files into a source of trusted software. On RHEL, the RPM keyring and rpm -K verify each package. On Debian, the signed-by keyring and apt update verify the repository’s Release file. The signing tools are the other side: rpmsign for the package, debsign for the upload metadata. The LFCS exam expects you to know both families, because the exam is distribution-neutral and the objective is to “search, install, verify, and maintain software packages or repositories.” Verification is the “verify” in that list, and it is the difference between installing software and trusting it.


Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!