开发者

Unable to exchange data encrypted with AES-256 between Java and PHP

My problem is: what I encrypt in Java I can decrypt perfectly in Java, but PHP mcrypt can't decrypt. What I encrypt with mcrypt I can decrypt with mcrypt, but can't in Java.

I want to send and receive encrypted data from a Java application to a PHP page, so I need it to be compatible.

Here's what I have...

JAVA...

public static String crypt(String input, String key){
    byte[] crypted = null;
    try{
        SecretKeySpec skey = new SecretKeySpec(Base64.decodeBase64(key), "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, skey);
        crypted = cipher.doFinal(input.getBytes());
    }catch(Exception e){
    }
    return Base64.encodeBase64String(crypted);
}

public static String decrypt(String input, String key){
    byte[] output = null;
    try{
        SecretKeySpec skey = new SecretKeySpec(Base64.decodeBase64(key), "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, skey);
        output = cipher.doFinal(Base64.decodeBase64(input));
    }catch(Exception e){
    }
    return new String(output);
}

Running:

public static void main(String[] args) {
    String key = "Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb+NONX/LIi8=";
    String data = "example";
    System.out.println(Cpt.decrypt(Cpt.crypt(data, key), key));
}

Output:

example

PHP...

function getEncrypt($sStr, $sKey) {
    return base64_encode(
        mcrypt_encrypt(
            MCRYPT_RIJNDAEL_256, 开发者_如何学Go
            $sKey,
            $sStr,
            MCRYPT_MODE_ECB
        )
    );
}

function getDecrypt($sStr, $sKey) {
    return mcrypt_decrypt(
        MCRYPT_RIJNDAEL_256, 
        $sKey, 
        base64_decode($sStr), 
        MCRYPT_MODE_ECB
    );
}

Running:

$crypt = getDecrypt(getEncrypt($str, $key), $key);
echo "<p>Crypt: $crypt</p>";

Output:

Crypt: example�������������������������

Using PHP to crypt "example" with key "Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb+NONX/LIi8=" I get "YTYhgp4zC+w5IsViTR5PUkHMX4i7JzvA6NJT1FqhoGY=". Using Java to crypt the same thing with the same key I get "+tdAZqTE7WAVPXhB3Tp5+g==".

I'm encoding and decoding to base64 in the right order and I tested base64 encode and decode compatibility between Java and PHP and it's working.


BUG#1

MCRYPT_RIJNDAEL_256 is not AES. The 256 in that constant refers to the blocksize, not the keysize. Use MCRYPT_RIJNDAEL_128 to get the same algorithm as AES. The keysize is set just by the number of bytes in the key argument you supply. So supply 32 bytes and you get AES with a 256-bit key.

BUG#2

These two lines are never correct in Java and indicate a fundamental misunderstanding of the nature of the arbitrary binary data produced by cryptographic transforms:

output = cipher.doFinal(Base64.decodeBase64(input));
return new String(output);

There is nothing wrong with transmitting and storing byte[] directly, but if you must use only printable strings then you should base64 encode/decode to do so. As you are already using base64 extensively that would seem like the way to go. I would guess that the correct two lines would be:

output = cipher.doFinal(Base64.decodeBase64(input));
return new String(Base64.encodeBase64(output), "UTF-8");

EDIT:

Just kidding about bug #2. Really, I was wrong, I didn't notice it was the decrypt direction. Of course, if you know the decrypted byte[] is a valid string then it is perfectly correct to do what your code does.


I know this is an old topic, but I will add my working solution.

You have to rewrite PHP side of the script:

function getEncrypt($sStr, $sKey) {
  return base64_encode(
    mcrypt_encrypt(
        MCRYPT_RIJNDAEL_128, 
        base64_decode($sKey),
        $sStr,
        MCRYPT_MODE_ECB
    )
  );
}

function getDecrypt($sStr, $sKey) {
  return mcrypt_decrypt(
    MCRYPT_RIJNDAEL_128, 
    base64_decode($sKey), 
    base64_decode($sStr), 
    MCRYPT_MODE_ECB
  );
}

You should base64_decode($sKey) because your key is base64 encoded.

$key = "Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb+NONX/LIi8=";

Then, you need to create this function (credit goes to beltrachi from http://www.php.net/manual/en/function.mcrypt-decrypt.php):

function pkcs5_pad ($text, $blocksize) { 
  $pad = $blocksize - (strlen($text) % $blocksize); 
  return $text . str_repeat(chr($pad), $pad); 
}

Use this code do encode/decode:

$decrypt = getDecrypt("6XremNEs1jv/Nnf/fRlQob6oG1jkge+5Ut3PL489oIo=", $key);
echo $decrypt;
echo "\n\n";
echo getEncrypt(pkcs5_pad("My very secret text:)", 16), $key);

I hope this will be useful for someone! :)


Please see here:

  • Difference in PHP encryption from iOS and .NET

  • AES Encrypt in C#, decrypt in PHP

  • DES Encryption in PHP and C#

The problem you're encountering is a padding-issue. I don't know Java, but AES/ECB/PKCS5Padding looks like you're using a PKCS#5 (that's essentially the same as PKCS#7) padding while PHP natively only support NULL-padding. That's what PKCS#5/7 does:

Pad the input with a padding string of between 1 and 8 bytes to make the total length an exact multiple of 8 bytes. The value of each byte of the padding string is set to the number of bytes added - i.e. 8 bytes of value 0x08, 7 bytes of value 0x07, ..., 2 bytes of 0x02, or one byte of value 0x01.

So the PHP code to do the padding right is trivial:

$blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$padding   = $blockSize - (strlen($data) % $blockSize);
$data      .= str_repeat(chr($padding), $padding);


Keep in mind to have the same encoding for the strings. Try to convert the strings in both languages to UTF-8, e.g., and than convert to binary data that is encoded:

PHP (s. utf8_encode() function):

$strAndBlob = utf8_encode("My string");

Java:

String str = "My string";
byte[] blob = str.getBytes("utf-8");

PHP, e.g., must not use UTF-8 by default.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜