When migrating from SSH to GPG key management, many developers encounter the frustrating limitation where imported SSH keys default to subkey status. This occurs because GPG expects a complete OpenPGP key structure with certification capabilities for primary keys, while SSH keys typically lack these properties.
Before proceeding, ensure you have:
gpg --version | head -n1 # Should be 2.1+
openssl version # OpenSSL 1.0.1+ required
ssh-keygen -l -f ~/.ssh/id_rsa # Verify your existing key
Here's the complete workflow to transform your SSH private key into a GPG primary key:
Step 1: Extract SSH Private Key Components
openssl rsa -in ~/.ssh/id_rsa -outform pem -out id_rsa.pem
openssl rsa -in id_rsa.pem -pubout -out id_rsa_pub.pem
Step 2: Create GPG-Compatible Key Structure
We'll use Python with the cryptography library to build a proper GPG key packet:
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
with open("id_rsa.pem", "rb") as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None
)
# Create GPG packet structure
gpg_key = private_key.private_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
Step 3: Import into GPG as Primary Key
This critical step ensures your key isn't treated as a subkey:
gpg --import-options import-export --import id_rsa.pem
gpg --edit-key KEYID
gpg> trust
gpg> 5 # Ultimate trust
gpg> save
Confirm your key now appears as a primary key:
gpg --list-secret-keys --with-keygrip
# Should show [SC] not [ssb] for your key
To properly integrate with existing SSH workflows:
echo "enable-ssh-support" >> ~/.gnupg/gpg-agent.conf
gpg-connect-agent reloadagent /bye
- Ensure your original SSH key uses the RSA algorithm (not ED25519)
- Modern GPG versions may require --allow-non-selfsigned-uid flag
- Key expiration dates from SSH won't transfer automatically
When using RSA-4096 keys:
- Signature generation: ~300ms (vs ~50ms for native GPG keys)
- Decryption operations: ~20% slower than native keys
When migrating SSH authentication keys to GPG, users often encounter the subkey limitation. Traditional conversion methods (like ssh-keygen -e
pipelines) produce keys that GPG treats as subkeys rather than primary keys. This creates functional gaps in GPG operations.
The issue stems from GPG's key hierarchy requirements. A primary key must contain:
-----BEGIN PGP PRIVATE KEY BLOCK----- Version: GnuPG v2.0.22 (GNU/Linux) lQO+BFXXXXXXXX [...] =ABCD -----END PGP PRIVATE KEY BLOCK-----
SSH keys lack the metadata GPG expects for primary keys, including:
- Key creation timestamps
- Owner trust signatures
- Algorithm specification headers
Here's how to properly convert an RSA SSH key (id_rsa) to a GPG primary key:
1. Extract the Private Key Components
openssl rsa -in ~/.ssh/id_rsa -text
This outputs the modulus (n), public exponent (e), private exponent (d), and primes (p,q). Save these values.
2. Create GPG-Compatible Key Structure
Use gpgsm
to build a proper PKCS#12 container:
openssl req -new -key ~/.ssh/id_rsa -out cert.req openssl x509 -req -days 365 -in cert.req -signkey ~/.ssh/id_rsa -out cert.pem openssl pkcs12 -export -inkey ~/.ssh/id_rsa -in cert.pem -out key.p12 gpgsm --import key.p12
3. Force Primary Key Assignment
Edit ~/.gnupg/gpg.conf
:
allow-non-selfsigned-uid allow-secret-key-import
Then import with elevated permissions:
gpg --import-options import-export --import key.p12
Confirm the key status with:
gpg --list-secret-keys --with-keygrip
Look for sec
(not ssb
) in the output, indicating primary key status.
Remember that:
- Key expiration dates won't carry over from SSH
- You'll need to manually add user IDs with
gpg --edit-key
- Existing SSH authorized_keys entries will continue working