Привет, Хабр!
Сегодня рассмотрим такую библиотеку в Rust, как RustCrypto. RustCrypto — это набор библиотек, реализующих различные криптографические алгоритмы, такие как AES, RSA, SHA, и многие другие.
Симметричное шифрование AES
AES — это стандарт симметричного шифрования, принятый в качестве стандарта федерального правительства США в 2001 году.AES поддерживает различные длины ключей, и на сегодняшний день является одним из самых используемых стандартов.
Чтобы приступить к работе с AES в Rust, необходимо установить соответствующие библиотеки RustCrypto. Для этого открываем файл Cargo.toml
и добавляем следующие зависимости:
[dependencies]
aes = "0.7"
aes-gcm = "0.10" # для работы с режимом GCM
rand = "0.8" # для генерации случайных значений
Начнем с того, как зашифровать текстовые данные с помощью AES-256-GCM:
use aes_gcm::aead::{Aead, KeyInit, OsRng};
use aes_gcm::{Aes256Gcm, Nonce}; // AES-256-GCM
use rand::Rng;
fn encrypt_data(plaintext: &[u8], key: &[u8]) -> Vec<u8> {
// создаем шифратор с ключом
let cipher = Aes256Gcm::new(key.into());
// генерируем случайный nonce
let nonce = Nonce::from_slice(&rand::thread_rng().gen::<[u8; 12]>());
// шифруем данные
let ciphertext = cipher.encrypt(nonce, plaintext)
.expect("Ошибка шифрования!");
// возвращаем зашифрованные данные и nonce
[nonce.as_slice(), &ciphertext].concat()
}
fn main() {
// пример открытого текста
let plaintext = b"Привет, Хабр! Это секретное сообщение.";
// генерируем ключ
let key = rand::thread_rng().gen::<[u8; 32]>(); // 256-битный ключ
// шифруем данные
let ciphertext = encrypt_data(plaintext, &key);
println!("Зашифрованные данные: {:?}", ciphertext);
}
После шифрования данных, следующий шаг – дешифрование:
use aes_gcm::aead::{Aead, KeyInit};
use aes_gcm::{Aes256Gcm, Nonce};
fn decrypt_data(ciphertext: &[u8], key: &[u8]) -> Vec<u8> {
// создаем дешифратор с ключом
let cipher = Aes256Gcm::new(key.into());
// извлекаем nonce (первые 12 байт)
let nonce = Nonce::from_slice(&ciphertext[..12]);
// извлекаем зашифрованные данные
let ciphertext = &ciphertext[12..];
// дешифруем данные
let plaintext = cipher.decrypt(nonce, ciphertext)
.expect("Ошибка дешифрования!");
plaintext
}
fn main() {
// пример зашифрованных данных
let ciphertext = vec![/* ваши зашифрованные данные */];
// используем ранее сгенерированный ключ
let key = [/* ваш 256-битный ключ */];
// дешифруем данные
let plaintext = decrypt_data(&ciphertext, &key);
println!("Дешифрованные данные: {:?}", String::from_utf8_lossy(&plaintext));
}
Необходимо убедиться, что ключи хранятся в защищенном месте и недоступны для посторонних. В Rust, генерация ключей может быть выполнена с помощью либы rand
:
use rand::Rng;
fn generate_key() -> [u8; 32] {
rand::thread_rng().gen::<[u8; 32]>() // генерируем 256-битный ключ
}
fn main() {
let key = generate_key();
println!("Сгенерированный ключ: {:?}", key);
}
Асимметричное шифрование
Асимметричное шифрование использует пару ключей: публичный ключ для шифрования данных и приватный ключ для их расшифровки. Основная фича заключается в том, что данные, зашифрованные публичным ключом, могут быть расшифрованы только соответствующим приватным ключом, и наоборот.
RSA — это один из самых известных и алгоритмов асимметричного шифрования. Рассмотрим его реализую в RustCrypto.
Принципы работы RSA:
Генерация ключей: В основе RSA лежит математика с использованием больших простых чисел. Сначала выбираются два случайных простых числа, которые перемножаются для получения модуля . Публичный ключ состоит из модуля и экспоненты , а приватный ключ включает модули и экспоненту , получаемую из и произведения .
Шифрование: Сообщение преобразуется в число, которое возводится в степень
e
и берётся по модулюn
, чтобы получить зашифрованное сообщение.Дешифрование: Зашифрованное сообщение возводится в степень
d
и берется по модулюn
для получения оригинального сообщения.
Для того чтобы начать работу с RSA в Rust, понадобится библиотека rsa
, которая входит в состав RustCrypto.
Откроем файл Cargo.toml
и добавим:
[dependencies]
rsa = "0.6.0" # версия библиотеки RSA
rand = "0.8" # для генерации случайных чисел
Начнем с генерации пары ключей:
use rsa::{RsaPrivateKey, RsaPublicKey, PaddingScheme};
use rand::rngs::OsRng; // используем надежный генератор случайных чисел
fn generate_keys() -> (RsaPrivateKey, RsaPublicKey) {
// генерируем 2048-битный приватный ключ
let mut rng = OsRng;
let private_key = RsaPrivateKey::new(&mut rng, 2048)
.expect("Ошибка генерации ключа");
// получаем публичный ключ из приватного
let public_key = RsaPublicKey::from(&private_key);
(private_key, public_key)
}
Теперь зашифруем некоторые данные с использованием публичного ключа:
fn encrypt_message(public_key: &RsaPublicKey, message: &[u8]) -> Vec<u8> {
let mut rng = OsRng;
let padding = PaddingScheme::new_pkcs1v15_encrypt(); // используем PKCS1 v1.5 для шифрования
// шифруем сообщение
public_key
.encrypt(&mut rng, padding, message)
.expect("Ошибка шифрования")
}
fn main() {
// генерируем ключи
let (private_key, public_key) = generate_keys();
// исходное сообщение
let message = b"Привет, Хабр!";
// зашифровываем сообщение
let encrypted_data = encrypt_message(&public_key, message);
println!("Зашифрованное сообщение: {:?}", encrypted_data);
}
Теперь расшифруем данные с использованием приватного ключа:
fn decrypt_message(private_key: &RsaPrivateKey, encrypted_data: &[u8]) -> Vec<u8> {
let padding = PaddingScheme::new_pkcs1v15_encrypt();
// дешифруем сообщение
private_key
.decrypt(padding, encrypted_data)
.expect("Ошибка дешифрования")
}
fn main() {
// генерируем ключи
let (private_key, public_key) = generate_keys();
// исходное сообщение
let message = b"Привет, Хабр!";
// зашифровываем сообщение
let encrypted_data = encrypt_message(&public_key, message);
// дешифровываем сообщение
let decrypted_data = decrypt_message(&private_key, &encrypted_data);
println!("Расшифрованное сообщение: {:?}", String::from_utf8_lossy(&decrypted_data));
}
Симметричное и асимметричное шифрование имеют свои особенности и подходят для разных задач.
Особенность |
Симметричное шифрование |
Асимметричное шифрование |
---|---|---|
Ключи |
Один ключ для шифрования и дешифрования |
Два ключа: публичный для шифрования, приватный для дешифрования |
Скорость |
Быстрее |
Медленнее |
Использование |
Шифрование данных, где важна скорость |
Обмен ключами, цифровые подписи |
Управление ключами |
Требует безопасного обмена ключами |
Не требует обмена приватным ключом |
Целостность и аутентификация |
Дополнительные механизмы, например, HMAC |
Цифровые подписи встроены |
HMAC
HMAC— это криптографический механизм, используемый для проверки целостности и аутентификации сообщений. HMAC строится на основе хэш-функций и симметричного ключа, обеспечивая защиту от атак, таких как подмена данных. В
Чтобы начать работу с HMAC, добавляем следующие зависимости в Cargo.toml
:
[dependencies]
hmac = "0.12" # для работы с HMAC
sha2 = "0.10" # для хэш-функций SHA-2
rand = "0.8" # для генерации случайных значений
Реализация HMAC с использованием SHA-256:
use hmac::{Hmac, Mac, NewMac};
use sha2::Sha256;
// создаем тип HMAC с SHA-256
type HmacSha256 = Hmac<Sha256>;
fn create_hmac(key: &[u8], data: &[u8]) -> Vec<u8> {
// создаем новый HMAC
let mut mac = HmacSha256::new_from_slice(key)
.expect("Ошибка создания HMAC");
// пишем данные в HMAC
mac.update(data);
// финализируем HMAC и извлекаем хеш-код
mac.finalize().into_bytes().to_vec()
}
fn main() {
// пример ключа и данных
let key = b"секретный_ключ";
let data = b"Привет, Хабр! Это важное сообщение.";
// генерируем HMAC
let hmac_code = create_hmac(key, data);
println!("HMAC: {:?}", hmac_code);
}
Проверим целостность сообщения с HMAC:
fn verify_hmac(key: &[u8], data: &[u8], expected_hmac: &[u8]) -> bool {
// создаем новый HMAC
let mut mac = HmacSha256::new_from_slice(key)
.expect("Ошибка создания HMAC");
// пишем данные в HMAC
mac.update(data);
// пытаемся проверить HMAC
mac.verify_slice(expected_hmac).is_ok()
}
fn main() {
// пример ключа и данных
let key = b"секретный_ключ";
let data = b"Привет, Хабр! Это важное сообщение.";
// генерируем HMAC
let hmac_code = create_hmac(key, data);
// проверяем целостность данных
if verify_hmac(key, data, &hmac_code) {
println!("Целостность данных подтверждена!");
} else {
println!("Целостность данных нарушена!");
}
}
Подробнее о RustCrypto можно узнать здесь.
Больше про все аспекты программирования эксперты OTUS рассказывают в рамках практических онлайн-курсов. С полным каталогом курсов можно ознакомиться по ссылке.
ALapinskas
Вроде как устареший и небезопасный.
domix32
Вики говорит, что одна из схем шифрования полтарашной версии уявима. При этом схема подписи этой же версии и схемы их кодирования вполне себе устойчивы.