• AES算法


    1.前言

    对接资方的时候用到了AES加密算法,之前经常用但是没好好梳理一下,这次有空就简单梳理一下,方面日后整理学习。
    AES是加密算法其中的一种,它是属于对称加密,对称加密的意思就是,加密以及解密用的都是同一个Key。相比于非对称加密RSA,SM2等,它的优点就是快。
    为了了解AES,我们大概从以下几个方面入手

    • 密钥支持的长度
    • 常用的工作模式(ECB模式/CBC模式)
    • Padding 的填充模式

    2. 密钥

    密钥是AES算法实现加解密的根本。AES 支持三种长度的密钥: 128bit (16B), 192bit(24B) , 256bit(32B)。
    AES256 安全性最高,AES128性能最优。本质是它们的加密处理轮数不同。

    AES128 10轮
    AES192 12轮
    AES256 14轮

    而我们默认的就是AES128,也就是会循环10轮。

    public static String generateAESKey() {
    	KeyGenerator keyGenerator = null;
    	try {
    		keyGenerator = KeyGenerator.getInstance("AES");
    	} catch (NoSuchAlgorithmException e) {
    		return null;
    	}
            // 这里代码秘钥的长度,128,192,256
    	keyGenerator.init(128);
    	SecretKey key = keyGenerator.generateKey();
    	byte[] keyExternal = key.getEncoded();
    	return Base64.encodeBase64String(keyExternal);
    }
    

    我们可以从如下图看出,生成的key的秘钥数组大小为 16,其中有些字符不是正常显示的ASCII。
    也就是说, 我们可以随机生成一段长度为16、24、32大小的字符串也可以充当秘钥。

    System.out.println(RandomUtil.randomString(16));

    当然这种秘钥我理解是不安全的,不建议使用

    3. 工作模式

    3.1. ECB模式

    ECB 模式是最简单块密码加密模式,加密前根据加密块大小(AES 128位)分成若干块,之后将每块使用相同的密钥单独加密,在该模式下,每个明文块的加密都是独立的,互不影响的。解密同理。
    优势

    • 简单
    • 有利于并行计算

    缺点

    • 相同的明文块经过加密会变成相同的密文块,因此安全性较差。

    3.2. CBC模式

    CBC模式引入一个新的概念:初始向量IV。IV的作用和MD5的"加盐"有些类似,目的是防止同样的明文块始终加密成相同的密文块。
    CBC模式原理:在每个明文块加密前会让那个明文块和IV向量先做异或操作。IV作为初始化变量,参与第一个明文块的异或,后续的每个明文块和它前一个明文块所加密出的密文块相异或,这样相同的明文块加密出来的密文块显然不一样。
    优势

    • 安全性更高

    缺点

    • 无法并行计算,性能上不如ECB
    • 引入初始向量IV,增加复杂度

    4. 常用的填充方式

    AES 算法在对明文加密的时候,并不是把整个明文加密成一整段密文,而是把明文拆分成几组独立的明文块,每一个明文块的长度128bit(16B),最后不足128bit(16B),会根据不同的Padding 填充模式进行填充,然后进行加密。
    总结:加密过程是先处理pading,后加密。解密过程是先进行分块解密,最后在处理Padding。
    例如:一段明文的长度198bit,按照128bit 拆分,第二个之后70bit,不足128bit,就需要对明文块进⾏填充(Padding)

    4.1 NoPadding

    不做任何填充,要求明文必须是16字节的整数倍。

    4.2 PKC5Padding(推荐)

    明文块少于128bit(16B),在明文块的末尾补足相应数量的字符,且每个字节的值等于缺少的字符数。
    如 明文:{1,2,3,4,5,6,7,8, a,b,c},缺少5个字节,则补全为{1,2,3,4,5,6,7,8, a,b,c,5,5,5,5,5,5}

    4.3 ISO10126Padding

    明文块少于128bit(16B),在明文块的末尾补足相应数量的字符,最后一个字符值等于缺少的字符数,其他字符填充随机数
    如 明文{1,2,3,4,5,6,7,8, a,b,c},缺少5个字节,则补全为{1,2,3,4,5,6,7,8, a,b,c,e,i,o,p,k,5}
    具体接口使用,请根据自己的业务需求去选择。

    5. AES代码示例

    private static final String KEY_ALGORITHM = "AES";
    private static final String ENCODING = StandardCharsets.UTF_8.name();
    private static final String AES_ECB_MODE = "AES/ECB/PKCS5Padding";
    private static final String AES_CBC_MODE = "AES/CBC/PKCS5Padding";
    
    // 因为AES块 为16字节,所以IV必须为16字节
    private static final String IV = RandomUtil.randomString(16);
    private static final IvParameterSpec ips = new IvParameterSpec(IV.getBytes());
    

    5.1 对称key的生成

    // key只要在16、24、32大小即可

    public static String generateAESKey() {
    	KeyGenerator keyGenerator = null;
    	try {
    		keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
    	} catch (NoSuchAlgorithmException e) {
    		return null;
    	}
    	keyGenerator.init(128);
    	SecretKey key = keyGenerator.generateKey();
    	byte[] keyExternal = key.getEncoded();
    	return Base64.encodeBase64String(keyExternal);
    }
    

    5.2 加密

    public static String encrypt(String content, String key) {
    	try {
    		byte[] bytesKey = Base64.decodeBase64(key);
    		SecretKeySpec secretKey = new SecretKeySpec(bytesKey, KEY_ALGORITHM);
    		Cipher cipher = Cipher.getInstance(AES_ECB_MODE);// 创建密码器
    		byte[] byteContent = content.getBytes(ENCODING);
    		cipher.init(Cipher.ENCRYPT_MODE, secretKey);// 初始化
    		byte[] result = cipher.doFinal(byteContent);// 加密
    		return Base64.encodeBase64String(result);
    	} catch (Exception e) {
    	}
    	return null;
    }
    

    5.3 解密

    public static String decrypt(String content, String key) {
    	try {
    		byte[] bytesKey = Base64.decodeBase64(key);
    		SecretKeySpec secretKey = new SecretKeySpec(bytesKey, KEY_ALGORITHM);
    		Cipher cipher = Cipher.getInstance(AES_ECB_MODE);// 创建密码器
    		cipher.init(Cipher.DECRYPT_MODE, secretKey);// 初始化
    		byte[] result = cipher.doFinal(Base64.decodeBase64(content));// 解密
    		return new String(result);
    	} catch (Exception e) {
    	}
    	return null;
    }
    

    备注:如果是CBC的模式,只要在cipher.init加入向量的ips即可

  • 相关阅读:
    与5G一起过中秋,天涯变咫尺
    用Python来开发安卓程序:(1)BeeWare安卓开发环境的搭建
    2023亚太杯数学建模思路 - 案例:最短时间生产计划安排
    2022关键之年,国产奶粉「争霸之秋」
    (2022版)一套教程搞定k8s安装到实战 | Kubernetes基础
    Jmeter怎么实现接口关联?
    Android:Canvas: trying to draw too large
    我是怎么从软件测试转到自动化测试岗的?亲身经历分享
    一文带你深入浅出指针和数组习题讲解
    在Windows下自己从源码编译Python3.10.13成安装包
  • 原文地址:https://www.cnblogs.com/wenbochang/p/17503923.html