V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
youxiaer
V2EX  ›  问与答

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

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

    最近有一个需求,需要将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 (NoSuchAlgorithmException 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   ·   实用小工具   ·   5660 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 01:30 · PVG 09:30 · LAX 17:30 · JFK 20:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.