我需要在Java和php中编写一个明文,其结果必须相同.
给出以下条件:
以下代码可以工作并满足第一个和第二个要求,但它使用ECB作为模式,因此不使用初始化向量:
PHP:
<?php
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
$cleartext = 'abcdefghijklmnop';
$key128 = '1234567890123456';
$iv = '1234567890123456';
if (mcrypt_generic_init($cipher, $key128, $iv) != -1) //Parameter iv will be ignored in ECB mode
{
$cipherText = mcrypt_generic($cipher,$cleartext );
mcrypt_generic_deinit($cipher);
printf(bin2hex($cipherText));
}
?>
Run Code Online (Sandbox Code Playgroud)
输出为:fcad715bd73b5cb0488f840f3bad7889
JAVA:
public class AES {
public static void main(String[] args) throws Exception {
String cleartext = "abcdefghijklmnop";
String key = "1234567890123456";
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(cleartext.getBytes());
System.out.println(asHex(encrypted));
}
public static String asHex(byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
}
Run Code Online (Sandbox Code Playgroud)
输出是(与PHP版本相同):fcad715bd73b5cb0488f840f3bad7889
所以现在为了满足需求3和4,我在PHP版本中将模式更改为MCRYPT_MODE_CFB,以便代码如下所示:
<?php
$cipher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CFB, '');
$cleartext = 'abcdefghijklmnop';
$key128 = '1234567890123456';
$iv = '1234567890123456';
if (mcrypt_generic_init($cipher, $key128, $iv) != -1) //Parameter iv will be ignored in ECB mode
{
$cipherText = mcrypt_generic($cipher,$cleartext );
mcrypt_generic_deinit($cipher);
printf(bin2hex($cipherText));
}
?>
Run Code Online (Sandbox Code Playgroud)
这导致以下输出:14a53328feee801b3ee67b2fd627fea0
在JAVA版本中,我还调整了模式并将iv添加到我的Cipher对象的init函数中.
public class AES {
public static void main(String[] args) throws Exception {
String cleartext = "abcdefghijklmnop";
String key = "1234567890123456";
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec("1234567890123456".getBytes()));
byte[] encrypted = cipher.doFinal(cleartext.getBytes());
System.out.println(asHex(encrypted));
}
public static String asHex(byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
}
Run Code Online (Sandbox Code Playgroud)
但这里的输出是141eae68b93af782b284879a55b36f70,这与PHP版本不同.
有没有人知道JAVA和PHP版本之间的区别是什么?
它不记录良好,但PHP的MCRYPT_RIJNDAEL_128使用MCRYPT_MODE_CFB产生的结果与Java的一致AES/CFB8/NoPadding.
所以在PHP中这一行:
$encrypted = base64_encode( mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $key, $cleartext, MCRYPT_MODE_CFB, $iv ) );
Run Code Online (Sandbox Code Playgroud)
在Java中匹配此块:
SecretKeySpec key = new SecretKeySpec(KEY.getBytes(), "AES");
IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] output = cipher.doFinal(cleartext.getBytes());
String signature = Base64.encode(output);
Run Code Online (Sandbox Code Playgroud)
这里有三件事:
PHP 的“MCRYPT_RIJNDAEL_128”很可能与 Java 的“AES”算法不完全相同。AES Wiki 条目在介绍的底部讨论了 RIJNDAEL 和 AES 之间的区别。
您在 PHP 版本中使用 CBC,而在 Java 版本中使用 CFB。即使算法相同,这也肯定会给你不同的输出。
PHP 版本没有 padding,而 Java 版本则使用 PKCS5Padding。Java 版本应使用“Cipher.getInstance("AES/CFB/NoPadding");”实例化密码。
此外,您实际上需要生成一个 AES 密钥,而不是使用密钥字符串的字节构造 SecretKeySpec。这看起来像:
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom sec = new SecureRandom(key.getBytes());
keygen.init(128, sec);
Key key = keygen.generateKey();
SecretKeySpec skeySpec = new SecretKeySpec(key.getEncoded(), "AES");
...
Run Code Online (Sandbox Code Playgroud)
本质上,String 密钥是生成 SecretKey 的种子,而不是密钥本身。