V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
rqxiao
V2EX  ›  程序员

关于 aes 算法的使用问题

  •  1
     
  •   rqxiao · May 26, 2020 · 2791 views
    This topic created in 2173 days ago, the information mentioned may be changed or developed.

    网上找的一段代码,用这代码的进行测试,生成的结果到好几个 aes 在线加密网站上去测试 ,测试结果都不对,麻烦请教下这段 aes 工具类代码有错吗?

    第二个就是如果秘钥长度用 256 位的, 这样设置 kg.init(256, new SecureRandom(key.getBytes()));和 kg.init(128, new SecureRandom(key.getBytes()));结果是一样的 是怎么回事额。

    import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.logging.Level; import java.util.logging.Logger;

    public class AESUtil2 {

    private static final String KEY_ALGORITHM = "AES";
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";//默认的加密算法
    
    /**
     * AES 加密操作
     *
     * @param content 待加密内容
     * @param key 加密密钥
     * @return 返回 Base64 转码后的加密数据
     */
    public static String encrypt(String content, String key) {
        try {
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 创建密码器
    
            byte[] byteContent = content.getBytes("utf-8");
    
            cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));// 初始化为加密模式的密码器
    
            byte[] result = cipher.doFinal(byteContent);// 加密
    
            return java.util.Base64.getEncoder().encodeToString(result);//通过 Base64 转码返回
    
        } catch (Exception ex) {
            Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex);
        }
    
        return null;
    }
    
    /**
     * AES 解密操作
     *
     * @param content
     * @param key
     * @return
     */
    public static String decrypt(String content, String key) {
    
        try {
            //实例化
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
    
            //使用密钥初始化,设置为解密模式
            cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key));
    
            //执行操作
            byte[] result = cipher.doFinal(java.util.Base64.getDecoder().decode(content));
    
    
            return new String(result, "utf-8");
        } catch (Exception ex) {
            Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex);
        }
    
        return null;
    }
    
    /**
     * 生成加密秘钥
     *
     * @return
     */
    private static SecretKeySpec getSecretKey(final String key) {
        //返回生成指定算法密钥生成器的 KeyGenerator 对象
        KeyGenerator kg = null;
    
        try {
            kg = KeyGenerator.getInstance(KEY_ALGORITHM);
    
            //AES 要求密钥长度为 128
            kg.init(128, new SecureRandom(key.getBytes()));
    
            //生成一个密钥
            SecretKey secretKey = kg.generateKey();
    
            return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);// 转换为 AES 专用密钥
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex);
        }
    
        return null;
    }
    
    public static void main(String[] args) {
        String content = "hello world";
        String key = "password";
        System.out.println("content:" + content);
        String s1 = AESUtil.encrypt(content, key);
        System.out.println("s1:" + s1);
        System.out.println("s2:"+AESUtil.decrypt(s1, key));
        //content:hello world
        //s1:xvhDfp2GjtmdjK2PbEF4WA==
        //s2:hello world
    }
    

    }

    Supplement 1  ·  May 26, 2020
    额 String s1 = AESUtil2.encrypt(content, key);
    System.out.println("s1:" + s1);
    System.out.println("s2:"+AESUtil2.decrypt(s1, key));

    这个部分有些错 128 和 256 不一样,sorry
    Supplement 2  ·  May 26, 2020
    content:hello world
    s1:TrpUaOCYnG+h7ff8EEE4Kg==
    s2:hello world

    这份是 256 的返回
    17 replies    2020-05-27 14:34:27 +08:00
    louislivi
        1
    louislivi  
       May 26, 2020
    ```java
    public class AESUtil {

    private static final String KEY_ALGORITHM = "AES";
    //默认的加密算法
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";

    private static String key = "1H5c65423RR23VcYdFdCGcpLlNGvUNvdM8yUNJb0809";

    /**
    * AES 加密操作
    *
    * @param content 待加密内容
    * @return 返回 Base64 转码后的加密数据
    */
    public static String encrypt(String content) {
    try {
    // 创建密码器
    Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);

    byte[] byteContent = content.getBytes("utf-8");
    // 初始化为加密模式的密码器
    cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key));
    // 加密
    byte[] result = cipher.doFinal(byteContent);
    //通过 Base64 转码返回
    return new String(Base64.encodeBase64(result));

    } catch (Exception ex) {
    Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex);
    }

    return null;
    }

    /**
    * AES 解密操作
    *
    * @param content
    * @return
    */
    public static String decrypt(String content) {
    try {
    //实例化
    Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
    //使用密钥初始化,设置为解密模式
    cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key));
    //执行操作
    byte[] result = cipher.doFinal(Base64.decodeBase64(content));
    return new String(result, "utf-8");
    } catch (Exception ex) {
    Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
    }

    /**
    * 生成加密秘钥
    *
    * @return
    */
    private static SecretKeySpec getSecretKey(final String key) {
    //返回生成指定算法密钥生成器的 KeyGenerator 对象
    KeyGenerator kg = null;

    try {
    kg = KeyGenerator.getInstance(KEY_ALGORITHM);
    //AES 要求密钥长度为 128
    SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
    secureRandom.setSeed(key.getBytes("utf-8"));
    kg.init(128, secureRandom);

    //生成一个密钥
    SecretKey secretKey = kg.generateKey();
    // 转换为 AES 专用密钥
    return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);
    } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
    Logger.getLogger(AESUtil.class.getName()).log(Level.SEVERE, null, ex);
    }

    return null;
    }
    ```
    Kamiyu0087
        2
    Kamiyu0087  
       May 26, 2020
    encrypt 和 decrypt 函数,实例化 Cipher 那里
    secretKey 不能直接调用 getSecretKey 方法,每次生成的都是不一样的
    应该先生成 Key,然后 encrypt 和 decrypt 函数把这个 Key 作为参数传过去生成 Cipher
    rqxiao
        3
    rqxiao  
    OP
       May 26, 2020
    @Kamiyu0087 试了下 用同一个 key 多次调用 getSecretKey 结果是一样的额
    Jrue0011
        4
    Jrue0011  
       May 26, 2020
    第二点试了下 128 和 256 生成字节数组长度是不一样的

    然后关于你使用在线网站测试错误的原因,你的工具类里传入的密码只是作为随机数种子来生成密钥,在线网站应该是直接用密码 getBytes 凑 16 字节作为密钥,结果就不一样了
    rqxiao
        5
    rqxiao  
    OP
       May 26, 2020
    @Jrue0011 第二点是不一样,自己没有仔细测试。不好意思还请问下 网上说 java aes 不支持 256 。但现在我用 java8 试了,能成功加解密,不清楚这个说法是指会出现什么情况
    rqxiao
        6
    rqxiao  
    OP
       May 26, 2020
    @louislivi 秘钥的确是要指定编码,谢谢
    Jrue0011
        7
    Jrue0011  
       May 26, 2020
    @rqxiao 这个我记得之前看到过,是说 jdk1.8.0_151 和 152 以前有限制,之后的版本就没有了
    https://my.oschina.net/u/1037605/blog/3026103
    dallaslu
        8
    dallaslu  
       May 26, 2020
    最近用过,不过是这么写的

    ```SecretKeySpec key = new SecretKeySpec(aeskey.getBytes(), "AES");```
    xiangyuecn
        9
    xiangyuecn  
       May 26, 2020
    IV 呢?要是你测试的那些网站不提供 填写 IV 或者 返回 IV 或者 告知固定 IV 的就是在耍流氓
    dallaslu
        10
    dallaslu  
       May 26, 2020
    @dallaslu aesKey 就是原密钥。如果在线加密网站也是这种方式的话,你应该用 secretKey.getEncoded() 的值做密钥去测试解密。
    dallaslu
        11
    dallaslu  
       May 26, 2020
    @xiangyuecn 貌似 ECB 模式不用 IV ?
    xiangyuecn
        12
    xiangyuecn  
       May 26, 2020
    @dallaslu #11 原来如此
    muzuiget
        13
    muzuiget  
       May 26, 2020
    严格说 AES 只支持加密 16 字节数据,同样的密钥的明文产生用样的密文,那些网站大概就是测试这种接口。

    实际使用在此基础上需要带各种“模式”,也就是对明文分组,16 字节一组,不足则填充为 16 字节,然后再加上一组随机数据,所谓 IV,还有校验码等等各种数据。所以说,要输出同样的结果,得把这些参数也搞成一致才行。
    wnpllrzodiac
        14
    wnpllrzodiac  
       May 26, 2020 via Android
    aes iv 加密方式还有好几种 padding 也有好几种。不同语言加密出来的秘文不一定一样,很头疼的
    rqxiao
        15
    rqxiao  
    OP
       May 26, 2020
    @muzuiget 百度 AES 第一个网站 模式 填充 位置 字符集都有 。就是不知道是我代码错了还是什么
    rqxiao
        16
    rqxiao  
    OP
       May 26, 2020
    @wnpllrzodiac 百度 AES 第一个网站 模式 填充 位置 字符集都有 。就是不知道是我代码错了还是什么
    rqxiao
        17
    rqxiao  
    OP
       May 27, 2020
    @Jrue0011 感谢
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5456 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 94ms · UTC 03:48 · PVG 11:48 · LAX 20:48 · JFK 23:48
    ♥ Do have faith in what you're doing.