AES – Cifrado con Crypto (node-js) / descifrado con Pycrypto (python)

Estoy escribiendo esta pregunta + respuesta porque luché mucho (tal vez debido a la falta de experiencia), me perdí en muchas formas diferentes de cifrar / descifrar cosas con nodo o python.

Pensé que tal vez mi caso podría ayudar a la gente en el futuro.

Lo que necesitaba hacer:

  • Obtenga datos de un formulario, cifrándolos usando Crypto (node-js)
  • Pase los datos cifrados en Python y descifre usando PyCrypto.

Elegí usar el cifrado AES.

Así es como empecé (no voy a pasar por todo lo que probé):

  • Seguí el ejemplo al final de esta página.

    Lo que dio en mi caso:

    (esta podría ser una mezcla muy mala entre javascript y coffeescript)

    crypto = require "crypto" [...] key = "mykeywhatever" cipher = crypto.createCipher('aes192', key) cipher.update('string i want to encode', 'binary', 'hex') encoded_string = cipher.final('hex') [...] 

    Esto funcionó bastante bien para codificar mi cadena.

  • Luego escribí mi script en Python para descifrar esta cadena, usando el archivo léame en la página de github de PyCrypto :

     from Crypto.Cipher import AES [...] my_string = data_coming_from_rabbitmq obj = AES.new('mykeywhatever', AES.MODE_CBC) obj.decrypt(ciphertext) [...] 

    Obviamente, esto no funcionó: en el archivo Léame hay una IV, pero como no le di una en el script del nodo, ¿por qué le daría una en el Python?

Después de buscar en Google, supe que el Crypto de nodo usa OpenSSL, mientras que PyCrypto aparentemente no lo hace. Así que miré eso y encontré esas páginas:

  • ¿Cómo puedo descifrar algo con PyCrypto que fue cifrado usando OpenSSL?
  • Es AES el mismo en las bibliotecas PyCrypto & Node.JS Crypto
  • y mucho más…

Entonces las cosas se complicaron, nadie está haciendo lo mismo para descifrar los datos, me perdí y pedí ayuda.

La respuesta es qué se nos ocurrió a mi compañero de trabajo y a mí (bueno, principalmente a mi corworker).

Así que comenzamos desde la respuesta de “Cómo puedo descifrar … OpenSSL”.

  • Necesitamos modificar el script de cifrado que dio:

     crypto = require "crypto" [...] var iv = new Buffer('asdfasdfasdfasdf') var key = new Buffer('asdfasdfasdfasdfasdfasdfasdfasdf') var cipher = crypto.createCipheriv('aes-256-cbc', key, iv); cipher.update(new Buffer("mystring")); var enc = cipher.final('base64'); [...] 

    iv necesita ser 16bytes de largo, la clave es de 32bytes. Y cambiamos createCipher a createCipheriv .

  • De vuelta al script de desencriptación de python:

    El proceso consistía simplemente en leer la documentación de PyCrypto y compararla con el código desde el que comenzamos .

    Entonces decidimos limitarnos a la API y comenzar de cero. Y le dio:

     from base64 import b64decode from Crypto.Cipher import AES [...] iv = 'asdfasdfasdfasdf' key = 'asdfasdfasdfasdfasdfasdfasdfasdf' encoded = b64decode('my_encrypted_string') dec = AES.new(key=key, mode=AES.MODE_CBC, IV=iv) value = dec.decrypt(encoded) 

Y fue tan simple como eso … ¡Espero que ayude a algunos de ustedes!

Actualizar:

Como escribió Perseidas en los comentarios de su respuesta, el IV debe ser aleatorio y diferente para cada mensaje.

El sistema que estás construyendo es probablemente inseguro.

A excepción del almacenamiento, básicamente, nunca desea cifrar sus datos, sino también autenticarlos . La autenticación en este contexto significa que un mensaje válido solo puede ser generado por alguien que conoce la clave. Un esquema de autenticación ampliamente utilizado es HMAC .

Si no autentica sus mensajes, cualquier persona puede ingresar datos a su servicio. Es posible que un atacante no pueda controlar completamente el resultado después del descifrado, pero aún puede ser muy peligroso. Por ejemplo, si usa CBC (lo que hace) y los esquemas de rellenos más comunes (AES es un cifrado de bloque y solo puede cifrar bloques de datos de 128 bits) y un atacante puede diferenciar entre un error de relleno y cualquier otro error, entonces todos sus mensajes Puede ser descifrado por un atacante . Esto se llama un ataque de oracle de relleno y es demasiado común.

Para protegerse de esta clase de ataques, puede utilizar un esquema de cifrado autenticado , por ejemplo, el modo de cifrado de bloque GCM .

También tienes que protegerte contra los ataques de repetición . Considere una aplicación bancaria y los datos que está transmitiendo son una orden de transferencia bancaria. A menos que haya un TAN, un atacante podría registrar una transacción anterior y repetir esta transacción a su servicio una y otra vez, transfiriendo así un múltiplo del dinero que el cliente quería originalmente.

¿El formulario del que está obteniendo los datos se transmite a través de HTTPS? Si no: ¿Puede la clave ser escuchada por un atacante? ¿Cómo sabe un usuario que obtuvo el formulario de usted y de nadie más (SSL / TLS tiene tanto que ver con la autenticación como con la confidencialidad)?

Probablemente he olvidado algunos otros vectores de ataque que ofrecen las ofertas de cifrado CBC.

Alternativas

Probablemente la forma más fácil de protegerse contra estos ataques es transmitir los datos del formulario a través de HTTPS. SSL / TLS se diseñó para evitar todos los ataques anteriores y las implementaciones del lado del cliente y del servidor tardaron mucho tiempo en madurar.