Encrypt/Decrypt with Key Pairs

Encrypt and decrypt data using public/private key pairs and derive symmetric keys from ECC key pairs.


RSA Encryption (OpenSSL)

 1# Generate RSA key pair
 2openssl genrsa -out private.pem 4096
 3openssl rsa -in private.pem -pubout -out public.pem
 4
 5# Encrypt file with public key
 6openssl pkeyutl -encrypt -pubin -inkey public.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -in message.txt -out encrypted.bin
 7
 8# Decrypt file with private key
 9openssl pkeyutl -decrypt -inkey private.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -in encrypted.bin -out decrypted.txt
10
11# For larger files, use hybrid encryption (RSA + AES)
12# Generate random AES key
13openssl rand -base64 32 > aes.key
14
15# Encrypt file with AES (openssl enc has no AEAD/MAC: no integrity check; prefer age, or AES-GCM as in the Python examples)
16openssl enc -aes-256-cbc -salt -pbkdf2 -iter 600000 -in largefile.txt -out largefile.enc -pass file:aes.key
17
18# Encrypt AES key with RSA
19openssl pkeyutl -encrypt -pubin -inkey public.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -in aes.key -out aes.key.enc
20
21# Decrypt AES key with RSA
22openssl pkeyutl -decrypt -inkey private.pem -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -in aes.key.enc -out aes.key.dec
23
24# Decrypt file with AES
25openssl enc -d -aes-256-cbc -pbkdf2 -iter 600000 -in largefile.enc -out largefile.dec -pass file:aes.key.dec

RSA Encryption (pkeyutl - Modern)

 1# Encrypt with public key
 2openssl pkeyutl -encrypt -pubin -inkey public.pem -in message.txt -out encrypted.bin
 3
 4# Decrypt with private key
 5openssl pkeyutl -decrypt -inkey private.pem -in encrypted.bin -out decrypted.txt
 6
 7# With OAEP padding (recommended)
 8openssl pkeyutl -encrypt -pubin -inkey public.pem -pkeyopt rsa_padding_mode:oaep \
 9  -pkeyopt rsa_oaep_md:sha256 -in message.txt -out encrypted.bin
10
11openssl pkeyutl -decrypt -inkey private.pem -pkeyopt rsa_padding_mode:oaep \
12  -pkeyopt rsa_oaep_md:sha256 -in encrypted.bin -out decrypted.txt

ECDH Key Exchange

 1# Alice generates key pair
 2openssl ecparam -name prime256v1 -genkey -noout -out alice_private.pem
 3openssl ec -in alice_private.pem -pubout -out alice_public.pem
 4
 5# Bob generates key pair
 6openssl ecparam -name prime256v1 -genkey -noout -out bob_private.pem
 7openssl ec -in bob_private.pem -pubout -out bob_public.pem
 8
 9# Alice derives shared secret using Bob's public key
10openssl pkeyutl -derive -inkey alice_private.pem -peerkey bob_public.pem -out alice_shared.bin
11
12# Bob derives shared secret using Alice's public key
13openssl pkeyutl -derive -inkey bob_private.pem -peerkey alice_public.pem -out bob_shared.bin
14
15# Both shared secrets are identical
16diff alice_shared.bin bob_shared.bin  # No output = identical
17
18# Derive AES key from shared secret
19openssl dgst -sha256 -binary alice_shared.bin > aes.key
20
21# Encrypt with derived key (no integrity check: prefer age or AES-GCM)
22openssl enc -aes-256-cbc -salt -pbkdf2 -in message.txt -out encrypted.bin -pass file:aes.key
23
24# Decrypt with derived key
25openssl enc -d -aes-256-cbc -pbkdf2 -in encrypted.bin -out decrypted.txt -pass file:aes.key

GPG/PGP Encryption

 1# Encrypt for recipient
 2gpg --encrypt --recipient recipient@example.com message.txt
 3# Creates message.txt.gpg
 4
 5# Encrypt (ASCII armor)
 6gpg --encrypt --armor --recipient recipient@example.com message.txt
 7# Creates message.txt.asc
 8
 9# Encrypt for multiple recipients
10gpg --encrypt --recipient alice@example.com --recipient bob@example.com message.txt
11
12# Encrypt and sign
13gpg --encrypt --sign --recipient recipient@example.com message.txt
14
15# Decrypt
16gpg --decrypt message.txt.gpg > decrypted.txt
17
18# Decrypt and verify signature
19gpg --decrypt message.txt.gpg

Age Encryption (Modern Alternative)

 1# Install age
 2sudo apt install age
 3
 4# Generate key pair
 5age-keygen -o key.txt
 6# Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
 7
 8# Encrypt with public key
 9age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p \
10  -o encrypted.age message.txt
11
12# Decrypt with private key
13age -d -i key.txt encrypted.age > decrypted.txt
14
15# Encrypt for multiple recipients
16age -r age1... -r age1... -o encrypted.age message.txt

Python: RSA Encryption

 1from cryptography.hazmat.primitives.asymmetric import rsa, padding
 2from cryptography.hazmat.primitives import hashes, serialization
 3from cryptography.hazmat.backends import default_backend
 4
 5# Generate key pair
 6private_key = rsa.generate_private_key(
 7    public_exponent=65537,
 8    key_size=4096,
 9    backend=default_backend()
10)
11public_key = private_key.public_key()
12
13# Encrypt with public key
14message = b"Hello, World!"
15ciphertext = public_key.encrypt(
16    message,
17    padding.OAEP(
18        mgf=padding.MGF1(algorithm=hashes.SHA256()),
19        algorithm=hashes.SHA256(),
20        label=None
21    )
22)
23
24print(f"Encrypted: {ciphertext.hex()[:64]}...")
25
26# Decrypt with private key
27plaintext = private_key.decrypt(
28    ciphertext,
29    padding.OAEP(
30        mgf=padding.MGF1(algorithm=hashes.SHA256()),
31        algorithm=hashes.SHA256(),
32        label=None
33    )
34)
35
36print(f"Decrypted: {plaintext.decode()}")

Python: ECDH Key Exchange

 1from cryptography.hazmat.primitives.asymmetric import ec
 2from cryptography.hazmat.primitives import hashes, serialization
 3from cryptography.hazmat.primitives.kdf.hkdf import HKDF
 4from cryptography.hazmat.primitives.ciphers.aead import AESGCM
 5from cryptography.hazmat.backends import default_backend
 6import os
 7
 8# Alice generates key pair
 9alice_private = ec.generate_private_key(ec.SECP256R1(), default_backend())
10alice_public = alice_private.public_key()
11
12# Bob generates key pair
13bob_private = ec.generate_private_key(ec.SECP256R1(), default_backend())
14bob_public = bob_private.public_key()
15
16# Alice derives shared secret
17alice_shared = alice_private.exchange(ec.ECDH(), bob_public)
18
19# Bob derives shared secret
20bob_shared = bob_private.exchange(ec.ECDH(), alice_public)
21
22# Both shared secrets are identical
23assert alice_shared == bob_shared
24
25# Derive AES key from shared secret using HKDF
26def derive_key(shared_secret):
27    return HKDF(
28        algorithm=hashes.SHA256(),
29        length=32,
30        salt=None,
31        info=b'handshake data',
32        backend=default_backend()
33    ).derive(shared_secret)
34
35aes_key = derive_key(alice_shared)
36
37# Encrypt with derived key (AES-GCM: authenticated)
38def encrypt_message(key, plaintext):
39    nonce = os.urandom(12)
40    return nonce + AESGCM(key).encrypt(nonce, plaintext, None)
41
42# Decrypt with derived key (raises InvalidTag if tampered)
43def decrypt_message(key, ciphertext):
44    return AESGCM(key).decrypt(ciphertext[:12], ciphertext[12:], None)
45
46# Example usage
47message = b"Hello, World!"
48encrypted = encrypt_message(aes_key, message)
49decrypted = decrypt_message(aes_key, encrypted)
50
51print(f"Original: {message}")
52print(f"Encrypted: {encrypted.hex()[:64]}...")
53print(f"Decrypted: {decrypted}")

Python: Hybrid Encryption (RSA + AES)

 1from cryptography.hazmat.primitives.asymmetric import rsa, padding
 2from cryptography.hazmat.primitives import hashes, serialization
 3from cryptography.hazmat.primitives.ciphers.aead import AESGCM
 4from cryptography.hazmat.backends import default_backend
 5import os
 6
 7class HybridEncryption:
 8    def __init__(self):
 9        # Generate RSA key pair
10        self.private_key = rsa.generate_private_key(
11            public_exponent=65537,
12            key_size=4096,
13            backend=default_backend()
14        )
15        self.public_key = self.private_key.public_key()
16    
17    def encrypt(self, plaintext: bytes) -> tuple:
18        """Encrypt using hybrid encryption (RSA + AES)"""
19        # Generate random AES key
20        aes_key = os.urandom(32)
21        iv = os.urandom(12)
22        
23        # Encrypt data with AES-GCM (authenticated)
24        ciphertext = AESGCM(aes_key).encrypt(iv, plaintext, None)
25        
26        # Encrypt AES key with RSA
27        encrypted_key = self.public_key.encrypt(
28            aes_key,
29            padding.OAEP(
30                mgf=padding.MGF1(algorithm=hashes.SHA256()),
31                algorithm=hashes.SHA256(),
32                label=None
33            )
34        )
35        
36        return encrypted_key, iv, ciphertext
37    
38    def decrypt(self, encrypted_key: bytes, iv: bytes, ciphertext: bytes) -> bytes:
39        """Decrypt using hybrid encryption"""
40        # Decrypt AES key with RSA
41        aes_key = self.private_key.decrypt(
42            encrypted_key,
43            padding.OAEP(
44                mgf=padding.MGF1(algorithm=hashes.SHA256()),
45                algorithm=hashes.SHA256(),
46                label=None
47            )
48        )
49        
50        # Decrypt data with AES-GCM (raises InvalidTag if tampered)
51        return AESGCM(aes_key).decrypt(iv, ciphertext, None)
52
53# Example usage
54hybrid = HybridEncryption()
55
56message = b"This is a long message that needs hybrid encryption!"
57encrypted_key, iv, ciphertext = hybrid.encrypt(message)
58
59print(f"Original: {message}")
60print(f"Encrypted key: {encrypted_key.hex()[:64]}...")
61print(f"IV: {iv.hex()}")
62print(f"Ciphertext: {ciphertext.hex()[:64]}...")
63
64decrypted = hybrid.decrypt(encrypted_key, iv, ciphertext)
65print(f"Decrypted: {decrypted}")

ECDH Flow Diagram

sequenceDiagram
    participant Alice
    participant Bob
    
    Note over Alice: Generate key pair<br/>(private_A, public_A)
    Note over Bob: Generate key pair<br/>(private_B, public_B)
    
    Alice->>Bob: Send public_A
    Bob->>Alice: Send public_B
    
    Note over Alice: shared = ECDH(private_A, public_B)
    Note over Bob: shared = ECDH(private_B, public_A)
    
    Note over Alice,Bob: shared_A == shared_B
    
    Note over Alice: AES_key = KDF(shared)
    Note over Bob: AES_key = KDF(shared)
    
    Alice->>Bob: Encrypt(message, AES_key)
    Bob->>Alice: Encrypt(response, AES_key)

Key Exchange Mathematics

For ECDH on curve \( E \) with generator \( G \):

Alice:

  • Private key: \( a \in \mathbb{Z}_n \)
  • Public key: \( A = aG \)

Bob:

  • Private key: \( b \in \mathbb{Z}_n \)
  • Public key: \( B = bG \)

Shared Secret:

$$ \text{Alice computes: } S = aB = a(bG) = abG $$$$ \text{Bob computes: } S = bA = b(aG) = abG $$$$ \therefore S_{\text{Alice}} = S_{\text{Bob}} $$

Best Practices

  1. RSA Encryption:

    • Use OAEP padding (not PKCS#1 v1.5)
    • Use 4096-bit keys minimum
    • Use hybrid encryption for large data
    • Never encrypt same data twice
  2. ECDH:

    • Use strong curves (P-256, P-384, Curve25519)
    • Use HKDF to derive keys from shared secret
    • Include context info in KDF
    • Use authenticated encryption (GCM)
  3. General:

    • Always use authenticated encryption
    • Generate new IV for each encryption
    • Use secure random number generator
    • Verify recipient's public key

Related Snippets