无法在Java和PHP之间交换用AES-256加密的数据

时间:2022-07-05 18:30:36

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.

我的问题是:我在Java中加密的东西可以在Java中完美地解密,但是PHP mcrypt不能。我用mcrypt加密的东西可以用mcrypt解密,但不能用Java解密。

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

我希望从Java应用程序向PHP页面发送和接收加密数据,因此需要它兼容。

Here's what I have...

这就是我…

JAVA...

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...

PHP……

function getEncrypt($sStr, $sKey) {
    return base64_encode(
        mcrypt_encrypt(
            MCRYPT_RIJNDAEL_256, 
            $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==".

使用PHP对“example”进行加密,键为“Zvzpv8/ pxbezpczpzqkzl /FeoPw68jIb+NONX/LIi8=“I get”YTYhgp4zC+ w5isvitr5pukhmx4i7jzva6njqnjt1fhogy =”。使用Java对同一个键进行加密,得到“+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.

我以正确的顺序对base64进行编码和解码,并测试了Java和PHP之间的base64编码和解码兼容性。

4 个解决方案

#1


14  

BUG#1

错误# 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.

MCRYPT_RIJNDAEL_256不是AES。这个常量中的256表示块大小,而不是键大小。使用MCRYPT_RIJNDAEL_128获得与AES相同的算法。keysize是根据您提供的关键参数中的字节数设置的。所以提供32字节,你得到AES与一个256位的密钥。

BUG#2

错误# 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:

这两行在Java中永远不会是正确的,并且表明了对加密转换生成的任意二进制数据的本质的根本误解:

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:

直接传输和存储字节并没有什么错,但是如果必须使用可打印的字符串,那么应该使用base64编码/解码来实现。正如您已经在广泛地使用base64一样,这似乎是一种可行的方法。我猜正确的两条线是:

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.

我只是在开第二个错误的玩笑。真的,我错了,我没注意到这是解密的方向。当然,如果您知道解密的字节[]是一个有效的字符串,那么执行您的代码所做的事情是完全正确的。

#2


9  

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

我知道这是一个老话题,但我将添加我的工作解决方案。

You have to rewrite PHP side of the script:

你必须重写PHP方面的脚本:

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.

您应该base64_decode($sKey),因为您的密钥是base64编码的。

$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):

然后,您需要创建这个函数(从http://www.php.net/manual/en/function.mcrypt-decrypt.php提供信贷给beltrachi):

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! :)

我希望这对某人有用!:)

#3


3  

Please see here:

请参阅:

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:

你遇到的问题是划桨的问题。我不知道Java,但是AES/ECB/PKCS5Padding表示为“PKCS#5”(本质上与PKCS#7相同),而PHP只支持“空补”。这是什么PKCS # 5/7:

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.

用填充字符串填充输入,填充长度在1到8字节之间,使总长度精确地乘以8字节。填充字符串的每个字节的值设置为增加的字节数,即8字节的值0x08, 7个字节的值0x07,…, 2字节的0x02,或1字节的0x01。

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

所以PHP代码做填充是很简单的:

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

#4


1  

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:

记住,对字符串有相同的编码。尝试将两种语言中的字符串转换为UTF-8,例如,而不是转换为编码的二进制数据:

PHP (s. utf8_encode() function):

PHP(s . utf8_encode()函数):

$strAndBlob = utf8_encode("My string");

Java:

Java:

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

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

PHP,例如,在默认情况下不能使用UTF-8。

#1


14  

BUG#1

错误# 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.

MCRYPT_RIJNDAEL_256不是AES。这个常量中的256表示块大小,而不是键大小。使用MCRYPT_RIJNDAEL_128获得与AES相同的算法。keysize是根据您提供的关键参数中的字节数设置的。所以提供32字节,你得到AES与一个256位的密钥。

BUG#2

错误# 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:

这两行在Java中永远不会是正确的,并且表明了对加密转换生成的任意二进制数据的本质的根本误解:

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:

直接传输和存储字节并没有什么错,但是如果必须使用可打印的字符串,那么应该使用base64编码/解码来实现。正如您已经在广泛地使用base64一样,这似乎是一种可行的方法。我猜正确的两条线是:

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.

我只是在开第二个错误的玩笑。真的,我错了,我没注意到这是解密的方向。当然,如果您知道解密的字节[]是一个有效的字符串,那么执行您的代码所做的事情是完全正确的。

#2


9  

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

我知道这是一个老话题,但我将添加我的工作解决方案。

You have to rewrite PHP side of the script:

你必须重写PHP方面的脚本:

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.

您应该base64_decode($sKey),因为您的密钥是base64编码的。

$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):

然后,您需要创建这个函数(从http://www.php.net/manual/en/function.mcrypt-decrypt.php提供信贷给beltrachi):

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! :)

我希望这对某人有用!:)

#3


3  

Please see here:

请参阅:

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:

你遇到的问题是划桨的问题。我不知道Java,但是AES/ECB/PKCS5Padding表示为“PKCS#5”(本质上与PKCS#7相同),而PHP只支持“空补”。这是什么PKCS # 5/7:

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.

用填充字符串填充输入,填充长度在1到8字节之间,使总长度精确地乘以8字节。填充字符串的每个字节的值设置为增加的字节数,即8字节的值0x08, 7个字节的值0x07,…, 2字节的0x02,或1字节的0x01。

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

所以PHP代码做填充是很简单的:

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

#4


1  

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:

记住,对字符串有相同的编码。尝试将两种语言中的字符串转换为UTF-8,例如,而不是转换为编码的二进制数据:

PHP (s. utf8_encode() function):

PHP(s . utf8_encode()函数):

$strAndBlob = utf8_encode("My string");

Java:

Java:

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

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

PHP,例如,在默认情况下不能使用UTF-8。