Use gpgsm for Practical CMS Signing and Encryption
gpgsm is GnuPG's tool for X.509 certificates and CMS messages, the corner of the crypto world that plain OpenPGP gpg never touches. You will finish with a repeatable workflow for inspecting certificates and using gpgsm to create or verify CMS signatures and encrypt or decrypt CMS data. The examples target the installed GnuPG 2.4.4, from package version 2.4.4-2ubuntu17.6, using placeholder paths and certificate identities: swap those for identities you have actually checked.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow 20 minutes for a first run, plus time to obtain a suitable certificate and private key. You need a shell, the gpgsm package, and an X.509 certificate in PEM or binary form. Signing and decryption also need the matching private key, normally managed through gpg-agent. No example here needs root privileges.
1. Confirm the installed tool
Start by checking the version and the available certificate database. This reads your normal GnuPG home, usually ~/.gnupg, but changes nothing:
$ gpgsm --version
gpgsm (GnuPG) 2.4.4
$ gpgsm --list-keys
The second command may print nothing and still exit successfully. It creates the local keybox on first use if one does not already exist. A normal listing is formatted for people; reach for --with-colons when another program has to parse the records.
Checkpoint
Record the exact certificate identity or fingerprint you intend to use. Do not rely on a short name when more than one certificate could match it.
2. Inspect a certificate before importing it
Inspect a certificate file without adding it to the keybox:
$ gpgsm --show-certs /path/to/recipient.crt
The input may be a single binary certificate or one or more PEM certificates. Review the subject, issuer, validity dates and fingerprint. PEM input normally starts with -----BEGIN CERTIFICATE-----. If automatic input detection fails, try --assume-armor for PEM or --assume-binary for binary input.
Import only a certificate whose source and fingerprint you actually trust:
$ gpgsm --import /path/to/recipient.crt
gpgsm: total number processed: 1
gpgsm: imported: 1
Output varies when the certificate is already present, so verify the result by listing the database:
$ gpgsm --list-keys --with-colons
For a certificate that must pass chain validation before import, add --with-validation. That can contact certificate-revocation services through Dirmngr and can be slow. Treat a validation failure as a reason to investigate the certificate, not as a prompt to disable checks.
3. Create a signed CMS message
Use a clear output path and select the signing certificate explicitly. The installed program writes binary output by default; --armor makes PEM-encoded CMS output that moves more easily through text-based systems.
$ gpgsm --sign --local-user 'SIGNER_ID_OR_FINGERPRINT' \
--armor --output signed-message.pem /path/to/message.txt
$ head -n 1 signed-message.pem
-----BEGIN CMS-----
The local user value can be a fingerprint, exact email address, subject DN or another supported user-ID pattern. A fingerprint is the least ambiguous choice. Without --local-user, gpgsm picks the first secret key it finds, which is a poor default for an unattended job.
For a detached signature, use the command shown by the installed help:
$ gpgsm --detach-sign --local-user 'SIGNER_ID_OR_FINGERPRINT' \
--armor --output message.sig /path/to/message.txt
Keep the original message beside the detached signature. The signature does not carry a replacement copy of the message.
4. Verify the signature
Verify an attached CMS message with:
$ gpgsm --verify signed-message.pem
For a detached signature, give the signature first and then the original data:
$ gpgsm --verify message.sig /path/to/message.txt
A successful exit status means the cryptographic check completed. It does not by itself prove that you trust the issuing certificate, or that the message came from the person you expected. Review gpgsm's diagnostic output and the certificate chain for that decision.
Checkpoint
Capture the status immediately when scripting a verification:
if gpgsm --verify message.sig /path/to/message.txt; then
printf '%s\n' 'signature check passed'
else
status=$?
printf 'signature check failed, status %s\n' "$status" >&2
exit "$status"
fi
5. Encrypt for a recipient and decrypt locally
First confirm the recipient certificate is in your keybox, then encrypt to an identity you have matched against the certificate you inspected:
$ gpgsm --encrypt --recipient 'RECIPIENT_FINGERPRINT' \
--armor --output encrypted-message.pem /path/to/message.txt
$ head -n 1 encrypted-message.pem
-----BEGIN CMS-----
Encryption can use more than one --recipient, so each intended recipient can decrypt their own copy of the content. The sender does not automatically get a decryptable copy. Add your own certificate as another recipient if you will need to decrypt the result later.
Decrypt with the matching private key available to gpg-agent:
$ gpgsm --decrypt --output recovered-message.txt encrypted-message.pem
$ cmp -- /path/to/message.txt recovered-message.txt
A successful cmp prints nothing and returns status 0. Decryption may pop open Pinentry for the private-key passphrase. Never put a passphrase in a command line or a shell history. For automation, study --batch, --pinentry-mode and the agent configuration together; casually bolting on --batch can turn an interactive request into a silent failure.
6. Keep certificate checks and output predictable
Certificate-revocation-list checks are enabled by default and use Dirmngr. Leave that in place for normal signing and verification. The manual documents --disable-crl-checks mainly for offline operation, but it weakens certificate checking and also changes issuer-certificate retrieval. Use it only for a stated, controlled offline requirement, never baked into a general configuration file.
Use --status-fd when a program needs machine-readable operation status, rather than scraping human diagnostics. Keep status output separate from the CMS data, especially when writing the data to standard output. Use --output for files, and check that you are not about to overwrite something you need.
Configuration comes from gpgsm.conf under the GnuPG home unless --options selects another file. For a test or a service account, isolate state with an explicit home directory. Do not copy a private key into a shared temporary directory, and do not use --homedir as a way to dodge ownership or trust checks.
7. Recover from common failures
- No suitable certificate found. Run
gpgsm --list-keys --with-colonsand compare the full fingerprint with the recipient or signer value. - Signing or decryption asks for a missing key. The certificate may be present without its private key. Check with
gpgsm --list-secret-keys; importing a public certificate alone can never create the secret part. - Verification fails after a text-mode transfer. Compare hashes of the original and received files. Do not edit a CMS file or convert its line endings.
- Certificate validation cannot reach its revocation service. Fix Dirmngr connectivity, or use an explicitly isolated offline process. Do not reach for
--disable-crl-checksas a universal repair.
Warning
gpgsm --delete-keys PATTERN removes matching certificates from the local keybox. There is no command to delete a secret key directly, and key deletion can make later operations hard to recover from. Export or back up what you need first, and never use a broad pattern in a cleanup script.
Done means
- Version confirmed and the intended certificate was inspected by fingerprint, not by name alone.
- Recipient certificates imported and visible in
gpgsm --list-keys. - A CMS signature verifies, with the exit status checked rather than just the screen text.
- Encryption and decryption produce matching files without exposing a passphrase.
- Default revocation checks remain enabled unless a documented offline exception is genuinely required.