Шифрование Java AES в Javascript с использованием CryptoJS

Я пытаюсь воспроизвести шифрование, используемое в приложении Java, с помощью Javascript и CryptoJS. Я не совсем уверен, как мне следует реплицировать SecretKeySpec, поскольку похоже, что CryptoJS ожидает строку для ключа.

Вот код шифрования Java, который мне нужно воспроизвести в JS:

public byte[] encrypt(byte[] origin)
    {
        String key = "testkey";
        SecretKeySpec sks = new SecretKeySpec(convertAESKey(key), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] iv = new byte[16];
        cipher.init(Cipher.ENCRYPT_MODE, sks, new IvParameterSpec(iv));
        return cipher.doFinal(origin);
    }

private byte[] convertAESKey(String key)
   {
        byte[] keyBytes;
        keyBytes = key.getBytes("UTF-8");
        byte[] keyBytes16 = new byte[16];
        System.arraycopy(keyBytes, 0, keyBytes16, 0,
                Math.min(keyBytes.length, 16));
        return keyBytes16;
    }
}

Мой код JS на данный момент:

var iv = new Uint8Array(16);
var key = "testkey";

var encrypted = CryptoJS.AES.encrypt(
    someByteArray,
    key,
    {
        iv: iv,
        mode: CryptoJS.mode.CBC
    }
);

Кроме того, окончательный вывод cipher - это зашифрованный массив байтов. Окончательный вывод CryptoJS выглядит как объект с cipherText. Есть ли способ получить вывод в виде байтового массива?

Единственное, что я мог сейчас придумать, - это преобразовать зашифрованный ответ в строку, а затем преобразовать ее в массив байтов, но, похоже, это не соответствует выходу шифрования Java.


person mildse7en    schedule 05.08.2016    source источник
comment
Использование шифрования из коробки работает для меня без проблем. Проблема заключается в репликации шифрования Java, поскольку текущая реализация - это от клиента (Java) к серверу (Java), и мне нужно сделать его клиентом (Javascript) на сервере (Java). Код сервера не может измениться, поэтому важно точно соответствовать шифрованию.   -  person mildse7en    schedule 06.08.2016


Ответы (1)


CryptoJS не обрабатывает Uint8Array по умолчанию, поэтому я не собираюсь его использовать, а вместо этого буду использовать WordArray по умолчанию для представления двоичных данных.

var iv = CryptoJS.lib.WordArray.create([0, 0, 0, 0]);
var key = "testkey";

key = CryptoJS.enc.Utf8.parse(key);
for (var i = key.words.length; i < 4; i++) {
    key.words[i] = 0;
}
key.sigBytes = 16;
key.clamp();

var encrypted = CryptoJS.AES.encrypt(
    someByteArray,
    key,
    {
        iv: iv,
        mode: CryptoJS.mode.CBC
    }
);
console.log(encrypted.ciphertext.toString(CryptoJS.enc.Hex));

Это даст шестнадцатеричную строку, которую можно легко сравнить с зашифрованным текстом, созданным Java.


Этот код не очень безопасен:

  • IV должен быть непредсказуемым (читай: случайным). Не используйте статический IV, потому что это делает шифр детерминированным и, следовательно, не семантически безопасным. Злоумышленник, который наблюдает за шифротекстами, может определить, когда тот же префикс сообщения был отправлен ранее. IV не является секретным, поэтому вы можете отправить его вместе с зашифрованным текстом. Обычно он просто добавляется к зашифрованному тексту и обрезается перед расшифровкой.

  • Ключ должен быть случайным (читай: похоже на случайный шум). Текст не выглядит случайным, и вы серьезно снизите свою безопасность, если (неверно) будете использовать пароль в качестве ключа. Если у вас есть пароль, вам нужно получить ключ из него, используя что-то вроде PBKDF2, bcrypt, scrypt или Argon2 с большим количеством итераций / фактором стоимости.

  • Вы должны аутентифицировать свои зашифрованные тексты, чтобы обнаружить (злонамеренные) манипуляции. Если вы этого не сделаете, то это может быть уязвимо для атаки оракула заполнения. Аутентификация может быть выполнена с использованием аутентифицированного режима работы, такого как GCM или EAX, или с использованием схемы шифрования, затем MAC с сильным MAC, такой как HMAC-SHA256.

person Artjom B.    schedule 06.08.2016
comment
Спасибо за это, я пытаюсь использовать это, и я получаю, что undefined не является объектом (оценка 'e.length') - person mildse7en; 09.08.2016
comment
Вы не сказали, что такое someByteArray, поэтому я предположил, что это либо строка, либо WordArray. Если это Uint8Array, вы можете включить файл lib-typedarrays.js, чтобы получить поддержку. - person Artjom B.; 09.08.2016
comment
Я попытался включить файл lib-typedarrays.js и по-прежнему получаю ту же ошибку. Я пытаюсь кодировать массив Uint8Array [120, 156, 203, 72, 205, 201, 201, 7, 0, 6, 44 , 2, 21] - person mildse7en; 12.08.2016
comment
Массив, который вы здесь показываете, слишком короткий, чтобы быть допустимым зашифрованным текстом. В любом случае вам может потребоваться преобразовать его самостоятельно с Uint8Array на WordArray. Включите lib-typedarrays.js, а затем преобразуйте его с помощью someByteArray = CryptoJS.lib.WordArray.create(someByteArray); - person Artjom B.; 12.08.2016