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

我的问题是:我在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 …

 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 "

Crypt: $crypt

";

输出:

 Crypt: example                          

使用PHP来密钥“示例”,密钥为“Zvzpv8 / PXbezPCZpxzQKzL / FeoPw68jIb + NONX / LIi8 =”我得到“YTYhgp4zC + w5IsViTR5PUkHMX4i7JzvA6NJT1FqhoGY =”。 使用Java用相同的密钥加密相同的东西,我得到“+ tdAZqTE7WAVPXhB3Tp5 + g ==”。

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

BUG#1

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

BUG#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[]是一个有效的字符串,那么完成代码所做的就完全正确了。

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

你必须重写脚本的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_decode($ sKey),因为你的密钥是base64编码的。

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

我希望这对某人有用! 🙂

请看这里:

  • iOS和.NET的PHP加密差异

  • AES加密在C#中,在PHP中解密

  • PHP和C#中的DES加密

您遇到的问题是填充问题。 我不了解Java,但AES/ECB/PKCS5Padding看起来像是在使用PKCS#5(基本上与PKCS#7相同)填充,而PHP本身只支持NULL -padding。 这就是PKCS#5/7的作用:

使用1到8个字节之间的填充字符串填充输入,使总长度成为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); 

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

PHP( s.utf8_encode()函数):

 $strAndBlob = utf8_encode("My string"); 

Java的:

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

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