Блочные алгоритмы шифрования работают с фиксированным размером блока данных. Например, AES оперирует блоками по 16 байт. При обработке произвольного текста возникает проблема: длина входных данных редко кратна размеру блока. Для корректного шифрования используется дополнение (padding), приводящее данные к необходимой длине.
PKCS#7 (Public-Key Cryptography Standards #7 padding) определяет универсальный способ дополнения данных для блочных алгоритмов. Его особенность заключается в простоте и однозначности восстановления исходного сообщения.
Алгоритм дополняет данные байтами, каждый из которых равен количеству добавленных байтов.
Пусть размер блока равен N, а длина сообщения —
L. Тогда вычисляется:
paddingLength = N - (L % N)Если сообщение уже кратно блоку, добавляется полный блок заполнения
длиной N.
Каждый байт padding имеет значение paddingLength.
Исходные данные (ASCII):
HELLO
Размер блока: 8 байт Длина сообщения: 5 байт Padding: 3 байта
Результат:
HELLO\x03\x03\x03
Каждый добавленный байт равен 0x03.
В библиотеке CryptoJS PKCS#7 используется как padding по умолчанию для блочных шифров.
Внутренняя логика реализуется через объект:
CryptoJS.pad.Pkcs7
Он применяется автоматически при использовании режимов CBC, ECB и других блочных режимов.
const CryptoJS = require("crypto-js");
const message = "HELLO";
const key = CryptoJS.enc.Utf8.parse("1234567890123456");
const encrypted = CryptoJS.AES.encrypt(message, key, {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: CryptoJS.enc.Utf8.parse("1234567890123456")
});
console.log(encrypted.toString());
PKCS#7 применяется автоматически для выравнивания строки до размера блока AES (16 байт).
const decrypted = CryptoJS.AES.decrypt(encrypted, key, {
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: CryptoJS.enc.Utf8.parse("1234567890123456")
});
console.log(decrypted.toString(CryptoJS.enc.Utf8));
После расшифровки библиотека удаляет PKCS#7 padding, используя значение последнего байта.
PKCS#7 в CryptoJS реализован через два основных метода:
pad: добавление байтовunpad: удаление байтовЛогика добавления:
pad: function (data, blockSize) {
const blockSizeBytes = blockSize * 4;
const padding = blockSizeBytes - (data.sigBytes % blockSizeBytes);
const paddingWord = (padding << 24) | (padding << 16) | (padding << 8) | padding;
const paddingWords = [];
for (let i = 0; i < padding; i += 4) {
paddingWords.push(paddingWord);
}
data.concat(CryptoJS.lib.WordArray.create(paddingWords, padding));
}
Логика удаления:
unpad: function (data) {
const padding = data.words[(data.sigBytes - 1) >>> 2] & 0xff;
data.sigBytes -= padding;
}
CryptoJS использует внутренний формат WordArray, где
данные хранятся 32-битными словами. PKCS#7 работает на уровне байтов,
поэтому происходит преобразование между байтовым и словесным
представлением.
Ключевой момент:
sigBytes определяет фактическую длину данныхPKCS#7 используется в CryptoJS для всех блочных режимов:
В потоковых режимах (CTR, CFB, OFB) padding не применяется, так как они не требуют выравнивания блока.
Заполнение нулями приводит к неоднозначности при расшифровке, если исходные данные заканчиваются нулевыми байтами.
Последний байт содержит длину padding, но остальные байты заполнены нулями.
Каждый байт содержит значение длины padding, что делает схему полностью однозначной.
padding: CryptoJS.pad.NoPadding
Использование без выравнивания данных приводит к ошибкам:
При ручной обработке данных перед CryptoJS возможно случайное добавление padding дважды, что приводит к искажению результата при расшифровке.
Сам по себе PKCS#7 не является механизмом безопасности, он лишь обеспечивает корректное выравнивание. Однако его неправильная реализация может привести к уязвимостям:
PKCS#7 встроен в стандартные вызовы:
CryptoJS.AES.encrypt(data, key)
CryptoJS.AES.decrypt(cipher, key)
Если явно не указано иное, используется:
При расшифровке происходит:
Если padding повреждён, возникает ошибка или некорректный результат.
PKCS#7 корректно обрабатывает:
Важно учитывать, что padding добавляется на уровне байтов, а не символов.
PKCS#7 стал де-факто стандартом для симметричного блочного шифрования благодаря:
CryptoJS следует этому стандарту, обеспечивая совместимость с другими криптографическими реализациями, включая OpenSSL и Node.js crypto API в совместимых режимах.