tutorial node integrar con php encryption node.js

tutorial - integrar node js con php



AES encripta en Node.js descifra en PHP. Fallar. (7)

AES es rijndael con tamaño fijo de 16 bytes IV. Detalles aquí . No se puede usar para descifrar Más importante aún, no puedo descifrar su cadena usando openssl:

% openssl aes-256-cbc -d -in dec.txt -a enter aes-256-cbc decryption password: bad magic number

O usando php:

$encrypted = ''OnNINwXf6U8XmlgKJj48iA==''; $text = ''Yes''; $pw = ''123456''; $decrypted = @openssl_decrypt($encrypted, ''aes-256-cbc'', $pw); var_dump($decrypted); var_dump(@openssl_encrypt($text, ''aes-256-cbc'', $pw, FALSE, $pw)); var_dump(@openssl_encrypt($text, ''aes-256-cbc'', $pw));

Salida:

bool(false) string(24) "xrYdu2UyJfxhhEHAKWv30g==" string(24) "findrYaZVpZWVhEgOEVQwQ=="

Entonces parece que node.js está usando alguna característica no documentada para crear el IV y no veo forma de proporcionar el IV en node.js.

En node.js, utilizo la función de compilación en para cifrar datos como ese:

var text = "Yes"; var password = "123456"; var encrypt = crypto.createCipher(''aes-256-cbc'', password); var encryptOutput1 = encrypt.update(text, ''base64'', ''base64''); var encryptOutput2 = encrypt.final(''base64''); var encryptedText = encryptOutput1 + encryptOutput2;

el resultado (texto encriptado) es: OnNINwXf6U8XmlgKJj48iA ==

Luego uso descifrarlo en PHP:

$encrypted = ''OnNINwXf6U8XmlgKJj48iA==''; (or $encrypted = base64_decode(''OnNINwXf6U8XmlgKJj48iA=='') ); $dtext2 = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CBC); echo "Decrypted: $dtext2";

Conseguiré algunos personajes divertidos, que no puedo descifrar. Intenté con / sin base64_decode o MCRYPT_RIJNDAEL_128 ... todos fallaron.

Luego verifico cómo el cifrado en PHP, se ve muy diferente de la salida de node.js.

$text = "Yes"; $key = "123456"; $eText = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC); echo "Encrypted: $eText /n"; echo "base64: " . base64_encode($eText) . " /n"; $dtext1 = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $eText, MCRYPT_MODE_CBC); echo "Decrypted: $dtext1 /n/n";

Puede cifrar y descifrar. y los datos cifrados son: njCE / fk3pLD1 / JfiQuyVa6w5H + Qb / utBIT3m7LAcetM =

que es muy diferente de la salida de node.js, por favor avísenme cómo puedo cifrar y descifrar entre node.js y php. Gracias. :)

@Mel aquí es lo que tengo en PHP:

$text = "Yes"; $key = "32BytesLongKey560123456789ABCDEF"; $iv = "sixteenbyteslong"; /* Open the cipher */ $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '''', MCRYPT_MODE_CBC, ''''); /* Intialize encryption */ mcrypt_generic_init($td, $key, $iv); /* Encrypt data */ $eText = mcrypt_generic($td, $text); echo "Encrypted Data: $eText /n"; echo "base64: " . base64_encode($eText) . " /n"; /* Terminate encryption handler */ mcrypt_generic_deinit($td); /* Initialize encryption module for decryption */ mcrypt_generic_init($td, $key, $iv); /* Decrypt encrypted string */ $dText = mdecrypt_generic($td, $eText); /* Terminate decryption handle and close module */ mcrypt_generic_deinit($td); mcrypt_module_close($td); /* Show string */ echo trim($dText) . "/n";

Sin embargo, todavía no funciona.

La base encriptada 64 en PHP es: 80022AGM4 / 4qQtiGU5oJDQ == La base encriptada 64 en nodejs es: EoYRm5SCK7EPe847CwkffQ ==

por lo tanto, no puedo descifrar el nodejs uno en PHP.

Me pregunto si es porque nodejs no requiere $ iv?


Node.js está haciendo magia con tu contraseña de entrada para derivar una clave y iv. Es difícil ver cómo funcionaría en PHP a menos que PHP haga exactamente la misma clave y la magia de derivación iv.

¿Por qué no usas createCipheriv en su lugar? Use una función de derivación de clave basada en contraseña para crear una clave a partir de la contraseña. Por ejemplo:

http://en.wikipedia.org/wiki/PBKDF2

Tal función está disponible en versiones posteriores de Node.js

http://nodejs.org/docs/latest/api/crypto.html#crypto_crypto_pbkdf2_password_salt_iterations_keylen_callback

Proporcione un buen iv también; puedes crear uno usando crypto.randomBytes. Si controla la clave y los parámetros iv, le resultará mucho más fácil determinar si puede redondear sus datos a PHP.

No puedes simplemente hash la contraseña para generar un iv. Se supone que el iv es diferente para cada mensaje encriptado, de lo contrario es inútil.

Además, le está diciendo a Node.js que su cadena de entrada "Sí" está codificada en Base64, pero creo que realmente es ASCII o UTF-8.


Si tiene una biblioteca de terceros que usa MCRYPT_RIJNDAEL_256 , sepa que el 256 especifica el tamaño del bloque, no el tamaño de la clave. AES utiliza un tamaño de bloque fijo de 128 bits, y openssl no implementa algoritmos Rijndael más genéricos. Para eludir esto, publiqué un módulo que se vincula a libmcrypt , tal como lo hace PHP. Es un caso de uso bastante limitado, pero asegura que será compatible con el bloque de 256 bits de tamaño rijndael.

Si estás usando esto en PHP

mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $plaintext, MCRYPT_MODE_ECB); mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $ciphertext, MCRYPT_MODE_ECB);

Puedes hacer lo mismo en Node:

var rijndael = require(''node-rijndael''); // straight through (must be buffers) rijndael.encrypt(plaintext, key); rijndael.decrypt(ciphertext, key); // or bound (can take a string for the key and an encoding) var rijn = rijndael(key); rijn.encrypt(plaintext); // gotta be a buffer again for implementation simplicity rijn.decrypt(ciphertext);

node-rijndael en GitHub

node-rijndael en npm


Encontré algunas cosas que podrían ser las razones por las que la descifrado / encriptación en PHP y Node.js no son lo mismo.

PHP usó MCRYPT_RIJNDAEL_256 algorythm. AES 256 se basa en MCRYPT_RIJNDAEL_256, pero no es lo mismo. AES 256 esto es estándar de encriptación, pero no algoritmo.

Si intenta encriptar algo mediante el uso de funciones simples estándar ("mcrypt_encrypt" y "mcrypt_decrypt" en PHP, por ejemplo), no puede ver todos los pasos y seguramente no puede saber por qué no puede descifrar eso. encriptado Puede ser el mismo para Node.js, ya que necesita utilizar una función que pueda encriptar paso a paso para evitar la sustitución de parámetros predeterminados.

Para encriptar / descifrar algo que necesita saber (configurar):

encryption method (algorythm) encryption mode (CBF, ECB, CBC...) key to decryption key lenght initialisation vector lenght

Y compruébalo en ambos lados. Debería ser lo mismo. También es necesario encontrar la combinación correcta "método de cifrado" + "modo de cifrado", que seguramente funciona en ambos lados.

Mi solución es RIJNDAEL_256 + ECB . Debería instalar node-rijndael , porque usa RIJNDAEL_256 con seguridad. Si no, mi ejemplo no funcionará.

Aquí está el ejemplo de Node.js para el cifrado .

Instale node-rijndael en alguna carpeta donde deberían estar dos archivos .js.

r256.js - es funciones para cifrar / descifrar. Lo encontré aquí .

var Rijndael = require(''node-rijndael''); /** * Pad the string with the character such that the string length is a multiple * of the provided length. * * @param {string} string The input string. * @param {string} chr The character to pad with. * @param {number} length The base length to pad to. * @return {string} The padded string. */ function rpad(string, chr, length) { var extra = string.length % length; if (extra === 0) return string; var pad_length = length - extra; // doesn''t need to be optimized because pad_length will never be large while (--pad_length >= 0) { string += chr; } return string; } /** * Remove all characters specified by the chr parameter from the end of the * string. * * @param {string} string The string to trim. * @param {string} chr The character to trim from the end of the string. * @return {string} The trimmed string. */ function rtrim(string, chr) { for (var i = string.length - 1; i >= 0; i--) if (string[i] !== chr) return string.slice(0, i + 1); return ''''; } /** * Encrypt the given plaintext with the base64 encoded key and initialization * vector. * * Null-pads the input plaintext. This means that if your input plaintext ends * with null characters, they will be lost in encryption. * * @param {string} plaintext The plain text for encryption. * @param {string} input_key Base64 encoded encryption key. * @param {string} input_iv Base64 encoded initialization vector. * @return {string} The base64 encoded cipher text. */ function encrypt(plaintext, input_key, input_iv) { var rijndael = new Rijndael(input_key, { mode: Rijndael.MCRYPT_MODE_ECB, encoding: ''base64'', iv: input_iv }); console.log("Rijndael.blockSize", Rijndael.blockSize); var padded = rpad(plaintext, ''/0'', Rijndael.blockSize); return rijndael.encrypt(padded, ''binary'', ''base64''); } /** * Decrypt the given ciphertext with the base64 encoded key and initialization * vector. * * Reverses any null-padding on the original plaintext. * * @param {string} ciphertext The base64 encoded ciphered text to decode. * @param {string} input_key Base64 encoded encryption key. * @param {string} input_iv Base64 encoded initialization vector. * @param {string} The decrypted plain text. */ function decrypt(ciphertext, input_key, input_iv) { var rijndael = new Rijndael(input_key, { mode: Rijndael.MCRYPT_MODE_ECB, encoding: ''base64'', iv: input_iv }); console.log(''lol'', rijndael.decrypt(ciphertext, ''base64'', ''binary'')); return rtrim(rijndael.decrypt(ciphertext, ''base64'', ''binary''), ''/0''); } exports.decrypt = decrypt; exports.encrypt = encrypt;

encrypt.js - es un ejemplo para el cifrado.

var crypto = require(''crypto''); var key = new Buffer(''theonetruesecretkeytorulethemall'', ''utf-8'').toString(''base64''); //secret key to decrypt var iv = crypto.randomBytes(32).toString(''base64''); console.log({"key":key, "iv":iv}); var rijndael = require(''./r256''); var plaintext = ''lalala''; //text to encrypt var ciphertext = rijndael.encrypt(plaintext, key, iv); console.log({"ciphertext":ciphertext});

Aquí está el ejemplo de PHP para descifrado .

<?php echo "<PRE>"; $mcrypt_method = MCRYPT_RIJNDAEL_256; $mcrypt_mode = MCRYPT_MODE_ECB; $mcrypt_iv = ''123456''; //needed only for encryption, but needed for mcrypt_generic_init, so for decryption doesn''t matter what is IV, main reason it is IV can exist. $mcrypt_key = ''theonetruesecretkeytorulethemall''; $data_to_decrypt = base64_decode(''ztOS/MQgJyKJNFk073oyO8KklzNJxfEphu78ok6iRBU=''); //node.js returns base64 encoded cipher text $possible_methods = array_flip(mcrypt_list_algorithms()); if(empty($possible_methods[$mcrypt_method])) { echo "method $mcrypt_method is impossible".PHP_EOL; exit(); } $possible_modes = array_flip(mcrypt_list_modes()); if(empty($possible_modes[$mcrypt_mode])) { echo "mode $mcrypt_mode is impossible".PHP_EOL; exit(); } if(!@mcrypt_get_block_size($mcrypt_method, $mcrypt_mode)) { echo "method $mcrypt_method does not support mode $mcrypt_mode".PHP_EOL; exit(); } $mcrypt = mcrypt_module_open($mcrypt_method,'''', $mcrypt_mode, ''''); $ivsize = mcrypt_enc_get_iv_size($mcrypt); if($ivsize != strlen($mcrypt_iv)) { $mcrypt_iv = str_pad($mcrypt_iv, $ivsize, ''#''); } if($ivsize < strlen($mcrypt_iv)) { $mcrypt_iv=substr($mcrypt_iv,0,$ivsize); } $keysize = mcrypt_enc_get_key_size($mcrypt); if($keysize != strlen($mcrypt_key)) { $mcrypt_key = str_pad($mcrypt_key, $keysize, ''#''); } if($keysize < strlen($mcrypt_key)) { $mcrypt_key=substr($mcrypt_key,0,$keysize); } $mcrypt_isblock = (int)mcrypt_enc_is_block_mode($mcrypt); $mcrypt_blocksize = mcrypt_enc_get_block_size($mcrypt); $mcrypt_method = mcrypt_enc_get_algorithms_name($mcrypt); $mcrypt_mode = mcrypt_enc_get_modes_name($mcrypt); echo "used method=$mcrypt_method /nmode=$mcrypt_mode /niv=$mcrypt_iv /nkey=$mcrypt_key /nkey with blocksize=$mcrypt_blocksize /nisblock=$mcrypt_isblock".PHP_EOL; if(mcrypt_generic_init($mcrypt,$mcrypt_key,$mcrypt_iv)< 0) { echo "mcrypt_generic_init failed...".PHP_EOL; exit(); } $result = mdecrypt_generic($mcrypt, $data_to_decrypt); echo PHP_EOL."decryption result|".$result.''|''; mcrypt_generic_deinit($mcrypt);

PD: No sé por qué, pero Node.js ignora IV (en mi ejemplo), por lo que el cifrado siempre será el mismo. PHP siempre utiliza IV y debe ser de longitud estricta, por lo que PHP devuelve cifrados diffirent siempre. Pero lo intenté al revés (cifrar por PHP y descifrar por Node.js) y funciona.


Siete meses tarde, pero yo estaba luchando con esto también, y encontré una solución. Aparentemente, PHP rellena la entrada con cero bytes para hacer que su tamaño sea un múltiplo del tamaño del bloque. Por ejemplo, usando AES-128, los "contrabasistas" de entrada de 14 bytes se rellenarán con dos bytes cero, como este:

"contrabassists/0/0"

AN * blocksize byte input se deja solo.

Sin embargo, las funciones de cifrado estándar de Node utilizan un esquema de relleno diferente denominado PKCS5. PKCS5 no agrega ceros, pero agrega la longitud del relleno, así que de nuevo con AES-128, los "contrabasistas" se convertirán en:

"contrabassists/2/2"

Incluso una entrada de N * blocksize bytes se rellena en PKCS5. De lo contrario, es imposible eliminar el relleno después de la decodificación. El "espectroheliograma" de entrada se convertiría en:

"spectroheliogram/16/16/16/16/16/16/16/16/16/16/16/16/16/16/16/16"

Para hacer que PHP m_crypt encryption sea compatible con el descifrado de Node, deberá rellenar la entrada usted mismo:

$pad = $blocksize - (strlen($input) % $blocksize); $input = $input . str_repeat(chr($pad), $pad);

A la inversa, deberás leer el último byte de los datos decodificados y cortar el relleno tú mismo.

Funciones de ejemplo: (agregado 14/01/2012)

En PHP, esta función devolvería AES-128 cifrado, datos codificados hexadecimales que pueden ser descifrados por el nodo:

function nodeEncrypt($data, $key, $iv) { $blocksize = 16; // AES-128 $pad = $blocksize - (strlen($data) % $blocksize); $data = $data . str_repeat(chr($pad), $pad); return bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv)); }

En Nodo, lo siguiente sería descifrar los datos:

function nodeDecrypt(data, key, iv) { var decipher = crypto.createDecipheriv(''aes-128-cbc'', key, iv); var chunks = [] chunks.push(decipher.update(data.toString(),''hex'',''binary'')) chunks.push(decipher.final(''binary'')) return chunks.join('''') }

Todavía no he hecho lo inverso, pero debería ser sencillo una vez que comprenda el esquema de relleno. No he hecho ninguna suposición sobre la generación clave / iv.


Estoy empezando a jugar con node.js, pero creo que su problema está relacionado con IVs que no coinciden. Intenta hacer lo siguiente en su lugar:

var encrypt = crypto.createCipheriv(''aes-256-cbc'', password, /* password.createHash(''md5'').toHex()*/);

PD: No estoy seguro de cómo crear un hash MD5 en node.js, tendrás que resolverlo por ti mismo y cambiar el código anterior en consecuencia.

Y en PHP:

$decrypt = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($encrypted), MCRYPT_MODE_CBC, md5($key)), "/0");

Esto debería garantizar que ambas implementaciones utilicen el mismo vector de inicialización.

También recomiendo que hagas los siguientes cambios:

  • contraseña: md5 (contraseña_original)
  • iv = md5 (md5 (contraseña_original))

Esto asegurará que PHP no lanzará ningún error estúpido. Consulte la mejor forma de usar PHP para cifrar y descifrar contraseñas.


Tengo otro ejemplo de trabajo en esta otra publicación si ayuda a alguien más.

Si se asegura de utilizar una "clave / secreto" de 32 caracteres y una longitud IV de 16 caracteres tanto en PHP como en Nodo, y base64 encriptación base64 y codificación de mensaje utf8 en Nodo, entonces no debería haber ningún problema con las diferencias en el padding schema.

Saludos, Ignacio