1、对称加密简述
对称加密,又称对称密钥加密或私钥加密,是一种在加密和解密过程中使用相同一个密钥的加密算法。这种加密方式的核心在于,发送方使用某个密钥对数据进行加密,接收方则使用完全相同的密钥对数据进行解密。由于加密和解密使用的是同一把“钥匙”,因此得名“对称加密”。
对称加密是一种强大且高效的加密方式,但其安全性高度依赖于密钥的安全管理。因此,在实际应用中,需要采取额外的安全措施来保护密钥的安全,如使用密钥分发协议、密钥托管服务等。
对称加密的主要特点包括:
- 密钥管理:由于加密和解密使用相同的密钥,因此密钥的管理和分发成为了安全性的关键。如果密钥被泄露,加密的数据就可能被解密,从而导致信息泄露。
- 加密效率:对称加密算法通常具有较高的加密和解密速度,适用于处理大量数据。
- 安全性:许多对称加密算法,如 AES(高级加密标准),被设计为非常强大且难以破解,只要密钥保持安全。
常见的加密解密工具,如下图:
2、AES 算法:高级加密标准
AES(Advanced Encryption Standard),即高级加密标准,是一种对称加密算法,被广泛应用于信息安全领域,保护敏感信息的安全。以下是对AES算法的详细介绍:
2.1 基本原理
AES 算法基于迭代和分组密码设计,通过一系列的加密操作,将明文转化为密文。其加密流程主要包括字节代换、行移位、列混合和轮密钥加变换等步骤。解密过程为加密过程的逆过程。
2.2 密钥管理
AES 算法支持不同的密钥长度,分别为128位、192位和256位。密钥的长度决定了加密的轮数:
- 128位密钥:加密轮数为10轮。
- 192位密钥:加密轮数为12轮。
- 256位密钥:加密轮数为14轮。
在加密过程中,密钥扩展模块用于生成每轮加密所需的轮密钥。这些轮密钥通过复杂的数学运算生成,确保了加密的安全性。
2.3 加密流程
- 字节代换:这是AES加密的第一步,通过一个查找表(S盒),将每个字节替换为另一个字节,从而实现对明文数据的混淆。
- 行移位:对经过字节代换后的4×4字节矩阵的每一行进行循环左移操作,不同行的移动位数不同,以此进一步打乱数据的顺序。
- 列混合:对矩阵的每一列进行特定的线性变换,使得每一列的数据都与其他列的数据相互关联,增加了密文的复杂性。
- 轮密钥加:将经过前面三步处理后的矩阵与当前轮的轮密钥进行异或操作。
AES算法在不同的加密模式下,轮数有所不同,一般情况下为10轮、12轮或14轮,这取决于密钥的长度。密钥长度可以是128位、192位或256位。在每一轮加密中,都会重复上述四个步骤(最后一轮无列混合变换)。
3、加密模式
AES 主要有两种加密模式:ECB(Electronic Codebook,电子密码本)与 CBC(Cipher Block Chaining,密码分组链接)是两种不同的加密模式,它们在处理明文块的方式上存在显著差异,从而影响了其安全性和适用场景。
3.1 ECB 模式(电子密码本)
工作原理:
在 ECB 模式中,明文被分成固定大小的块,每个块独立加密。
加密过程是通过同一个密钥和加密算法实现的,每个块都使用相同的密钥和加密算法进行加密。
优点:
- 加密和解密过程相对简单。
- 有利于并行计算。
- 误差不会被传递。
缺点:
- 安全性较低,由于相同的明文块会产生相同的密文块,因此攻击者可以通过分析密文块来推测明文块的内容,从而可能导致机密性的泄露。
适用场景:
- 一般只用于加密数据块基本不重复,且较小的数据块进行加密操作。
3.2 CBC 模式(密码分组链接)
工作原理:
在 CBC 模式中,明文被分成固定大小的数据块,每个数据块分别与前一个块的加密结果进行异或运算,然后再进行加密。
第一个明文块在进行异或运算之前,需要与一个初始化向量(IV)进行异或运算。
优点:
- 增加了加密的随机性和安全性。由于每个明文块都与前一个加密块相关,因此即使两个明文块相同,它们的密文块也会不同。
- 防止了攻击者通过分析密文块来推测明文块的内容。
缺点:
- 相对于 ECB 模式,CBC 模式更复杂,需要额外的初始化向量(IV)。
- 在解密过程中,需要按照加密时的顺序依次解密每个块,因此解密过程相对较慢。
适用场景:
- 常用于加密机密性较高的数据。
- 适用于需要较高安全性的场景,如网络通信、金融交易等。
4、填充模式
在 AES 加密算法中,填充模式是一个重要的概念,它用于处理待加密数据长度不符合算法块大小要求的情况。PKCS5Padding 和 NoPadding 是 AES 算法中两种常见的填充模式。PKCS5Padding 填充模式通过添加填充字节来确保待加密数据的长度符合块大小要求,从而保证了加密过程的正确性和安全性。而 NoPadding 填充模式则不进行任何填充,要求待加密数据长度必须是块大小的整数倍。在实际应用中,应根据具体需求和安全要求选择合适的填充模式。
4.1 PKCS5Padding 填充模式
PKCS5Padding 填充模式是由 RSA 安全公司设计的一种填充方式,用于对数据的长度进行填充,以满足加密算法要求。在 AES 算法中,由于块大小为128位(即16字节),如果待加密的数据长度不是16的整数倍,就需要使用填充模式进行填充。
PKCS5Padding 填充模式的规则如下:
- 计算输入数据的长度与块大小(16字节)之间的差值。
- 根据这个差值,在输入数据的末尾添加相应数量的填充字节。
- 每个填充字节的值都等于差值本身(即填充的字节数)。
例如,如果输入数据的长度为45字节(即距离下一个块大小还差11字节),则 PKCS5Padding 会在数据末尾添加11个值为 0x0B(即11的十六进制表示)的填充字节。
在解密过程中,PKCS5Padding 填充模式会首先检查填充字节的正确性,即验证填充字节的值是否等于填充的字节数。如果验证通过,则删除填充字节,得到原始的明文数据。
4.2 NoPadding 填充模式
与 PKCS5Padding 填充模式不同,NoPadding 填充模式不进行任何填充。它要求待加密的数据长度必须是块大小的整数倍(即16字节的整数倍)。如果数据长度不符合要求,则无法进行加密。
NoPadding 填充模式的优点在于它不需要额外的填充字节,从而减少了加密数据的长度。然而,这也限制了它的使用范围,因为在实际应用中,很难保证待加密的数据长度总是符合块大小的要求。
此外,需要注意的是,NoPadding 填充模式并不是所有AES工作模式都支持的。在AES的CBC(Cipher Block Chaining,密码分组链接)工作模式下,由于需要前一个密文块来加密当前明文块,因此不支持NoPadding填充模式。而在ECB(Electronic Codebook,电子密码本)和OFB(Output Feedback,输出反馈)等模式下,由于每个明文块都是独立加密的,因此可以支持NoPadding填充模式。但是,由于ECB模式存在安全性问题(如容易受到选择明文攻击),因此在实际应用中应谨慎使用。
5、综合实例
【实例】使用 AES 算法,实现 ECB 模式(电子密码本)和 CBC 模式(密码分组链接)的数据加密与解密。
5.1 创建 AES 对称加密工具类
package com.pjb.util;import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;/*** AES 对称加密工具类* @author pan_junbiao**/
public class AESUtil
{//字符编码private final static String CHARSET_NAME = StandardCharsets.UTF_8.name();//算法private final static String ALGORITHM = "AES";//算法/加密模式/填充方式private static final String AES_ECB_PKCS5PADDING = "AES/ECB/PKCS5Padding";private static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5Padding";/*** 加密* 加密模式:ECB 电子密码本** @param data 原文数据* @param secretkey 密钥(密钥长度:32个字节)* @return 加密后的数据*/public static String encryptECB(String data, String secretkey){String result = null;try{//创建加密对象Cipher cipher = Cipher.getInstance(AES_ECB_PKCS5PADDING);//创建密钥对象SecretKey secretKey = getSecretKey(secretkey);//初始化加密对象(模式类型:加密模式)cipher.init(Cipher.ENCRYPT_MODE, secretKey);//执行加密方法byte[] bytes = cipher.doFinal(data.getBytes(CHARSET_NAME));//使用 Base64 编码,否则输出的结果是一串乱码result = Base64.getEncoder().encodeToString(bytes);return result;} catch (Exception ex){ex.printStackTrace();}return result;}/*** 解密* 加密模式:ECB 电子密码本** @param encodeData 加密数据* @param secretkey 密钥(密钥长度:32个字节)* @return 解密后的数据*/public static String decryptECB(String encodeData, String secretkey){String result = null;try{//创建解密对象Cipher cipher = Cipher.getInstance(AES_ECB_PKCS5PADDING);//创建密钥对象SecretKey secretKey = getSecretKey(secretkey);//初始化解密对象(模式类型:解密模式)cipher.init(Cipher.DECRYPT_MODE, secretKey);//使用 Base64 解码byte[] decodeBase64 = Base64.getDecoder().decode(encodeData.getBytes(CHARSET_NAME));//执行解密方法byte[] bytes = cipher.doFinal(decodeBase64);// 返回解密后的数据return new String(bytes);} catch (Exception ex){ex.printStackTrace();}return result;}/*** 加密* 加密模式:CBC 密码分组链接(更安全,但性能慢)** @param data 原文数据* @param secretkey 密钥(密钥长度:32个字节)* @param iv 向量(向量长度:16个字节)* @return 加密后的数据*/public static String encryptCBC(String data, String secretkey, String iv){String result = null;try{//创建加密对象Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);//创建密钥对象SecretKey secretKey = getSecretKey(secretkey);//创建向量规范IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(CHARSET_NAME));//初始化加密对象(模式类型:加密模式)cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);//执行加密方法byte[] bytes = cipher.doFinal(data.getBytes(CHARSET_NAME));//使用 Base64 编码,否则输出的结果是一串乱码result = Base64.getEncoder().encodeToString(bytes);return result;} catch (Exception ex){ex.printStackTrace();}return result;}/*** 解密* 加密模式:CBC 密码分组链接(更安全,但性能慢)** @param encodeData 加密数据* @param secretkey 密钥(密钥长度:32个字节)* @param iv 向量(向量长度:16个字节)* @return 解密后的数据*/public static String decryptCBC(String encodeData, String secretkey, String iv){String result = null;try{//创建解密对象Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);//创建密钥对象SecretKey secretKey = getSecretKey(secretkey);//创建向量规范IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(CHARSET_NAME));//初始化解密对象(模式类型:解密模式)cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);//使用 Base64 解码byte[] decodeBase64 = Base64.getDecoder().decode(encodeData.getBytes(CHARSET_NAME));//执行解密方法byte[] bytes = cipher.doFinal(decodeBase64);// 返回解密后的数据return new String(bytes);} catch (Exception ex){ex.printStackTrace();}return result;}/*** 创建密钥对象** @param secretkey 密钥*/private static SecretKey getSecretKey(String secretkey) throws Exception{//判断密钥长度int keyLength = secretkey.length();if (keyLength != 32){throw new Exception("请使用长度为256位(即32字节)的密钥");}//生成密钥对象SecretKeySpec secretKey = new SecretKeySpec(secretkey.getBytes(CHARSET_NAME), ALGORITHM);return secretKey;}
}
5.2 使用 ECB 模式的加密与解密
/*** 使用 AES 对称加密* 加密模式:ECB 电子密码本*/
@Test
public void testAesECB()
{String data = "您好,欢迎访问 pan_junbiao的博客";String secretkey = "ABCDEFGHABCDEFGHABCDEFGHABCDEFGH"; //密钥(密钥长度:32个字节)//使用 AES 加密String encodeAES = AESUtil.encryptECB(data, secretkey);//使用 AES 解密String decodeAES = AESUtil.decryptECB(encodeAES, secretkey);//打印结果System.out.println("使用 AES 对称加密,加密模式为 ECB 电子密码本:");System.out.println("原始数据:" + data);System.out.println("AES加密:" + encodeAES);System.out.println("AES解密:" + decodeAES);
}
执行结果:
5.3 使用 CBC 模式的加密与解密
/*** 使用 AES 对称加密* 加密模式:CBC 密码分组链接(更安全,但性能慢)*/
@Test
public void testAesCBC()
{String data = "您好,欢迎访问 pan_junbiao的博客";String secretkey = "ABCDEFGHABCDEFGHABCDEFGHABCDEFGH"; //密钥(密钥长度:32个字节)String iv = "ABCDEFGH65412356"; //向量(向量长度:16个字节)//使用 AES 加密String encodeAES = AESUtil.encryptCBC(data, secretkey, iv);//使用 AES 解密String decodeAES = AESUtil.decryptCBC(encodeAES, secretkey, iv);//打印结果System.out.println("使用 AES 对称加密,加密模式为 CBC 密码分组链接(更安全,但性能慢):");System.out.println("原始数据:" + data);System.out.println("AES加密:" + encodeAES);System.out.println("AES解密:" + decodeAES);
}
执行结果: