2.Java开源AES/SM4/3DES对称加密算法介绍及其实现
前期内容导读:
对称秘钥加密
一般叫做对称加密
,对称
主要是指秘钥对
是对称的,对称
即相等
的意思;密钥对
就是指加密时使用的秘钥和解密时使用的秘钥,对称秘钥
也就是加密秘钥和解密秘钥相同;
综上,
对称秘钥加解密
就是加密和解密的秘钥相同的加密算法,即实际只有1个秘钥的加密算法;
1. bq-encryptor对称秘钥加密介绍
- 加密组件引入方法:
<dependency> <groupId>com.biuqu</groupId> <artifactId>bq-encryptor</artifactId> <version>1.0.5</version> </dependency>
1.1 对称秘钥加密算法列表如下:
名称 | 全称 | 秘钥长度 | 分组长度 | 常用模式 | 填充模式 | 生成秘钥? | 常用算法 | 特点 |
---|---|---|---|---|---|---|---|---|
AES | Advanced Encryption Standard | 256 | 128 | CBC CTR CFB | NoPadding PKCS5Padding | ✔ | AES/CBC/PKCS5Padding | 加密效率高 通常使用CBC/CTR模式 |
SM4 | SM4分组密码算法 | 128 | 128 | CBC CTR CFB | NoPadding PKCS5Padding | ✔ | SM4/CBC/PKCS5Padding SM4/CTR/NoPadding | 国密算法,安全性优于AES 256,可用于替换AES; 通常使用CBC/CTR模式 |
Triple Data Encryption Algorithm | 192 | 64 | NoPadding PKCS5Padding | ✖ | 安全性较差,建议使用AES/SM4来替代 |
加密长度
: 在加密算法中通常是指秘钥长度;在摘要算法中通常是指内容块的长度;分组长度
: 在对称加密算法中,通常是指一次性可加密的密文块大小,比如AES
/SM4
一次性可加密出128bit的数据,3DES
只能加密出64bit的数据;3DES
:是3个64bit的秘钥组合而成,所以才叫3重数据加密算法,每个加密的秘钥长度是64bit;对应的分组长度
也是64bit,而AES
/SM4
都是128bit;
1.2 对称秘钥加密算法的特点如下:
- 对称秘钥加密算法基本上都支持分段加密(也叫做
分组加密
),即报文超过了单次加密的最大长度,则自动叠加采取多次加密的方式,理论上对称加密是不限定报文大小的; - 对称加密的明文长度和密文长度的关系如下:
- 在无填充模式(如
NoPadding
)下,对应的密文长度为:(${data.length}/${groupLen})*${groupLen}
,即密文长度和明文长度相等,且要求明文数据长度必须为分组长度的整数倍,否则会报错; - 在有填充模式(如
PKCS5Padding
)下,密文长度为:(${data.length}/${groupLen}+1)*${groupLen}
;
- 在无填充模式(如
- 对称加密的
ECB工作模式
不支持偏移,体现在加密实现逻辑上就是不能加盐值,ECB工作模式
是不安全的,不推荐使用; - 基于
BouncyCastle
的封装,对称秘钥加密算法的加密逻辑和解密逻辑基本相同; - 在
BouncyCastle
中,AES/SM4/3DES
都支持由外部生成的任意长度的秘钥,但是AES
/SM4
只支持许可秘钥长度的秘钥加解密,3DES
支持大于或者等于合法秘钥长度的秘钥(实际是截取其前面的合法秘钥长度内容)加解密; - 对称加密的效率高,但是在请求双方都需要解密的场景下,需要把秘钥同时发给对方,存在一定的秘钥管理的安全风险;
- 考虑到加密的安全性,特地把
加密偏移量
(俗称盐值
)的加解密当成常规使用;而提供没有盐值
的加解密则仅为了和外部对接(己方为客户端,被迫接受服务端的不安全加密方式);
备注:
${data.length}/${groupLen}
计算为整除;${data.length}
表示明文长度,${groupLen}
表示分组长度;3DES
超出合法秘钥长度的秘钥与截取其合法秘钥长度的秘钥的加解密效果相同;
2. bq-encryptor对称秘钥加密实现
2.1 对称秘钥加密代码设计
bq-encryptor对称秘钥加密代码设计 | |||||
---|---|---|---|---|---|
算法名称 | 算法实现类 | 抽象类 | 是否安全 | 补充说明 | |
AES | AesEncryption | BaseSingleEncryption | ✔ | 只有256位是安全的 | |
SM4 | Sm4Encryption | ✔ | AES256的国内替代算法 | ||
3DES | Des3Encryption | ✖ | 不安全算法,不推荐使用 | ||
AES | AesSecureEncryption | BaseSecureSingleEncryption | ✔ | 在AES加解密时增加了盐值 | |
SM4 | Sm4SecureEncryption | ✔ | 在SM4加解密时增加了盐值 |
2.2 对称秘钥加密核心逻辑
-
对称秘钥加密的核心逻辑在
BaseSingleEncryption
抽象类(BaseSecureSingleEncryption
亦为其子类)中:/** * 加解密 * * @param data 报文 * @param key 秘钥 * @param salt 盐值(偏移向量) * @param cipherMode 加密/解密(1和2分别表示加密和解密,参见{@link javax.crypto.Cipher#DECRYPT_MODE}) * @return 加解密后的报文 */ public byte[] doCipher(byte[] data, byte[] key, byte[] salt, int cipherMode) { try { Key keyObj = toKey(key); Cipher cipher = Cipher.getInstance(this.getPaddingMode(), this.getProvider()); if (null == salt) { salt = new byte[EncryptionConst.DEFAULT_SALT_LEN]; } IvParameterSpec vector = new IvParameterSpec(salt, 0, cipher.getBlockSize()); if (!this.getPaddingMode().contains(ECB_MODE)) { cipher.init(cipherMode, keyObj, vector); } else { cipher.init(cipherMode, keyObj); } return cipher.doFinal(data); } catch (Exception e) { throw new EncryptionException("do single key encrypt/decrypt error.", e); } }
总结下加密/解密的处理逻辑:
- 根据二进制秘钥获取秘钥对象;
- 设置盐值(ECB模式没有盐值);
- 初始化
cipher
对象并加密/解密;
- 在抽象类
BaseSingleEncryption
的基础上,AesEncryption/Sm4Encryption/Des3Encryption仅需要定义其默认的加密长度、加密算法及填充模式默认值即可; BaseSecureSingleEncryption
则是在抽象类BaseSingleEncryption
的基础上,对加解密逻辑都拼接了盐值的处理:/** * 自动填充盐值的对称秘钥加密计算 * * @param data 明文 * @param key 秘钥 * @param salt 盐值(安全加密中不使用) * @return 随机盐值拼接加密后的报文 */ @Override public byte[] encrypt(byte[] data, byte[] key, byte[] salt) { byte[] saltBytes = this.genSalt(); byte[] enData = this.doCipher(data, key, saltBytes, Cipher.ENCRYPT_MODE); byte[] newData = new byte[saltBytes.length + enData.length]; System.arraycopy(saltBytes, 0, newData, 0, saltBytes.length); System.arraycopy(enData, 0, newData, saltBytes.length, enData.length); return newData; } /** * 解密带盐值的对称加密数据 * * @param data 盐值拼接密文的数据 * @param key 秘钥 * @param salt 盐值(安全加密中不使用) * @return 明文 */ @Override public byte[] decrypt(byte[] data, byte[] key, byte[] salt) { byte[] saltBytes = new byte[this.saltLen]; byte[] enData = new byte[data.length - saltBytes.length]; System.arraycopy(data, 0, saltBytes, 0, saltBytes.length); System.arraycopy(data, saltBytes.length, enData, 0, enData.length); return this.doCipher(enData, key, saltBytes, Cipher.DECRYPT_MODE); }
- 在抽象类
BaseSecureSingleEncryption
的基础上,AesSecureEncryption
/Sm4SecureEncryption
仅需要定义其默认的加密长度、加密算法及填充模式默认值即可;
3. bq-encryptor对称秘钥加密使用
以SM4算法为例,可以有如下3种使用方式:
- 使用方式1:直接创建SM4加密对象
BaseSingleEncryption encryption = new Sm4Encryption();
- 使用方式2:通过算法工厂创建SM4加密对象
BaseSingleEncryption encryption = EncryptionFactory.SM4.createAlgorithm();
- 使用方式3:安全加密器(SecurityFacade)/加密器(HsmFacade)获取初始化好了的带初始秘钥的SM4加密器
BaseSingleEncryptor encryptor1 = ((EncryptSecurityImpl)securityFacade.getEncryptSecurity()).getSingleEncryptor(true); BaseSingleEncryptor encryptor2 = ((EncryptHsmImpl)hsmFacade.getEncryptHsm()).getSingleEncryptor(true);