AES(高级加密标准)是一种对称密钥加密算法,用于加密和解密数据。它被广泛应用于各种安全领域,包括但不限于网络通信、数据存储和软件保护。
历史背景
AES是由比利时密码学家Joan Daemen和Vincent Rijmen设计的Rijndael算法演变而来的。美国国家标准与技术研究院(NIST)在2001年将其选为新的数据加密标准,以取代旧的DES(数据加密标准)。
工作原理
AES是一种对称密钥算法,这意味着加密和解密使用相同的密钥。它通过一系列复杂的数学变换来加密数据,这些变换包括字节替换、行移位、列混合和轮密钥加。
密钥长度
AES支持三种密钥长度:128位、192位和256位。密钥长度越长,安全性越高,但计算开销也越大。
轮数
AES的加密过程由多轮变换组成。对于128位密钥,有10轮变换;对于192位密钥,有12轮变换;对于256位密钥,有14轮变换。
安全性
AES被认为是安全的,因为它没有已知的有效攻击方法,除非使用暴力破解。然而,随着计算能力的提高,较短密钥长度的AES可能会变得不安全。
应用
AES被广泛应用于各种领域,包括但不限于:
- 网络安全:用于保护网络通信,如SSL/TLS协议。
- 数据存储:用于加密硬盘驱动器和数据库中的数据。
- 软件保护:用于防止软件被非法复制或篡改。
- 移动设备:用于保护智能手机和平板电脑上的数据。
性能
AES的性能取决于多种因素,包括密钥长度、数据大小和硬件平台。一般来说,AES的性能可以通过优化算法实现和使用硬件加速来提高。
标准和规范
AES的标准和规范由NIST发布,包括FIPS-197(联邦信息处理标准197)。
AES加密模式
-
电子密码本模式(Electronic Codebook, ECB)
- ECB模式是对每个独立的数据块直接使用相同的密钥进行加密,不考虑相邻块之间的关系。
- 优点:简单易实现,可以并行处理。
- 缺点:同一明文块会被加密成相同的密文块,导致模式重复,容易受到针对模式的攻击,不适用于加密模式重复或相关性强的数据。
-
密码分组链接模式(Cipher Block Chaining, CBC)
- CBC模式中,每个数据块在加密前与前一个块的密文进行异或操作,首次加密时与一个初始化向量(IV)异或。
- 优点:通过这种方式,每个块的加密不仅依赖于密钥,还依赖于前一个块的内容,从而增强了安全性,避免了ECB模式中的模式重放问题。
- 缺点:处理流式数据时需要保持状态,不适合并行处理,并且对IV的要求较高,如果IV泄露或重复,可能会削弱安全性。
-
密文反馈模式(Cipher Feedback, CFB)
- CFB模式下,加密过程产生的密文部分被用作下一个块的输入,形成一个自我同步的流。
- 优点:类似于流密码的工作方式,适合传输错误校验和恢复。
- 缺点:也需保持状态,不是完全并行的,并且对初始IV敏感。
-
输出反馈模式(Output Feedback, OFB)
- OFB模式将AES算法作为伪随机数生成器来创建密钥流,该密钥流随后与明文进行异或操作来加密数据。
- 优点:同样具有良好的错误传播特性,加密过程独立于明文。
- 缺点:同样依赖于初始IV,若IV重复则会导致密钥流重复,影响安全性。
-
计数器模式(Counter, CTR)
- CTR模式使用一个递增的计数器或nonce结合密钥产生密钥流,这个密钥流与明文进行异或加密。
- 优点:高度并行,适合硬件加速,同时具备优秀的错误恢复能力。
- 缺点:nonce必须唯一,否则安全性受损,且需要妥善管理nonce和计数器以防止重播攻击。
-
伽罗瓦/计数器模式(GCM):
-
AES用于生成密钥流,其原理与CTR模式相似,但使用的不是简单的递增计数器,而是基于计数器值(Nonce + 计数器)计算出的一个GHASH值,然后与明文进行异或操作实现加密。这里的Nonce应当是随机生成并且在系统内唯一的,以保证安全性。
-
提供数据机密性、完整性和认证。
-
AES填充算法
-
PKCS#7 / PKCS#5填充:
- 优点:
- 易于实现和识别,即使数据的结尾被修改也能正确检测出填充错误。
- 它对于所有块大小都适用,不仅是AES,而且可以应用于任何分组大小。
- 缺点:
- 对于非常小的数据,尤其是接近分组大小的数据,填充后的数据量会显著增加。
- 如果加密后的内容没有经过适当的身份验证,恶意用户可能尝试篡改填充数据以达到攻击目的。
- 优点:
-
Zero Padding(零填充):
- 优点:
- 实现极其简单。
- 缺点:
- 如果数据天然就以零结尾,或者在传输过程中自然出现零,填充可能混淆不清,这可能导致解密失败或者安全漏洞。
- 同样缺乏错误检测能力,容易遭受填充 oracle 攻击。
- 优点:
-
ANSI X.923填充:
- 优点:
- 解决了Zero Padding可能带来的混淆问题。
- 缺点:
- 同样需要额外的数据验证才能确保填充正确,否则仍有可能遭受攻击。
- 优点:
-
ISO 10126填充:
- 优点:
- 由于包含了随机成分,理论上提高了抵抗填充oracle攻击的能力。
- 缺点:
- 需要生成随机数据,可能会增加实现复杂度和开销,尤其是在资源受限的环境中。
- 同样需要填充数据的完整性检查。
- 优点:
-
No Padding(无填充):
- 优点:
- 当数据长度刚好是分组大小的整数倍时,不需要额外填充,节省空间。
- 缺点:
- 不适用于大多数实际场景,因为大部分数据长度不可能总是恰好匹配分组大小。
- 优点:
java实现
AES的 ECB模式PKCS5Padding填充算法
package com.yang.crypto.utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* <p>AES-128 ECB加密</p>
* <pre>
* 字符集:UTF-8
* 算法模式:ECB
* 数据块:128位
* 补码方式:PKCS5Padding
* 加密结果编码方式:Base64
* </pre>
*
* @author By: zhangchunyang
* Package com.yang.crypto.utils
* Ceate Time 2024-03-06 11:27
*/
public class CryptoAesUtil {
private static final String UTF8 = "UTF-8";
private static final String ALGORITHM = "AES";
/**
* 默认的加密算法
*/
private static final String ALGORITHM_CIPHER = "AES/ECB/PKCS5Padding";
private static final int LIMIT_LEN = 32;
public static String genAesKey() {
return StringUtil.random(LIMIT_LEN);
}
/**
* 生成一个SecretKey
*
* @param password 长度必须小于等于16
* @return 返回SecretKey
*/
public static SecretKey getSecretKey(String password) {
byte[] passwordData = password.getBytes();
if (passwordData.length > LIMIT_LEN) {
throw new IllegalArgumentException("password 长度必须小于等于32");
}
// 创建一个空的LIMIT_LEN位字节数组(默认值为0),16byte(128bit)
byte[] keyData = new byte[LIMIT_LEN];
System.arraycopy(passwordData, 0, keyData, 0, passwordData.length);
return new SecretKeySpec(keyData, ALGORITHM);
}
/**
* 加密
*
* @param data 待加密数据
* @param password 密码
* @return 返回加密成功后数据
* @throws Exception
*/
public static byte[] encrypt(byte[] data, String password) throws Exception {
SecretKey secretKey = getSecretKey(password);
// Ciphr完成加密或解密工作类
Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER);
// 对Cipher初始化,解密模式
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 加密data
return cipher.doFinal(data);
}
/**
* 解密
*
* @param data 待解密数据
* @param password 密码
* @return 返回解密后的数据
* @throws Exception
*/
public static byte[] decrypt(byte[] data, String password) throws Exception {
SecretKey secretKey = getSecretKey(password);
// Cipher完成加密或解密工作类
Cipher cipher = Cipher.getInstance(ALGORITHM_CIPHER);
// 对Cipher初始化,解密模式
cipher.init(Cipher.DECRYPT_MODE, secretKey);
// 解密data
return cipher.doFinal(data);
}
/**
* 文本加密
*
* @param content 明文
* @param password 密码
* @return 返回base64内容
* @throws Exception
*/
public static String encryptToBase64String(String content, String password) throws Exception {
byte[] data = content.getBytes(UTF8);
byte[] result = encrypt(data, password);
return Base64.encodeBase64String(result);
}
/**
* 文本解密
*
* @param base64String 待解密文本
* @param password 密码
* @return 返回明文
* @throws Exception
*/
public static String decryptFromBase64String(String base64String, String password) throws Exception {
byte[] data = Base64.decodeBase64(base64String);
byte[] contentData = decrypt(data, password);
return new String(contentData, UTF8);
}
/**
* 文本加密
*
* @param content 明文
* @param password 密码
* @return 返回16进制内容
* @throws Exception
*/
public static String encryptToHex(String content, String password) throws Exception {
byte[] data = content.getBytes(UTF8);
byte[] result = encrypt(data, password);
return Hex.encodeHexString(result);
}
/**
* 文本解密
*
* @param hex 待解密文本
* @param password 密码
* @return 返回明文
* @throws Exception
*/
public static String decryptFromHex(String hex, String password) throws Exception {
byte[] data = Hex.decodeHex(hex);
byte[] contentData = decrypt(data, password);
return new String(contentData, UTF8);
}
}