Криптографические алгоритмы сами по себе стандартизированы, но их реализации в разных экосистемах часто отличаются деталями, которые критически влияют на совместимость. При использовании Crypto-js на стороне JavaScript и серверных библиотек (PHP, Java, Python, Go) основная проблема заключается не в алгоритмах, а в представлении данных, режимах работы и параметрах ключей.
Ключевые факторы, определяющие совместимость:
Crypto-js оперирует внутренним типом WordArray, который
не совпадает напрямую с бинарными типами серверных языков. При передаче
данных между системами чаще всего используются:
Типичная ошибка совместимости возникает при смешивании кодировок:
CryptoJS.enc.Utf8mb_convert_encoding,
openssl_encryptbytes() / .encode('utf-8')Даже при одинаковом алгоритме различие в кодировке приводит к полностью несовместимому шифртексту.
Наиболее часто используется AES-128/192/256. В Crypto-js базовый пример выглядит следующим образом:
const CryptoJS = require("crypto-js");
const key = CryptoJS.enc.Utf8.parse("1234567890123456");
const iv = CryptoJS.enc.Utf8.parse("1234567890123456");
const encrypted = CryptoJS.AES.encrypt("message", key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
const result = encrypted.toString();
На серверной стороне эквивалент должен строго повторять:
Пример PHP:
$data = "message";
$key = "1234567890123456";
$iv = "1234567890123456";
$encrypted = openssl_encrypt(
$data,
"AES-128-CBC",
$key,
OPENSSL_RAW_DATA,
$iv
);
echo base64_encode($encrypted);
Ключевой момент — Crypto-js при toString() возвращает
Base64, тогда как PHP по умолчанию может возвращать бинарные данные.
При использовании пароля вместо ключа Crypto-js часто генерирует OpenSSL-совместимый формат:
Salted__ + salt + ciphertext
Это поведение возникает при:
CryptoJS.AES.encrypt("message", "password").toString();
В этом случае:
На серверной стороне это вызывает несовместимость, если используется:
AES-256-CBC с ручным ключомPHP может расшифровать такой формат только при ручной реализации EVP-подобного KDF или использовании совместимых библиотек.
Crypto-js поддерживает PBKDF2:
const key = CryptoJS.PBKDF2("password", salt, {
keySize: 256 / 32,
iterations: 1000
});
Серверные языки:
hash_pbkdf2SecretKeyFactory PBKDF2WithHmacSHA256hashlib.pbkdf2_hmacНесовместимость возникает, когда:
EVP_BytesToKey не имеет параметра итераций в классическом виде, что делает результаты несовместимыми при одинаковом пароле.
IV должен:
Ошибка типична при:
Crypto-js:
CryptoJS.enc.Utf8.parse("1234567890123456")
PHP:
$iv = "1234567890123456";
Java:
new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
Наиболее проблемные режимы:
Crypto-js:
Особенность GCM:
Crypto-js использует:
Pkcs7 padding по умолчаниюСерверные аналоги:
Несовпадение padding приводит к ошибкам:
Для HMAC и SHA Crypto-js обычно совместим без проблем, так как отсутствует сложная бинарная упаковка.
Пример:
CryptoJS.HmacSHA256("message", "key").toString()
PHP:
hash_hmac("sha256", "message", "key");
Java:
Mac.getInstance("HmacSHA256");
Python:
hmac.new(key, msg, hashlib.sha256).hexdigest()
Критический момент — формат вывода:
Проблема: различие в дефолтных опциях
openssl_encrypt
строгая типизация
явное указание трансформации:
"AES/CBC/PKCS5Padding"требует ручного Base64 декодирования
Проблема: отсутствие OpenSSL salted формата
Для стабильной совместимости между Crypto-js и серверными языками обычно фиксируются:
Такой набор параметров позволяет добиться детерминированного результата между JavaScript и серверными платформами без зависимости от внутренней реализации конкретной библиотеки