Що таке симетричний і асиметричний ключ в шифруванні?
Симетричне шифрування використовує один спільний секретний ключ і для шифрування, і для дешифрування даних. Асиметричне шифрування використовує пару ключів: публічний шифрує, приватний дешифрує.
Теорія
TL;DR
- Симетричне - це ніби скринька з одним ключем, яким поділилися обидві сторони; асиметричне - поштова скринька, куди всі кидають листи (публічний ключ), але відкрити її можеш тільки ти (приватний ключ).
- Головна різниця: симетричне швидке, але обидві сторони мусять заздалегідь отримати один ключ; асиметричне вирішує цю проблему, але дорожче по CPU.
- Великі обсяги даних: симетричне (AES). Обмін ключем через ненадійний канал: асиметричне (RSA).
- AES - стандарт для симетричного шифрування; RSA і ECC - для асиметричного.
Швидкий приклад
from cryptography.fernet import Fernet
# Симетричне: один ключ для обох операцій
key = Fernet.generate_key() # Цей ключ має залишатися секретним
cipher = Fernet(key)
encrypted = cipher.encrypt(b"Secret message")
decrypted = cipher.decrypt(encrypted)
print(decrypted) # b'Secret message'
# encrypt() і decrypt() використовують той самий ключОдин і той самий key виконує обидві операції. У цьому вся суть симетричного шифрування. Проблема виникає тоді, коли цей ключ треба безпечно передати іншій стороні.
Головна різниця
При симетричному шифруванні обидві сторони мусять мати однаковий ключ ще до початку спілкування. Якщо цей ключ пройде через відкритий канал, той хто перехопить його прочитає все. Асиметричне шифрування обходить цю проблему: публічний ключ можна передавати відкрито, він тільки шифрує. Для дешифрування потрібен приватний ключ, який ніколи не покидає власника.
Платиш за це швидкодією. RSA використовує модульне піднесення до степеня на великих числах, що приблизно в 100-1000 разів повільніше за операції AES. На практиці це не вибір між двома підходами, а їх комбінування: TLS, PGP і SSH використовують асиметричне шифрування щоб встановити сесію, а симетричне - щоб передавати дані.
Коли що використовувати
- Шифрування файлів, стовпців БД, дисків: симетричне (AES-256).
- Передати секрет людині через ненадійний канал: асиметричне (RSA доставляє ключ).
- TLS-рукостискання (handshake): асиметричне для початкового обміну ключем, симетричне для сесії.
- Цифрові підписи і підтвердження авторства: асиметричне (приватний ключ підписує, публічний перевіряє).
- Потокове шифрування великих обсягів або локальне шифрування: тільки симетричне.
Таблиця порівняння
| Аспект | Симетричне (AES) | Асиметричне (RSA) |
|---|---|---|
| Ключі | 1 спільний ключ | Пара: публічний + приватний |
| Швидкість | Швидке (апаратне прискорення AES-NI) | Повільне (важкі математичні операції) |
| Розмір ключа | 128-256 біт | 2048+ біт |
| Розподіл ключів | Ризикований через відкриті канали | Публічний ключ безпечно передавати |
| Застосування | Масиви даних, шифрування дисків | Обмін ключами, підписи |
| Приклади алгоритмів | AES, ChaCha20 | RSA, ECC, Diffie-Hellman |
Як це працює всередині
AES обробляє 128-бітові блоки через 10-14 раундів підстановок і перестановок. Сучасні CPU виконують це апаратно через AES-NI, тому навантаження мінімальне. RSA працює інакше: шифрування - це c = m^e mod n за публічним показником і модулем; дешифрування - m = c^d mod n за приватним показником. Саме модульне піднесення до степеня на 2048-бітних числах і є дорогою частиною. Детальніше про те, як влаштована пара публічний/приватний ключ, читай в окремій статті.
Типові помилки
Передавати симетричний ключ у відкритому вигляді.
# Неправильно
key = b'my-secret-key-123'
send_over_http(key) # Хто слухає мережу, отримає ключРішення: передавати ключ через асиметричне шифрування або делегувати це сервісу управління ключами (AWS KMS, HashiCorp Vault).
Повторне використання IV у AES-CBC.
# Неправильно: той самий IV для кожного повідомлення
iv = b'fixed-iv-1234567'
cipher1 = AES.new(key, AES.MODE_CBC, iv)
cipher2 = AES.new(key, AES.MODE_CBC, iv) # Однакові тексти = однакові шифротекстиNIST забороняє повторне використання IV. Генеруй новий випадковий IV для кожного повідомлення і передавай його разом з шифротекстом.
RSA з ключем 1024 біт. Такі ключі вже розкривали на обладнанні 2023 року. Мінімум - 2048 біт, або перейди на ECC (256 біт дають еквівалентний захист при меншому навантаженні на CPU).
Симетричне шифрування без автентифікації. AES-CBC сам по собі не підтверджує, хто надіслав повідомлення. Додай HMAC або перейди на AES-GCM, який включає автентифікацію без додаткових кроків.
Де зустрічається в реальному коді
- TLS 1.3: RSA/ECDH для рукостискання, AES-256-GCM для сесії.
- AWS KMS: RSA обгортає AES-ключ для кожного запиту (гібридна модель).
- PostgreSQL
pgcrypto: AES для шифрування окремих стовпців. - Node.js:
crypto.createCipheriv('aes-256-gcm', key, iv)для API-запитів. - PGP: RSA шифрує AES-ключ сесії, AES шифрує тіло повідомлення.
Питання на співбесіді
Q: Чому TLS використовує і симетричне, і асиметричне шифрування?
A: Асиметричне потрібне для початкового рукостискання, щоб безпечно обмінятися сесійним ключем. Далі в роботу йде симетричне (AES-GCM), бо воно набагато швидше.
Q: Що таке Diffie-Hellman і як він пов'язаний з цим?
A: Це протокол погодження ключів, який дозволяє двом сторонам вивести спільний симетричний ключ, не передаючи його напряму. TLS 1.3 використовує ECDHE - варіант на еліптичних кривих.
Q: В чому різниця між AES-CBC і AES-GCM?
A: CBC тільки шифрує; для перевірки цілісності потрібен окремий HMAC. GCM поєднує шифрування і автентифікацію в одному кроці. Зараз стандартом є GCM.
Q (senior): Поясни атаку padding oracle на AES-CBC і як від неї захиститися.
A: Зловмисник надсилає модифіковані шифротексти і читає відповіді сервера. Різниця між повідомленнями про помилку padding і помилку дешифрування дозволяє розшифровувати дані байт за байтом без ключа. Захист: використовувати AES-GCM, або щонайменше encrypt-then-MAC, щоб виявляти будь-яку зміну до запуску дешифрування.
Приклади
Node.js: обмін ключем за схемою HTTPS
Цей патерн повторює те, що TLS робить під час рукостискання: RSA захищає передачу ключа, AES обробляє дані.
const crypto = require('crypto');
// Сервер генерує RSA-пару
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048
});
// Клієнт генерує AES-ключ і шифрує його публічним ключем сервера
const aesKey = crypto.randomBytes(32);
const encryptedKey = crypto.publicEncrypt(publicKey, aesKey);
// Сервер розшифровує приватним ключем; тепер обидві сторони мають aesKey
const decryptedKey = crypto.privateDecrypt(privateKey, encryptedKey);
console.log(Buffer.compare(aesKey, decryptedKey) === 0); // true
// Далі обидві сторони використовують aesKey для швидкого симетричного шифруванняRSA не може напряму шифрувати великі дані (ліміт для 2048-бітного RSA - близько 245 байт). Тому ним шифрують тільки ключ, а не самі дані.
Python: гібридне шифрування для великих даних
RSA шифрує AES-ключ. AES шифрує самі дані. Цей патерн називається гібридним шифруванням (hybrid encryption) - саме так влаштовані PGP і TLS всередині.
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
# Генеруємо RSA-пару
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
# Генеруємо AES-ключ сесії
aes_key = os.urandom(32)
iv = os.urandom(16)
# Шифруємо дані через AES
data = b"Large payload " * 100
cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv))
enc = cipher.encryptor()
ciphertext = enc.update(data) + enc.finalize()
# Шифруємо AES-ключ через RSA (можна передавати разом з шифротекстом)
wrapped_key = public_key.encrypt(
aes_key,
padding.OAEP(mgf=padding.MGF1(hashes.SHA256()), algorithm=hashes.SHA256(), label=None)
)
# Отримувач: розшифровує AES-ключ через RSA, потім дані через 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) # TrueAES-ключ займає 32 байти - добре вкладається в ліміт RSA. Самі дані проходять повз RSA повністю. Саме тому і існує гібридне шифрування.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.