You can sign Debian packages (.deb) using a virtual GPG key and the Signum Linux Agent.
Prerequisites
-
Signum Linux Agent
-
Linux distribution supported by the Linux Agent
-
sudoprivileges
Step 1 - Install Dependencies
-
Install the packages:
sudo apt update
sudo apt install -y \
gnupg2 \
pinentry-curses \
opensc \
build-essential \
bzip2 \
wget \
devscripts \
libgpg-error-dev \
libgcrypt20-dev \
libassuan-dev \
libpkcs11-helper1-dev \
pkg-config
-
Build gnupg-pkcs11-scd 0.11.0 from source with a one-line patch in
cmd_keyinfo:
The version of gnupg-pkcs11-scd available in Debian apt repositories is 0.10.0 and has a known bug. Version 0.11.0 partially fixes this but still requires a one-line patch in cmd_keyinfo.
cd /tmp
wget https://github.com/alonbl/gnupg-pkcs11-scd/releases/download/gnupg-pkcs11-scd-0.11.0/gnupg-pkcs11-scd-0.11.0.tar.bz2
tar -xjf gnupg-pkcs11-scd-0.11.0.tar.bz2
cd gnupg-pkcs11-scd-0.11.0
# Apply the patch
python3 << 'EOF'
with open('gnupg-pkcs11-scd/command.c', 'r') as f:
content = f.read()
old = '\t\tif (error != GPG_ERR_NO_ERROR) {\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\n\terror = found ? GPG_ERR_NO_ERROR : GPG_ERR_NOT_FOUND;'
new = '\t\tif (error == GPG_ERR_WRONG_PUBKEY_ALGO) {\n\t\t\terror = GPG_ERR_NO_ERROR;\n\t\t}\n\t\tif (error != GPG_ERR_NO_ERROR) {\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\n\terror = found ? GPG_ERR_NO_ERROR : GPG_ERR_NOT_FOUND;'
if old in content:
content = content.replace(old, new)
with open('gnupg-pkcs11-scd/command.c', 'w') as f:
f.write(content)
print("Patch applied!")
else:
print("Pattern not found!")
EOF
# Build and install
./configure && make && sudo make install
# Verify
/usr/local/bin/gnupg-pkcs11-scd --version
Expected output:
gnupg-pkcs11-scd 0.11.0
Step 2 - Verify Signum Agent and PKCS#11 Library
-
Verify the Agent is authenticated and certificates are visible:
signum-util test
signum-util listcertificates
-
Verify the PKCS#11 library is present and keys are accessible:
ls -la /usr/lib/libsignumpkcs11.so
pkcs11-tool --module /usr/lib/libsignumpkcs11.so --list-objects
Step 3 - Create GPG Configuration Files
-
Create the GPG configuration files:
mkdir -p ~/.gnupg
chmod 700 ~/.gnupg
cat > ~/.gnupg/gpg-agent.conf << 'EOF'
verbose
debug-all
log-file /tmp/gpg-agent.log
scdaemon-program /usr/local/bin/gnupg-pkcs11-scd
pinentry-program /usr/bin/pinentry-curses
allow-loopback-pinentry
EOF
cat > ~/.gnupg/gnupg-pkcs11-scd.conf << 'EOF'
providers signum
provider-signum-library /usr/lib/libsignumpkcs11.so
EOF
chmod 600 ~/.gnupg/gpg-agent.conf
chmod 600 ~/.gnupg/gnupg-pkcs11-scd.conf
-
Set GPG_TTY and start the smart card daemon:
GPG_TTY must always be set before signing.
export GPG_TTY=$(tty)
gpgconf --kill all
gpg --card-status
Expected result is the card detected with Application type: OpenPGP.
-
Get the
KEY-FRIENDLYhash for your certificate from Signum:
gpg-connect-agent << 'EOF'
SCD LEARN --force
SCD GETATTR KEYPAIRINFO
EOF
Note the KEY-FRIENDLY hash for the certificate you want to use:
S KEY-FRIENDLY 2588F2F67BBC82E872B15287621964636FB074D9 /CN=SignumCertificate on Signum for Linux
Save this hash for the following steps.
Step 4 - Map the Signing Key
Update gnupg-pkcs11-scd.conf with the KEY-FRIENDLY hash:
-
Replace
<KEY-FRIENDLY-HASH>with your actual hash value:
cat > ~/.gnupg/gnupg-pkcs11-scd.conf << 'EOF'
providers signum
provider-signum-library /usr/lib/libsignumpkcs11.so
openpgp-sign <KEY-FRIENDLY-HASH>
EOF
-
Restart the daemon to apply the change:
gpgconf --kill all
gpg --card-status
-
Confirm the
Signature keyfield is now populated. It should show the keygrip instead of[none]:
Signature key ....: 2588 F2F6 7BBC 82E8 72B1 5287 6219 6463 6FB0 74D9
Step 5 - Create the Virtual GPG Key
-
Generate the virtual GPG key:
gpg --expert --full-generate-key
-
When prompted, provide the following values:
|
Prompt |
Value |
|---|---|
|
Key type |
|
|
Keygrip |
|
|
Capabilities |
|
|
Expiry |
|
|
Real name |
Your full name |
|
Email address |
Your email |
|
Confirm |
|
|
PIN prompt |
Your Signum password |
Expected output:
pub rsa2048 2026-05-21 [SCE]
BB07CE2D9778F6CED7D0F05A46C163CE62A3B77B
uid Your Name <your@email.com>
-
Verify the key is linked to the Signum card:
gpg --list-secret-keys --with-keygrip
Confirm sec> (with >) appears, meaning the private key is in the Signum HSM, not on disk:
sec> rsa2048 2026-05-21 [SCE]
BB07CE2D9778F6CED7D0F05A46C163CE62A3B77B
Keygrip = 2588F2F67BBC82E872B15287621964636FB074D9
Card serial no. = 3131 DC6624E9
uid [ultimate] Your Name <your@email.com>
-
Set this key as the GPG default. Replace
<FINGERPRINT>with the full fingerprint
from the output above:
echo "default-key <FINGERPRINT>" >> ~/.gnupg/gpg.conf
-
Make GPG_TTY permanent across sessions:
echo 'export GPG_TTY=$(tty)' >> ~/.bashrc
source ~/.bashrc
Step 6 - Sign a DEB Package
-
Sign the package, which creates a detached
.ascsignature file alongside the.deb:
gpg --armor --detach-sign /path/to/yourpackage.deb
Expected output:
yourpackage.deb
yourpackage.deb.asc ← detached GPG signature
-
Verify the signature locally:
gpg --verify /path/to/yourpackage.deb.asc /path/to/yourpackage.deb
Expected output:
gpg: Signature made Thu 21 May 2026
gpg: using RSA key BB07CE2D9778F6CED7D0F05A46C163CE62A3B77B
gpg: Good signature from "Your Name <your@email.com>" [ultimate]
Export Public Key for Distribution
For recipients to validate your signature, you need to share your public key:
gpg --armor --export <your_email> > /tmp/signum-pubkey.asc
Recipients then import it and verify:
gpg --import signum-pubkey.asc
gpg --verify yourpackage.deb.asc yourpackage.deb
Troubleshooting
gpg --card-status Returns "No card" or Hangs
-
Confirm
gnupg-pkcs11-scdis the patched 0.11.0 build:/usr/local/bin/gnupg-pkcs11-scd --version -
Check the
scdaemon-programpath ingpg-agent.confpoints to the correct binary. -
Kill all GPG daemons and retry:
gpgconf --kill all gpg --card-status -
Review the agent log for errors:
cat /tmp/gpg-agent.log
KEY-FRIENDLY Hash Not Appearing in SCD GETATTR KEYPAIRINFO
-
Confirm the Signum Agent is authenticated:
signum-util test -
Confirm the PKCS#11 library is accessible:
pkcs11-tool --module /usr/lib/libsignumpkcs11.so --list-objects -
If the token has mixed RSA + ECC certificates, confirm you applied the patch to the
gnupg-pkcs11-scdbuild, or the loop stops before reaching RSA certificates.
gpg --full-generate-key Fails at Keygrip Prompt
-
The keygrip entered must match exactly the
KEY-FRIENDLYhash. -
Make sure you ran
gpg --card-statusafter updatinggnupg-pkcs11-scd.conf
and that theSignature keyfield is populated (not[none]).
sec (Without >) After Key Generation
-
This means the private key was stored locally on disk, not linked to the HSM.
-
Delete the key (
gpg --delete-secret-and-public-key <FINGERPRINT>) and repeat
Step 5, confirming you selected key type 13 (Existing key) and entered the
correct keygrip.
Signature Verification Fails on Recipient Machine
-
The recipient must have imported your public key before running
gpg --verify. -
Confirm the
.ascfile and the.debfile are both present and unmodified. -
Ensure the recipient's GPG version supports the key algorithm used.