DevToys Web Pro iconDevToys Web Proالمدونة
مُترجم بواسطة LocalePack logoLocalePack
قيّمنا:
جرّب إضافة المتصفح:
← Back to Blog

RSA Key Generator Guide: Public/Private Keys, PEM, DER, JWK, and Key Sizes

11 min read

RSA key pairs are the backbone of TLS certificates, SSH authentication, JWT signing, and code-signing workflows. Yet there are surprisingly many ways to get the details wrong: wrong key size, wrong file format, unprotected private key, wrong algorithm choice. Use the RSA Key Generator to create key pairs and follow along with the examples in this guide.

Public vs Private Key Roles

RSA is an asymmetric algorithm: two mathematically linked keys that can never be derived from each other in reasonable time. They have complementary roles depending on the operation:

OperationWho uses the private keyWho uses the public key
SigningSigner computes signatureAnyone verifies the signature
EncryptionOwner decrypts ciphertextSender encrypts plaintext
SSH authClient proves identity (signs challenge)Server checks against authorized_keys
TLS handshakeServer decrypts pre-master secretClient encrypts pre-master secret

The private key is secret and must never leave the machine or service that owns it. The public key is meant to be shared freely — embed it in certificates, push it to SSH servers, publish it in a JWK Set endpoint.

Key Sizes and Security Levels

RSA security comes from the difficulty of factoring large numbers. Larger keys are harder to factor, but are slower to use. NIST recommendations as of 2024:

Key SizeSecurity LevelNIST StatusNotes
1024 bits80-bit (broken)Disallowed since 2013Factored in practice; reject on sight
2048 bits112-bit securityAcceptable through 2030Minimum for new deployments today
3072 bits128-bit securityRecommended after 2030Good balance of security and performance
4096 bits140-bit securityStrong long-term optionNoticeably slower; use for CA root keys

For most server TLS certificates and SSH keys, 2048 bits remains the practical baseline. Use 4096 bits when the key is long-lived (certificate authority root keys, code-signing keys) and performance is not critical. The marginal security gain of 4096 over 2048 is real but small — the bigger wins come from key rotation policy and passphrase protection.

PEM vs DER vs JWK

An RSA key pair is ultimately just a set of large integers. The format describes how those integers are serialized and transported:

PEM (Privacy Enhanced Mail)

PEM is the most common format for file-based key storage. It wraps a base64-encoded DER structure in ASCII header/footer lines:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2a2rwplBQLF29amygykE
...
-----END PUBLIC KEY-----

PEM files are text, so they survive copy-paste, email, and environment variables without corruption. The header line tells parsers what kind of key or certificate to expect. PEM is used by OpenSSL, nginx, Apache, most SSH tools, and TLS libraries.

DER (Distinguished Encoding Rules)

DER is the binary encoding that PEM wraps. It is the raw ASN.1 structure without the base64 or header lines. DER files are not human-readable, use less space, and are required by some Java (keytool/JSSE) and Windows (CryptoAPI) environments. The file extension is typically .der or .cer.

Convert between PEM and DER with OpenSSL:

# PEM to DER
openssl rsa -in key.pem -outform DER -out key.der

# DER to PEM
openssl rsa -in key.der -inform DER -out key.pem

JWK (JSON Web Key)

JWK represents keys as JSON objects, making them native to web APIs. A JWK Set (JWKS) is a JSON document containing multiple public keys, typically served at a /.well-known/jwks.json endpoint for JWT signature verification:

{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "kid": "2026-04",
      "alg": "RS256",
      "n": "2a2rwplBQLzHPZe5RJr9...",
      "e": "AQAB"
    }
  ]
}

Fields: kty = key type (RSA), use = sig (signing) or enc (encryption), kid = key ID for rotation, n = modulus (base64url), e = public exponent (base64url). Private JWKs also include d, p, q, and CRT parameters.

PKCS#1 vs PKCS#8

Two PEM header variants appear in the wild for RSA private keys, and confusing them causes parsing errors:

HeaderStandardAlgorithm infoCommon usage
-----BEGIN RSA PRIVATE KEY-----PKCS#1RSA-only; algorithm implicitOpenSSL legacy, older SSH tools
-----BEGIN PRIVATE KEY-----PKCS#8Algorithm OID embeddedModern standard; works for RSA, EC, Ed25519
-----BEGIN ENCRYPTED PRIVATE KEY-----PKCS#8 encryptedAlgorithm OID + encryption paramsPassphrase-protected private key

PKCS#8 is the modern format. Prefer it for new keys. Convert a PKCS#1 key to PKCS#8 with OpenSSL:

# PKCS#1 → PKCS#8 (unencrypted)
openssl pkcs8 -topk8 -nocrypt -in pkcs1.pem -out pkcs8.pem

# PKCS#1 → PKCS#8 (AES-256 encrypted)
openssl pkcs8 -topk8 -v2 aes-256-cbc -in pkcs1.pem -out pkcs8-enc.pem

For public keys, the equivalent split is PKCS#1 (-----BEGIN RSA PUBLIC KEY-----) vs X.509 SubjectPublicKeyInfo (-----BEGIN PUBLIC KEY-----). Most modern tools expect the X.509 SPKI form.

RSA vs ECDSA vs Ed25519

RSA is not the only asymmetric algorithm. For new systems, elliptic-curve alternatives are often preferable:

AlgorithmKey size for 128-bit securitySignature sizeSpeedNotes
RSA-30723072 bits384 bytesSlow keygen, fast verifyUniversal compatibility
ECDSA P-256256 bits64 bytesFastRequires good randomness per signature
ECDSA P-384384 bits96 bytesFastSuite B compliant; used in government PKI
Ed25519256 bits64 bytesFastestNo per-signature randomness; deterministic

When to use RSA: legacy systems that do not support elliptic curves, Java environments with limited provider support, X.509 certificates for broad compatibility, JWT with RS256 when the receiver cannot handle ES256.

When to use Ed25519: SSH keys on modern OpenSSH (6.5+), new JWT signing (EdDSA), and any system where you control both ends. Smaller keys, smaller signatures, no randomness dependency, and faster than RSA at equivalent security.

Generating Keys

OpenSSL

# Generate RSA-2048 private key (PKCS#8, unencrypted)
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem

# Generate RSA-2048 private key (PKCS#8, AES-256 encrypted)
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
  -aes-256-cbc -out private-enc.pem

# Extract public key from private key
openssl pkey -in private.pem -pubout -out public.pem

# Generate RSA-4096
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out private4096.pem

ssh-keygen

# RSA-4096 SSH key with passphrase prompt
ssh-keygen -t rsa -b 4096 -C "user@example.com" -f ~/.ssh/id_rsa

# Ed25519 SSH key (recommended for new keys)
ssh-keygen -t ed25519 -C "user@example.com" -f ~/.ssh/id_ed25519

# Change passphrase on existing key
ssh-keygen -p -f ~/.ssh/id_rsa

Node.js (crypto module)

import { generateKeyPair } from 'node:crypto';
import { promisify } from 'node:util';

const generateKeyPairAsync = promisify(generateKeyPair);

const { privateKey, publicKey } = await generateKeyPairAsync('rsa', {
  modulusLength: 2048,
  publicKeyEncoding: {
    type: 'spki',    // X.509 SubjectPublicKeyInfo
    format: 'pem',
  },
  privateKeyEncoding: {
    type: 'pkcs8',   // PKCS#8
    format: 'pem',
    // Optional: encrypt with passphrase
    // cipher: 'aes-256-cbc',
    // passphrase: 'your-passphrase',
  },
});

console.log(publicKey);  // -----BEGIN PUBLIC KEY-----
console.log(privateKey); // -----BEGIN PRIVATE KEY-----

Python (cryptography library)

from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization

# Generate private key
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
)

# Serialize private key to PEM (PKCS#8, unencrypted)
pem_private = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption(),
)

# Serialize public key to PEM (SPKI)
pem_public = private_key.public_key().public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo,
)

print(pem_public.decode())
print(pem_private.decode())

Passphrase Protection

A private key stored on disk without a passphrase is only as secure as the file system permissions protecting it. If an attacker gains read access to the file — through a server breach, backup leak, or misconfigured permissions — they have your private key immediately.

Passphrase protection encrypts the private key using a symmetric cipher (typically AES-256 in CBC mode) derived from the passphrase via a KDF (PBKDF2 or scrypt). The result is an -----BEGIN ENCRYPTED PRIVATE KEY----- PEM block. Without the passphrase, the file is useless.

The tradeoffs:

  • Interactive systems (SSH, git): Use a passphrase and add the key to ssh-agent so you only enter it once per session. The agent holds the decrypted key in memory.
  • Automated systems (CI/CD, daemons): Passphrase protection is less practical. Use a secrets manager (Vault, AWS Secrets Manager, GCP Secret Manager) to store the raw key and inject it at runtime via environment variable or tmpfs mount.
  • TLS certificates on servers: Most web servers load the private key unencrypted at startup. Protect the file with strict permissions and consider HSM-backed keys for high-value certificates.
# Add key to ssh-agent (decrypts once, holds in memory for the session)
ssh-add ~/.ssh/id_rsa

# List keys in agent
ssh-add -l

# Remove all keys from agent
ssh-add -D

Storage Best Practices

  • File permissions: Private key files must be readable only by their owner. On Linux/macOS: chmod 600 ~/.ssh/id_rsa. OpenSSH refuses to use keys with world-readable permissions.
  • Never commit private keys to version control. Add *.pem, id_rsa, and *.key to .gitignore. If a key is ever committed, treat it as compromised and rotate immediately — git history is not erasable in practice.
  • Secrets managers: For application keys in production, store in HashiCorp Vault, AWS Secrets Manager, or GCP Secret Manager. Retrieve at runtime; do not bake keys into container images or config files.
  • HSM / KMS: For CA root keys, code-signing keys, and high-value certificates, use a Hardware Security Module or cloud KMS (AWS KMS, GCP Cloud KMS, Azure Key Vault). The private key never leaves the hardware boundary; only the sign/decrypt operation is exposed.
  • Key rotation: Plan for rotation before you need it. Document which services depend on each key, and automate certificate renewal (ACME / Let's Encrypt for TLS).
# Set correct permissions on SSH private key
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub

# Verify permissions
ls -la ~/.ssh/

SSH Key Workflow

SSH public-key authentication replaces password authentication: the server holds your public key in ~/.ssh/authorized_keys and your client proves identity by signing a challenge with the corresponding private key.

Setting up a new SSH key

# 1. Generate the key pair
ssh-keygen -t ed25519 -C "user@example.com" -f ~/.ssh/id_ed25519

# 2. Copy public key to server
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server.example.com
# Or manually: append ~/.ssh/id_ed25519.pub to ~/.ssh/authorized_keys on the server

# 3. Test the connection
ssh -i ~/.ssh/id_ed25519 user@server.example.com

The authorized_keys format

Each line in ~/.ssh/authorized_keys contains one public key in OpenSSH wire format:

ssh-ed25519 AAAA...base64... user@example.com
ssh-rsa AAAA...base64... user@laptop
# Options prefix restricts usage:
from="10.0.0.0/8",no-pty ssh-ed25519 AAAA... deploy-bot

Using ~/.ssh/config for multiple keys

# ~/.ssh/config
Host github.com
  IdentityFile ~/.ssh/id_ed25519_github
  User git

Host prod-server
  HostName 203.0.113.10
  User deploy
  IdentityFile ~/.ssh/id_rsa_prod
  Port 2222

Host bastion
  HostName bastion.example.com
  IdentityFile ~/.ssh/id_ed25519
  ForwardAgent yes

ForwardAgent yes forwards the ssh-agent connection through the bastion so you can reach internal servers without copying private keys to the bastion host. Only enable it for trusted jump hosts.

Common Pitfalls

  • Storing private keys alongside public keys in a repo. The public key is safe to commit; the private key is not. Automate a check with pre-commit hooks or secret scanning (GitGuardian, TruffleHog).
  • Using RSA-1024 in legacy code. Audit dependencies and TLS configurations. Any RSA-1024 key should be replaced immediately.
  • Confusing PKCS#1 and PKCS#8 headers. If a library throws a parse error on a valid-looking PEM, check the header line — convert with openssl pkcs8 as shown above.
  • Forgetting to protect private key file permissions in Docker. Files copied with COPY default to world-readable. Use --chmod=600 or a multi-stage build to avoid leaking secrets in intermediate layers.
  • Relying on key size alone. A 4096-bit RSA key with a weak passphrase stored world-readable on a public S3 bucket provides zero security. Key management matters more than key size.

Generate RSA key pairs of any size and export them in PEM, DER, or JWK format directly in your browser with the RSA Key Generator — all computation happens client-side, no keys are transmitted. For related reading, see X.509 Certificate Decoding Explained and Hashes, Encryption, and Password Hashing.