Java 使用 AES 进行加密和解密
在 上一文 一文中,介绍了如何在 Java 中使用 RSA 非对称加密算法 进行加密、解密、生成数字签名和验签。
和 “非对称加密算法” 对应的就是 “对称加密算法”。非对称加密算法(如 RSA)的密钥通常由 公钥 和 私钥 组成,且遵守公钥加密、私钥解密的模式。而对称加密算法则只有一个密钥,加密和解密都使用同一个密钥。
对称加密算法中,比较安全且流行的就是 AES 算法,本文将会带你了解如何在 Java 中使用 AES 对数据进行加密和解密。
AES 介绍
AES(Advanced Encryption Standard)是一种对称加密算法,也被称为高级加密标准。它是一种广泛使用的加密算法,具有高度的安全性和效率,已被广泛应用于各种领域,包括网络通信、数据存储和加密协议等。AES 使用相同的密钥进行加密和解密操作,因此被归类为对称加密算法。
密钥
可以使用 Java 中的 javax.crypto.KeyGenerator
API 来生成随机的 AES 密钥。
package cn.springdoc.demo.test;
import java.security.SecureRandom;
import java.util.Base64;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
public class Main {
public static void main(String[] args) throws Exception {
// 获取 AES 密钥生成器
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
// 设置密钥长度和随机源
keyGenerator.init(128, new SecureRandom());
// 生成密钥
SecretKey secretKey = keyGenerator.generateKey();
// 获取密钥内容
byte[] key = secretKey.getEncoded();
System.out.println(Base64.getEncoder().encodeToString(key));
}
}
如上,首先通过工厂方法获取到 AES 密钥生成器,然后设置密钥长度以及随机源,最后生成密钥。
执行 main 方法,输出的密钥如下(Base64编码):
wsWcvZhevZ2QzkL+f3x8dw==
AES 算法支持不同的密钥长度,包括 128 位、192 位和 256 位。密钥长度越长,理论上破解该密钥的难度就越大,因此安全性也更高。但也会导致加密和解密操作的计算成本增加。
加密和解密
在 Java 中使用 AES 进行加密、解密的操作示例如下:
package cn.springdoc.demo.test;
import java.security.SecureRandom;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class Main {
/**
* 加密
* @param key 密钥
* @param data 加密数据
* @return 密文
* @throws Exception
*/
public static byte[] encode(byte[] key, byte[] data) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
return cipher.doFinal(data);
}
/**
* 解密
* @param key 密钥
* @param data 密文
* @return 解密后的数据
* @throws Exception
*/
public static byte[] decode(byte[] key, byte[] data) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
// 生成随机密钥
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256, new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
byte[] key = secretKey.getEncoded();
System.out.println("AES 密钥:" + Base64.getEncoder().encodeToString(key));
String content = "Hello springdoc.cn";
System.out.println("原文:" + content);
byte[] ret = encode(key, content.getBytes());
System.out.println("加密后的密文:" + Base64.getEncoder().encodeToString(ret));
byte[] raw = decode(key, ret);
System.out.println("解密后的原文:" + new String(raw));
}
}
首先,encode
方法用于加密,第一个参数就是 byte[]
形式的密钥,第二个参数就是要加密的数据,最后返回加密后的密文。
接着,decode
方法用于解密,第一个参数也是 byte[]
形式的密钥,第二个参数就是加密后的密文,最后返回的是解密后的原文。
你可以看到,encode
和 decode
方法中的内容基本上大致相同。首先通过构造函数指定密钥和算法创建 SecretKeySpec
实例,它是一个密钥规范对象,该对象包含了使用字节数组表示的密钥及其相关的算法信息。然后通过工厂方法获取到 Cipher
实例,它是加密和解密操作的引擎。Cipher
工厂方法中的参数 AES/ECB/PKCS5Padding
,分别指定了加密标准、加密模式以及填充模式:
- AES:高级加密标准(Advanced Encryption Standard),一种对称加密算法,用于加密和解密数据。
- ECB:电子密码本(Electronic Codebook)模式是一种基本的加密模式,将待加密的数据分成固定大小的块,并使用相同的密钥对每个块进行独立的加密。
- PKCS5Padding:PKCS #5填充模式是一种数据块填充方案,用于在加密过程中将数据块的长度扩展到特定的块大小。它通过在数据块的末尾添加额外的字节,使数据块长度符合指定的块大小要求。
最后,在 main
方法中,对加密、解密方法进行了测试。首先成一个 256 位的 AES KEY,然后定义了一个字符串作为要加密的数据。然后调用 encode
方法使用生成的 KEY 对数据进行加密,获得密文。最后调用 decode
使用同一密钥对密文进行解密,获得原文。
运行 main
方法,输出如下:
AES 密钥:vx3v5NCf0G5CDhQhvZIxjD0p+03gvvTzw4KFecsVmM8=
原文:Hello springdoc.cn
加密后的密文:o2hHuR+qNr5zGf9PG+EO/7gJK4C+ZG+LWsOYxur1+xg=
解密后的原文:Hello springdoc.cn
如你所见,成功地使用 AES 密钥进行了加密和解密。
使用自定义密钥
AES 密钥也可以自己定义,只要是合法的长度即可。
修改一下上述 main
方法,使用自定义的 AES 密钥:
byte[] key = "Hello springdoc.cn springdoc.cn!".getBytes();
System.out.println("AES 密钥:" + Base64.getEncoder().encodeToString(key));
String content = "Hello springdoc.cn";
System.out.println("原文:" + content);
byte[] ret = encode(key, content.getBytes());
System.out.println("加密后的密文:" + Base64.getEncoder().encodeToString(ret));
byte[] raw = decode(key, ret);
System.out.println("解密后的原文:" + new String(raw));
上述代码中定义的 AES 密钥为:Hello springdoc.cn springdoc.cn!
,刚好 32 个 ASCII 字符,也就是 32 字节,即 128 位(32 x 8)。
运行 main
,输出如下:
AES 密钥:SGVsbG8gc3ByaW5nZG9jLmNuIHNwcmluZ2RvYy5jbiE=
原文:Hello springdoc.cn
加密后的密文:eA7FjuNumExamoQj8bd0j91UCRBvkN0aBwVhHogRuc8=
解密后的原文:Hello springdoc.cn
同样,也没任何问题。