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

14

我的问题是:我在Java中加密的内容可以在Java中完美解密,但PHP中的mcrypt无法解密。我使用mcrypt加密的内容可以用mcrypt解密,但不能在Java中解密。

我想要在Java应用程序和PHP页面之间发送和接收加密数据,因此我需要它们能够兼容。

以下是我的代码...

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);
}

运行中:

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));
}

输出:

example

PHP是一种流行的服务器端脚本语言,经常用于开发Web应用程序。 它可以与HTML结合使用,并可以轻松访问数据库。 PHP代码在服务器上执行,并生成HTML输出以向客户端显示内容。

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
    );
}

运行中:

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

输出:

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

用PHP和密钥"Zvzpv8/PXbezPCZpxzQKzL/FeoPw68jIb+NONX/LIi8="对"example"进行加密,得到了"YTYhgp4zC+w5IsViTR5PUkHMX4i7JzvA6NJT1FqhoGY="。
使用相同的密钥和Java加密同样的内容,得到了"+tdAZqTE7WAVPXhB3Tp5+g=="。
我已按正确顺序进行了base64编码和解码,并测试了Java和PHP之间的base64编码和解码兼容性,一切正常。

4个回答

14

错误#1

MCRYPT_RIJNDAEL_256不是AES。该常量中的256指的是块大小,而不是密钥大小。使用MCRYPT_RIJNDAEL_128可以获得与AES相同的算法。密钥大小仅由您提供的密钥参数中字节数设置。因此,提供32个字节即可获得具有256位密钥的AES。

错误#2

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

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

直接传输和存储byte[]并没有什么问题,但如果您必须使用可打印的字符串,则应该使用base64进行编码/解码。既然您已经广泛使用了base64,那么这似乎是可行的方法。我猜正确的两行代码应该是:

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

编辑:

关于错误#2我只是开玩笑。实际上,我错了,没有注意到它是解密方向的问题。当然,如果你知道解密后的byte[]是一个有效的字符串,那么你的代码就是完全正确的。


我明白了,我有点发现了,但我以为我在使用AES-128。所以确实是256。至于那些行,我只是这样做是因为我确定加密数据将始终只包含字符串,而没有二进制数据。 - LZZ

9

我知道这是一个旧话题,但是我会提供我的工作解决方案。

您需要重新编写脚本的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
  );
}

因为您的密钥是base64编码的,所以需要使用base64_decode($sKey)。

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

然后,您需要创建这个函数(感谢来自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); 
}

使用此代码进行编码/解码:
$decrypt = getDecrypt("6XremNEs1jv/Nnf/fRlQob6oG1jkge+5Ut3PL489oIo=", $key);
echo $decrypt;
echo "\n\n";
echo getEncrypt(pkcs5_pad("My very secret text:)", 16), $key);

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

pkcs5_pad 使我们免受了很多痛苦! - kouton

3
请看这里:

你遇到的问题是填充问题。我不知道Java,但是 AES/ECB/PKCS5Padding 看起来像是使用了PKCS#5(本质上与PKCS#7相同)填充,而PHP本地仅支持NULL填充。这就是PKCS#5/7的作用:

用一个填充字符串填充输入,使总长度恰好为8的倍数,填充字符串的每个字节的值设置为添加的字节数,即8个值为0x08的字节,7个值为0x07的字节,...,2个值为0x02的字节或一个值为0x01的字节。

因此,正确填充的PHP代码非常简单:

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

我按照你说的做了,但问题没有解决。不过,我注意到如果我使用128位,PHP可以解密Java加密的文本。我猜测我的Java应用程序正在使用AES-128进行加密,即使使用32字节密钥。 - LZZ

1

请记得为字符串使用相同的编码方式。尝试将两种语言的字符串转换为UTF-8格式,然后再将其转换为编码的二进制数据:

PHP(参见utf8_encode()函数):

$strAndBlob = utf8_encode("My string");

Java:

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

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


1
是的,那很重要,但这不是问题所在。 - LZZ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接