1、加密算法

一两个月没写过博客了,上次把尚筹网的博客补了一波,然后就在找工作,这不,这个月刚入职,领导给了个代码优化的任务,使用SornarQube对项目代码进行分析,然后根据分析出来的点逐点优化。

加密算法有很多,时间长了,以前的加密算法可能会被发现漏洞,所以加密算法也要不停地更新。在项目中SornarQube就分析出了这么个问题,提示当前的加密算法(DES)太老了,不安全,应该使用AES加密。

2、AES加密

对于一个加密算法,我们首先需要了解几个概念:

  • 明文(没有加密的数据)
  • 密文(加密后的数据)
  • 密钥(约定好的用于加密和解密的钥匙)
  • 加密函数(具体加密解密的算法)

弄懂了这个之后,就可以很清楚地知道,要在Java中应用AES加密算法,需要定义两个方法,一个是加密,一个是解密。

2.1 具体加解密算法

AES算法在Java中已有具体实现,我们只需要调用即可:

private  byte[] aes(byte[] contentArray, int mode, SecretKey secretKey) throws Exception {
    Cipher cipher = Cipher.getInstance(ALGORITHM);
    cipher.init(mode, secretKey);
    return cipher.doFinal(contentArray);
}

其中三个参数分别为:mode表示本次操作是加密还是解密,contentArray表示加密的明文或解密的密文,secretKey表示密钥。

2.2 加密方法

/**
 * 加密
 * @param content 原文
 * @return 密文
 * @throws Exception 异常
 */
public String encode(String content) throws Exception {
    BASE64Encoder base64Encoder = new BASE64Encoder();
    return base64Encoder.encode(aes(content.getBytes(charset), Cipher.ENCRYPT_MODE, secretKey));
}

2.3 解密方法

/**
 * 解密
 * @param content 密文
 * @return 原文
 * @throws Exception 异常
 */
public String decode(String content) throws Exception {
    BASE64Decoder base64Decoder = new BASE64Decoder();
    byte[] result = aes(base64Decoder.decodeBuffer(content),Cipher.DECRYPT_MODE,secretKey);
    return new String(result,charset);
}

2.4 代码总览

将上面几个方法放在一个工具类中,代码总体如下:

public class EncryptUtil {

    private static final Logger log = LoggerFactory.getLogger(EncryptUtil.class);
    private static final String ALGORITHM = "AES";
    private Charset charset = null;
    private SecretKey secretKey = null;

    /**
     * 初始化
     * @param deSkey 密钥
     * @throws Exception
     */
    public EncryptUtil(String deSkey, String charset) throws Exception {
        if (StringUtils.isNotBlank(charset)) {
            this.charset = Charset.forName(charset);
        }
        secretKey = generateKey();
    }

    /**
     * 生成密钥
     * @return 密钥对象
     * @throws NoSuchAlgorithmException
     */
    public SecretKey generateKey() throws NoSuchAlgorithmException {
        KeyGenerator secretGenerator = KeyGenerator.getInstance(ALGORITHM);
        SecureRandom secureRandom = new SecureRandom();
        secretGenerator.init(secureRandom);
        return secretGenerator.generateKey();
    }

    /**
     * 加密
     * @param content 原文
     * @return 密文
     * @throws Exception 异常
     */
    public String encode(String content) throws Exception {
        BASE64Encoder base64Encoder = new BASE64Encoder();
        return base64Encoder.encode(aes(content.getBytes(charset), Cipher.ENCRYPT_MODE, secretKey));
    }

    /**
     * 解密
     * @param content 密文
     * @return 原文
     * @throws Exception 异常
     */
    public String decode(String content) throws Exception {
        BASE64Decoder base64Decoder = new BASE64Decoder();
        byte[] result = aes(base64Decoder.decodeBuffer(content),Cipher.DECRYPT_MODE,secretKey);
        return new String(result,charset);
    }

    private  byte[] aes(byte[] contentArray, int mode, SecretKey secretKey) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(mode, secretKey);
        return cipher.doFinal(contentArray);
    }

    public static void main(String[] args) {
        try {
            //String test = "pms-adapter-pwd";
            String test = "pms-adapter-pwd-prod";
            // 自定义密钥
            String key = "abcd9ba45bfd500642328ec03ad8ef1b6e75";
            EncryptUtil des = new EncryptUtil(key, "utf-8");
            System.out.println("加密前的字符:" + test);
            String encode = des.encode(test);
            System.out.println("加密后的字符:" + encode);
            String decode = des.decode(encode);
            System.out.println("解密后的字符:" + decode);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }
}

在工具类初始化的时候,需要传入两个值,一个是加密方和解密方约定的密钥,一个是字符串的编码格式。

3、工具类测试

public static void main(String[] args) {
    try {
        // 明文
        String test = "I love you.";
        // 自定义密钥
        String key = "abcd9ba45bfd500642328ec03ad8ef1b6e75";
        // 密钥生成
        EncryptUtil des = new EncryptUtil(key, "utf-8");
        System.out.println("加密前的字符:" + test);
        // 加密
        String encode = des.encode(test);
        System.out.println("加密后的字符:" + encode);
        // 解密
        String decode = des.decode(encode);
        System.out.println("解密后的字符:" + decode);
    } catch (Exception e) {
        log.error(e.getMessage());
    }
}

4、注意事项

在写这个代码的时候,我也看了好多博客,它们有一个共同点,就是在加密和解密的中间过程,输出的是字节数组,但是因为我这个是代码优化,我必须要返回字符串,于是我就用了byte数组转String的函数,最后失败了,因为byte数组转String后再转回byte数组,数据会发生变化,而AES加密算法最重要的就是解密的时候密文的位数必须是16位的倍数,而数据发生变化后,解密就会失败,所以要用BASE64Decoder。