Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected result decrypting using PHP AES CCM mode

I am attempting to reproduce an encryption operation using AES-256-CCM that is currently performed in Java with the Bouncy Castle provider. When attempting the same operation in PHP using openssl I cannot find a set of parameters that produces the same output.

As the AEAD modes were recently added to PHP (7.1), documentation on how this works is scarce.

A minimum example of the "working" encryption in Java looks like:

    public static void main(String args[]) {
    try {
        java.security.Security.addProvider(new BouncyCastleProvider());
        byte[] key = Base64.decodeBase64("Z4lAXU62WxDi46zSV67FeLj3hSK/th1Z73VD4/y6Eq4=".getBytes());
        byte[] iv = Base64.decodeBase64("rcFcdcgZ3Q/A+uHW".getBytes());

        SecretKey aesKey = new SecretKeySpec(key, 0, key.length, "AES");
        Cipher aesCipher = Cipher.getInstance("AES/CCM/NoPadding", "BC");
        aesCipher.init(1, aesKey, new IvParameterSpec(iv));

        byte[] encrypted = aesCipher.doFinal("test".getBytes());
        System.out.println(Hex.encodeHex(encrypted));

        // Output: 411d89ff74205c106d8d85a8
    }
    catch (Throwable e) {
        e.printStackTrace();
    }
}

As I am trying to re-produce this using different two different libraries and languages I have set the key and iv to known values.

When trying to re-produce this using PHP and openssl I am trying with the following code

$key = base64_decode("Z4lAXU62WxDi46zSV67FeLj3hSK/th1Z73VD4/y6Eq4=");
$iv = base64_decode('rcFcdcgZ3Q/A+uHW');
$data = 'test';
$tag = null;

$encrypted = openssl_encrypt($data,'aes-256-ccm', $key,OPENSSL_RAW_DATA, $iv, $tag,"",8);
echo(bin2hex($encrypted . $tag));
// d1a7403799b8c37240f36edb

Clearly the results do not match. In search of an answer as to what is incorrect I created the same operation using SJCL in javascript. The example for that is:

var data = "test";
var key = sjcl.codec.base64.toBits("Z4lAXU62WxDi46zSV67FeLj3hSK/th1Z73VD4/y6Eq4=");
var iv = sjcl.codec.base64.toBits("rcFcdcgZ3Q/A+uHW");
var  p = { 
        adata: "",
        iter: 0,
        mode: "ccm",
        ts: 64,
        ks: 256,
        iv: iv,
        salt: ""
        };
var encrypted = sjcl.encrypt(key, data, p, {}); 
console.log(encrypted);
// Output: {"iv":"rcFcdcgZ3Q/A+uHW","v":1,"iter":0,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"","ct":"QR2J/3QgXBBtjYWo"}

// QR2J/3QgXBBtjYWo === 411d89ff74205c106d8d85a8

The Bouncy Castle and SJCL libraries produce the same output but I can't tell what is different.

I have tried pre-processing the key with PBKDF2 as suggested in Encrypt in Javascript with SJCL and decrypt in PHP with no success. I have tried SHA256'ing the key with no success.

Why is the output in php/openssl different than Bouncy Castle and SJCL?

like image 363
Josh Glick Avatar asked Dec 31 '17 04:12

Josh Glick


1 Answers

When I stumbled upon a similar problem, I discovered that the problem resided in the IV, more precisely: the length of it. As far as You use an IV with the length under 12, it results with the same hashes. You can try it with your own code:

java.security.Security.addProvider(new BouncyCastleProvider());
byte[] key = Base64.getDecoder().decode("Z4lAXU62WxDi46zSV67FeLj3hSK/th1Z73VD4/y6Eq4=".getBytes());
byte[] iv = "12345678901".getBytes();

SecretKey aesKey = new SecretKeySpec(key, 0, key.length, "AES");
Cipher aesCipher = Cipher.getInstance("AES/CCM/NoPadding", "BC");
aesCipher.init(1, aesKey, new IvParameterSpec(iv));

byte[] encrypted = aesCipher.doFinal("test".getBytes());
System.out.println(Hex.encodeHex(encrypted));
// Output: e037af9889af21e78252ab58

and same with PHP:

$key = base64_decode("Z4lAXU62WxDi46zSV67FeLj3hSK/th1Z73VD4/y6Eq4=");
$iv = "12345678901";
$tag = null;

$encrypted = openssl_encrypt("test", "aes-256-ccm", $key, OPENSSL_RAW_DATA, $iv, $tag, null, 8);
print bin2hex($encrypted . $tag);
# e037af9889af21e78252ab58

If you would extend the IV, you'll see the results will differ. NB! Keep in mind that if you'd shorten the AES key (to 128 bytes), then Java will automatically switch to aes-128, but in PHP you have to change the algorithm manually.

like image 64
Tiit Avatar answered Oct 05 '22 07:10

Tiit