如何用 Java 实现 AES 加密算法(已有 php 版本) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
youxiaer
V2EX    问与答

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

  •  
  •   youxiaer 2015-05-04 19:16:24 +08:00 6679 次点击
    这是一个创建于 3817 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近有一个需求,需要将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"

    第 1 条附言    2015-05-04 19:50:37 +08:00
    上面的两个方法有错误,正确的方法是下面这两个

    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"
    11 条回复    2015-05-04 22:42:39 +08:00
    Septembers
        1
    Septembers  
       2015-05-04 19:19:22 +08:00
    Java和PHP,AES无非就是参数不匹配,主要就是Padding参数
    ilotuo
        2
    ilotuo  
       2015-05-04 19:26:24 +08:00 via Android
    丢 直接github搜 aes.java咩有
    wy315700
        3
    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 (NoSuchAlgorithmExeption e) {
    e.printStackTrace();
    return null;
    } catch (InvalidKeySpecException e) {
    e.printStackTrace();
    return null;
    }
    youxiaer
        4
    youxiaer  
    OP
       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
        5
    wy315700  
       2015-05-04 19:39:24 +08:00
    @youxiaer IV是16字节的,

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

    另外,自定义IV或者固定IV都是不安全的。正确的做法应该是,生成一个随机块nonce,然后用一个独立的key加密用作IV
    youxiaer
        6
    youxiaer  
    OP
       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
        7
    youxiaer  
    OP
       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
        8
    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
        9
    youxiaer  
    OP
       2015-05-04 20:51:31 +08:00
    @wy315700
    我后来也怀疑IV是hex编码,但是线上环境的的IV的32个字节并不是16进制的数字。
    我再找找方法,非常感谢。
    Gonster
        10
    Gonster  
       2015-05-04 22:33:37 +08:00
    @wy315700 搭车问一下0 ,0 , 如果我用AES的CBC工作模式或者其他使用IV的工作模式加密一些数据存储起来,为了安全不使用固定的IV或自定义的IV,那事后要解密的话,我是不是应该在先前把IV和密文都存储起来?或者解密的是另一方,秘钥是约定好的,我还需要将IV传递给那一放?
    wy315700
        11
    wy315700  
       2015-05-04 22:42:39 +08:00   2
    @Gonster CBC模式里,IV是要和密文一起传输的,所以为了安全起见,
    1 IV不能重复
    2 IV不可预测
    3 IV是密文空间的一个元素。

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

    然后 如果 存储的话,不推荐用CBC,理由就是,CBC里面,明文修改了一个字节以后,从之后的密文都要修改,
    推荐用random - CTR模式
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5118 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 09:24 PVG 17:24 LAX 02:24 JFK 05:24
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86