AES шифруется в Node.js Расшифровывать в PHP. Потерпеть неудачу.

В node.js я использую встроенную функцию для шифрования данных:

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; 

вывод (зашифрованный текст): OnNINwXf6U8XmlgKJj48iA ==

Затем я использую расшифровку в PHP:

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

Я получу несколько забавных персонажей, которые я не могу расшифровать. Я пробовал с / без base64_decode или MCRYPT_RIJNDAEL_128 .. все сбой.

Затем я проверяю, как шифрование в PHP, оно очень сильно отличается от вывода 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"; 

Он может шифровать и расшифровывать. и зашифрованные данные: njCE / fk3pLD1 / JfiQuyVa6w5H + Qb / utBIT3m7LAcetM =

который сильно отличается от вывода node.js, пожалуйста, сообщите, как я могу зашифровать и расшифровать между node.js & php. Благодарю. 🙂


@Mel вот что я имею в 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"; 

Однако он все еще не работает.

Зашифрованная база 64 в PHP: 80022AGM4 / 4qQtiGU5oJDQ == Зашифрованная база 64 в nodejs: EoYRm5SCK7EPe847CwkffQ ==

таким образом, я не могу расшифровать nodejs один в PHP.

Интересно, это потому, что nodejs не требует $ iv?

Семь месяцев поздно, но я тоже боролся с этим, и нашел решение. По-видимому, PHP вводит ввод с нулевыми байтами, чтобы сделать его размер кратным размеру блока. Например, используя AES-128, 14-байтовый ввод «контрабасистов» будет дополнен двумя нулевыми байтами, например:

 "contrabassists\0\0" 

AN * бит-байтовый вход остается в покое.

Однако стандартные криптографические функции Node используют другую схему дополнений, называемую PKCS5. PKCS5 не добавляет нулей, но добавляет длину отступов, поэтому снова используя AES-128, «контрабасисты» станут:

 "contrabassists\2\2" 

Даже N * blockize-байтовый вход дополняется в PKCS5. В противном случае после декодирования невозможно удалить отступы. Входная «спектрогелиограмма» затем станет:

 "spectroheliogram\16\16\16\16\16\16\16\16\16\16\16\16\16\16\16\16" 

Чтобы сделать шифрование PHP m_crypt совместимым с расшифровкой Узла, вам придется самостоятельно вводить ввод:

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

И наоборот, вам нужно будет прочитать последний байт декодированных данных и отрезать прокладку самостоятельно.

Примеры функций: (добавлено 01-14-2012)

В PHP эта функция возвращает AES-128 зашифрованные шестнадцатеричные кодированные данные, которые могут быть расшифрованы узлом:

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

В узле следующие данные расшифруют данные:

 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('') } 

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

Я только начинаю возиться с node.js, но я думаю, что ваша проблема связана с несогласованностью IV. Попробуйте сделать следующее:

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

PS: Я не уверен, как создать хэш MD5 в node.js, вам придется разобраться в себе и соответствующим образом изменить приведенный выше код.

И в PHP:

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

Это должно гарантировать, что обе реализации используют один и тот же вектор инициализации.

Я также рекомендую внести следующие изменения:

  • пароль: md5 (original_password)
  • iv = md5 (md5 (original_password))

Это позволит убедиться, что PHP не вызовет каких-либо глупых ошибок. См. Лучший способ использования PHP для шифрования и расшифровки паролей?

AES – rijndael с фиксированным размером 16 байт. Подробности здесь . Не может использоваться для расшифровки. Что еще более важно, я не могу расшифровать вашу строку с помощью openssl:

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

Или используя 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)); 

Вывод:

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

Таким образом, кажется, что node.js использует некоторую недокументированную функцию для создания IV, и я не вижу возможности предоставить IV в node.js.

Я нашел пару вещей, которые могут быть причиной того, что дешифрование / шифрование на PHP и Node.js не совпадают.

PHP использовал алгоритм MCRYPT_RIJNDAEL_256. AES 256 основан на MCRYPT_RIJNDAEL_256, но это не то же самое. AES 256 это стандарт шифрования, но не алгоритм.

Если вы пытаетесь зашифровать некоторые вещи, используя стандартные простые функции (например, «mcrypt_encrypt» и «mcrypt_decrypt» на PHP, например), вы не можете видеть все шаги, и вы наверняка не знаете, почему вы не можете расшифровать то, что вы зашифрованы. Он может быть таким же для Node.js, потому что нужно использовать функцию, которая может зашифровать шаг за шагом, чтобы предотвратить замену по умолчанию.

Чтобы зашифровать / расшифровать кое-что, что вам нужно знать (установить):

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

И проверьте это с обеих сторон. Это должно быть одинаково. Также необходимо найти правильную комбинацию «метод шифрования» + «режим шифрования», который, безусловно, работает с обеих сторон.

Мое решение – RIJNDAEL_256 + ECB . Вы должны установить node-rijndael , потому что он наверняка использует RIJNDAEL_256. Если нет – мой пример не сработает.

Вот пример Node.js для шифрования .

Установите node-rijndael в какую-то папку, где должно быть два файла .js.

r256.js – это функции для шифрования / дешифрования. Я нашел его здесь .

 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 – пример шифрования.

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

Вот пример PHP для дешифрования .

 <?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); 

PS Я не знаю почему, но Node.js игнорирует IV (в моем примере), поэтому шифр будет всегда одинаковым. PHP всегда использует IV, и он должен быть строгим, поэтому PHP всегда возвращает diffirent ciphers. Но я попробовал это наоборот: зашифруйте PHP и расшифруйте Node.js, и он работает.

Node.js делает магию с вашим паролем ввода для получения ключа и iv. Трудно понять, как это работает на PHP, если PHP не делает точно такую ​​же ключевую и двуличную магию вывода.

Почему бы вам не использовать createCipheriv. Используйте функцию деривации ключа на основе пароля для создания ключа из пароля. Например:

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

Такая функция доступна в более поздних версиях Node.js

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

Обеспечьте также хороший iv; вы можете создать его с помощью crypto.randomBytes. Если вы будете контролировать параметры ключа и iv, вам будет намного легче определить, можете ли вы объединить ваши данные в PHP.

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

Кроме того, вы сообщаете Node.js, что ваша строка ввода «Да» кодируется Base64, но я думаю, что это действительно ASCII или UTF-8.

Если вы застряли в сторонней библиотеке, которая использует MCRYPT_RIJNDAEL_256 , знайте, что 256 задает размер блока, а не размер ключа. AES использует фиксированный размер блока 128 бит, а openssl не реализует более общие алгоритмы Rijndael. Чтобы обойти это, я опубликовал модуль, который связывается с libmcrypt , как это делает PHP. Это довольно ограниченный случай использования, но он гарантирует, что он будет совместим с 256-битным размером блока rijndael.

Если вы используете это в PHP

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

Вы можете сделать то же самое в узле:

 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 на GitHub

node-rijndael на npm

У меня есть другой рабочий пример в этом другом сообщении, если он помогает кому-то еще.

Если вы убедитесь, что используете «ключ / секрет» длиной 32 символа и длиной 16 символов в формате PHP и Node, а также кодировку кодирования base64 и кодировку сообщений utf8 в узле, тогда у вас не должно быть проблем с различиями в дополняющая схема.

С уважением, Игнасио