Nano Hash - криптовалюты, майнинг, программирование

Шифрование и дешифрование между Java и Javascript не будут работать

ИЗМЕНИТЬ 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
});


EDIT 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? Я попытался преобразовать содержимое большого двоичного объекта в строку, получить данные как ArrayBuffer и преобразовать их в строку, получить их в виде текста и передать методу расшифровки, но, похоже, ничего не работает.

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

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

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


  • Работает ли один и тот же код шифрования-дешифрования для простого текста, а не для файла? Вы можете попробовать что-то вроде Hello World. 08.06.2018
  • Это работает для текста 10.06.2018
  • Честно говоря, если вы не можете найти способ использовать только часть AES, а не часть PKBDF2, то вам не следует писать криптографический код. По крайней мере, не как нечто иное, как учебное упражнение, никогда не используемое в производстве. Криптовалюта — это не та область, где вы можете просто взять код, который на самом деле не понимаете, и взламывать его до тех пор, пока он покажется, что работает. Если вы попробуете, вы, скорее всего, получите что-то, что выглядит хорошо на первый взгляд, но имеет достаточно большие отверстия, чтобы через них проехал фургон наблюдения АНБ. 11.06.2018

Ответы:


1

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

20.05.2018
  • Это метод расшифровки текста, который работает. Неправильно работает метод decryptFile, использующий Latin1. 21.05.2018
  • Не имеет значения. Вы не можете передать случайные двоичные данные текстовому кодировщику и надеяться, что данные просто окажутся в правильном формате. Это так не работает. Двоичные данные практически и семантически не являются строкой. Ответ по-прежнему актуален. 21.05.2018
  • Вопрос все еще остается, как я могу изменить метод в Javascript, чтобы я мог его расшифровать. Даже если я изменю его с Latin1 на отсутствие кодировки или что-то еще, это не сработает. 21.05.2018
  • Это не сработает - не очень полезное описание проблемы. Обновите свой код в своем вопросе, указав поведение, которое вы наблюдаете после удаления из вывода всего, что связано с кодировками символов. 21.05.2018
  • В вашем коде до сих пор есть .toString() в конце... Я действительно не знаю, как это прояснить. Вы не можете преобразовать двоичные данные в строку... 21.05.2018
  • Ну, главная проблема заключается в том, что с toString или без него не будет никакого результата. Мне нужно, чтобы он работал, и я уже потратил много времени, пытаясь заставить его работать, поэтому я пробую все возможности но ничего не работает.. 21.05.2018
  • Какое поведение вы ожидаете от кода? Имея в виду, что JavaScript только недавно добавил поддержку Uint8Array. 21.05.2018
  • Поведение, которое я хотел бы, заключается в том, что файл, зашифрованный с помощью Java, можно расшифровать с помощью Javascript и загрузить. Я реализовал часть загрузки, но части расшифровки, похоже, не работают. 21.05.2018
  • быть неработающим - как? Вы должны быть более описательными. Чего вы ожидаете и что происходит на самом деле? Вы вообще отлаживали? 21.05.2018
  • Не работайте как в: нет результата. Если я загружу зашифрованный файл и попытаюсь его расшифровать, результата не будет. пусть расшифровывается просто будет пусто. Я ожидаю, что он вернет байты, которые он содержал изначально (поэтому, когда он не был зашифрован). Я многое отладил. Я пробовал передавать большие двоичные объекты, передавать буферы массивов, передавать файл в виде текста, но при расшифровке ничего не получается. Я много искал, но я просто не могу найти решение 21.05.2018
  • Если бы вы могли указать мне правильное направление, как я должен иметь возможность как шифровать, так и расшифровывать их на разных платформах, что я должен настроить, чтобы заставить его работать или что-то в этом роде. 21.05.2018
  • Посмотрите примеры кода здесь. Есть один для Java и для Web Crypto. 21.05.2018

  • 2

    Во-первых, попробуйте отправить простой зашифрованный текст из 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 сначала расшифруйте строку, а затем преобразуйте ее в файловый объект.

    Eg.

     function base64toFile(encodedstring,filename,mimeType){
       return new File([encodedstring.arrayBuffer()],filename, {type:mimeType});
    
    }   
    
    //Usage example:
    base64toFile('aGVsbG8gd29ybGQ=', 'hello.txt', 'text/plain');
    
    08.06.2018
  • Вы попробовали мое предложение тогда? 10.06.2018

  • 3

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

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

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

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

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

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


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

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

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

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

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

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


    *) Другими словами, приличная программа для взлома паролей, основанная на глубоких знаниях распространенных человеческих методов и привычек подбора паролей, редко требует более нескольких миллиардов ( 230 ) или триллион ( 240) попыток угадать большинство паролей, выбранных человеком. Фактически, в зависимости от того, насколько ленивы или неопытны ваши пользователи, даже простое тестирование нескольких тысяч наиболее распространенные пароли могут оказаться весьма эффективными.

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

    11.06.2018
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..