简体   繁体   中英

Node.js aes128 encryption/decryption port to PHP

So in node.js I have an encryption scheme using aes128 and without passing IV. An example being the following

 var cipher = require('crypto').createCipher('aes128','password');
 cipher.update('test','utf8','base64')+cipher.final('base64');

which outputs CjZ3R/tW8jiyLvg+/TE6bA== . The issue is that I can't reproduce this in PHP even though I've tried a bunch of implementations and variations.

When I then try running this through openssl, I run the following:

 echo "test" | openssl enc -e -aes128 -a -k password

which outputs U2FsdGVkX19Ccfv3SWvuzuZWeov9GDuwx1RMK2HWa/s= which also doesn't match either. I have also tried using the -md with all possible options and that didn't match either (they aren't even the same block size). When running in PHP I've tried hashing the key/not hashing, using padding, using the computed padding on the input where the padded character is the number of padded characters needed (saw something online saying this is what nodejs uses). And I have tried setting the IV to the password and also to null bytes but still I can't get a match.

Any suggestions/ideas here?

edit:

So I just discovered the function openssl_encrypt in php and tried the same test through that and once again got a completely different output (this one now takes exactly the same args as node.js and supposedly they both use openssl):

 openssl_encrypt ( "test" , 'aes128' , "password")

which outputs (already in base64) JleA91MvYHdEdnj3KYHmog== which is at least now matching in block count but still not the same ciphertext.

I should also mention that yes passing an IV to nodejs is an option and might resolve the discrepancy but this scheme in php will be replacing the old one in nodejs which is already live so it must be able to decrypt already created ciphertexts

So I figured out the solution. After looking through the c++ source code of node.js I found that the key and iv are generated using the openssl function EVP_BytesToKey. After searching for an implementation of this function I found this stack overflow post Encrypting data in Cocoa, decoding in PHP (and vice versa) which contains a version of this function. Modifying that and adding the fact that the openssl version pads the data with the ascii character equal to the number of bytes needed for padding I came up with the following function which fully matched the nodejs encryption scheme:

function aes128Encrypt($key, $data) {
    $padding = 16 - (strlen($data) % 16);
    $data .= str_repeat(chr($padding), $padding);

    $keySize   = 16;
    $ivSize    = 16;

    $rawKey = $key;
    $genKeyData = '';
    do
    {
        $genKeyData = $genKeyData.md5( $genKeyData.$rawKey, true );
    } while( strlen( $genKeyData ) < ($keySize + $ivSize) );

    $generatedKey = substr( $genKeyData, 0, $keySize );
    $generatedIV  = substr( $genKeyData, $keySize, $ivSize );

    print($generatedIV);
    print($generatedKey);

    return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $generatedKey, $data, MCRYPT_MODE_CBC, $generatedIV);
}

which matches exactly with the node.js function:

 function aes128Encrypt(key,data) {
      var cipher = require('crypto').createCipher('aes128',key);
      return cipher.update(data,'utf8','binary')+cipher.final('binary');
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM