TripleDES Java Encryption для расшифровки Javascript

Я использую Java для шифрования текстовой полезной нагрузки с помощью Triple DES. Сначала я создаю эфемерный ключ, который буду использовать для шифрования полезной нагрузки:

private byte[] createEphemeralKey() throws Exception {
    KeyGenerator keygen = KeyGenerator.getInstance("DESede");
    keygen.init(168);
    return keygen.generateKey().getEncoded();
}

Затем я шифрую свою полезную нагрузку указанным ключом:

private String encryptTripleDES(byte[] ephemeralKey, String payload) throws Exception {

    Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(ephemeralKey, "DESede"));

    byte[] plainTextBytes = payload.getBytes();
    byte[] cipherText = cipher.doFinal(plainTextBytes);
    return Base64.getEncoder().encodeToString(cipherText);
}

Также нужна функция заполнения, чтобы длина данных делилась на 8:

private String adjustPadding(String input, int blockSize) {

    int len = input.length() % blockSize;
    int paddingLength = (len == 0) ? 0 : (blockSize - len);

    while (paddingLength > 0) {
        input += "F";
        paddingLength--;
    }

    return input;
}

И вот мой процесс от начала до конца:

String data = "Marnus"
byte[] = ephemeralKey = createEphemeralKey();

String adjustedData = adjustPadding (data,8);

String encryptedPayload = encryptTripleDES(ephemeralKey, adjustedData);

String encodedKey = Base64.getEncoder().encodeToString(ephemeralKey)

Итак, я беру две переменные encryptedPayload и encodedKey, которые являются строками в кодировке Base64, и отправляю их через HTTP в экспресс-приложение узла.

Что касается Javascript, я использую node-forge. Вот часть моего экспресс-приложения, которое выполняет расшифровку:

let nodeBuffer = Buffer.from(data, 'base64')    
let input = forge.util.createBuffer(nodeBuffer.toString('binary'))

// 3DES key and IV sizes
let keySize = 24;
let ivSize = 8;

let derivedBytes = forge.pbe.opensslDeriveBytes(ephemeralKey, null, keySize + ivSize);
let buffer = forge.util.createBuffer(derivedBytes);
let key = buffer.getBytes(keySize)
let iv = buffer.getBytes(ivSize)

let decipher = forge.cipher.createDecipher('3DES-ECB', key)
decipher.start({iv: iv})
decipher.update(input)
console.log('decipher result', decipher.finish())

let decryptedResult = decipher.output.data;

Вот пример Triples DES в документации node-forge: введите здесь описание изображения

Несколько замечаний: я создаю буфер node-forge из обычного буфера, поскольку у меня нет входного файла, как в примерах. Вот как в документах говорится, что нужно создавать один буфер из другого: введите здесь описание изображения

* Я использую base64, так как это то, что я использовал на стороне java для кодирования отправленных данных.

Затем у меня нет соли, поэтому я оставил 2-й параметр null в opensslDeriveBytes, как указано в документах, которые я должен сделать.

В-третьих, я также не уверен, что мой размер ключа 24 правильный?

Мои результаты

Таким образом, выполнение сквозного теста дает следующее:

В моем приложении Java тестовые данные были "Marnus", encryptedPayload были ez+RweSAd+4=, а encodedKey были vCD9mBnWHPEBiQ0BGv7gc6GUCOoBgLCu.

Затем в моем коде javascript data было, очевидно, ez+RweSAd+4=(encryptedPayload), а ephemeralKey было vCD9mBnWHPEBiQ0BGv7gc6GUCOoBgLCu(encodedKey).

После запуска расшифровки значение decryptedResult было ©ýÕ?µ{', что, очевидно, просто мусор, поскольку оно еще не было закодировано, но я не могу понять, какую кодировку использовать?

Я пытался использовать forge.util.encode64(decipher.output.data), но это дало мне только qf3VP7UYeyc=, что неправильно.

Что бы это ни стоило, вот тот тип, который decipher.output


person Marnus Steyn    schedule 23.09.2020    source источник
comment
Просто быстрый ответ: на стороне Java нет функции DeriveBytes - есть только массив байтов, непосредственно взятый из функции generateKey. Типичный ключ DES имеет длину 8 байтов, поэтому длина Triple DES составляет 3 * 8 = 24 байта.   -  person Michael Fehr    schedule 23.09.2020
comment
Java-код не компилируется из-за некоторых ошибок. Более того, ваши тестовые данные кажутся непоследовательными: вы не используете отступы (NoPadding). Тогда длина открытого текста должна быть целым числом, кратным размеру блока (8 байтов для 3DES). Но открытый текст Marnus имеет длину всего 6 байт. Это вызывает ошибку времени выполнения. Кроме того, кодировка не определена при кодировании (getBytes(???)). Исправьте эти проблемы для определенной отправной точки. Спасибо.   -  person user 9014097    schedule 23.09.2020
comment
Я обновил пост, мой плохой, забыл функцию заполнения. Кроме того, разместил и ответ как решение моей проблемы :)   -  person Marnus Steyn    schedule 29.09.2020


Ответы (1)


С гораздо большей настройкой и тестированием различных параметров у меня все заработало, и хорошая новость заключается в том, что мне удалось заставить все это работать со встроенной библиотекой crypto в nodejs (v12.18.4).

Перво-наперво, стороне JAVA просто нужно изменить размер ключа (со 168 до 112), остальное остается прежним - см. пример ниже как один единственный метод (должен быть разделен в окончательной реализации, конечно, для тестируемости и удобства использования) :

//Some data:
String payload = "{\"data\":\"somedata\"}";

// Create Key
KeyGenerator keygen = KeyGenerator.getInstance("DESede");
keygen.init(112);
byte[] ephemeralKey = keygen.generateKey().getEncoded();

// Adjust the data, see adjustPadding method in the question for details.
String data = adjustPadding (payload,8);
// Wil now be "{"data":"somedata"}FFFFF", can just chop off extra in JS if need be. When sending JSON one knows the end of the object will always be "}"

// Do Encrypt
Cipher cipher = Cipher.getInstance("DESede/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(ephemeralKey, "DESede"));

byte[] plainTextBytes = data.getBytes();
byte[] cipherText = cipher.doFinal(plainTextBytes);
String encryptedPayload = Base64.getEncoder().encodeToString(cipherText);

//Lastly, Base64 the key so you can transport it too
String encodedKey = Base64.getEncoder().encodeToString(ephemeralKey)

со стороны Javascript мы делаем это просто:

// I'm using TS, so change the import if you do plain JS
import crypto = require('crypto')

//need bytes from the base64 payload
let buff = Buffer.from(ephemeralKey, 'base64')

const decipher = crypto.createDecipheriv('des-ede3', buff, null)
decipher.setAutoPadding(false)
let decrypted = decipher.update(data, 'base64', 'utf8')
decrypted += decipher.final('utf8')

console.log(decrypted)
//{"data":"somedata"}FFFFF"
person Marnus Steyn    schedule 29.09.2020