Шифрование с помощью модуля Node.js Crypto и расшифровка с помощью Java (в приложении для Android)
Ищете способ шифрования данных (в основном строк) в узле и дешифрования в приложении Android (java).
Успешно сделали это в каждом (шифрование / дешифрование в узле и шифрование / дешифрование в java), но, похоже, не могут заставить его работать между ними.
Возможно, я не шифрую / дешифруя точно так же, но каждая библиотека на каждом языке имеет разные имена для одних и тех же вещей …
- Не удалось выполнить dex: превышение верхнего предела GC в Eclipse
- ANDROID: анализ XML
- Печать целого числа в двоичном формате в Java
- Android имитирует нажатие клавиши
- Как поймать особые исключения Firebase Auth
Любая помощь оценивается.
вот какой код: 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; }
- Бесконечный список прокрутки не работает
- Получение NoClassDefFoundError при использовании common.lang.StringUtils в javas-коде android?
- Окно оверлей системы Android
- Где получить строковый литерал «UTF-8» в Java?
- Как выйти из приложения Android с помощью кода?
- Как включить Spongy Castle JAR в Android?
- Задание полей в LinearLayout программно
- Какая версия HTTP-клиента Apache поставляется в Android 1.6?
По-видимому, если вы передаете кодовую фразу 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 для транспортного шифрования. (Конечно, это работает только в том случае, если у вас есть связь в реальном времени между обеими сторонами.)