Skip to main content

What are symmetric and asymmetric keys in encryption?

Symmetric encryption uses one shared secret key for both encrypting and decrypting data. Asymmetric encryption uses a key pair: a public key to encrypt, a private key to decrypt.

Theory

TL;DR

  • Symmetric is like a locked box with one key both sides copy; asymmetric is like a mailbox where anyone drops mail (public key) but only you can open it (private key).
  • Main difference: symmetric is fast but both sides must share a secret first; asymmetric solves that at a higher CPU cost.
  • Use symmetric for bulk data (AES for files and streams). Use asymmetric to deliver the symmetric key safely (RSA).
  • AES is the standard symmetric algorithm; RSA and ECC are the common asymmetric ones.

Quick example

python
from cryptography.fernet import Fernet # Symmetric: one key, same for both operations key = Fernet.generate_key() # This key must stay secret cipher = Fernet(key) encrypted = cipher.encrypt(b"Secret message") decrypted = cipher.decrypt(encrypted) print(decrypted) # b'Secret message' # Both encrypt() and decrypt() use the same key

The same key does both jobs. That is the whole point. The problem shows up the moment you need to get that key to the other side.

Key difference

With symmetric encryption, both the sender and receiver need the same key before any communication starts. If that key travels over an open channel, anyone who intercepts it can read everything encrypted with it. Asymmetric encryption avoids this: the public key can be sent openly because it only encrypts. Decryption requires the private key, which never leaves its owner.

The trade-off is speed. RSA uses modular exponentiation on large integers, which is roughly 100-1000x slower than AES block operations. In most production systems I've worked with, this isn't really a choice between the two. TLS, PGP, and SSH all use asymmetric to bootstrap a symmetric session. The two types solve different problems.

When to use

  • Encrypting files, database columns, or disk volumes: symmetric (AES-256).
  • Sending a secret to someone over an untrusted channel: asymmetric (RSA delivers the key).
  • TLS handshake: asymmetric for initial key exchange, then symmetric for session data.
  • Digital signatures and non-repudiation: asymmetric (private key signs, public key verifies).
  • High-throughput data streams or local encryption: symmetric only.

Comparison table

AspectSymmetric (AES)Asymmetric (RSA)
Keys1 shared keyPublic + private pair
SpeedFast (AES-NI hardware support)Slow (big-integer math)
Key size128-256 bits2048+ bits
Key distributionRisky over open channelsPublic key safe to share
Use caseBulk data, disk encryptionKey exchange, signing
Algorithm examplesAES, ChaCha20RSA, ECC, Diffie-Hellman

How it works internally

AES applies 10-14 rounds of substitution and permutation on 128-bit blocks. Modern CPUs run AES-NI instructions in hardware, so the CPU cost is minimal. RSA works differently: encryption is c = m^e mod n using the public exponent and modulus; decryption is m = c^d mod n using the private exponent. That modular exponentiation on 2048-bit integers is what makes RSA expensive. For more detail on the key pair mechanism, see how public and private keys work.

Common mistakes

Sending the symmetric key in plain text.

python
# Wrong key = b'my-secret-key-123' send_over_http(key) # Anyone listening now has your key

Fix: deliver the symmetric key using asymmetric encryption, or delegate to a key management service like AWS KMS or HashiCorp Vault.

Reusing the same IV with AES-CBC.

python
# Wrong: same IV for every message iv = b'fixed-iv-1234567' cipher1 = AES.new(key, AES.MODE_CBC, iv) cipher2 = AES.new(key, AES.MODE_CBC, iv) # Identical plaintexts = identical ciphertexts

NIST forbids IV reuse. Generate a fresh random IV for each message and prepend it to the ciphertext.

Using 1024-bit RSA. Keys that size have been factored on 2023 hardware. Use 2048-bit minimum, or switch to ECC (256-bit gives equivalent security with lower CPU cost).

Using AES-CBC alone for authentication. CBC only encrypts; it does not prove who sent the message. Add HMAC or switch to AES-GCM, which includes authentication built in.

Real-world usage

  • TLS 1.3: RSA/ECDH for the handshake, AES-256-GCM for the session.
  • AWS KMS: RSA wraps a per-request AES key (hybrid model).
  • PostgreSQL pgcrypto: AES for column-level encryption.
  • Node.js API payloads: crypto.createCipheriv('aes-256-gcm', key, iv).
  • PGP email: RSA encrypts the AES session key, AES encrypts the message body.

Follow-up questions

Q: Why does TLS use both symmetric and asymmetric encryption?
A: Asymmetric handles the handshake to safely exchange a session key. After that, symmetric (AES-GCM) handles all data because it is orders of magnitude faster.

Q: What is Diffie-Hellman and how does it relate?
A: DH is a key agreement protocol that lets two parties derive a shared symmetric key without transmitting it directly. TLS 1.3 uses ECDHE (an elliptic curve variant) for this step.

Q: AES-CBC vs AES-GCM: what is the difference?
A: CBC only encrypts; you need a separate HMAC for integrity. GCM combines encryption and authentication in one step and is the standard choice today.

Q (senior): Explain a padding oracle attack on AES-CBC and how to prevent it.
A: An attacker sends crafted ciphertexts and reads error responses. Differences between a padding error and a decryption error let the attacker decrypt data byte by byte without the key. Fix: use AES-GCM, or at minimum apply encrypt-then-MAC so any tampering is detected before decryption runs.

Examples

Node.js: HTTPS-style key exchange

This pattern mirrors what TLS does during a handshake: RSA secures the key transfer, AES handles the data.

javascript
const crypto = require('crypto'); // Server generates RSA key pair const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 }); // Client generates an AES key and encrypts it with the server's public key const aesKey = crypto.randomBytes(32); const encryptedKey = crypto.publicEncrypt(publicKey, aesKey); // Server decrypts with private key; now both sides have aesKey const decryptedKey = crypto.privateDecrypt(privateKey, encryptedKey); console.log(Buffer.compare(aesKey, decryptedKey) === 0); // true // Both sides now use aesKey for fast symmetric encryption

RSA cannot directly encrypt large payloads (the limit for 2048-bit RSA is around 245 bytes). That is why it handles only the key transfer, not the actual data.

Python: hybrid encryption for large payloads

RSA encrypts the AES key. AES encrypts the data. This is called hybrid encryption and it is how PGP and TLS work internally.

python
from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes import os # Generate RSA key pair private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) public_key = private_key.public_key() # Generate AES session key aes_key = os.urandom(32) iv = os.urandom(16) # Encrypt data with AES data = b"Large payload " * 100 cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv)) enc = cipher.encryptor() ciphertext = enc.update(data) + enc.finalize() # Wrap AES key with RSA (safe to send alongside the ciphertext) wrapped_key = public_key.encrypt( aes_key, padding.OAEP(mgf=padding.MGF1(hashes.SHA256()), algorithm=hashes.SHA256(), label=None) ) # Receiver: unwrap AES key with RSA, then decrypt data with AES unwrapped_key = private_key.decrypt( wrapped_key, padding.OAEP(mgf=padding.MGF1(hashes.SHA256()), algorithm=hashes.SHA256(), label=None) ) dec_cipher = Cipher(algorithms.AES(unwrapped_key), modes.CBC(iv)) dec = dec_cipher.decryptor() print(dec.update(ciphertext) + dec.finalize() == data) # True

The AES key is 32 bytes, well within RSA's size limit. The 1.4 KB of actual data bypasses RSA entirely. That is why hybrid encryption exists.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?