AES加解密【示例】

代码

/**
 * AES算法加密。JRE默认只能用16个字节(128)位密钥
 */
public class AESUtils {
    //使用指定转换的 Cipher 对象
    public static final String CIPHER_ALGORITHM_AES = "AES";
    public static final String CIPHER_ALGORITHM_ECB = "AES/ECB/PKCS5Padding";
    public static final String CIPHER_ALGORITHM_CBC = "AES/CBC/PKCS5Padding";
    //【AES/CBC/NoPadding】模式下,待加密内容的长度必须是16的倍数,否则: javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes
    public static final String CIPHER_ALGORITHM_CBC_NoPadding = "AES/CBC/NoPadding";

    private static final String ALGORITHM_MD5 = "md5";
    private static final String CHARSET = "UTF-8";
    private static final String ALGORITHM_AES = "AES";

    public static void main(String[] args) throws Exception {
        byte[] key = getAESKeyBytes("1");//要求密钥必须是16位的
        test("0123456789123456".getBytes(CHARSET), key);
        test("1".getBytes(CHARSET), key);
    }
    private static void test(byte[] source, byte[] key) throws Exception {
        System.out.println("待加密内容的长度为【" + source.length + "】密钥的长度为【" + key.length + "】");
        test2(source, key, CIPHER_ALGORITHM_AES);
        test2(source, key, CIPHER_ALGORITHM_ECB);
        test2(source, key, CIPHER_ALGORITHM_CBC);
        test2(source, key, CIPHER_ALGORITHM_CBC_NoPadding);
        System.out.println("================================================");
    }
    private static void test2(byte[] source, byte[] key, String mode) throws Exception {
        //生成的密文
        byte[] cryptograph = encryptOrDecrypt(source, key, mode, Cipher.ENCRYPT_MODE);
        //通过Base64编码为ASCII字符后传输
        String cryptographStr = Base64.getEncoder().encodeToString(cryptograph);
        //收到后先用Base64解码
        byte[] targetBase64 = Base64.getDecoder().decode(cryptographStr.getBytes(CHARSET));
        // 解密密文
        byte[] target = encryptOrDecrypt(targetBase64, key, mode, Cipher.DECRYPT_MODE);
        System.out.println("加密前【" + new String(source, CHARSET) + "】\n加密后【" + cryptographStr + "】\n解密后【" + new String(target, CHARSET) + "】");
        //如果原始数据长度等于16*n,则使用NoPadding时加密后数据长度等于16*n,其它情况下加密数据长度等于16*(n+1)
        //在不足16的整数倍的情况下,假如原始数据长度等于16*n+m(m小于16),除了NoPadding填充之外的任何方式,加密数据长度都等于16*(n+1)
        System.out.println("加密前字节数【" + source.length + "】加密后字节数【" + cryptograph.length + "】解密后字节数【" + target.length + "】\n");
    }

    /**
     * 加密或解密。加密和解密用的同一个算法和密钥
     * @param source    要加密或解密的数据
     * @param key    密钥
     * @param transformation
     * @param mode        加密或解密模式。值请选择Cipher.DECRYPT_MODE或Cipher.ENCRYPT_MODE
     * @return         加密或解密后的数据
     */
    public static byte[] encryptOrDecrypt(byte[] source, byte[] key, String transformation, int mode) throws Exception {
        Cipher cipher = Cipher.getInstance(transformation);
        Key secretKey = new SecretKeySpec(key, ALGORITHM_AES); //密钥
        if (transformation.equals(CIPHER_ALGORITHM_CBC) || transformation.equals(CIPHER_ALGORITHM_CBC_NoPadding)) {
            cipher.init(mode, secretKey, new IvParameterSpec(getIV()));//指定一个初始化向量 (Initialization vector,IV), IV 必须是16位
            return cipher.doFinal(source);
        } else {
            cipher.init(mode, secretKey);
            return cipher.doFinal(source);
        }
    }
    /**
     * 根据字符串生成AES的密钥字节数组<br>
     */
    public static byte[] getAESKeyBytes(String sKey) throws Exception {
        //获得指定摘要算法的 MessageDigest 对象
        MessageDigest md = MessageDigest.getInstance(ALGORITHM_MD5);
        //使用指定的字节更新摘要(继续多部分加密或解密操作,以处理其他数据部分)
        md.update(sKey.getBytes(CHARSET));
        //获得密文。注意:长度为16而不是32。一个字节(byte)占8位(bit)
        return md.digest();
    }
    /**
     * 指定一个初始化向量 (Initialization vector,IV),IV 必须是16位
     */
    private static final byte[] getIV() throws Exception {
        return "1234567812345678".getBytes(CHARSET);
    }

}


结果

待加密内容的长度为【16】密钥的长度为【16】
加密前【0123456789123456】
加密后【sYT9Qk02gZc20Xfxgr5I6QPIY0E8Zrgj6d20wPlxxCg=】
解密后【0123456789123456】
加密前字节数【16】加密后字节数【32】解密后字节数【16】

加密前【0123456789123456】
加密后【sYT9Qk02gZc20Xfxgr5I6QPIY0E8Zrgj6d20wPlxxCg=】
解密后【0123456789123456】
加密前字节数【16】加密后字节数【32】解密后字节数【16】

加密前【0123456789123456】
加密后【W4Lsqr4gXF2B/JuT5A46N1dslm+BNO+l40T36CUcPNg=】
解密后【0123456789123456】
加密前字节数【16】加密后字节数【32】解密后字节数【16】

加密前【0123456789123456】
加密后【W4Lsqr4gXF2B/JuT5A46Nw==】
解密后【0123456789123456】
加密前字节数【16】加密后字节数【16】解密后字节数【16】
================================================
待加密内容的长度为【1】密钥的长度为【16】
加密前【1】
加密后【EfgHFA7AwlCEtnAW6VJG5A==】
解密后【1】
加密前字节数【1】加密后字节数【16】解密后字节数【1】

加密前【1】
加密后【EfgHFA7AwlCEtnAW6VJG5A==】
解密后【1】
加密前字节数【1】加密后字节数【16】解密后字节数【1】

加密前【1】
加密后【I1neHBjrE8NoYocaxiCuDg==】
解密后【1】
加密前字节数【1】加密后字节数【16】解密后字节数【1】

Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes

附件列表

上一篇:Qt addStretch()详解


下一篇:基于Grafana的监控数据钻取功能应用实践