AES-CBC (Advanced Encryption Standard in Cipher Block Chaining mode) в Web Crypto API реализует блочный симметричный шифр, в котором каждый блок открытого текста перед шифрованием комбинируется с предыдущим зашифрованным блоком. Это устраняет повторяемость одинаковых блоков данных и повышает устойчивость к анализу структуры сообщения.
В браузерной реализации используется интерфейс
crypto.subtle, где AES-CBC доступен как один из режимов
алгоритма AES.
Режим CBC работает по следующему принципу:
Формально:
C1 = AES(K, P1 ⊕ IV)
C2 = AES(K, P2 ⊕ C1)
C3 = AES(K, P3 ⊕ C2)
...
где:
P — блоки открытого текста,C — блоки шифротекста,K — секретный ключ,IV — вектор инициализации.AES-CBC реализуется через SubtleCrypto:
encryptdecryptgenerateKeyimportKeyАлгоритм задаётся структурой:
{
name: "AES-CBC",
iv: ...
}
Ключи могут иметь длину:
Вектор инициализации является критически важной частью AES-CBC.
Свойства IV:
IV предотвращает детерминированность шифрования: одинаковые входные данные при одинаковом ключе дают разные результаты.
Пример генерации:
const iv = crypto.getRandomValues(new Uint8Array(16));
AES — блочный алгоритм, и Web Crypto API в режиме CBC не выполняет автоматическое дополнение данных.
Это означает:
На практике используется PKCS#7.
Если нужно добавить n байт, каждый байт имеет значение
n.
Пример:
Исходные данные: "HELLO" (5 байт)
Блок: 16 байт
Добавляется: 11 байт со значением 0x0B
function pkcs7Pad(data) {
const blockSize = 16;
const pad = blockSize - (data.length % blockSize);
const result = new Uint8Array(data.length + pad);
result.set(data);
result.fill(pad, data.length);
return result;
}
function pkcs7Unpad(data) {
const pad = data[data.length - 1];
return data.slice(0, data.length - pad);
}
const key = await crypto.subtle.generateKey(
{
name: "AES-CBC",
length: 256
},
true,
["encrypt", "decrypt"]
);
const iv = crypto.getRandomValues(new Uint8Array(16));
const encoder = new TextEncoder();
const plaintext = encoder.encode("secret message");
const padded = pkcs7Pad(plaintext);
const ciphertext = await crypto.subtle.encrypt(
{
name: "AES-CBC",
iv
},
key,
padded
);
const decrypted = await crypto.subtle.decrypt(
{
name: "AES-CBC",
iv
},
key,
ciphertext
);
const unpadded = pkcs7Unpad(new Uint8Array(decrypted));
const decoder = new TextDecoder();
const result = decoder.decode(unpadded);
Так как IV не секретен, его обычно добавляют в начало сообщения:
function concat(iv, data) {
const result = new Uint8Array(iv.length + data.length);
result.set(iv);
result.set(new Uint8Array(data), iv.length);
return result;
}
При расшифровке:
const iv = data.slice(0, 16);
const ciphertext = data.slice(16);
ArrayBuffer /
TypedArray;TextEncoder;Использование одного IV с одним ключом приводит к утечке информации о структуре данных.
Попытка шифровать данные, длина которых не кратна 16 байтам, вызывает ошибку выполнения.
AES-CBC не обеспечивает целостность данных. Возможны атаки с подменой блоков (bit flipping attack).
Для защиты обычно добавляют:
Без корректного хранения IV расшифровка невозможна, даже при наличии ключа.
Типичная схема хранения данных:
[ IV (16 байт) ][ ciphertext (n байт) ]
или расширенная:
[ IV ][ ciphertext ][ HMAC ]
AES-CBC используется в системах, где требуется совместимость или контроль над процессом шифрования, однако в современных приложениях чаще предпочтение отдаётся режимам с аутентификацией, поскольку CBC требует дополнительного слоя защиты целостности данных.