Verifying RPM signatures
Like other package formats, RPMs can be cryptographically verified to ensure packages are still intact before they are installed. This post is going to go over how you can verify packages.
Why verify RPMs?
RPMs are normally sourced from the Internet, this presents two problems:
- RPMs can become truncated or corrupt during transit.
- RPMs could be maliciously modified as part of a man in the middle attack.
To mitigate these problems RPMs contain a signature section; the section includes a checksum and optionally a cryptographic signature. Checksums ensure truncated or corrupt packages are not installed. The signature (if included) can be used to verify the contents of the RPM has not been modified, since the author signed the package. RPMs normally use GPG public-key cryptography to achieve this.
GPG public keys
On Red Hat based distributions like CentOS, you can normally find
GPG public keys in /etc/pki/rpm-gpg/
. The files in the directory should be
ASCII Armoured GPG public keys.
Although keys are normally deployed to /etc/pki/rpm-gpg/
, they are actually
imported to an RPM database before they are used. You can query which RPMs have
been imported by running rpm -q gpg-pubkey
:
$ rpm -q gpg-pubkey
gpg-pubkey-f4a80eb5-53a7ff4b
Note: the RPM database files can be found in /var/lib/rpm
.
Importing keys with YUM
When installing packages with YUM, if the gpgcheck
option has been enabled,
YUM will automatically try to verify packages against imported public keys. If
the key has not yet been imported you will see something like this:
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Importing GPG key 0xF4A80EB5:
Userid : "CentOS-7 Key (CentOS 7 Official Signing Key) <[email protected]>"
Fingerprint: 6341 ab27 53d7 8a78 a7c2 7bb1 24c6 a8a7 f4a8 0eb5
Package : centos-release-7-1.1503.el7.centos.2.8.x86_64 (@anaconda)
From : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Is this ok [y/N]:
Saying yes will import the public key and continue the installation. The path to the public key which should be imported is managed on a per repository basis. For example the base repository for CentOS 7 has the following configuration:
[base]
name=CentOS-$releasever - Base
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra
#baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Note: other protocols like https
can be used instead of file
, refer to
the yum.conf(5) man page for more details.
Importing keys with RPM
Alternatively rpm --import
can be used to import public keys:
$ rpm -q gpg-pubkey
package gpg-pubkey is not installed
$ sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
$ rpm -q gpg-pubkey
gpg-pubkey-f4a80eb5-53a7ff4b
Listing imported keys
The rpm
command can be used to list GPG keys currently imported into the RPM
database:
$ rpm -q --qf "%{version}-%{release} %{summary}\n" gpg-pubkey
f4a80eb5-53a7ff4b gpg(CentOS-7 Key (CentOS 7 Official Signing Key) <[email protected]>)
b6792c39-53c4fbdd gpg(CentOS-7 Debug (CentOS-7 Debuginfo RPMS) <[email protected]>)
8fae34bd-538f1e51 gpg(CentOS-7 Testing (CentOS 7 Testing content) <[email protected]>)
Note: a query string is used in the example above to make the keys easier to identify.
Removing keys
To remove GPG keys from the RPM database you can use rpm -e
followed by
gpg-pubkey-<version>-<release>
. For example:
$ sudo rpm -e gpg-pubkey-b6792c39-53c4fbdd
Version and release?
GPG public keys have a key id and a POSIX timestamp. You can list these using the gpg command:
$ gpg --list-packets /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
:public key packet:
version 4, algo 1, created 1403518795, expires 0
pkey[0]: [4096 bits]
pkey[1]: [17 bits]
keyid: 24C6A8A7F4A80EB5
:user ID packet: "CentOS-7 Key (CentOS 7 Official Signing Key) <[email protected]>"
:signature packet: algo 1, keyid 24C6A8A7F4A80EB5
version 4, created 1403518795, md5len 0, sigclass 0x13
digest algo 2, begin of digest 4c dd
hashed subpkt 2 len 4 (sig created 2014-06-23)
hashed subpkt 27 len 1 (key flags: 03)
hashed subpkt 11 len 5 (pref-sym-algos: 9 8 7 3 2)
hashed subpkt 21 len 3 (pref-hash-algos: 2 8 3)
hashed subpkt 22 len 2 (pref-zip-algos: 2 1)
hashed subpkt 30 len 1 (features: 01)
hashed subpkt 23 len 1 (key server preferences: 80)
subpkt 16 len 8 (issuer key ID 24C6A8A7F4A80EB5)
data: [4095 bits]
Note: the details of how the key id is calculated are described in RFC 4880.
So what does this have to do with the seemingly arbitrary version and release
of gpg-pubkey
entries in the RPM database? Well the version maps directly to
the last eight characters of the GPG key id, and the release is the hexadecimal
version of the creation timestamp.
Verifying packages
So long as the gpgcheck
option has been enabled, YUM will automatically check
the GPG signature of packages it downloads. If the signature matches, YUM will
carry on as normal. If the package doesn't match the configured public key you
will get a message similar to the following and the package won't be installed:
The GPG keys listed for the "CentOS-7 - Base" repository are already
installed but they are not correct for this package.
Check that the correct key URLs are configured for this repository.
Verifying packages with rpm
You can also manually verify RPM packages using the rpm
command. If both the
signature and the checksum are correct you'll get output similar to the
following:
$ rpm --checksig tcpdump-4.5.1-3.el7.x86_64.rpm
tcpdump-4.5.1-3.el7.x86_64.rpm: rsa sha1 (md5) pgp md5 OK
If no key in the RPM database matches the package signature you will get a message similar to the following:
$ rpm --checksig tcpdump-4.5.1-3.el7.x86_64.rpm
tcpdump-4.5.1-3.el7.x86_64.rpm: RSA sha1 ((MD5) PGP) md5 NOT OK (MISSING KEYS: (MD5) PGP#f4a80eb5)
Assuming the RPM is not corrupt you can get signature information using the
--info
query option:
$ rpm -qp --info tcpdump-4.5.1-3.el7.x86_64.rpm
...
Signature : RSA/SHA256, Wed 25 Nov 2015 15:43:25 GMT, Key ID 24c6a8a7f4a80eb5
Source RPM : tcpdump-4.5.1-3.el7.src.rpm
Build Date : Fri 20 Nov 2015 08:36:02 GMT
Build Host : worker1.bsys.centos.org
...
If you only want to verify the contents of the package against the checksum,
you can use the --nosignature
option:
$ rpm --checksig --nosignature tcpdump-4.5.1-3.el7.x86_64.rpm
tcpdump-4.5.1-3.el7.x86_64.rpm: sha1 md5 OK
If the file is corrupt or truncated verifying against the checksum should fail:
$ rpm --checksig --nosignature tcpdump-4.5.1-3.el7.x86_64.rpm
tcpdump-4.5.1-3.el7.x86_64.rpm: sha1 MD5 NOT OK
It is also possible to use both the --nosignature
and --nodigest
options at
the same time:
$ rpm --checksig --nosignature --nodigest tcpdump-4.5.1-3.el7.x86_64.rpm
tcpdump-4.5.1-3.el7.x86_64.rpm: OK
Although this initial seems pointless, it does actually check the file header looks like an RPM:
$ rpm --checksig --nosignature --nodigest /dev/null
error: /dev/null: not an rpm package