RSA-шифрование как математическая операция само по себе не обеспечивает семантическую безопасность без корректного форматирования сообщения. Для этого используется схема заполнения PKCS#1 v1.5, которая преобразует исходные данные перед возведением в степень по модулю RSA.
При шифровании формируется блок:
EM = 0x00 || 0x02 || PS || 0x00 || M
где:
0x00 0x02 — фиксированные байты, обозначающие режим
шифрованияPS — случайная непустая строка заполнения (padding
string), состоящая из ненулевых байтов0x00 — разделительM — полезная нагрузка (сообщение)Ключевой момент заключается в том, что корректность структуры становится проверяемым свойством. Любое отклонение от формата может быть обнаружено при расшифровании.
При расшифровке:
M = RSA_decrypt(C)
реализация должна:
Если система различает:
то возникает утечка информации.
Такой механизм называется padding oracle — источник бинарной информации о корректности структуры сообщения.
Атака, предложенная Даниэлем Блайхенбахером, использует адаптивный выбор шифртекстов для восстановления исходного сообщения без знания приватного ключа.
Основная идея:
Cs, формируя новые
шифртексты:C' = (C * s^e) mod n
M' = (M * s) mod n
MАтака строится на постепенном сокращении интервала:
[M_min, M_max]
Каждый успешный oracle-запрос позволяет уточнить границы, используя математические свойства RSA:
0x00 0x02 ...В результате множество возможных значений сужается до единственного.
Слабость схемы заключается в том, что корректность легко проверяется:
0x00 0x020x00Любая реализация, которая возвращает различимые ошибки, фактически реализует oracle.
Библиотека Jsrsasign реализует RSA через объекты:
RSAKeyKJUR.crypto.CipherKEYUTILПример типичного использования:
const rsa = new RSAKey();
rsa.generate(2048, "10001");
const ciphertext = rsa.encrypt("secret");
const plaintext = rsa.decrypt(ciphertext);
При использовании PKCS#1 v1.5 padding:
В JavaScript-окружении проблема усиливается:
Уязвимость появляется, если:
try {
const msg = rsa.decrypt(ciphertext);
} catch (e) {
if (e.message.includes("decryption error")) {
// различимое поведение
}
}
или если:
null при ошибке paddingПсевдологика уязвимого дешифрования:
function decrypt(ciphertext) {
const m = rsaRawDecrypt(ciphertext);
if (!m.startsWith("\x00\x02")) {
throw new Error("padding error");
}
return stripPadding(m);
}
Такое поведение уже создаёт бинарный оракул:
Атака строится на многократных запросах:
sВ JavaScript это реализуется как цикл:
while (!solved) {
const s = findNextMultiplier();
const c2 = (c * modExp(s, e, n)) % n;
if (oracle(c2)) {
narrowInterval(s);
}
}
Где oracle() — функция, основанная на поведении
decrypt.
Даже если исключения не различаются, остаются:
Это превращает даже «мягкую» проверку в утечку информации.
Исторически атака была критичной для:
Современные библиотеки стараются исключать:
Основная замена PKCS#1 v1.5:
OAEP (Optimal Asymmetric Encryption Padding)
Преимущества:
В RSA это выглядит как:
RSA-OAEP(message) → ciphertext
и не допускает частичной проверки структуры.
Использование Jsrsasign в браузере создаёт дополнительные риски:
Таким образом, RSA в клиентском JS не должен использоваться для расшифрования секретов, особенно с PKCS#1 v1.5.
Критические требования к безопасной реализации:
Пример более безопасного поведения:
function decrypt(ciphertext) {
try {
return rsa.decryptOAEP(ciphertext);
} catch (e) {
throw new Error("decryption failed");
}
}
Атака остаётся актуальной из-за сочетания факторов:
Даже частичная реализация этих условий в JavaScript-криптографии приводит к уязвимостям.
При анализе безопасности следует учитывать:
Любая реализация RSA decrypt в такой среде должна рассматриваться как потенциально наблюдаемая функция, а значит — как источник утечек при PKCS#1 v1.5.