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 rsautl -encrypt -pubin -inkey public.pem -in message.txt -out encrypted.bin
7
8# Decrypt file with private key
9openssl rsautl -decrypt -inkey private.pem -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
16openssl enc -aes-256-cbc -salt -in largefile.txt -out largefile.enc -pass file:aes.key
17
18# Encrypt AES key with RSA
19openssl rsautl -encrypt -pubin -inkey public.pem -in aes.key -out aes.key.enc
20
21# Decrypt AES key with RSA
22openssl rsautl -decrypt -inkey private.pem -in aes.key.enc -out aes.key.dec
23
24# Decrypt file with AES
25openssl enc -d -aes-256-cbc -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
22openssl enc -aes-256-cbc -salt -in message.txt -out encrypted.bin -pass file:aes.key
23
24# Decrypt with derived key
25openssl enc -d -aes-256-cbc -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 import Cipher, algorithms, modes
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
38def encrypt_message(key, plaintext):
39 iv = os.urandom(16)
40 cipher = Cipher(
41 algorithms.AES(key),
42 modes.CBC(iv),
43 backend=default_backend()
44 )
45 encryptor = cipher.encryptor()
46
47 # Pad plaintext to block size
48 pad_length = 16 - (len(plaintext) % 16)
49 padded = plaintext + bytes([pad_length] * pad_length)
50
51 ciphertext = encryptor.update(padded) + encryptor.finalize()
52 return iv + ciphertext
53
54# Decrypt with derived key
55def decrypt_message(key, ciphertext):
56 iv = ciphertext[:16]
57 actual_ciphertext = ciphertext[16:]
58
59 cipher = Cipher(
60 algorithms.AES(key),
61 modes.CBC(iv),
62 backend=default_backend()
63 )
64 decryptor = cipher.decryptor()
65
66 padded = decryptor.update(actual_ciphertext) + decryptor.finalize()
67
68 # Remove padding
69 pad_length = padded[-1]
70 return padded[:-pad_length]
71
72# Example usage
73message = b"Hello, World!"
74encrypted = encrypt_message(aes_key, message)
75decrypted = decrypt_message(aes_key, encrypted)
76
77print(f"Original: {message}")
78print(f"Encrypted: {encrypted.hex()[:64]}...")
79print(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 import Cipher, algorithms, modes
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(16)
22
23 # Encrypt data with AES
24 cipher = Cipher(
25 algorithms.AES(aes_key),
26 modes.CBC(iv),
27 backend=default_backend()
28 )
29 encryptor = cipher.encryptor()
30
31 # Pad plaintext
32 pad_length = 16 - (len(plaintext) % 16)
33 padded = plaintext + bytes([pad_length] * pad_length)
34
35 ciphertext = encryptor.update(padded) + encryptor.finalize()
36
37 # Encrypt AES key with RSA
38 encrypted_key = self.public_key.encrypt(
39 aes_key,
40 padding.OAEP(
41 mgf=padding.MGF1(algorithm=hashes.SHA256()),
42 algorithm=hashes.SHA256(),
43 label=None
44 )
45 )
46
47 return encrypted_key, iv, ciphertext
48
49 def decrypt(self, encrypted_key: bytes, iv: bytes, ciphertext: bytes) -> bytes:
50 """Decrypt using hybrid encryption"""
51 # Decrypt AES key with RSA
52 aes_key = self.private_key.decrypt(
53 encrypted_key,
54 padding.OAEP(
55 mgf=padding.MGF1(algorithm=hashes.SHA256()),
56 algorithm=hashes.SHA256(),
57 label=None
58 )
59 )
60
61 # Decrypt data with AES
62 cipher = Cipher(
63 algorithms.AES(aes_key),
64 modes.CBC(iv),
65 backend=default_backend()
66 )
67 decryptor = cipher.decryptor()
68
69 padded = decryptor.update(ciphertext) + decryptor.finalize()
70
71 # Remove padding
72 pad_length = padded[-1]
73 return padded[:-pad_length]
74
75# Example usage
76hybrid = HybridEncryption()
77
78message = b"This is a long message that needs hybrid encryption!"
79encrypted_key, iv, ciphertext = hybrid.encrypt(message)
80
81print(f"Original: {message}")
82print(f"Encrypted key: {encrypted_key.hex()[:64]}...")
83print(f"IV: {iv.hex()}")
84print(f"Ciphertext: {ciphertext.hex()[:64]}...")
85
86decrypted = hybrid.decrypt(encrypted_key, iv, ciphertext)
87print(f"Decrypted: {decrypted}")
ECDH Flow Diagram
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
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
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)
General:
- Always use authenticated encryption
- Generate new IV for each encryption
- Use secure random number generator
- Verify recipient's public key
Related Snippets
- Asymmetric Encryption & Key Exchange
Asymmetric (public-key) cryptography with mathematical foundations, including … - Cryptographic Hash Functions
Cryptographic hash functions with mathematical properties and practical … - Digital Signatures
Digital signature algorithms with mathematical foundations. Mathematical … - Generate Public/Private Key Pairs
Generate public/private key pairs on Linux for various cryptographic purposes. … - Hash and Sign Text with Key Pairs
Hash and digitally sign text using public/private key pairs. Hash Text (OpenSSL) … - Homomorphic Encryption Schemes
Homomorphic encryption allows computation on encrypted data without decryption, … - Key Derivation Functions
Key Derivation Functions (KDFs) for password hashing and key derivation. … - Key Sharding (Secret Sharing)
Key sharding splits a secret into multiple shares where a threshold of shares is … - Multi-Signature (Multisig) Schemes
Multi-signature schemes require multiple parties to sign a transaction or … - PGP Signature Operations
PGP/GPG signature operations for files, emails, and git commits. Generate GPG … - Setup PGP with Git (Auto-sign Commits)
Setup GPG/PGP to automatically sign Git commits and tags. Generate GPG Key for … - Symmetric Encryption
Symmetric encryption algorithms with mathematical foundations and practical … - Threshold Signatures
Threshold signatures enable a group to sign messages without ever reconstructing …