0

Hi am trying to check the compatability of encryption done on BE.

BE provided the below code `

$secret_key = VALUE1`;
$secret_iv = VALUE2;
$encrypt_method = "AES-256-CBC";
$key = hash('sha256', $secret_key);
$iv = substr(hash('sha256', $secret_iv), 0, 16);
openssl_encrypt($string, $encrypt_method, $key, 0, $iv)`

the openssl gave SNtvZ3Dpv1S88Ha6aVBdcg== when input string is abc

I tried few alogritham but I wasnt able to correctly match it with BE

he $key and $iv value I generated same as BE. but when it comes to encryption it doesnt give any result or saying Expected IV length is 16 but got 8

The following are the packages I tried

  1. import Aes from 'react-native-aes-crypto';
  Aes.encrypt(inputText, hash, _IV, 'aes-256-cbc').then(cipher => {
  console.log('cipher', cipher); });

This throws an error Error: expected IV length of 16 but was 8 I checked the length of _IV and it is certanly 16

  1. import CryptoAesCbc from 'react-native-crypto-aes-cbc';
  CryptoAesCbc.encryptInBase64(
              base64.encode(ivHash.substr(0, 16)),
              base64.encode(hash),
              'abc',
              '128',
              )
             
              .then(encryptString => {   
                console.log('encryptString', encryptString);
               })
              .catch(error => {
                console.log('error  ', error);
              });

This the encryptString value prints empty string

Please someone give me some insight into this.

FYI am checking in Android for the time being

EDIT 1

I red in some post about converting to _IV to hex

so I did this

  Aes.encrypt(
                  inputText,
                  hash,
                  Buffer.from(_IV).toString('hex'),
                  'aes-256-cbc',
                )
                  .then(cipherText => console.log('cipherText', cipherText))
                  .catch(error => console.log('error here', error));

It gave me wrong out out cipherText: yLvY847qCMHHGHdachjKGw==

Any help is appreciated

Thanks in advance

Topaco
  • 40,594
  • 4
  • 35
  • 62
suja
  • 1,198
  • 2
  • 12
  • 32
  • why do you limit your iv like that? substr(hash('sha256', $secret_iv), 0, 16), hint: 16 hex characters are 8 bytes. AES-256 is a block cipher using 256 bits or 32 byte keys but 16 byte blocks. The IV is (usually) the initial block and has to be the same length as a block. I.e. AES-256 = 32 byte (64 hex char) key and 16 byte (32 hex char) IV – zapl Sep 23 '22 at 01:42
  • am sorry I need to confirm with BE team why is it so – suja Sep 23 '22 at 01:45
  • their openssl_encrypt is maybe padding the IV with 0's since it looks like it is too short for aes-256 – zapl Sep 23 '22 at 01:47
  • so u have any suggestion/correction – suja Sep 23 '22 at 01:48
  • would fix their code, or if they can't, I'd try out what happens if you add 8 bytes of 0s (or 16 hex 0s) to your iv, e.g. if those are strings, `Aes.encrypt(inputText, hash, _IV + "0000000000000000", .....` – zapl Sep 23 '22 at 01:55
  • I tried the same and got this result 9qwGVX/psWv8uHV4PYrkuw== but the BE result for openssl is SNtvZ3Dpv1S88Ha6aVBdcg== – suja Sep 23 '22 at 01:56
  • the code with which I tried Aes.encrypt( inputText, hash, `${_IV}0000000000000000`, 'aes-256-cbc', ) .then(cipherText => console.log('cipherText', cipherText)) – suja Sep 23 '22 at 01:57
  • if you make your _IV a substring(0, 32)? Maybe it's something strange I don't understand about php (guess that's what that is). Encryption between plaforms is a nightmare, they all have different defaults and it gets really fun when you encrypt text because php might take ansi character encoding while react takes utf8 which have different byte representations – zapl Sep 23 '22 at 02:00
  • It could also be the padding algorithm that's applied to the input to enlarge it to a full block length, it should default to aes-256-cbc pkcs7 (or 5) padding in php, you get that via `Cipher.getInstance("AES/CBC/PKCS5Padding")` on Android for example, for your react library no idea if that's supported. If it has no options, try a different one maybe. – zapl Sep 23 '22 at 02:18
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/248275/discussion-between-suja-and-zapl). – suja Sep 23 '22 at 02:28

1 Answers1

1

In the hash() function of the PHP code, the third parameter is false by default, so $key and $iv are returned as hex encoded strings and not as raw binary data.
For SHA256 with a digest output length of 32 bytes $key therefore has a length of 64 bytes, since $key is not explicitly truncated, unlike $iv.
Nevertheless, only the first 32 bytes of the 64 bytes are used for encryption, because OpenSSL/PHP silently truncates keys that are too large (and pads keys that are too short with 0x00 values) to achieve the specified length (32 bytes for AES-256-CBC).
On the CryptoJS side, both the hex encoding of key and IV must be taken into account as well as the truncation of the key.

A possible implementation with CryptoJS (referring to your solution in the chat) is:

var plaintext = "The quick brown fox";
var keyMaterial = "my key passphrase";
var ivMaterial = "my IV passphrase"

var truncHexKey = CryptoJS.SHA256(keyMaterial).toString().substr(0, 32); // hex encode and truncate
var truncHexIV = CryptoJS.SHA256(ivMaterial).toString().substr(0, 16); // hex encode and truncate 
var key = CryptoJS.enc.Utf8.parse(truncHexKey); 
var iv = CryptoJS.enc.Utf8.parse(truncHexIV); 
var ciphertext = CryptoJS.AES.encrypt(plaintext, key, {iv: iv}); // default values: CBC, PKCS#7 padding

console.log('Ciphertext: ' + ciphertext.toString());
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

which gives the same ciphertext as the PHP code.

In general, it should also be noted that the use of upper and lower case letters is not consistently implemented in hex encoding. However, like PHP's hash() function, CryptoJS uses lowercase letters, so this is not a problem here.

Topaco
  • 40,594
  • 4
  • 35
  • 62