说明
原理部分: 略
本文只关心实践。主要流程为:
- 生成AES Key:每次传输新生成一个或者一直用同一个。
- 生成RSA 公钥: 自己留着
- 生成RSA 私钥: 给到接收方,叫他藏起来
- 使用AES Key 对数据加密
- 使用RSA公钥对 AES Key加密
- 把加密后的数据/AES Key发送给接收方
- 接收方使用RSA 私钥把 AES Key 解密
- 接收方使用解密后的AES Key把数据解密
- 接收方over
注:本文使用的AES模式比较简单,可以使用更复杂的方式。不过有RSA的条件下也基本没问题了。不然连RSA私钥都被别人拿到了,AES也不可能再安全了。
依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.78.1</version>
</dependency>
代码
import static org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
import lombok.SneakyThrows;
import org.apache.commons.lang3.tuple.Pair;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* @author siyang
* @date 2024/4/29 11:29
* @description 加密工具类
*/
public class EncryptUtils {
/*
将 BouncyCastleProvider 添加到Java的安全提供者列表中,之后就可以通过 "BC" 引用它来获取或实例化加密算法等组件
如 Cipher.getInstance("AES/ECB/PKCS7Padding", "BC")
*/
static {
Security.addProvider(new BouncyCastleProvider());
}
private static final String AES_ALGORITHM = "AES";
private static final String RSA_ALGORITHM = "RSA";
private static final String AES_ALGORITHM_MODEL = "AES/ECB/PKCS5Padding";
/* ============================== AES: 加密业务数据 ============================== */
/**
* 获取Aes密钥。
*
* @param keySize 密钥长度,可选值:128, 192;理论可填写256,由于返回时会对字节进行base64编码,无形中扩大了字节数,所以实际选择128、192
* @return base64格式密钥
*/
@SneakyThrows
public static String createAesKey(int keySize) {
KeyGenerator keygen = KeyGenerator.getInstance(AES_ALGORITHM);
keygen.init(keySize);
SecretKey secretKey = keygen.generateKey();
byte[] byteKey = secretKey.getEncoded();
return Base64.getEncoder().encodeToString(byteKey);
}
/**
* AES 加密
*
* @param content 需要加密的内容
* @param key aes密钥
* @return 加密后的密文
*/
@SneakyThrows
public static String aesEncrypt(String content, String key) {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM_MODEL, PROVIDER_NAME);
SecretKeySpec secretKeySpec =
new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] encryptedBytes = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
/**
* AES 解密
*
* @param encryptedContent 已加密的密文
* @param key aes密钥,必须与加密时使用的密钥一致
* @return 解密后的内容
*/
@SneakyThrows
public static String aesDecrypt(String encryptedContent, String key) {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM_MODEL, PROVIDER_NAME);
SecretKeySpec secretKeySpec =
new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] decodedBytes =
Base64.getDecoder().decode(encryptedContent.getBytes(StandardCharsets.UTF_8));
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
/* ============================== RSA: 加密AES密钥 ============================== */
/**
* 生成rsa密钥对
*
* @param keySize 密钥长度
* @return 密钥对:left:公钥; right:私钥
*/
@SneakyThrows
public static Pair<String, String> createPemRsaKeyPair(int keySize) {
String pemPublicKeyFormat = "-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----";
String pemPrivateKeyFormat = "-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----";
KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA_ALGORITHM, PROVIDER_NAME);
generator.initialize(keySize);
KeyPair kp = generator.generateKeyPair();
String publicKey =
Base64.getEncoder()
.encodeToString(kp.getPublic().getEncoded())
.replaceAll("(.{64})", "$1\n");
String privateKey =
Base64.getEncoder()
.encodeToString(kp.getPrivate().getEncoded())
.replaceAll("(.{64})", "$1\n");
return Pair.of(
String.format(pemPublicKeyFormat, publicKey),
String.format(pemPrivateKeyFormat, privateKey));
}
/**
* 使用公钥加密数据
*
* @param pemPublicKey pem格式公钥
* @param content 待加密的数据
* @return 加密后的数据Base64编码字符串
*/
@SneakyThrows
public static String rsaEncrypt(String content, String pemPublicKey) {
byte[] publicKeyBytes = parsePemKey(pemPublicKey);
KeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM, PROVIDER_NAME);
PublicKey key = keyFactory.generatePublic(keySpec);
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(content.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
/**
* 使用私钥解密数据
*
* @param pemPrivateKey pem格式私钥
* @param encryptedContent 加密后的数据Base64编码字符串
* @return 原始数据字符串
*/
@SneakyThrows
public static String rsaDecrypt(String encryptedContent, String pemPrivateKey) {
byte[] privateKeyBytes = parsePemKey(pemPrivateKey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM, PROVIDER_NAME);
PrivateKey key = keyFactory.generatePrivate(keySpec);
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedContent);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
@SneakyThrows
public static byte[] parsePemKey(String pemKey) {
PemReader pemReader = new PemReader(new StringReader(pemKey));
PemObject pemObject = pemReader.readPemObject();
pemReader.close();
return pemObject.getContent();
}
public static void main(String[] args) {
System.out.println("================准备各类密钥===================");
String aesKey = createAesKey(192);
System.out.println("AES key: " + aesKey);
Pair<String, String> rsaKeyPair = createPemRsaKeyPair(1024);
String rsaPublicKey = rsaKeyPair.getLeft();
String rsaPrivateKey = rsaKeyPair.getRight();
System.out.println("RSA public key: \n" + rsaPublicKey);
System.out.println();
System.out.println("RSA private key: \n" + rsaPrivateKey);
System.out.println("\n================准备待传输(加密)数据===================");
String data = "hello word! hello lrd!";
System.out.println("待加密数据: " + data);
System.out.println("\n================使用AES key对数据进行加密===================");
String encryptedText = aesEncrypt(data, aesKey);
System.out.println("AES 加密后的数据: " + encryptedText);
System.out.println("\n================使用RSA公钥对AES key进行加密===================");
String encryptAesKey = rsaEncrypt(aesKey, rsaPublicKey);
System.out.println("加密后的AES key: " + encryptAesKey);
System.out.println("\n================使用RSA私钥对AES key进行解密===================");
String decryptAesKey = rsaDecrypt(encryptAesKey, rsaPrivateKey);
System.out.println("解密后的AES key: " + decryptAesKey);
System.out.println("\n================使用解密后的AES key进行数据解密===================");
String decryptedText = aesDecrypt(encryptedText, decryptAesKey);
System.out.println("解密后的数据: " + decryptedText);
}
}