Skip to main content

Що таке симетричний і асиметричний ключ в шифруванні?

Симетричне шифрування використовує один спільний секретний ключ і для шифрування, і для дешифрування даних. Асиметричне шифрування використовує пару ключів: публічний шифрує, приватний дешифрує.

Теорія

TL;DR

  • Симетричне - це ніби скринька з одним ключем, яким поділилися обидві сторони; асиметричне - поштова скринька, куди всі кидають листи (публічний ключ), але відкрити її можеш тільки ти (приватний ключ).
  • Головна різниця: симетричне швидке, але обидві сторони мусять заздалегідь отримати один ключ; асиметричне вирішує цю проблему, але дорожче по CPU.
  • Великі обсяги даних: симетричне (AES). Обмін ключем через ненадійний канал: асиметричне (RSA).
  • AES - стандарт для симетричного шифрування; RSA і ECC - для асиметричного.

Швидкий приклад

python
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, ChaCha20RSA, ECC, Diffie-Hellman

Як це працює всередині

AES обробляє 128-бітові блоки через 10-14 раундів підстановок і перестановок. Сучасні CPU виконують це апаратно через AES-NI, тому навантаження мінімальне. RSA працює інакше: шифрування - це c = m^e mod n за публічним показником і модулем; дешифрування - m = c^d mod n за приватним показником. Саме модульне піднесення до степеня на 2048-бітних числах і є дорогою частиною. Детальніше про те, як влаштована пара публічний/приватний ключ, читай в окремій статті.

Типові помилки

Передавати симетричний ключ у відкритому вигляді.

python
# Неправильно key = b'my-secret-key-123' send_over_http(key) # Хто слухає мережу, отримає ключ

Рішення: передавати ключ через асиметричне шифрування або делегувати це сервісу управління ключами (AWS KMS, HashiCorp Vault).

Повторне використання IV у AES-CBC.

python
# Неправильно: той самий 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 обробляє дані.

javascript
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 всередині.

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 # Генеруємо 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) # True

AES-ключ займає 32 байти - добре вкладається в ліміт RSA. Самі дані проходять повз RSA повністю. Саме тому і існує гібридне шифрування.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?