如何用 Java 实现 AES 加密算法(已有 php 版本)

2015-05-04 19:16:24 +08:00
 youxiaer

最近有一个需求,需要将php写的加密算法用java实现,php的代码已经有了,但是用java改写,尝试了好多久也没有成功。有知道的同学,希望不吝赐教,谢谢。

php的代码如下:

<?php

function simple_encrypt($text)
{
$key = "anything";
$vi = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND);
return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $vi)));
}

function simple_decrypt($text)
{
$key = "anything";
$vi = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND);
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($text), MCRYPT_MODE_ECB, $vi));
}

$str = '12345';
$en = simple_encrypt($str);
$de = simple_decrypt($en);
var_dump($en, $de);

?>

执行结果为:
string(44) "uEJfzXHxFVIlu2KGCtToiouT/EeMeat8CHvgkN5kzA4="
string(5) "12345"

6298 次点击
所在节点    问与答
11 条回复
Septembers
2015-05-04 19:19:22 +08:00
Java和PHP,AES无非就是参数不匹配,主要就是Padding参数
ilotuo
2015-05-04 19:26:24 +08:00
丢 直接github搜 aes.java咩有
wy315700
2015-05-04 19:30:04 +08:00
ECB模式没有IV啊
CBC才有

public static String encryptWithAES(byte[] text, String key) {
Cipher c = null;
try {
c = Cipher.getInstance("AES/ECB/PKCS7Padding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
try {
c.init(Cipher.ENCRYPT_MODE, generateAESKey(key));
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] enBytes = new byte[0];
try {
enBytes = c.doFinal(text);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
String s = Utility.urlSafeBase64Enc(enBytes);
return s;
}
public static byte[] decryptWithAES(String text, String key) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException {
Cipher c = Cipher.getInstance("AES/ECB/PKCS7Padding");
c.init(Cipher.DECRYPT_MODE, generateAESKey(key));
byte[] enBytes = c.doFinal(Utility.urlSafeBase64Dec(text));
return enBytes;
}

public static SecretKey generateAESKey(String key){
SecretKeyFactory factory = null;
byte[] tmp2;
try {
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(key.toCharArray(), "key".getBytes(), 64, 128);
SecretKey tmp = factory.generateSecret(spec);
tmp2 = tmp.getEncoded();

// return secret;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch (InvalidKeySpecException e) {
e.printStackTrace();
return null;
}
youxiaer
2015-05-04 19:36:27 +08:00
@wy315700
恩,是CBC,我写错了,然后我用java实现,IV是32字节,执行就会报错。
java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16 bytes long
wy315700
2015-05-04 19:39:24 +08:00
@youxiaer IV是16字节的,

CBC里,IV是和密文块一样长的,AES一个Block是128位,也就是16字节,

另外,自定义IV或者固定IV都是不安全的。正确的做法应该是,生成一个随机块nonce,然后用一个独立的key加密用作IV
youxiaer
2015-05-04 19:42:42 +08:00
@wy315700
php 中的这个函数 mcrypt_decrypt,最后一个参数$vi 可以是32字节。其实我最想知道就是如何用java实现php的 mcrypt_decrypt函数。

string mcrypt_decrypt ( string $cipher , string $key , string $data , string $mode [, string $iv ] )
youxiaer
2015-05-04 19:49:45 +08:00
@wy315700
php中这两个方法应该是下面这样的。

function simple_encrypt($text)
{
$key = "anything";
$vi = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=';
$key = hash('sha256', $key, true);
$vi = base64_decode($vi);
return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $vi)));
}

function simple_decrypt($text)
{
$key = "anything";
$vi = 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=';
$key = hash('sha256', $key, true);
$vi = base64_decode($vi);
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($text), MCRYPT_MODE_CBC, $vi));
}


$str = '1234567890987654321';
$en = simple_encrypt($str);
$de = simple_decrypt($en);
var_dump($en, $de);

执行结果:

string(44) "OgmKJ5gu4rRYB47pVV5N1GyTW5+aKNjQnx2TFYjNdUI="
string(19) "1234567890987654321"
wy315700
2015-05-04 20:43:50 +08:00
@youxiaer http://www.cnblogs.com/arix04/archive/2009/06/26/1511839.html

不太清楚为什么PHP的IV是32字节的。。。难道是hex编码,就是4位用一个16进制数字。
youxiaer
2015-05-04 20:51:31 +08:00
@wy315700
我后来也怀疑IV是hex编码,但是线上环境的的IV的32个字节并不是16进制的数字。
我再找找方法,非常感谢。
Gonster
2015-05-04 22:33:37 +08:00
@wy315700 搭车问一下0 ,0 , 如果我用AES的CBC工作模式或者其他使用IV的工作模式加密一些数据存储起来,为了安全不使用固定的IV或自定义的IV,那事后要解密的话,我是不是应该在先前把IV和密文都存储起来?或者解密的是另一方,秘钥是约定好的,我还需要将IV传递给那一放?
wy315700
2015-05-04 22:42:39 +08:00
@Gonster CBC模式里,IV是要和密文一起传输的,所以为了安全起见,
1 IV不能重复
2 IV不可预测
3 IV是密文空间的一个元素。

TLS 1.1里有个BUG就是 把上一个密文片段的最后一块当做下一个CBC链的IV,

然后 如果 存储的话,不推荐用CBC,理由就是,CBC里面,明文修改了一个字节以后,从之后的密文都要修改,
推荐用random - CTR模式

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/188419

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX