Шифрование и дешифрование между Java и Javascript не будут работать - PullRequest
0 голосов
/ 21 мая 2018

РЕДАКТИРОВАТЬ 1

В методе decryptFile часть дешифрования ничего не выводит ..

let decrypted = CryptoJS.AES.decrypt(e.target.result, CryptoJS.enc.Utf8.parse(key), {
    iv: CryptoJS.enc.Utf8.parse(iv),
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
});

РЕДАКТИРОВАТЬ 2 Ссылка , указанная в разделе комментариев, частично решает проблему.Он зашифровывает и дешифрует кроссплатформенность, но он довольно медленный из-за PBKDF2 с хэшированием SHA256.Я не могу найти способ использовать только часть AES, а не часть PKBDF2.

ОРИГИНАЛЬНЫЙ ТЕКСТ

Я использую один и тот же ключ и IV для версий Java и Javascript.Я не могу расшифровать файл в Javascript, который был зашифрован в Java, и я не могу расшифровать файл в Java, который был зашифрован в Javascript.Мне нужно, чтобы эти два были совместимы друг с другом, но я не могу понять, как я пытаюсь расшифровать файл в Javascript, который был ранее зашифрован в Java.Я успешно реализовал дешифрование и шифрование текста между ними, но когда мне нужно, например, расшифровать файл, зашифрованный на Java, он просто не будет работать.

Шифрование / дешифрование файла на Java:

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Test {
    private SecretKey secretKey;
    private IvParameterSpec ivParameterSpec;

    private String key = "ThisIsMyGreatKey";
    private byte[] ivKey = "ABCDEFGHabcdefgh".getBytes();

    public static void main(String[] args) {
        try {
            new Test().run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void run() {
        ivParameterSpec = new IvParameterSpec(ivKey);
        secretKey = new SecretKeySpec(key.getBytes(), "AES");
        encryptOrDecryptFile(Cipher.ENCRYPT_MODE,
            new File("src/cactus.jpg"), new File("src/cactus-encrypted.jpg"));
    }

    private void encryptOrDecryptFile(int mode, File inputFile, File outputFile) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(mode, secretKey, ivParameterSpec);

            // Read input
            byte[] input = new byte[(int) inputFile.length()];
            FileInputStream inputStream = new FileInputStream(inputFile);
            inputStream.read(input);

            // Encrypt and write to output
            byte[] output = cipher.doFinal(input);
            FileOutputStream outputStream = new FileOutputStream(outputFile);
            outputStream.write(output);

            inputStream.close();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Шифрование / дешифрование в Javascript

<input type="file" id="file-input" onchange="handleFile(this)">
<button onclick="useEncryptionForFile()" id="encrypt-file">Encrypt File</button>
<button onclick="useDecryptionForFile()" id="decrypt-file">Decrypt File</button>
<textarea id="output"></textarea>
<img id="example">

<script>
    let key = "ThisIsMyGreatKey";
    let iv = "ABCDEFGHabcdefgh";
    let useEncryption, useDecryption;

    let input = document.getElementById("file-input");
    let output = document.getElementById("output");
    let example = document.getElementById("example");

    function handleFile(element) {
        if (element.files && element.files[0]) {
            let file = element.files[0];
            if (useDecryption) {
                decryptFile(file);
            } else {
                encryptFile(file);
            }
        }
    }

    function encryptFile(file) {
        let reader = new FileReader();

        reader.onload = function (e) {
            let encrypted = CryptoJS.AES.encrypt(e.target.result, CryptoJS.enc.Utf8.parse(key), {
                iv: CryptoJS.enc.Utf8.parse(iv),
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Pkcs7
            });

            output.textContent = encrypted;

            let a = document.createElement("a");
            a.setAttribute('href', 'data:application/octet-stream,' + encrypted);
            a.setAttribute('download', file.name + '.encrypted');
            a.click();
        };

        reader.readAsDataURL(file);
    }

    function decryptFile(file) {
        let reader = new FileReader();
        reader.onload = function (e) {
            let decrypted = CryptoJS.AES.decrypt(e.target.result, CryptoJS.enc.Utf8.parse(key), {
                iv: CryptoJS.enc.Utf8.parse(iv),
                mode: CryptoJS.mode.CBC,
                padding: CryptoJS.pad.Pkcs7
            });

             // Decrypted is emtpy    
            output.textContent = decrypted;

            // Desperate try to get something working
            example.src = "data:image/png;base64," + btoa(decrypted);

            let a = document.createElement("a");
            a.setAttribute('href', decrypted);
            a.setAttribute('download', file.name.replace('encrypted', 'decrypted'));
            a.click();
        };

        reader.readAsText(file);
    }

    function useEncryptionForFile() {
        document.getElementById("encrypt-file").style.backgroundColor = "#757575";
        document.getElementById("decrypt-file").style.backgroundColor = "#FFFFFF";
        useEncryption = true;
      useDecryption = false;
    }

    function useDecryptionForFile() {
        document.getElementById("encrypt-file").style.backgroundColor = "#FFFFFF";
        document.getElementById("decrypt-file").style.backgroundColor = "#757575";
        useDecryption = true;
      useEncryption = false;
    }
</script>    

Я также сделал Fiddle на случай, если вам понравится больше :), и исходный код Java можетбыть скачано здесь .

В исходном коде Java я использовал cactus.jpg в качестве файла, но можно использовать любой файл :).Кактус можно найти здесь .

Как мне расшифровать файл, зашифрованный на Java?Я попытался преобразовать содержимое BLOB-объекта в String, получить данные как ArrayBuffer и преобразовать их в String, получить их в виде текста и передать в метод расшифровки, но, похоже, ничего не работает.

Библиотека, которую я используюв Javascript есть CryptoJS , а в Java стандартные библиотеки Crypto.

Я нашел другие похожие ( 1 , 2 ) вопросы.Тем не менее, я думаю, что они слишком сильно различаются, поскольку ответ на эти вопросы касается не этой проблемы, а скорее маленькой ошибки.

Если я забыл какие-либо данные, пожалуйста, сообщите мне.

Ответы [ 3 ]

0 голосов
/ 08 июня 2018

Во-первых, попробуйте отправить простой зашифрованный текст из java в javascript или наоборот и проверьте, работает ли код.

Если код работает для простого текста, т. Е. Вы можете отправить зашифрованную строкуиз Java и успешно расшифровывает его в JavaScript или наоборот, то, что вы можете сделать, это Base64 кодировать зашифрованные байты / файл, а затем передать текст, а затем декодировать и расшифровать его на другом конце.

Если кодне работает для простого текста, тогда вы можете попробовать зашифровать простой текст в javascript и java независимо и проверить, совпадают ли результаты.Если нет, то есть некоторое несоответствие в логике шифрования / дешифрования между java и javascript.

РЕДАКТИРОВАТЬ:

Как вы упоминали, что код работает для String, у меня естьниже приведен пример преобразования файла в строку Base64 с использованием библиотеки общих кодеков apache в java.

private static String encodeFileToBase64Binary(String fileName) throws IOException {
    File file = new File(fileName);
    byte[] encoded = Base64.encodeBase64(FileUtils.readFileToByteArray(file));
    return new String(encoded, StandardCharsets.US_ASCII);
}

Теперь вы шифруете эту строку и отправляете ее в javascript.В javascript сначала расшифруйте строку, а затем преобразуйте ее в объект файла.

Например.

 function base64toFile(encodedstring,filename,mimeType){
   return new File([encodedstring.arrayBuffer()],filename, {type:mimeType});

}   

//Usage example:
base64toFile('aGVsbG8gd29ybGQ=', 'hello.txt', 'text/plain');
0 голосов
/ 11 июня 2018

Ссылка , указанная в разделе комментариев, частично решает проблему.Он зашифровывает и дешифрует кроссплатформенность, но он довольно медленный из-за PBKDF2 с хэшированием SHA256.Я не могу найти способ использовать только часть AES, а не часть PKBDF2.

Цель PBKDF2 - повернуть выбранный пользователем пароль (который обычно является переменнойдлина текстовой строки и редко содержит более нескольких десятков бит эффективной энтропии *) в ключе AES (который должен быть двоичной строкой ровно 128, 192 или 256 бит, в зависимости от варианта AESиспользуется, и должен иметь полную энтропию **, если вы хотите, чтобы шифр был таким же сильным, как и предполагалось).

Для этого ему необходимо, чтобы был медленным;единственный способ сделать так, чтобы угадать пароль с, скажем, 30-битной энтропией так же сложно, как угадать ключ AES с 128-битной энтропией, состоит в том, чтобы процесс преобразования пароля в ключ занял бы 2 времени.128 - 30 = 2 98 AES-шифрование.Конечно, это не совсем возможно, поэтому на практике люди склонны применять только несколько тысяч (≈ 2 10 ) к нескольким миллиардам (≈ 2 30 ) итераций PBKDF2 и надеюсь, что этого достаточно .Что может быть, если ваш счетчик итераций ближе к верхнему пределу диапазона, если пользователь достаточно умен и достаточно мотивирован, чтобы выбрать достаточно хороший пароль (скажем, случайную Diceware фразу-пароль вместо abc123 или pa$$w0rd) для начала, и если ваш противник не является АНБ.

В любом случае, смысл всего этого, , если вы хотите использовать шифрование на основе пароля , тогда вы в значительной степени должны использовать PBKDF2 (или что-то подобное ).Однако это не обязательно означает, что вам нужно запускать функцию медленного вывода ключа каждый раз, когда вы что-то шифруете или дешифруете.На самом деле, если вы знаете, что вам нужно зашифровать или расшифровать несколько файлов с одним и тем же паролем, гораздо эффективнее получить ключ AES из пароля один раз, а затем сохранить ключ AES в памяти столько, сколько вам нужно.Это.(Безопасное выполнение не является тривиальным, либо , но многие криптографические библиотеки будут справляться с этим, по крайней мере, достаточно хорошо, если вы используете их встроенные ключевые объекты для хранения ключей, и в любом случае это вряд лисамое слабое звено в безопасности вашего приложения.)

Другой вариант, конечно, совсем не использовать пароли, а просто генерировать (псевдо) случайный ключ AES (используя криптографически безопасный генератор случайных битовых строк)и хранить на всех устройствах, которым необходим доступ к нему.Опять же, конечно, самая сложная часть - надежное хранение ключа.

В частности, если вы выполняете шифрование на стороне клиента с помощью JavaScript в браузере, то просто встраиваете ключ в код JS (или где-либо еще на странице) предоставит его любому, у кого есть доступ к консоли разработчика браузера (и может также привести к тому, что ключ останется лежать, например, в кеше диска браузера).И, конечно, вы должны использовать HTTPS, так как в противном случае любой, например, прослушивающий на общедоступном Wi-Fi-соединении клиента, также может получить копию ключа.


Ps.Вы могли заметить, что я на самом деле не включил код, показывающий, как сделать простое шифрование AES с помощью PBKDF2 выше.Я не сделал этого по двум причинам:

  1. Если вы не понимаете крипто-примитивы, с которыми вы работаете достаточно хорошо, чтобы, скажем, отделить вывод ключа от шифрования,тогда вам действительно не следует писать крипто-код (как что-либо кроме игрушечного упражнения).

    Это может звучать грубо, но это реальность - в отличие от многих других областей программирования, где вы можете просто взятькусок кода, который вы не полностью понимаете и настраиваете до тех пор, пока он не заработает, с помощью крипто (и вообще с кодом, связанным с безопасностью) вам нужно знать, что вы делаете, и делать это правильно с первого раза.

    С другими типами кода, то, что , кажется, работает в большинстве случаев, часто достаточно хорошо, и вы можете исправить любые оставшиеся ошибки по мере их появления.С помощью криптографии ошибочный код может быть совершенно небезопасным, в то время как выглядит как будто он отлично работает, и вы не узнаете, что он сломан, пока кто-то не сломает его и не украдет все данные, которые вы так усердно работали, чтобы сохранить в тайне.Или, возможно, только длинный после , что уже произошло.

  2. В любом случае, коллекция примеров кода, которые вы связали с (и который я не рассмотрел более подробно ) уже включает набор методов, которые принимают двоичные ключи AES и не используют PBKDF2.В частности, это методы encrypt() и decrypt() (в отличие от encryptString() и decryptString(), которые используют PBKDF2).

    Обратите внимание, что при вызове encrypt() и / или decrypt() вам нужно будет предоставить ключ AES в формате, ожидаемом реализацией.В целом, это может зависеть от базовой криптографической библиотеки, используемой кодом. Например, реализация Java ожидает массива 128/8 = 16 элементов byte[].(Было бы неплохо, если бы он мог также напрямую принимать объект SecretKeySpec, но это не так.) Реализация JS WebCrypto , похоже, требует 16-байтовый Uint8Array вместо.Это очевидно хорошо для реализации node.js , хотя она также может принимать Buffer.


*) То есть приличная программа для взлома паролей, основанная на глубоких знаниях общепринятых методов и привычек выбора паролей, редко занимает более нескольких миллиардов (≈ 2 30 ) или триллион (≈ 2 40 ) попыток угадать большинство выбранных человеком паролей.На самом деле, в зависимости от того, насколько ленивы или неопытны ваши пользователи, даже просто тестирование нескольких тысяч самых распространенных паролей может быть весьма эффективным.

**) То естьугадать ключ не должно быть проще, чем угадать совершенно случайно выбранную цепочку битов той же длины.

0 голосов
/ 21 мая 2018

Проблема в том, что вы интерпретируете результат дешифрования как строку UTF8.Это не так, как это работает.Файлы - это произвольные байты, они не обязательно составляют строку UTF8.Результат вашего дешифрования верен, если вы просто не пытаетесь интерпретировать его как UTF8.

...