RsaUtils
加密和解密操作
生成密钥对
以下示例展示了如何使用 RsaUtils
生成 RSA 公钥和私钥,并将其编码为 Base64 字符串。
package com.litongjava.maxkb.dao;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import org.junit.Test;
import com.litongjava.tio.utils.crypto.RsaUtils;
public class SystemSettingDaoTest {
@Test
public void getPublicKeyAndPrivateKey() {
// 生成 RSA 密钥对
KeyPair pair = RsaUtils.generateKeyPair();
PublicKey publicKey = pair.getPublic();
PrivateKey privateKey = pair.getPrivate();
// 将密钥编码为 Base64 字符串
String publicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
String privateKeyStr = Base64.getEncoder().encodeToString(privateKey.getEncoded());
// 输出密钥
System.out.println("Public Key:");
System.out.println(publicKeyStr);
System.out.println("\nPrivate Key:");
System.out.println(privateKeyStr);
}
}
示例输出:
Public Key:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApLt4COWWcLo5XrhgSqirNFIQ1CktDMMjHOboLvWd0wNYNM7+jNYUtAaFQ8YSoBPgxgWxYW0e/MmmK4idJYOAHOgdkjAvP3Rg2YRRhtHFeR+R3VmM2+Y8meDaQg8YwiNvTEShi19Sm2uQCtV2r3+cbFnqsR1/NbmXWkJBQQ0AuWOmuF7n9qeFFfpo7U0+nS02JKVQDkq2d28Ma6lkDOIZ5CFsyWAb+LvxqUQOGDsOFijzCb/XFbMKIcxTKj1UjBynd6YFEMX55AnRG/VmXXtY/mOZU1WofCT9rUZZpiAds/7UaGyPNotfJJY6thuYsVlJW97whAOFM5/7miO8TPzwBwIDAQAB
Private Key:
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCku3gI5ZZwujleuGBKqKs0UhDUKS0MwyMc5ugu9Z3TA1g0zv6M1hS0BoVDxhKgE+DGBbFhbR78yaYriJ0lg4Ac6B2SMC8/dGDZhFGG0cV5H5HdWYzb5jyZ4NpCDxjCI29MRKGLX1Kba5AK1Xavf5xsWeqxHX81uZdaQkFBDQC5Y6a4Xuf2p4UV+mjtTT6dLTYkpVAOSrZ3bwxrqWQM4hnkIWzJYBv4u/GpRA4YOw4WKPMJv9cVswohzFMqPVSMHKd3pgUQxfnkCdEb9WZde1j+Y5lTVah8JP2tRlmmIB2z/tRobI82i18kljq2G5ixWUlb3vCEA4Uzn/uaI7xM/PAHAgMBAAECggEBAI4SiQHTUJ5EXfpNNe7t7UoghRcVtB7PpVbl5tWpS8aTmd1hsLQyZoSlIwZCrAmXW0It4r/N5u3J1CCzoCScdz93CRntqLInYuf2cIHAJXnDOoAXHZ8BwL5N6K8Uahv1h6XVgyW6vnoAmKhfVg8iFfx0yC6c6/uf5uMXRUQfPMKlw9OdZ6kj+FC85eAAlUZER+P13a7Xn7IClP1HC/qN5bOmB4RFJiBc3/I18y2Ymf7EbdhmWsbKdz9YDwBwx7Z8iNEgnr4KJ25H9zTECbTys91itoe8SkJxCed0+Nv6R+N/l+eBdiB4GjB0RZlfTtmh5LtOqZlWlOZbYPPBpJ476NECgYEA6uvvepq6+WDH7q+EAtsvUiaxVsgfGl0g2XfUTBvyIgRBh3Eu6P4mjsIzIsZDfdmvVdY6rSIqY1jCgx4fyC4Vo/ECSVmjtM5yXb49fiPUNfBjol9MV7r76SdA/V/OAgqI9Msx1xBLMfS/obmbyXKml9hW4LAe/PPLrbSwSXyYtpUCgYEAs4NPZA25bnkbcG1QQvJBzQTNYu/26h2TBTeW1p9pg1JmwuvQccGCzl3lGOwde9uOniYMpTQ7goQpR7KehXonFW+bSEfrahqoclIwwNyl8lQ8eaxcye9MVX9QUUOwSf8sbjdFSsvmpi/MyX7rFA62Uv4odtcNo2SmiQP1ABSk8SsCgYBQQTv14ahgi3yiARoTM7gN06Qf5owhoJEZWgOP4LgugM+2KqeA91pKyPNZPOxPsS9iHqa7AQIEII2K556p73x7HlnOny37FdAiUFyHnEviBXa0QOQ+0GVA+KUOKk2hiuHQ5x3mv+1AasGQyWz8PAvHsiOz7NcZSVxawnC7GjK9pQKBgDepUpeijcnMcY6lxopLlc2dXJX99CDZtLmcZ//6g/v9M/HirspnB0k34g6dP/MkvA8bmzYqgP1SP36EyxR2MOI7rfl2m29V/r8b7xQOFsKSDgJoSIkw0wl7pYrdIy9+mOc/6hNsMAN9tNERspCQk0YemqtmuUrdram0eWZiAFbrAoGAA5+l/Kyh191GdC2OHfFVpFw1P9t3zacpf62wYUMSCNxH45Sjg6tfj/6iSAwZGOxISkB99XZrV7eEZ5/e1F+2kr8a84SfZDwa750N2mosVwV1ESzQgWx8I9LA6NX1P1Hf2ryyYosqssW9yw3AWFzrhEWjEIADuKP6r6UOd3TFIZI=
加密和解密消息
以下示例展示了如何使用生成的 RSA 公钥加密消息,并使用私钥解密。
package com.litongjava.maxkb.dao;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import org.junit.Test;
import com.litongjava.tio.utils.crypto.RsaUtils;
public class SystemSettingDaoTest {
@Test
public void test() {
// 生成 RSA 密钥对
KeyPair pair = RsaUtils.generateKeyPair();
PublicKey publicKey = pair.getPublic();
PrivateKey privateKey = pair.getPrivate();
// 将密钥编码为 Base64 字符串
String publicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
String privateKeyStr = Base64.getEncoder().encodeToString(privateKey.getEncoded());
// 输出密钥
System.out.println("Public Key:");
System.out.println(publicKeyStr);
System.out.println("\nPrivate Key:");
System.out.println(privateKeyStr);
// 原始消息
String originalMessage = "Hello, RSA!";
System.out.println("原始消息: " + originalMessage);
// 使用公钥加密消息
String encryptedMessage = RsaUtils.encrypt(originalMessage, publicKey);
System.out.println("加密消息: " + encryptedMessage);
// 使用私钥解密消息
String decryptedMessage = RsaUtils.decrypt(encryptedMessage, privateKey);
System.out.println("解密消息: " + decryptedMessage);
}
}
示例输出:
Public Key:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApLt4COWWcLo5XrhgSqirNFIQ1CktDMMjHOboLvWd0wNYNM7+jNYUtAaFQ8YSoBPgxgWxYW0e/MmmK4idJYOAHOgdkjAvP3Rg2YRRhtHFeR+R3VmM2+Y8meDaQg8YwiNvTEShi19Sm2uQCtV2r3+cbFnqsR1/NbmXWkJBQQ0AuWOmuF7n9qeFFfpo7U0+nS02JKVQDkq2d28Ma6lkDOIZ5CFsyWAb+LvxqUQOGDsOFijzCb/XFbMKIcxTKj1UjBynd6YFEMX55AnRG/VmXXtY/mOZU1WofCT9rUZZpiAds/7UaGyPNotfJJY6thuYsVlJW97whAOFM5/7miO8TPzwBwIDAQAB
Private Key:
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCku3gI5ZZwujleuGBKqKs0UhDUKS0MwyMc5ugu9Z3TA1g0zv6M1hS0BoVDxhKgE+DGBbFhbR78yaYriJ0lg4Ac6B2SMC8/dGDZhFGG0cV5H5HdWYzb5jyZ4NpCDxjCI29MRKGLX1Kba5AK1Xavf5xsWeqxHX81uZdaQkFBDQC5Y6a4Xuf2p4UV+mjtTT6dLTYkpVAOSrZ3bwxrqWQM4hnkIWzJYBv4u/GpRA4YOw4WKPMJv9cVswohzFMqPVSMHKd3pgUQxfnkCdEb9WZde1j+Y5lTVah8JP2tRlmmIB2z/tRobI82i18kljq2G5ixWUlb3vCEA4Uzn/uaI7xM/PAHAgMBAAECggEBAI4SiQHTUJ5EXfpNNe7t7UoghRcVtB7PpVbl5tWpS8aTmd1hsLQyZoSlIwZCrAmXW0It4r/N5u3J1CCzoCScdz93CRntqLInYuf2cIHAJXnDOoAXHZ8BwL5N6K8Uahv1h6XVgyW6vnoAmKhfVg8iFfx0yC6c6/uf5uMXRUQfPMKlw9OdZ6kj+FC85eAAlUZER+P13a7Xn7IClP1HC/qN5bOmB4RFJiBc3/I18y2Ymf7EbdhmWsbKdz9YDwBwx7Z8iNEgnr4KJ25H9zTECbTys91itoe8SkJxCed0+Nv6R+N/l+eBdiB4GjB0RZlfTtmh5LtOqZlWlOZbYPPBpJ476NECgYEA6uvvepq6+WDH7q+EAtsvUiaxVsgfGl0g2XfUTBvyIgRBh3Eu6P4mjsIzIsZDfdmvVdY6rSIqY1jCgx4fyC4Vo/ECSVmjtM5yXb49fiPUNfBjol9MV7r76SdA/V/OAgqI9Msx1xBLMfS/obmbyXKml9hW4LAe/PPLrbSwSXyYtpUCgYEAs4NPZA25bnkbcG1QQvJBzQTNYu/26h2TBTeW1p9pg1JmwuvQccGCzl3lGOwde9uOniYMpTQ7goQpR7KehXonFW+bSEfrahqoclIwwNyl8lQ8eaxcye9MVX9QUUOwSf8sbjdFSsvmpi/MyX7rFA62Uv4odtcNo2SmiQP1ABSk8SsCgYBQQTv14ahgi3yiARoTM7gN06Qf5owhoJEZWgOP4LgugM+2KqeA91pKyPNZPOxPsS9iHqa7AQIEII2K556p73x7HlnOny37FdAiUFyHnEviBXa0QOQ+0GVA+KUOKk2hiuHQ5x3mv+1AasGQyWz8PAvHsiOz7NcZSVxawnC7GjK9pQKBgDepUpeijcnMcY6lxopLlc2dXJX99CDZtLmcZ//6g/v9M/HirspnB0k34g6dP/MkvA8bmzYqgP1SP36EyxR2MOI7rfl2m29V/r8b7xQOFsKSDgJoSIkw0wl7pYrdIy9+mOc/6hNsMAN9tNERspCQk0YemqtmuUrdram0eWZiAFbrAoGAA5+l/Kyh191GdC2OHfFVpFw1P9t3zacpf62wYUMSCNxH45Sjg6tfj/6iSAwZGOxISkB99XZrV7eEZ5/e1F+2kr8a84SfZDwa750N2mosVwV1ESzQgWx8I9LA6NX1P1Hf2ryyYosqssW9yw3AWFzrhEWjEIADuKP6r6UOd3TFIZI=
将私钥和公钥存入数据库
为了安全地存储 RSA 密钥对,我们将私钥和公钥存入数据库中的 system_setting
表。以下步骤展示了如何创建数据表、存储密钥,并使用 JSON 格式进行存储。
创建数据表
首先,创建一个用于存储系统设置的表,包括 RSA 密钥对。
CREATE TABLE "public"."system_setting" (
"type" "pg_catalog"."int4" NOT NULL PRIMARY KEY,
"meta" "pg_catalog"."jsonb" NOT NULL,
"create_time" "pg_catalog"."timestamptz" NOT NULL,
"update_time" "pg_catalog"."timestamptz" NOT NULL
);
字段说明:
type
: 标识系统设置的类型,这里1
可以代表 RSA 密钥对。meta
: 使用jsonb
类型存储密钥的详细信息,包括私钥和公钥。create_time
: 记录创建时间。update_time
: 记录最后更新时间。
存入数据库
以下 Java 代码展示了如何生成 RSA 密钥对,并将其存入 system_setting
表中。
package com.litongjava.maxkb.dao;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import org.junit.Test;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.maxkb.MaxKbApp;
import com.litongjava.tio.boot.testing.TioBootTest;
import com.litongjava.tio.utils.crypto.RsaUtils;
public class SystemSettingDaoTest {
@Test
public void test() {
// 初始化测试环境
TioBootTest.scan(MaxKbApp.class);
// 生成 RSA 密钥对
KeyPair pair = RsaUtils.generateKeyPair();
PublicKey publicKey = pair.getPublic();
PrivateKey privateKey = pair.getPrivate();
// 将密钥编码为 Base64 字符串
String privateKeyStr = Base64.getEncoder().encodeToString(privateKey.getEncoded());
String publicKeyStr = Base64.getEncoder().encodeToString(publicKey.getEncoded());
// 使用 AOP 获取 SystemSettingDao 实例并保存密钥
Aop.get(SystemSettingDao.class).saveRsa(privateKeyStr, publicKeyStr);
}
}
SystemSettingDao
类实现:
package com.litongjava.maxkb.dao;
import java.sql.SQLException;
import java.util.Date;
import org.postgresql.util.PGobject;
import com.jfinal.kit.Kv;
import com.litongjava.db.activerecord.Db;
import com.litongjava.db.activerecord.Record;
import com.litongjava.tio.utils.json.JsonUtils;
public class SystemSettingDao {
/**
* 获取 RSA 密钥对
*
* @return 包含 RSA 密钥对的 PGobject 对象
*/
public PGobject getRsa() {
String sql = "SELECT meta FROM system_setting WHERE type = 1";
return Db.queryFirst(sql);
}
/**
* 保存 RSA 密钥对到数据库
*
* @param privateKeyStr 私钥的 Base64 编码字符串
* @param publicKeyStr 公钥的 Base64 编码字符串
*/
public void saveRsa(String privateKeyStr, String publicKeyStr) {
// 创建一个键值对,存储私钥和公钥
Kv kv = Kv.by("key", privateKeyStr).set("value", publicKeyStr);
// 将键值对转换为 JSONB 类型
PGobject meta = new PGobject();
try {
meta.setType("jsonb");
meta.setValue(JsonUtils.toJson(kv));
} catch (SQLException e) {
e.printStackTrace();
}
// 创建记录对象并设置各字段
Record record = Record.by("meta", meta).set("type", 1);
record.set("create_time", new Date());
record.set("update_time", new Date());
// 保存记录到 system_setting 表
Db.save("system_setting", record);
}
}
加密私钥
为了增强私钥的安全性,可以使用密码对私钥进行加密。以下示例使用 Bouncy Castle 库,采用 PKCS#8 格式、scrypt 密钥派生函数和 AES128-CBC 加密算法对私钥进行加密保护。
添加依赖
在 pom.xml
中添加 Bouncy Castle 的依赖:
<dependencies>
<!-- Bouncy Castle Provider -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version> <!-- 请使用最新版本 -->
</dependency>
<!-- Bouncy Castle PKIX/CMS/EAC -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.70</version>
</dependency>
</dependencies>
添加工具类
创建 BouncyCastleUtils
工具类,负责加密和解密私钥。
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.security.PrivateKey;
import java.security.Security;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.PKCS8Generator;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.OutputEncryptor;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.bouncycastle.util.io.pem.PemGenerationException;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
public class BouncyCastleUtils {
// 静态块,确保 Bouncy Castle 提供者已添加
static {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
/**
* 使用 PKCS#8、scrypt 和 AES-128-CBC 加密私钥。
*
* @param privateKey 要加密的 PrivateKey 对象
* @param password 用于加密的密码
* @return 加密后的 PEM 格式字符串
*/
public static String encryptPrivateKey(PrivateKey privateKey, String password) {
// 将 PrivateKey 转换为 PrivateKeyInfo
PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(privateKey.getEncoded());
// 初始化 PKCS8 加密器构建器,使用 AES-128-CBC
JceOpenSSLPKCS8EncryptorBuilder encryptorBuilder = new JceOpenSSLPKCS8EncryptorBuilder(PKCS8Generator.AES_128_CBC);
encryptorBuilder.setProvider("BC");
encryptorBuilder.setPassword(password.toCharArray());
encryptorBuilder.setIterationCount(16384); // Scrypt 的 N 参数
// 构建 OutputEncryptor
OutputEncryptor encryptor;
try {
encryptor = encryptorBuilder.build();
} catch (OperatorCreationException e) {
throw new RuntimeException(e);
}
// 生成加密的 PKCS#8 私钥
PKCS8Generator pkcs8Generator = new PKCS8Generator(privateKeyInfo, encryptor);
PemObject pemObject;
try {
pemObject = pkcs8Generator.generate();
} catch (PemGenerationException e) {
throw new RuntimeException(e);
}
// 将 PemObject 写入 PEM 格式字符串
StringWriter stringWriter = new StringWriter();
try (PemWriter pemWriter = new PemWriter(stringWriter)) {
pemWriter.writeObject(pemObject);
} catch (IOException e) {
throw new RuntimeException(e);
}
return stringWriter.toString();
}
/**
* 使用提供的密码解密 PEM 格式的加密私钥。
*
* @param encryptedPrivateKeyPem 加密后的 PEM 格式私钥字符串
* @param password 用于解密的密码
* @return 解密后的 PrivateKey 对象
*/
public static PrivateKey decryptPrivateKey(String encryptedPrivateKeyPem, String password) {
try (StringReader stringReader = new StringReader(encryptedPrivateKeyPem);
PEMParser pemParser = new PEMParser(stringReader)) {
Object object = pemParser.readObject();
if (!(object instanceof PKCS8EncryptedPrivateKeyInfo)) {
throw new IllegalArgumentException("提供的 PEM 不包含加密的私钥。");
}
PKCS8EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) object;
// 初始化解密器提供者
InputDecryptorProvider decryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder()
.setProvider("BC")
.build(password.toCharArray());
// 解密获取 PrivateKeyInfo
PrivateKeyInfo privateKeyInfo = encryptedPrivateKeyInfo.decryptPrivateKeyInfo(decryptorProvider);
// 将 PrivateKeyInfo 转换为 PrivateKey
JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter().setProvider("BC");
PrivateKey privateKey = keyConverter.getPrivateKey(privateKeyInfo);
return privateKey;
} catch (IOException | PKCSException | OperatorCreationException e) {
throw new RuntimeException("解密私钥失败", e);
}
}
}
工具类说明:
- 静态块: 确保 Bouncy Castle 提供者已添加到 Java 安全提供者列表中。
encryptPrivateKey
方法:- 将
PrivateKey
转换为PrivateKeyInfo
对象。 - 使用
JceOpenSSLPKCS8EncryptorBuilder
配置加密参数(AES-128-CBC、scrypt)。 - 生成加密后的 PKCS#8 格式私钥,并将其写入 PEM 格式字符串。
- 将
decryptPrivateKey
方法:- 从 PEM 格式字符串中读取加密的私钥。
- 使用提供的密码解密,获取
PrivateKeyInfo
。 - 将
PrivateKeyInfo
转换回PrivateKey
对象。
使用工具类
以下示例展示了如何使用 BouncyCastleUtils
工具类对私钥进行加密和解密。
package com.litongjava.maxkb.utils;
import java.security.KeyPair;
import java.security.PrivateKey;
import org.junit.Test;
import com.litongjava.tio.utils.crypto.RsaUtils;
public class BouncyCastleUtilsTest {
@Test
public void test() {
// 生成 RSA 密钥对
KeyPair pair = RsaUtils.generateKeyPair();
PrivateKey privateKey = pair.getPrivate();
System.out.println("原始私钥(编码后): " + privateKey.getEncoded());
// 使用密码加密私钥
String encryptPrivateKey = BouncyCastleUtils.encryptPrivateKey(privateKey, "max_kb_password");
System.out.println("加密后的私钥:\n" + encryptPrivateKey);
// 使用密码解密私钥
PrivateKey decryptPrivateKey = BouncyCastleUtils.decryptPrivateKey(encryptPrivateKey, "max_kb_password");
System.out.println("解密后的私钥(编码后): " + decryptPrivateKey.getEncoded());
}
}
示例输出:
原始私钥(编码后): [B@6d06d69c
加密后的私钥:
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHzBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIipG1NbTwYNCAggA
...
-----END ENCRYPTED PRIVATE KEY-----
解密后的私钥(编码后): [B@6d06d69c
注意事项:
- 密码安全: 请确保密码的强度和安全性,以防止私钥被未授权访问。
- 加密算法: 本示例使用了 AES-128-CBC 加密算法和 scrypt 密钥派生函数。根据需求,您可以选择更强的加密算法。
- Bouncy Castle 版本: 请确保使用最新版本的 Bouncy Castle 库,以获得最新的安全修复和功能增强。
总结
本文档详细介绍了如何生成 RSA 密钥对、加密解密消息、将密钥存储到数据库,以及使用 Bouncy Castle 库对私钥进行加密保护的完整流程。通过这些步骤,您可以确保在 Java 应用程序中安全地生成、存储和使用 RSA 密钥对。