How to Convert Existing RSA SSH Private Key to GPG Primary Key (Not Subkey)


24 views

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