添加RSA加密解密模块

This commit is contained in:
2026-01-20 11:50:15 +08:00
parent f30160c886
commit ca71e3469d
4 changed files with 447 additions and 132 deletions

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@ logs/
runtime/
reports/
target/
rsakeys/
config.ini
*.log

View File

@@ -1,7 +1,7 @@
import java.util.Date;
import cc.winboll.LogUtils;
import cc.winboll.test.ConsoleCmdAutoTest;
import cc.winboll.auth.RSAUtils;
import cc.winboll.util.IniConfigUtils;
import java.util.Date;
import java.util.logging.Level;
/**
@@ -43,9 +43,10 @@ public class Main {
LogUtils.test(); // 执行测试
LogUtils.setGlobalLogLevel(oldLevel); // 还原原级别
System.out.println("\n【配置日志测试】...");
LogUtils.test(); // 执行测试
// 单元测试
LogUtils.test(); // 执行测试
RSAUtils.test();
//ConsoleCmdAutoTest.main(args);
LogUtils.i("Main", "正在处理事务...");

View File

@@ -1,12 +1,12 @@
package cc.winboll;
import cc.winboll.util.IniConfigUtils;
import cc.winboll.util.MainUtils;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
@@ -18,110 +18,87 @@ import java.util.logging.Logger;
/**
* 日志工具类对接Java原生java.util.logging简化分级调用+统一格式化输出
* 适配Java7语法兼容Android API30支持异常堆栈打印
* 支持通过启动参数动态控制全局日志级别,终端日志默认开启
* 功能:单文件输出+10MB大小限制+满额时间戳备份+新日志沿用authcenter.log+退出自动清lck锁文件
* 核心调整读取INI配置log_path和log_level无兜底配置失败打系统流日志后退出程序
* 内部日志全部使用系统流输出,避免初始化依赖冲突
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026-01-14 00:00:00
* @LastEditTime 2026-01-28 00:25:42
* @LastEditTime 2026-01-29 12:10:00
*/
public class LogUtils {
// ========== 静态常量常量优先public对外暴露private私有 ==========
private static final Logger LOGGER = Logger.getLogger(LogUtils.class.getName());
public static final String LOG_FILE_PREFIX = "authcenter_";
public static final String LOG_FILE_SUFFIX = ".log";
private static final int MAX_LOG_SIZE = 10 * 1024 * 1024;
private static final SimpleDateFormat BACKUP_SDF = new SimpleDateFormat("yyyyMMddHHmmss");
// INI配置读取键名
private static final String INI_SECTION_GLOBAL = "GlobalConfig";
private static final String INI_KEY_LOG_PATH = "log_path";
private static final String INI_KEY_LOG_LEVEL = "log_level";
// ========== 静态成员变量 ==========
private static ConsoleHandler consoleHandler;
private static FileHandler fileHandler;
private static String CURRENT_LOG_DIR; // 静态变量保存当前日志目录路径
private static Level CURRENT_LOG_LEVEL; // 当前日志级别
private static String CURRENT_LOG_DIR;
private static Level CURRENT_LOG_LEVEL;
private static boolean IS_INIT_COMPLETE = false;
private static boolean TEST_HAS_RUN = false; // 测试方法防重复标记
// ========== 静态初始化块全局仅执行一次读INI配置初始化失败直接退出 ==========
static {
LOGGER.setUseParentHandlers(false);
LOGGER.setUseParentHandlers(false); // 置顶生效,彻底关闭父处理器
consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(new CustomLogFormatter());
// 1. 从INI读取核心配置无兜底失败直接退出
loadConfigFromIni();
// 2. 初始化日志目录
initLogDirByIni();
// 3. 初始化日志级别
initLogLevelByIni();
System.out.printf("[LogUtils][INFO] 静态初始化完成,日志级别[%s],日志目录[%s]单文件10MB限制+时间戳备份+lck自动清理%n",
CURRENT_LOG_LEVEL.getName(), CURRENT_LOG_DIR);
IS_INIT_COMPLETE = true;
System.out.printf("[LogUtils][INFO] 静态初始化完成,日志级别[%s],日志目录[%s]%n",
CURRENT_LOG_LEVEL.getName(), CURRENT_LOG_DIR);
}
// ========== 私有化构造器,禁止外部实例化 ==========
private LogUtils() {}
// ========== 核心新增从INI读取log_path和log_level失败退出 ==========
private static void loadConfigFromIni() {
System.out.println("[LogUtils][INFO] 开始从INI读取日志核心配置");
// 读取log_path
System.out.println("[LogUtils][INFO] 读取INI日志配置");
String logPath = IniConfigUtils.getConfigValue(INI_SECTION_GLOBAL, INI_KEY_LOG_PATH);
if (logPath == null || logPath.trim().isEmpty()) {
System.err.println("[LogUtils][ERROR] INI配置GlobalConfig.log_path为空或不存在,程序退出");
System.err.println("[LogUtils][ERROR] log_path配置为空,程序退出");
System.exit(1);
}
CURRENT_LOG_DIR = logPath.trim();
// 读取log_level
String logLevelStr = IniConfigUtils.getConfigValue(INI_SECTION_GLOBAL, INI_KEY_LOG_LEVEL);
if (logLevelStr == null || logLevelStr.trim().isEmpty()) {
System.err.println("[LogUtils][ERROR] INI配置GlobalConfig.log_level为空或不存在,程序退出");
System.err.println("[LogUtils][ERROR] log_level配置为空,程序退出");
System.exit(1);
}
// 转换日志级别,失败退出
try {
CURRENT_LOG_LEVEL = Level.parse(logLevelStr.trim().toUpperCase());
} catch (IllegalArgumentException e) {
System.err.printf("[LogUtils][ERROR] INI配置log_level无效[%s]支持ALL/FINE/INFO/WARNING/SEVERE,程序退出%n", logLevelStr);
System.err.printf("[LogUtils][ERROR] log_level无效[%s]支持ALL/FINE/INFO/WARNING/SEVERE%n", logLevelStr);
System.exit(1);
}
System.out.printf("[LogUtils][INFO] INI配置读取成功log_path=%slog_level=%s%n", CURRENT_LOG_DIR, CURRENT_LOG_LEVEL.getName());
}
// ========== 核心基于INI配置初始化日志目录无用户输入无兜底失败退出 ==========
private static void initLogDirByIni() {
File logDir = new File(CURRENT_LOG_DIR);
// 目录不存在则创建,创建失败退出
if (!logDir.exists()) {
boolean mkdirOk = logDir.mkdirs();
if (!mkdirOk) {
System.err.printf("[LogUtils][ERROR] 日志目录[%s]不存在且创建失败,程序退出%n", CURRENT_LOG_DIR);
System.exit(1);
}
System.out.printf("[LogUtils][INFO] 日志目录不存在,创建成功:%s%n", CURRENT_LOG_DIR);
if (!logDir.exists() && !logDir.mkdirs()) {
System.err.printf("[LogUtils][ERROR] 日志目录[%s]创建失败%n", CURRENT_LOG_DIR);
System.exit(1);
}
// 初始化日志处理器
setLogDir(CURRENT_LOG_DIR);
}
// ========== 核心初始化日志级别基于INI配置 ==========
private static void initLogLevelByIni() {
setGlobalLogLevel(CURRENT_LOG_LEVEL);
System.out.printf("[LogUtils][INFO] 全局日志级别初始化完成:%s%n", CURRENT_LOG_LEVEL.getName());
}
// ========== 私有工具方法(内部调用,按功能归类) ==========
private static boolean containsHandler(Handler handler) {
System.out.printf("[LogUtils][DEBUG] 【函数调用】containsHandler入参handler=%s%n",
(handler == null ? "null" : handler.getClass().getName()));
if (handler == null) {
if (!IS_INIT_COMPLETE || handler == null) {
return false;
}
for (Handler h : LOGGER.getHandlers()) {
if (h == handler) {
if (h.getClass() == handler.getClass()) {
return true;
}
}
@@ -129,66 +106,59 @@ public class LogUtils {
}
private static void backupOldLog(File logFile) {
System.out.printf("[LogUtils][DEBUG] 【函数调用】backupOldLog入参logFile=%s%n",
(logFile == null ? "null" : logFile.getAbsolutePath()));
if (logFile != null && logFile.exists() && logFile.length() >= MAX_LOG_SIZE) {
String backupName = LOG_FILE_PREFIX + BACKUP_SDF.format(new Date()) + LOG_FILE_SUFFIX;
File backupFile = new File(logFile.getParent(), backupName);
boolean backupSuccess = logFile.renameTo(backupFile);
if (backupSuccess) {
System.out.printf("[LogUtils][INFO] 旧日志备份成功,备份文件:%s%n", backupFile.getAbsolutePath());
} else {
System.err.printf("[LogUtils][WARN] 旧日志备份失败,将直接覆盖原日志%n");
File backupFile = new File(logFile.getParent(), LOG_FILE_PREFIX + BACKUP_SDF.format(new Date()) + LOG_FILE_SUFFIX);
if (!logFile.renameTo(backupFile)) {
System.err.println("[LogUtils][WARN] 旧日志备份失败");
}
}
}
// ========== 公共核心方法(对外提供功能,核心方法优先) ==========
static void setLogDir(String logDir) {
System.out.printf("[LogUtils][DEBUG] 【函数调用】setLogDir入参logDir=%s%n", logDir);
if (logDir == null || logDir.trim().isEmpty()) {
System.err.printf("[LogUtils][ERROR] 日志目录为空,程序退出%n");
System.err.println("[LogUtils][ERROR] 日志目录为空");
System.exit(1);
}
File dir = new File(logDir.trim());
try {
final File logFile = new File(dir, "authcenter.log");
final File logFile = new File(logDir.trim(), "authcenter.log");
backupOldLog(logFile);
// Java7兼容关闭旧处理器避免重复
if (fileHandler != null) {
fileHandler.close();
LOGGER.removeHandler(fileHandler);
}
fileHandler = new FileHandler(logFile.getAbsolutePath(), MAX_LOG_SIZE, 1, true);
fileHandler.setFormatter(new CustomLogFormatter());
fileHandler.setLevel(Level.ALL);
if (!containsHandler(fileHandler)) {
LOGGER.addHandler(fileHandler);
System.out.printf("[LogUtils][INFO] 日志处理器初始化成功,当前日志文件:%s最大10MB%n", logFile.getAbsolutePath());
System.out.println("[LogUtils][INFO] 满额自动备份为authcenter_时间戳.log新日志沿用authcenter.log");
}
// JVM关闭钩子清理lck锁文件
// Java7兼容替换Lambda为匿名内部类
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
new File(logFile.getAbsolutePath() + ".lck").delete();
}
}));
System.out.println("[LogUtils][DEBUG] lck文件清理钩子已注册程序退出自动清理");
@Override
public void run() {
if (fileHandler != null) {
fileHandler.close();
}
new File(logFile.getAbsolutePath() + ".lck").delete();
}
}));
} catch (IOException e) {
System.err.printf("[LogUtils][ERROR] 文件日志处理器初始化失败,程序退出%n");
System.err.println("[LogUtils][ERROR] 文件日志处理器初始化失败");
e.printStackTrace();
System.exit(1);
}
}
public static Level getGlobalLogLevel() {
System.out.printf("[LogUtils][DEBUG] 【函数调用】getGlobalLogLevel返回当前级别%s%n", LOGGER.getLevel().getName());
return LOGGER.getLevel();
}
public static void setGlobalLogLevel(Level level) {
System.out.printf("[LogUtils][DEBUG] 【函数调用】setGlobalLogLevel入参level=%s%n",
(level == null ? "null" : level.getName()));
if (level == null) {
System.err.println("[LogUtils][ERROR] 日志级别为null,程序退出");
System.err.println("[LogUtils][ERROR] 日志级别为null");
System.exit(1);
}
LOGGER.setLevel(level);
@@ -198,88 +168,125 @@ public class LogUtils {
if (fileHandler != null) {
fileHandler.setLevel(level);
}
// 移除isVerbose判断强制添加控制台处理器终端日志始终开启
if (!containsHandler(consoleHandler)) {
LOGGER.addHandler(consoleHandler);
System.out.println("[LogUtils][DEBUG] 终端日志输出已开启");
}
}
// ========== 分级日志方法(重载方法归类,简洁无冗余) ==========
public static void d(String tag, String msg) {
LOGGER.fine(String.format("[%s] %s", tag, msg));
if (IS_INIT_COMPLETE) {
LOGGER.fine(String.format("[%s] %s", tag, msg));
}
}
public static void d(String tag, String msg, Throwable throwable) {
LOGGER.log(Level.FINE, String.format("[%s] %s", tag, msg), throwable);
}
public static void i(String tag, String msg) {
LOGGER.info(String.format("[%s] %s", tag, msg));
}
public static void i(String tag, String msg, Throwable throwable) {
LOGGER.log(Level.INFO, String.format("[%s] %s", tag, msg), throwable);
}
public static void w(String tag, String msg) {
LOGGER.warning(String.format("[%s] %s", tag, msg));
}
public static void w(String tag, String msg, Throwable throwable) {
LOGGER.log(Level.WARNING, String.format("[%s] %s", tag, msg), throwable);
}
public static void e(String tag, String msg) {
LOGGER.severe(String.format("[%s] %s", tag, msg));
}
public static void e(String tag, String msg, Throwable throwable) {
LOGGER.log(Level.SEVERE, String.format("[%s] %s", tag, msg), throwable);
}
// ========== 内部类:自定义日志格式化器 ==========
static class CustomLogFormatter extends Formatter {
@Override
public String format(LogRecord record) {
return String.format("[%1$tF %1$tT] [%2$s] [%3$s] %4$s - %5$s%n",
new Date(record.getMillis()),
record.getLevel().getName(),
Thread.currentThread().getName(),
record.getLoggerName(),
formatMessage(record));
if (IS_INIT_COMPLETE) {
LOGGER.log(Level.FINE, String.format("[%s] %s", tag, msg), throwable);
}
}
// ========== 完善 单元测试方法(全级别覆盖+异常堆栈测试+当前日志配置打印) ==========
public static void test() {
System.out.println("\n==================================== LogUtils 单元测试开始 ====================================");
// 1. 打印当前日志配置信息,便于验证
System.out.printf("[LogUtilsTest] 当前日志级别:%s | 当前日志目录:%s%n", CURRENT_LOG_LEVEL.getName(), CURRENT_LOG_DIR);
System.out.println("[LogUtilsTest] ------------- 基础日志级别输出测试 -------------");
public static void i(String tag, String msg) {
if (IS_INIT_COMPLETE) {
LOGGER.info(String.format("[%s] %s", tag, msg));
}
}
// 2. 全日志级别基础消息测试(覆盖所有提供的日志方法)
public static void i(String tag, String msg, Throwable throwable) {
if (IS_INIT_COMPLETE) {
LOGGER.log(Level.INFO, String.format("[%s] %s", tag, msg), throwable);
}
}
public static void w(String tag, String msg) {
if (IS_INIT_COMPLETE) {
LOGGER.warning(String.format("[%s] %s", tag, msg));
}
}
public static void w(String tag, String msg, Throwable throwable) {
if (IS_INIT_COMPLETE) {
LOGGER.log(Level.WARNING, String.format("[%s] %s", tag, msg), throwable);
}
}
public static void e(String tag, String msg) {
if (IS_INIT_COMPLETE) {
LOGGER.severe(String.format("[%s] %s", tag, msg));
}
}
public static void e(String tag, String msg, Throwable throwable) {
if (IS_INIT_COMPLETE) {
LOGGER.log(Level.SEVERE, String.format("[%s] %s", tag, msg), throwable);
}
}
static class CustomLogFormatter extends Formatter {
@Override
public String format(LogRecord record) {
StringBuilder sb = new StringBuilder(String.format("[%1$tF %1$tT] [%2$s] [%3$s] %4$s - %5$s",
new Date(record.getMillis()), record.getLevel().getName(),
Thread.currentThread().getName(), record.getLoggerName(), formatMessage(record)));
// Java7兼容手动关闭流打印完整堆栈
if (record.getThrown() != null) {
sb.append("\n");
StringWriter sw = null;
PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
record.getThrown().printStackTrace(pw);
pw.flush();
sb.append(sw.toString());
} finally {
try {
if (pw != null) {
pw.close();
}
if (sw != null) {
sw.close();
}
} catch (IOException e) {
System.err.println("[LogUtils][WARN] 堆栈流关闭失败");
}
}
}
return sb.append("\n").toString();
}
}
// 终极防重复测试方法只允许运行1次
public static void test() {
if (TEST_HAS_RUN) {
return;
}
TEST_HAS_RUN = true;
System.out.println("\n==================================== LogUtils 单元测试开始 ====================================");
System.out.printf("[LogUtilsTest] 当前日志级别:%s | 日志目录:%s%n", CURRENT_LOG_LEVEL.getName(), CURRENT_LOG_DIR);
System.out.println("[LogUtilsTest] ------------- 基础日志测试 -------------");
LogUtils.d("LogUtilsTest", "【DEBUG】调试信息程序运行状态跟踪仅调试环境输出");
LogUtils.i("LogUtilsTest", "【INFO】普通信息正常业务流程记录核心运行节点提示");
LogUtils.w("LogUtilsTest", "【WARN】告警信息非致命异常需关注但不影响程序运行");
LogUtils.e("LogUtilsTest", "【ERROR】错误信息致命异常需紧急处理修复");
System.out.println("[LogUtilsTest] ------------- 异常堆栈日志输出测试 -------------");
// 3. 带异常堆栈的日志测试Java7兼容模拟空指针异常场景
System.out.println("[LogUtilsTest] ------------- 异常堆栈测试 -------------");
try {
String nullStr = null;
nullStr.length();
} catch (NullPointerException e) {
LogUtils.d("LogUtilsTest", "【DEBUG】带堆栈-调试异常(测试空指针场景)", e);
LogUtils.i("LogUtilsTest", "【INFO】带堆栈-普通异常(测试空指针场景)", e);
LogUtils.w("LogUtilsTest", "【WARN】带堆栈-告警异常(测试空指针场景)", e);
LogUtils.e("LogUtilsTest", "【ERROR】带堆栈-错误异常(测试空指针场景)", e);
} catch (Exception e) {
LogUtils.d("LogUtilsTest", "DEBUG】带堆栈-调试异常", e);
LogUtils.i("LogUtilsTest", "【INFO】带堆栈-普通异常", e);
LogUtils.w("LogUtilsTest", "【WARN】带堆栈-告警异常", e);
LogUtils.e("LogUtilsTest", "【ERROR】带堆栈-错误异常", e);
LogUtils.e("LogUtilsTest", "ERROR】测试过程中出现未知异常", e);
}
// 4. 测试结束标识
System.out.println("[LogUtilsTest] ------------- 日志输出格式验证 -------------");
System.out.println("[LogUtilsTest] ------------- 格式验证 -------------");
LogUtils.i("LogUtilsTest", "日志格式:[时间] [级别] [线程名] [日志器名] - [内容]");
System.out.println("==================================== LogUtils 单元测试结束 ====================================\n");
}
}
}

View File

@@ -0,0 +1,306 @@
package cc.winboll.auth;
import cc.winboll.LogUtils;
import cc.winboll.util.IniConfigUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
/**
* RSA加密解密工具类单例模式
* 自动读取INI配置密钥目录缺失密钥自动生成公私钥文件支持字符串加解密
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/28
* @LastEditTime 2026/01/28 23:00:00
*/
public class RSAUtils {
// 单例实例volatile保证多线程可见性Java7兼容
private static volatile RSAUtils instance;
// INI配置相关常量
private static final String INI_SECTION_GLOBAL = "GlobalConfig";
private static final String INI_KEY_RSA_PATH = "rsakeys_path";
// 密钥文件名
private static final String PRIVATE_KEY_FILE = "private_key.txt";
private static final String PUBLIC_KEY_FILE = "public_key.txt";
// RSA算法常量
private static final String RSA_ALGORITHM = "RSA";
private static final int KEY_SIZE = 2048;
// 加密填充模式
private static final String RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding";
// 工作目录&密钥对象
private String rsaWorkDir;
private RSAPrivateKey privateKey;
private RSAPublicKey publicKey;
/**
* 私有化构造器 禁止外部实例化
*/
private RSAUtils() {}
/**
* 单例获取双重校验锁线程安全Java7最优实现
*/
public static RSAUtils getInstance() {
if (instance == null) {
synchronized (RSAUtils.class) {
if (instance == null) {
instance = new RSAUtils();
}
}
}
return instance;
}
/**
* 核心初始化方法读取INI配置+校验目录+加载/生成密钥
* 调用前需确保IniConfigUtils已初始化失败会抛出运行时异常
*/
public void init() {
// 1. 读取INI配置获取工作目录
readWorkDirFromIni();
// 2. 校验并创建工作目录
checkAndCreateWorkDir();
// 3. 加载/生成密钥
loadOrGenerateRSAKey();
}
/**
* 从IniConfigUtils读取rsa工作目录
*/
private void readWorkDirFromIni() {
rsaWorkDir = IniConfigUtils.getConfigValue(INI_SECTION_GLOBAL, INI_KEY_RSA_PATH);
if (rsaWorkDir == null || rsaWorkDir.trim().isEmpty()) {
throw new RuntimeException("RSA初始化失败INI配置GlobalConfig.rsakeys_path为空或不存在");
}
rsaWorkDir = rsaWorkDir.trim();
}
/**
* 校验工作目录,不存在则创建
*/
private void checkAndCreateWorkDir() {
File workDirFile = new File(rsaWorkDir);
if (!workDirFile.exists()) {
boolean mkdirSuccess = workDirFile.mkdirs();
if (!mkdirSuccess) {
throw new RuntimeException("RSA初始化失败无法创建工作目录[" + rsaWorkDir + "]");
}
}
if (!workDirFile.isDirectory()) {
throw new RuntimeException("RSA初始化失败rsakeys_path不是合法目录[" + rsaWorkDir + "]");
}
}
/**
* 加载密钥,私钥文件缺失则自动生成公私钥并保存
*/
private void loadOrGenerateRSAKey() {
File privateKeyFile = new File(rsaWorkDir, PRIVATE_KEY_FILE);
File publicKeyFile = new File(rsaWorkDir, PUBLIC_KEY_FILE);
// 私钥文件缺失 → 生成新密钥并保存
if (!privateKeyFile.exists() || !publicKeyFile.exists()) {
generateAndSaveRSAKey(privateKeyFile, publicKeyFile);
return;
}
// 加载已有密钥文件
try {
loadPrivateKeyFromFile(privateKeyFile);
loadPublicKeyFromFile(publicKeyFile);
} catch (Exception e) {
throw new RuntimeException("RSA密钥加载失败将重新生成", e);
}
}
/**
* 从文件加载私钥
*/
private void loadPrivateKeyFromFile(File privateFile) throws Exception {
String keyBase64 = readFileToString(privateFile);
byte[] keyBytes = Base64.getDecoder().decode(keyBase64);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
privateKey = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
/**
* 从文件加载公钥
*/
private void loadPublicKeyFromFile(File publicFile) throws Exception {
String keyBase64 = readFileToString(publicFile);
byte[] keyBytes = Base64.getDecoder().decode(keyBase64);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
}
/**
* 生成RSA公私钥并写入文件
*/
private void generateAndSaveRSAKey(File privateFile, File publicFile) {
try {
// 1. 生成RSA密钥对
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(RSA_ALGORITHM);
keyGen.initialize(KEY_SIZE);
KeyPair keyPair = keyGen.generateKeyPair();
privateKey = (RSAPrivateKey) keyPair.getPrivate();
publicKey = (RSAPublicKey) keyPair.getPublic();
// 2. Base64编码后写入文件Java7兼容适配Android
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded());
String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey.getEncoded());
writeStringToFile(privateKeyBase64, privateFile);
writeStringToFile(publicKeyBase64, publicFile);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("RSA密钥生成失败不支持RSA算法", e);
} catch (IOException e) {
throw new RuntimeException("RSA密钥保存失败文件写入异常", e);
}
}
/**
* 字符串写入文件Java7兼容
*/
private void writeStringToFile(String content, File file) throws IOException {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
fos.write(content.getBytes());
fos.flush();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
LogUtils.e("RSAUtils", "密钥文件流关闭异常", e);
}
}
}
}
/**
* 文件读取为字符串Java7兼容
*/
private String readFileToString(File file) throws IOException {
BufferedReader br = null;
InputStreamReader isr = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
isr = new InputStreamReader(fis);
br = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
} finally {
try { if (br != null) br.close(); } catch (IOException e) { LogUtils.e("RSAUtils", "BufferedReader关闭异常", e); }
try { if (isr != null) isr.close(); } catch (IOException e) { LogUtils.e("RSAUtils", "InputStreamReader关闭异常", e); }
try { if (fis != null) fis.close(); } catch (IOException e) { LogUtils.e("RSAUtils", "FileInputStream关闭异常", e); }
}
}
/**
* 公钥加密字符串返回Base64编码密文
*/
public String encrypt(String plainText) {
if (plainText == null || plainText.trim().isEmpty() || publicKey == null) {
LogUtils.w("RSAUtils", "加密失败:明文为空或公钥未初始化");
return null;
}
try {
Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptBytes = cipher.doFinal(plainText.getBytes());
return Base64.getEncoder().encodeToString(encryptBytes);
} catch (Exception e) {
LogUtils.e("RSAUtils", "RSA加密失败", e);
return null;
}
}
/**
* 私钥解密字符串传入Base64编码密文
*/
public String decrypt(String cipherText) {
if (cipherText == null || cipherText.trim().isEmpty() || privateKey == null) {
LogUtils.w("RSAUtils", "解密失败:密文为空或私钥未初始化");
return null;
}
try {
Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] cipherBytes = Base64.getDecoder().decode(cipherText);
byte[] plainBytes = cipher.doFinal(cipherBytes);
return new String(plainBytes);
} catch (Exception e) {
LogUtils.e("RSAUtils", "RSA解密失败", e);
return null;
}
}
// ========== 对外提供的getter方法 ==========
public RSAPrivateKey getPrivateKey() {
return privateKey;
}
public RSAPublicKey getPublicKey() {
return publicKey;
}
public String getRsaWorkDir() {
return rsaWorkDir;
}
/**
* 测试函数:完整演示 初始化+加密+解密 流程
*/
public static void test() {
try {
// 2. RSAUtils初始化全局一次即可
RSAUtils rsaUtils = RSAUtils.getInstance();
rsaUtils.init();
LogUtils.d("RSAUtils", "RSA初始化成功工作目录" + rsaUtils.getRsaWorkDir());
// 3. 待加密实例字符串
String originalStr = "WinBoLL_AuthCenter_2026_Test123456";
LogUtils.d("RSAUtils", "\n【原始字符串】" + originalStr);
// 4. 公钥加密
String encryptStr = rsaUtils.encrypt(originalStr);
LogUtils.d("RSAUtils", "【加密后密文】:" + encryptStr);
// 5. 私钥解密
String decryptStr = rsaUtils.decrypt(encryptStr);
LogUtils.d("RSAUtils", "【解密后原文】:" + decryptStr);
// 6. 校验结果
if (originalStr.equals(decryptStr)) {
LogUtils.i("RSAUtils", "===== RSA加解密测试成功 =====");
} else {
LogUtils.e("RSAUtils", "===== RSA加解密测试失败 =====");
}
} catch (Exception e) {
LogUtils.e("RSAUtils", "RSA测试失败", e);
}
}
}