Implementation encryption and decryption OpenSSL and zlib in reactJS

402 Views Asked by At

I've PHP code that have 2 functions there are encryption and decryption that using openSSL, actually before I refactored it, I encoded the openSSL result to base64, cause it too long I change to compress using zlib (gzdeflate, gzinflate) then convert the last result to hexadecimal.

here is my PHP Code, it's working fine on PHP:

$secret_key = 'thisIsK3y';
$secret_iv  = 'tHis1s1v';

$output         = false;
$encrypt_method = "AES-256-CBC";
$key            = hash( 'sha256', $secret_key );
$iv             = substr( hash( 'sha256', $secret_iv ), 0, 16 );
$action = 'e';
$string = "33f6f1d3ebe5b9da5fd0e3a1c7dd71c6e484be914731cf96adac0c00" //decrypted = "test"
if( $action == 'e' ) 
{
    $output = bin2hex(gzdeflate( gzdeflate(openssl_encrypt( $string, $encrypt_method, $key, 0, $iv ), 9),  9) );
}
else if( $action == 'd' )
{
    if (!empty($string))
        $output = openssl_decrypt( gzinflate(gzinflate(pack("H*", $string ) ) ), $encrypt_method, $key, 0, $iv );
}
echo $output; // output : "test", it's working fine on PHP

then I tried to translanguage to JS on React and made simple utils, here is my JS code so far, :

     let secret_key = "thisIsK3y";
      let secret_iv = "tHis1s1v";

      var output = false;
      let encrypt_method = "AES-256-CBC";
      let key = String(sha256(secret_key)).toString(Hex).substr(0, 32);
      let iv = String(sha256(secret_iv)).toString(Hex).substr(0, 16);

      if (action == 'd') { // Decryption
        let enc = gzinflate(gzinflate(hex2bin(string)));
        enc = enc.toString(Utf8);
      
        let decrypted = AES.decrypt(enc, Utf8.parse(key), {
          iv: Utf8.parse(iv),
        }).toString(Utf8);
        console.log('sss', hex2bin('33f6f1d3ebe5b9da5fd0e3a1c7dd71c6e484be914731cf96adac0c00'));
        debugger;
        output = decrypted;
      }

I splitted gzinfalte, gzdeflate, hex2bin, bin2hex method to another JS file called string.js and import some packages CryptoJS and PakoJS

string.js contains some methods like this :

export function gzdeflate(str) {
    return pako.deflateRaw(str);
}

export function gzinflate(str) {
    return pako.inflateRaw(str);
}

export function bin2hex(s) {
    let i
    let l
    let o = ''
    let n
    s += ''
    for (i = 0, l = s.length; i < l; i++) {
        n = s.charCodeAt(i)
        .toString(16)
        o += n.length < 2 ? '0' + n : n
    }
    return o
}

export function hex2bin(hex) {
    let bytes = [], str;

    for(var i=0; i< hex.length-1; i+=2){
        bytes.push(parseInt(hex.substr(i, 2), 16));
    }

    str = String.fromCharCode.apply(String, bytes);
    return str;
}

when try to debug it, there is no error on hex2bin method

console.log(hex2bin('33f6f1d3ebe5b9da5fd0e3a1c7dd71c6e484be914731cf96adac0c00'));

BUT, when I try to convert the binary to gzinflate it's show me error

console.log(gzinflate(gzinflate(hex2bin(33f6f1d3ebe5b9da5fd0e3a1c7dd71c6e484be914731cf96adac0c00))))

Cannot read property 'ciphertext' of undefined

and when I try to change the secret_key and iv there is error to :

let secret_key = 'ffffffffffffffffffffffff{Curi}ty';
let secret_iv = 'ffffffffffffffffffffffff{Curi}ty';

show me error

Error: invalid stored block lengths

Anyone can help me out ?

2

There are 2 best solutions below

0
On

I'm no PHP expert but I have others suggest that PHP may be truncating some keys for you.

I suggest making sure the key generation functions are behaving the same for you in both languages and make sure they are both just 32 bytes long.

I want to point out another problem that I see in your code. While it will work, you are degrading the security of your encryption.

let key = String(sha256(secret_key)).toString(Hex).substr(0, 32);

Any time you take a 32 byte binary value, convert to HEX then take only 32 characters of the hex output, you have significantly reduced the strength of your key. Find a way to keep as byte value.

You are trying to create a 256 bit key (32 bytes with 256 possible values for each byte). You are actually creating a 32 character string and each character has only 16 possible values (0-1a-f). You have changed your key security of your encryption key from the intended 256^32 (1.1E77) to 16^32 (3.4E38).

Find a way to use the byte values produced by SHA256.

0
On

I am using below functions and working perfectly.


function hex2str( $hex ) {
$h='H*';
return pack($h, $hex);
}

function str2hex( $str ) {
$h='H*';
return array_shift( unpack($h, $str) );
}
//define("encryption_method", "AES-128-CBC");
define("encryption_method", "AES-128-CTR");
define("key", "HtR94FreWzxaEQ6q76sD83o1058nBvVf");
function encrypt($data) {
    $key = key;
    $plaintext = $data;
    $ivlen = openssl_cipher_iv_length($cipher = encryption_method);
    $iv = openssl_random_pseudo_bytes($ivlen);
    $ciphertext_raw = openssl_encrypt($plaintext, $cipher, $key, $options = OPENSSL_RAW_DATA, $iv);
    $hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary = true);
    //$ciphertext = base64_encode($iv . $hmac . $ciphertext_raw);
    $ciphertext = str2hex($iv . $hmac . $ciphertext_raw);

    return $ciphertext;
}
function decrypt($data) {
    $key = key;
    //$c = base64_decode($data);
    $c = hex2str($data);
    $ivlen = openssl_cipher_iv_length($cipher = encryption_method);
    $iv = substr($c, 0, $ivlen);
    $hmac = substr($c, $ivlen, $sha2len = 32);
    $ciphertext_raw = substr($c, $ivlen + $sha2len);
    $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options = OPENSSL_RAW_DATA, $iv);
    $calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary = true);
    if (hash_equals($hmac, $calcmac))
    {
        return $original_plaintext;
    }
} 

and use is like below -

$string = 'Sample String';
echo "Encrypted:" . encrypt($string) . "</br></br>";
$string = encrypt($string);
echo "Decrypted:" . decrypt($string). "</br></br>";