Шифрование с помощью модуля Node.js Crypto и расшифровка с помощью Java (в приложении для Android)

Ищете способ шифрования данных (в основном строк) в узле и дешифрования в приложении Android (java).

Успешно сделали это в каждом (шифрование / дешифрование в узле и шифрование / дешифрование в java), но, похоже, не могут заставить его работать между ними.

Возможно, я не шифрую / дешифруя точно так же, но каждая библиотека на каждом языке имеет разные имена для одних и тех же вещей …

Любая помощь оценивается.

вот какой код: Node.js

var crypto = require('crypto') var cipher = crypto.createCipher('aes-128-cbc','somepass') var text = "uncle had a little farm" var crypted = cipher.update(text,'utf8','hex') crypted += cipher.final('hex') //now crypted contains the hex representation of the ciphertext 

и java

 private static String decrypt(byte[] raw, byte[] encrypted) throws Exception { SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, skeySpec ); byte[] decrypted = cipher.doFinal(encrypted); return new String(decrypted); } 

исходный ключ создается таким образом

 private static byte[] getRawKey(String seed) throws Exception { KeyGenerator kgen = KeyGenerator.getInstance("AES"); SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); byte[] seedBytes = seed.getBytes() sr.setSeed(seedBytes); kgen.init(128, sr); // 192 and 256 bits may not be available SecretKey skey = kgen.generateKey(); byte[] raw = skey.getEncoded(); return raw; } 

в то время как зашифрованная шестнадцатеричная строка преобразуется в байты, подобные этому

 public static byte[] toByte(String hexString) { int len = hexString.length()/2; byte[] result = new byte[len]; for (int i = 0; i < len; i++) result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue(); return result; } 

По-видимому, если вы передаете кодовую фразу crypto.createCipher() она использует EVP_BytesToKey() OpenSSL для вывода ключа. Вы можете передать необработанный байт-буфер и использовать его для инициализации EVP_BytesToKey() Java или эмулировать EVP_BytesToKey() в вашем Java-коде. $ man EVP_BytesToKey получения дополнительной информации используйте $ man EVP_BytesToKey , но по существу он несколько раз hashирует кодовую фразу с помощью MD5 и объединяет соль.

Что касается использования сырого ключа, что-то вроде этого должно позволить вам использовать необработанный ключ:

var c = crypto.createCipheriv("aes-128-ecb", new Buffer("00010203050607080a0b0c0d0f101112", "hex").toString("binary"), "");

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

Обязательное предупреждение: реализация криптового протокола сама по себе редко является хорошей идеей. Даже если вы это заработаете, собираетесь ли вы использовать один и тот же ключ для всех сообщений? Как долго? Если вы решите повернуть ключ, как вам управлять этим. Etc, &et;

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

Что касается причин сделать это вообще в первую очередь: у меня есть приложение, состоящее из трех частей: A. бэкэнд-сервис B. стороннее хранилище данных C. приложение android в качестве клиента.

Бэкэнд-сервис готовит данные и отправляет их третьей стороне. Приложение android получает и / или обновляет данные в хранилище данных, на которые служба может действовать.

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

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

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

Anywho, это код: Encrypt on Node.js

 var crypto = require('crypto') var cipher = crypto.createCipher('aes-128-ecb','somepassword') var text = "the big brown fox jumped over the fence" var crypted = cipher.update(text,'utf-8','hex') crypted += cipher.final('hex') //now crypted contains the hex representation of the ciphertext 

Расшифровать на Java:

 public static String decrypt(String seed, String encrypted) throws Exception { byte[] keyb = seed.getBytes("UTF-8"); MessageDigest md = MessageDigest.getInstance("MD5"); byte[] thedigest = md.digest(keyb); SecretKeySpec skey = new SecretKeySpec(thedigest, "AES/ECB/PKCS7Padding"); Cipher dcipher = Cipher.getInstance("AES/ECB/PKCS7Padding"); dcipher.init(Cipher.DECRYPT_MODE, skey); byte[] clearbyte = dcipher.doFinal(toByte(encrypted)); return new String(clearbyte); } public static byte[] toByte(String hexString) { int len = hexString.length()/2; byte[] result = new byte[len]; for (int i = 0; i < len; i++) result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue(); return result; } 

Пример из предыдущих ответов не работал для меня при попытке Java SE, так как Java 7 жалуется на то, что «AES / ECB / PKCS7Padding» использовать нельзя.

Однако это сработало:

для шифрования:

 var crypto = require('crypto') var cipher = crypto.createCipher('aes-128-ecb','somepassword') var text = "the big brown fox jumped over the fence" var crypted = cipher.update(text,'utf-8','hex') crypted += cipher.final('hex') //now crypted contains the hex representation of the ciphertext 

для расшифровки:

 private static String decrypt(String seed, String encrypted) throws Exception { byte[] keyb = seed.getBytes("UTF-8"); MessageDigest md = MessageDigest.getInstance("MD5"); byte[] thedigest = md.digest(keyb); SecretKeySpec skey = new SecretKeySpec(thedigest, "AES"); Cipher dcipher = Cipher.getInstance("AES"); dcipher.init(Cipher.DECRYPT_MODE, skey); byte[] clearbyte = dcipher.doFinal(toByte(encrypted)); return new String(clearbyte); } private static byte[] toByte(String hexString) { int len = hexString.length()/2; byte[] result = new byte[len]; for (int i = 0; i < len; i++) { result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue(); } return result; } 

Вы должны убедиться, что используете

  • тот же ключ
  • тот же алгоритм, режим работы и отступы.

по обе стороны от соединения.

Для ключа на стороне Java вы используете довольно определенную работу для получения ключа из строки – на стороне node.js такой вещи не делается. Здесь используется стандартный алгоритм вывода ключей (и тот же самый с обеих сторон).

Глядя снова, линия

 var cipher = crypto.createCipher('aes-128-cbc','somepass') 

действительно ли какой-то ключевой вывод, только документация умалчивает о том, что именно он делает :

crypto.createCipher (алгоритм, пароль)

Создает и возвращает объект шифрования с заданным алгоритмом и паролем.

algorithm зависит от OpenSSL, примеры – 'aes192' и т. д. В последних выпусках openssl list-cipher-algorithms будут отображать доступные алгоритмы шифрования. password используется для вывода ключа и IV, который должен быть 'binary' кодированной строкой (см. Буферы для получения дополнительной информации).

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

createCipher каким-то образом вызовет функцию C ++ CipherInit в node_crypto.cc. Это использует по существу функцию EVP_BytesToKey для получения ключа из предоставленной строки (с MD5, пустой солью и счетчиком 1), а затем выполняет то же самое, что и CipherInitiv (который вызывается createCipheriv и напрямую использует IV и ключ).

Поскольку AES использует 128 бит ключа и вектора инициализации, а MD5 имеет 128 бит вывода, это фактически означает

 key = MD5(password) iv = MD5(key + password) 

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

Лучше всего было бы использовать алгоритм медленного генерации ключа, особенно если ваш пароль – это то, что человек может запомнить. Затем используйте функцию pbkdf2 для генерации этого ключа на стороне node.js и PBEKeySpec вместе с SecretKeyFactory (с алгоритмом PBKDF2WithHmacSHA1 ) на стороне Java. (Выберите счетчик итераций, который просто не заставит ваших клиентов жаловаться на медлительность на наиболее распространенных устройствах.)

Для вашего алгоритма шифрования на стороне Java вы говорите: «Используйте алгоритм AES с любым режимом работы по умолчанию и режимом заполнения по умолчанию здесь». Не делайте этого, так как он может меняться от поставщика к провайдеру.

Вместо этого используйте явные указания о режиме работы ( CBC в вашем случае) и явную индикацию режима заполнения. Одним из примеров может быть:

 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 

Посмотрите на документацию node.js, чтобы увидеть, как указывать там режим заполнения (или какой из них по умолчанию, чтобы выбрать то же самое на стороне Java). (Из документации OpenSSL EVP , похоже, по умолчанию здесь также находится PKCS5Padding.)

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

  • Android - получить реальный путь .txt-файла, выбранного из файла explorer
  • Есть ли способ в Java преобразовать целое число в его порядковый номер?
  • Как выполнить итерацию свойств id classа R.java?
  • Пользовательская панель прогресса в Android?
  • Каков разумный порядок модификаторов Java (абстрактный, окончательный, общеansible, статический и т. Д.)?
  • Android-соединение с локальным хостом
  • remove () в списке, созданном с помощью Arrays.asList () throws UnsupportedOperationException
  • Как использовать устаревший HTTP-клиент Apache на Android Marshmallow?
  • Неустранимая ошибка: неверный макет java.lang.String по значению
  • Может (a == 1 && a == 2 && a == 3) оценивать true в Java?
  • Android загружает двоичные файлы
  • Interesting Posts

    Удалить определенную строку из текстового файла?

    Тенденция Android View

    Итерирование атрибутов элементов с помощью jQuery

    Как я могу расширить кортеж в аргументы функции вариационной матрицы?

    Формат метода (String, Object ) в типе String не применим для аргументов (…)

    Отредактируйте файл реестра Windows, system.dat

    ClassLoader ссылается на неизвестный путь: / data / app /

    Есть ли способ .NET перечислять все доступные сетевые принтеры?

    Каковы преимущества схем SQL Server?

    Как получить позицию перетаскиваемого объекта

    Как создать многого для большого количества спящего режима для дополнительного свойства из таблицы объединения?

    Ошибка: переход к метке case

    Какое аппаратное устройство использовало, чтобы съесть 1,4 ГБ моей 4 ГБ ОЗУ, и теперь внезапно после того, как никакие аппаратные изменения не съедят 2,2 ГБ?

    Память 4 ГБ рассматривается как 3 ГБ в BIOS

    getIntent () Дополнительно всегда NULL

    Давайте будем гением компьютера.