Cifrar con Node.js módulo Crypto y descifrar con Java (en la aplicación de Android)

Buscando una forma de cifrar datos (principalmente cadenas) en el nodo y descifrar en una aplicación para Android (java).

Han hecho con éxito en cada uno (cifrar / descifrar en el nodo, y cifrar / descifrar en java), pero no puede parecer para conseguir que funcione entre ellos.

Posiblemente no estoy cifrando / descifrando de la misma manera, pero cada biblioteca en cada idioma tiene nombres diferentes para las mismas cosas …

Cualquier ayuda apreciada.

Aquí hay un código: 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 

Y 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); } 

La clave en bruto se crea de esta manera

 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; } 

Mientras que la cadena hexadecimal cifrada se convierte en bytes como este

 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; } 

Aparentemente, si pasas una frase de crypto.createCipher() a crypto.createCipher() utiliza EVP_BytesToKey() OpenSSL para derivar la clave. Puede pasar un búfer de bytes sin procesar y utilizar el mismo para inicializar Java SecretKey , o emular EVP_BytesToKey() en su código Java. Utilice $ man EVP_BytesToKey para más detalles, pero esencialmente hashes la passphrase varias veces con MD5 y concatena una sal.

En cuanto a usar una clave en bruto, algo como esto debería permitirle usar una clave en bruto:

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

Tenga en cuenta que dado que está utilizando CBC, debe utilizar el mismo IV para el cifrado y el descifrado (es posible que desee anexarlo a su mensaje, etc.)

Advertencia obligatoria: la implementación de un protocolo criptográfico rara vez es una buena idea. Incluso si usted consigue que esto funcione, ¿va a usar la misma clave para todos los mensajes? ¿Por cuanto tiempo? Si usted decide rotar la llave, cómo usted maneja esto. Etcétera etcétera.

Gracias a todos ustedes. Sus respuestas y comentarios me señalaron en la dirección correcta, y con un poco más de investigación me las arreglé para obtener un prototipo de trabajo (pegado a continuación). Resulta que el nodo de cripto utiliza MD5 para hash la clave, y el relleno es aparentemente (que uno con ensayo y error) hecho con PKCS7Padding

En cuanto a las razones para hacerlo en absoluto en primer lugar: Tengo una aplicación compuesta de tres partes: A. un servicio backend B. un tercero de datos de almacenamiento C. una aplicación de Android como un cliente.

El servicio de back-end prepara los datos y los envía al tercero. La aplicación android obtiene y / o actualiza los datos en el almacén de datos, sobre los que el servicio puede actuar.

La necesidad de cifrado, es mantener los datos privados, incluso desde el proveedor de terceros.

En cuanto a la gestión de claves – supongo que puedo tener el servidor de crear una nueva clave cada período preconfigurado de tiempo, cifrar con la clave antigua y publicarlo en el almacén de datos para el cliente para descifrar y empezar a usar, pero es una especie de exceso de mis necesidades.

También puedo crear un par de claves y usarlo para transferir la nueva clave simétrica de vez en cuando, pero eso es aún más exagerado (sin mencionar el trabajo)

Anywho, este es el código: 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 

Descifrar en 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; } 

Debes asegurarte de que estás usando

  • La misma llave
  • El mismo algoritmo, modo de funcionamiento y relleno.

En ambos lados de la conexión.

Para la clave , en el lado Java está utilizando bastante trabajo para derivar una clave de una cadena – no se hace tal cosa en el lado node.js. Utilice un algoritmo de derivación de clave estándar aquí (y el mismo en ambos lados).

Mirando de nuevo, la línea

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

De hecho alguna derivación clave, sólo la documentación es silenciosa sobre lo que hace exactamente :

Crypto.createCipher (algoritmo, contraseña)

Crea y devuelve un objeto cifrado, con el algoritmo y la contraseña dados.

algorithm depende de OpenSSL, los ejemplos son 'aes192' , etc. En las versiones recientes, openssl list-cipher-algorithms mostrarán los algoritmos de cifrado disponibles. password se utiliza para derivar clave y IV, que debe ser cadena codificada 'binary' (Ver los Buffers para más información).

Bueno, al menos esto dice cómo codificarlo, pero no lo que se hace aquí. Por lo tanto, podemos utilizar el otro método de inicialización crypto.createCipheriv (que toma la clave y el vector de inicialización directamente, y los usa sin ninguna modificación), o mirar la fuente.

createCipher invocará de alguna manera la función C ++ CipherInit en node_crypto.cc. Esto utiliza en esencia la función EVP_BytesToKey para derivar la clave de la cadena proporcionada (con MD5, sal vacía y contar 1), y luego hacer lo mismo que CipherInitiv (que es llamado por createCipheriv , y utiliza IV y clave directamente.)

Como AES utiliza 128 bits de clave y el vector de inicialización y MD5 tiene 128 bits de salida, esto en efecto significa

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

(Donde + denota concatenación, no adición). Puede volver a implementar esta clave-derivación en Java utilizando la clase MessageDigest, si es necesario.

Una mejor idea sería utilizar algún algoritmo de derivación de clave lenta, especialmente si su contraseña es algo que un ser humano puede memorizar. A continuación, utilice la función pbkdf2 para generar esta clave en el lado node.js y PBEKeySpec junto con un SecretKeyFactory (con el algoritmo PBKDF2WithHmacSHA1 ) en el lado de Java. (Elija un recuento de iteraciones que simplemente no haga que sus clientes se quejan de la lentitud en los dispositivos más comunes).

Para su algoritmo de cifrado , en el lado de Java está diciendo "utilizar el algoritmo AES con cualquiera que sea el modo predeterminado de operación y el modo de relleno predeterminado aquí". No haga esto, ya que podría cambiar de proveedor a proveedor.

En su lugar, utilice indicaciones explícitas del modo de operación ( CBC , en su caso) e indicación explícita del modo de relleno. Un ejemplo podría ser:

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

Echa un vistazo a la documentación de node.js para ver cómo indicar un modo de relleno allí (o cuál es el predeterminado, para seleccionar el mismo en el lado de Java). (De la documentación OpenSSL EVP , parece que el valor por defecto es PKCS5Padding aquí, también.)

Además, en lugar de implementar el cifrado usted mismo, considere el uso de TLS para el cifrado de transporte. (Por supuesto, esto sólo funciona si tiene una conexión en tiempo real entre ambos lados.)

El ejemplo de las respuestas anteriores no funcionó para mí al intentar en Java SE, ya que Java 7 se queja de que "AES / ECB / PKCS7Padding" no puede utilizarse.

Sin embargo, esto funcionó:

Cifrar

 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 

Para descifrar:

 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; } 
  • Bucle de redireccionamiento infinito en Android cuando se redirecciona desde el servidor con AngularJS
  • ¿Cuál es la diferencia entre Apache Cordova y PhoneGap?
  • Durante la instalación Cordova (PhoneGap) estoy recibiendo advertencia como "npm WARN motor Cordova-js @ 3. 8.0: wanted: ... "
  • ¿Cómo dar acceso a los datos de CouchDB a los visitantes de un sitio web móvil?
  • ¿No puede ejecutar node.js en el teléfono Android?
  • React Native: fallo de la solicitud de búsqueda con error - TypeError: Error en la solicitud de red (...)
  • react-native run-android no se reconoce
  • Cordova Android Upgrade - No se puede encontrar el módulo 'lodash / object / assign' Error
  • ¿Cómo ejecutar mi proyecto node.js en android?
  • Encriptación y descifrado entre Android, PHP y node.js
  • ¿Dos servidores node.js?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.