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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4537099/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 06:49:40  来源:igfitidea点击:

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

javaphpencryptionaes

提问by LZZ

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

我的问题是:我用Java加密的我可以用Java完美解密,但PHPmcrypt无法解密。我加密的东西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...

爪哇...

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 使用密钥“Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb+NONX/LIi8=”加密“示例”,我得到“YTYhgp4zC+w5IsViTR5PUkHMX4i7JzvA6NJT1FqhoGY=”。使用 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 编码和解码兼容性,并且它正在工作。

回答by President James K. Polk

BUG#1

错误#1

MCRYPT_RIJNDAEL_256is notAES. The 256 in that constant refers to the blocksize, not the keysize. Use MCRYPT_RIJNDAEL_128to 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。密钥大小仅由您提供的密钥参数中的字节数设置。因此,提供 32 个字节,您将获得带有 256 位密钥的 AES。

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:

byte[]直接传输和存储没有任何问题,但是如果您必须只使用可打印的字符串,那么您应该使用 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。真的,我错了,我没注意到是解密方向。当然,如果你知道解密byte[]是一个有效的字符串,那么执行你的代码所做的事情是完全正确的。

回答by Xdg

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

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

回答by Stefan Gehrig

Please see here:

请看这里:

The problem you're encountering is a padding-issue. I don't know Java, but AES/ECB/PKCS5Paddinglooks 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 本身仅支持NULL-padding。这就是 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);

回答by Stefan Gehrig

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:

爪哇:

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

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

例如,PHP 不得默认使用 UTF-8。