Совместимость между реализациями криптографии в JavaScript и OpenSSL осложняется тем, что они используют разные уровни абстракции и разные форматы представления зашифрованных данных.
OpenSSL ориентирован на потоковый формат командной строки и бинарные структуры с минимальной метаинформацией. SJCL (Stanford Javascript Crypto Library) напротив оперирует высокоуровневым JSON-форматом, в котором хранится вся информация о параметрах шифрования.
Ключевая проблема совместимости заключается не в самом AES, а в:
При использовании openssl enc данные обычно имеют
следующий вид:
Salted__ + 8 байт соли + зашифрованный текст
Пример команды:
openssl enc -aes-256-cbc -salt -pbkdf2 -in file.txt -out file.enc -a
Особенности:
префикс Salted__ (8 байт)
соль (8 байт)
дальше ciphertext
часто применяется base64 (-a)
KDF зависит от параметров:
EVP_BytesToKeyPBKDF2 (если указан
-pbkdf2)SJCL использует JSON-структуру:
{
"iv": "...",
"v": 1,
"iter": 1000,
"ks": 256,
"ts": 64,
"mode": "ccm",
"adata": "",
"cipher": "aes",
"salt": "...",
"ct": "..."
}
Особенности:
key = EVP_BytesToKey(password, salt, md5, iterations=1)
Особенности:
-pbkdf2 -iter 10000
Используется:
PBKDF2-HMAC-SHA256(password, salt, iter)
SJCL использует:
PBKDF2-HMAC-SHA256 (по умолчанию)
с параметрами:
iv поле)Поддержка через -aes-256-cbc, -aes-128-cbc
и т.д.
Поддерживает:
Критический момент:
Чтобы SJCL мог расшифровать данные OpenSSL:
openssl enc -aes-256-cbc -salt -pbkdf2 -iter 10000 -in msg.txt -out msg.enc -a
OpenSSL формат нужно вручную разобрать:
function opensslDecrypt(base64, password) {
const raw = sjcl.codec.base64.toBits(base64);
const bytes = sjcl.codec.bytes.fromBits(raw);
const saltHeader = bytes.slice(0, 8);
const salt = bytes.slice(8, 16);
const ciphertext = bytes.slice(16);
const keyAndIv = sjcl.misc.pbkdf2(password, sjcl.codec.bytes.toBits(salt), 10000, 256 + 128);
const key = keyAndIv.slice(0, 8);
const iv = keyAndIv.slice(8, 12);
const decrypted = sjcl.mode.cbc.decrypt(
new sjcl.cipher.aes(key),
sjcl.codec.bytes.toBits(ciphertext),
iv
);
return sjcl.codec.utf8String.fromBits(decrypted);
}
SJCL шифрует в JSON, поэтому OpenSSL не может напрямую прочитать результат.
Пример SJCL:
const encrypted = sjcl.encrypt("password", "secret text", {
iter: 10000,
ks: 256,
mode: "cbc"
});
Результат:
{"iv":"...","salt":"...","ct":"..."}
Извлекаются:
Далее нужно собрать формат OpenSSL:
Salted__ + salt + ciphertext
и затем:
openssl enc -aes-256-cbc -d -salt -pbkdf2 -in file.enc
-a)Это создаёт несоответствие:
Критический момент при совместимости:
bitArray)Ошибки возникают при:
Рекомендуемая модель:
utf8String.fromBitsОба решения используют PKCS#7 в CBC режиме:
Несовместимость возникает только при:
OpenSSL:
-pbkdf2 -iter 10000
SJCL:
iter: 10000
mode: "cbc"-aes-256-cbcОн создаёт фундаментальную несовместимость с SJCL.
Наиболее стабильная схема взаимодействия:
При соблюдении этих условий SJCL и OpenSSL могут работать в рамках одной криптографической схемы без потери совместимости.