Compare commits

..
Author SHA1 Message Date
database-mysql 963584e361 chore: 版本号升级为 1.8-SNAPSHOT-Java21,更新 INFO.md 2026-11-02 10:21:48 +08:00
database-mysql e464dfc35a chore: 版本号升级为 1.8-SNAPSHOT-Java21,更新 INFO.md 2026-11-02 10:07:10 +08:00
database-mysql 6c843c4030 fix: 修复质量插件在 Java 21 下不可用,并修复 SpotBugs 发现的空指针风险
- checkstyle: 升级至 3.6.0,新增 checkstyle.xml 基础规则,绑定 verify 阶段
- findbugs: 替换为 spotbugs-maven-plugin 4.8.6.6(FindBugs 已停止维护且无法解析 Java 21 字节码),
  绑定 verify 阶段,新增 spotbugs-exclude.xml 排除遗留命名等检查
- javadoc: 升级至 3.12.0(3.12.1 在中央仓库不存在),禁用 doclint
- 修复 SpotBugs 发现的问题:压缩/解压方法中 getFileName()/getParent() 可能为 null 的防御,
  共享 SecureRandom 实例避免重复创建
2026-11-02 09:56:28 +08:00
database-mysql 28eaccf35f add pom.xml: plugins of javadoc 2026-11-02 09:14:44 +08:00
database-mysql c0b4dc6612 editor INFO.md 2026-11-02 08:31:08 +08:00
database-mysql da31bedc88 modified: pom.xml 2026-11-02 08:26:48 +08:00
database-mysql 781e77ada0 build: 适配 Java 21(编译目标 21,版本常量与文档同步) 2026-11-02 08:22:20 +08:00
database-mysql 644933e2cc Merge branch 'main' of https://git.opensource-studio.info/database-mysql/gnu_java_utils_library 2026-10-18 16:58:29 +08:00
database-mysql 30f2c04d13 Merge pull request 'fix: 安全修复与功能改进(1.7-SNAPSHOT)' (#3) from fix/security-and-quality into main 2026-10-18 16:41:38 +08:00
database-mysql 15357768c7 docs: 按 1.7 更新说明 README 与 INFO 更新日志 2026-10-18 16:40:26 +08:00
database-mysql 8933b4c652 修复了README一些问题 2026-09-24 02:45:45 +08:00
8 changed files with 228 additions and 41 deletions

No files matched your search

+33 -7
View File
@@ -1,20 +1,46 @@
1.5-SNAPSHOT 1.8-SNAPSHOT-Java21
此版本包含: 此版本包含:
1. 压缩/解压 (`compress`) 1. 压缩/解压 (`compress`)
1.1 Tar.Gz / Zip 压缩与解压
1.2 PNG 图片压缩
2. 终端彩色输出 (`echo`) 2. 终端彩色输出 (`echo`)
2.1 预定义颜色打印 — `ColorPrintln` 2.1 预定义颜色打印 — `ColorPrintln`
2.2 自定义颜色打印 — `ColorHexPrintln` 2.2 自定义颜色打印 — `ColorHexPrintln`
2.3 Minecraft 风格多色打印 — `ColorPrintlnPlus` 2.3 Minecraft 风格多色打印 — `ColorPrintlnPlus`(格式码支持叠加)
3. 文件创建与保存 (`file/CreateFile`) 3. 文件创建与保存 (`file/CreateFile`)
4. 文件加密/解密 (`file/EncryptionAndDecryption`) 4. 文件加密/解密 (`file/EncryptionAndDecryption`)
4.1 AES-256/GCM 加密
4.2 RSA-2048/OAEP 混合加密
4.3 DES 加密(已废弃,仅兼容旧版本)
5. 随机生成器 (`random`) 5. 随机生成器 (`random`)
6. 硬件标识生成器 (`hwid`) 6. 硬件标识生成器 (`hwid`)
7. 版本信息 (`version`) 7. 版本信息 (`version`)
8. 运行时信息 (`runtime`) 8. 运行时信息 (`runtime`)
6.1 获取运行时临时目录 8.1 获取运行时临时目录
6.2 新建临时目录 8.2 新建临时目录
6.3 获取临时目录 8.3 获取当前运行的 JAR 文件路径
6.4 获取当前运行的JAR文件路径
在 1.4-SNAPSHOT 的基础上调整了API接口,修复了一些BUG 1.8 更新内容:
- 版本号升级为 1.8-SNAPSHOT-Java21(pom / version 常量 / README 引用同步)
- 质量插件适配 Java 21:checkstyle 升级至 3.6.0(新增 checkstyle.xml 并绑定 verify 阶段),
SpotBugs 4.8.6.6 替换已停止维护的 FindBugs(新增排除规则),javadoc 升级至 3.12.0(禁用 doclint)
- 修复 SpotBugs 发现的空指针风险:压缩/解压方法 getFileName()/getParent() 空值防御,
加密模块共享 SecureRandom 实例
- 构建验证通过:mvn clean verify(53 个测试用例 + checkstyle + SpotBugs 0 问题)
与 mvn javadoc:javadoc 均成功
1.7 更新内容(历史):
- 安全:修复 Zip-Slip 路径穿越漏洞,解压时校验条目路径并拒绝符号/硬链接条目
- 安全:AES 升级为 AES-256/GCM(随机 IV + 认证标签),RSA 实现真实混合加密
(RSA-2048/OAEP 包裹随机 AES-256 会话密钥),密钥文件限制为仅属主可读写
- 健壮性:加解密改为流式处理(支持大文件),临时文件 + 原子替换,中途失败不损坏原数据
- 修复:随机数上界 Integer.MAX_VALUE 溢出;ColorPrintlnPlus 格式码叠加(颜色+加粗等)
- 修复:getHWID 命令执行增加超时并并发排空输出流,Windows 改用 PowerShell CIM(wmic 回退),
修正 Linux 磁盘序列号命令,MAC 优先物理网卡
- 修复:getJarPath 经 URI 解析(正确处理空格与 URL 编码),临时目录改用唯一命名
- 工程:测试全部迁移至 JUnit 5(53 个用例),pom 补充元数据与插件版本,
版本号统一由 pom / MANIFEST Implementation-Version 提供
- 兼容性:向下降级为 Java 21
在 1.7-SNAPSHOT-Java21 的基础上进行版本升级与质量插件修复。
+52 -21
View File
@@ -7,7 +7,7 @@
## 技术栈 ## 技术栈
- **Java 25** - **Java 21**
- **Maven** 构建 - **Maven** 构建
- **Apache Commons Compress 1.28.0**(压缩功能依赖) - **Apache Commons Compress 1.28.0**(压缩功能依赖)
- **JUnit Jupiter 5.13.0**(测试框架) - **JUnit Jupiter 5.13.0**(测试框架)
@@ -41,7 +41,7 @@ src/main/java/top/gtb520/java/gnu/java/utils/library/
### 1. 压缩/解压 (`compress`) ### 1. 压缩/解压 (`compress`)
支持 **Tar.Gz** 和 **Zip** 两种格式的压缩与解压,支持单文件和目录递归处理。 支持 **Tar.Gz** 和 **Zip** 两种格式的压缩与解压,支持单文件和目录递归处理;另提供 PNG 图片压缩。
| 方法 | 说明 | 参数 | | 方法 | 说明 | 参数 |
|------------------------------------|--------------------------|-------------------------------------| |------------------------------------|--------------------------|-------------------------------------|
@@ -49,6 +49,10 @@ src/main/java/top/gtb520/java/gnu/java/utils/library/
| `compress.UnTarGz(source, dest)` | 解压 `.tar.gz` 到指定目录 | `source` 压缩包路径,`dest` 解压目标目录 | | `compress.UnTarGz(source, dest)` | 解压 `.tar.gz` 到指定目录 | `source` 压缩包路径,`dest` 解压目标目录 |
| `compress.MakeZip(source, dest)` | 将文件或目录压缩为 `.zip` | `source` 源路径,`dest` 压缩包输出路径 | | `compress.MakeZip(source, dest)` | 将文件或目录压缩为 `.zip` | `source` 源路径,`dest` 压缩包输出路径 |
| `compress.UnZip(source, dest)` | 解压 `.zip` 到指定目录 | `source` 压缩包路径,`dest` 解压目标目录 | | `compress.UnZip(source, dest)` | 解压 `.zip` 到指定目录 | `source` 压缩包路径,`dest` 解压目标目录 |
| `CompressPNG.PNG(pngFile, threshold)` | 压缩 PNG 图片,返回原文件 | `pngFile` PNG 文件,`threshold` 压缩阈值(1~5 档) |
> **安全说明**:解压时会校验条目路径,压缩包内含 `../` 越界路径或符号/硬链接条目将抛出
> `IllegalArgumentException`(防 Zip-Slip 攻击)。PNG 压缩采用临时文件 + 原子替换,写入失败不会损坏原图。
**使用示例:** **使用示例:**
@@ -195,12 +199,12 @@ main.echo.ColorPrintlnPlus("&a多色 &c文本");
### 3. 文件创建与保存 (`file/CreateFile`) ### 3. 文件创建与保存 (`file/CreateFile`)
支持创建普通/二进制文件,以及异步字节流文件保存。 支持创建普通/二进制文件,以及字节流文件保存。
| 方法 | 说明 | 参数 | | 方法 | 说明 | 参数 |
|-------------------------------------------------------|---------------------------------------|-------------------------------------------------------------| |-------------------------------------------------------|---------------------------------------|-------------------------------------------------------------|
| `CreateFile.NewFile(FileName, isBin, binFileType)` | 创建空白文件,二进制文件自动写入文件头 | `FileName` 文件名,`isBin` 是否二进制,`binFileType` 二进制类型(普通文件传 `null`) | | `CreateFile.NewFile(FileName, isBin, binFileType)` | 创建空白文件,二进制文件自动写入文件头;文件已存在时直接返回不覆盖 | `FileName` 文件名,`isBin` 是否二进制,`binFileType` 二进制类型(普通文件传 `null`) |
| `CreateFile.StreamSaveFile(file, TargePath, FileName)` | 以字节流方式异步保存文件到目标路径 | `file` 源文件,`TargePath` 目标目录,`FileName` 保存的文件名 | | `CreateFile.StreamSaveFile(file, TargePath, FileName)` | 以字节流方式保存文件到目标路径(覆盖同名文件) | `file` 源文件,`TargePath` 目标目录,`FileName` 保存的文件名 |
**二进制文件类型 (`BinFileType`):** **二进制文件类型 (`BinFileType`):**
@@ -224,29 +228,32 @@ File elfFile = CreateFile.NewFile("/path/to/program", true, BinFileType.ELF);
// 创建 PE 二进制文件(自动写入 MZ 文件头) // 创建 PE 二进制文件(自动写入 MZ 文件头)
File peFile = CreateFile.NewFile("/path/to/app.exe", true, BinFileType.PE); File peFile = CreateFile.NewFile("/path/to/app.exe", true, BinFileType.PE);
// 异步字节流保存文件 // 字节流保存文件(覆盖同名文件)
boolean success = CreateFile.StreamSaveFile(sourceFile, Path.of("/path/to/dest"), "output.bin"); boolean success = CreateFile.StreamSaveFile(sourceFile, Path.of("/path/to/dest"), "output.bin");
``` ```
### 4. 文件加密/解密 (`file/EncryptionAndDecryption`) ### 4. 文件加密/解密 (`file/EncryptionAndDecryption`)
支持 **AES**、**DES** 对称加密,加密后覆盖原文件,密钥单独保存到指定目录。解密时从密钥文件读取密钥,解密后覆盖还原原文件。 支持 **AES**、**RSA** 加密与 **DES**(已废弃,仅兼容旧版本)解密,加密后覆盖原文件,密钥单独保存到指定目录。解密时从密钥文件读取密钥,解密后覆盖还原原文件。
| 方法 | 说明 | 参数 | | 方法 | 说明 | 参数 |
|----------------------------------------------------------------|--------------------------|-----------------------------------------------| |----------------------------------------------------------------|--------------------------|-----------------------------------------------|
| `EncryptionAndDecryption.EncryptionFile(file, type, keyPath)` | 异步加密文件,覆盖原文件,密钥保存到 keyPath | `file` 文件,`type` 加密类型,`keyPath` 密钥目录路径 | | `EncryptionAndDecryption.EncryptionFile(file, type, keyPath)` | 加密文件,覆盖原文件,密钥保存到 keyPath | `file` 文件,`type` 加密类型,`keyPath` 密钥目录路径 |
| `EncryptionAndDecryption.DecryptionFile(file, type, keyfile)` | 异步解密文件,覆盖还原原文件 | `file` 加密文件,`type` 加密类型,`keyfile` 密钥文件 | | `EncryptionAndDecryption.DecryptionFile(file, type, keyfile)` | 解密文件,覆盖还原原文件 | `file` 加密文件,`type` 加密类型,`keyfile` 密钥文件 |
**加密类型 (`EncryptionType`):** **加密类型 (`EncryptionType`):**
| 枚举值 | 算法 | 密钥长度 | | 枚举值 | 算法 | 密钥长度 |
|-------|---------------------|---------| |-------|---------------------|---------|
| `AES` | AES/ECB/PKCS5Padding | 128 bit | | `AES` | AES-256/GCM(随机 IV + 认证标签) | 256 bit |
| `DES` | DES/ECB/PKCS5Padding | 56 bit | | `RSA` | RSA-2048/OAEP 混合加密(RSA 包裹随机 AES-256 会话密钥,数据用 AES-GCM 加密) | 2048 bit |
| `RSA` | AES(对称替代) | 128 bit | | `DES` | DES/ECB/PKCS5Padding(已废弃,不安全) | 56 bit |
> 加密后密钥文件命名为 `原文件名.key`,保存在 `keyPath` 目录下。 > 加密后密钥文件命名为 `原文件名.key`,保存在 `keyPath` 目录下:AES/DES 保存原始密钥,RSA 保存 PKCS#8 私钥;
> 加密/解密均覆盖写回原文件,不添加额外后缀。 > POSIX 系统上密钥文件权限自动限制为仅属主可读写。
> 加密/解密均为流式处理,支持大文件;先写临时文件、成功后原子替换原文件,中途失败不会损坏原数据。
> 入参非法(文件/密钥不存在或为 null)返回 `null`;加解密实际失败(密钥错误、密文损坏等)抛出 `RuntimeException`。
> 新代码请使用 `AES` 或 `RSA`,不要使用已废弃的 `DES`。
```java ```java
import top.gtb520.java.gnu.java.utils.library.utils.file.EncryptionAndDecryption; import top.gtb520.java.gnu.java.utils.library.utils.file.EncryptionAndDecryption;
@@ -276,10 +283,12 @@ File decrypted = EncryptionAndDecryption.DecryptionFile(file, EncryptionType.AES
```java ```java
import top.gtb520.java.gnu.java.utils.library.utils.processing.random.defaults; import top.gtb520.java.gnu.java.utils.library.utils.processing.random.defaults;
int num = defaults.random(1, 100); // 生成 1~100 的随机整数 int num = defaults.random(1, 100); // 生成 1~100 的随机整数(包含两端)
String str = defaults.randomString(16); // 生成 16 位随机字符串(大小写字母+数字) String str = defaults.randomString(16); // 生成 16 位随机字符串(大小写字母+数字)
``` ```
> `random(start, end)` 结果包含两端;`randomString(length)` 的长度不能为负数,否则抛出 `IllegalArgumentException`。
### 6. 硬件标识生成器 (`hwid`) ### 6. 硬件标识生成器 (`hwid`)
组合 CPU、主板、MAC 地址、硬盘序列号等信息,通过 SHA-256 生成唯一的硬件标识符(HWID)。支持 Windows、macOS、Linux 及 Android 平台。 组合 CPU、主板、MAC 地址、硬盘序列号等信息,通过 SHA-256 生成唯一的硬件标识符(HWID)。支持 Windows、macOS、Linux 及 Android 平台。
@@ -295,6 +304,9 @@ String hwid = getHWID.generateHWID();
System.out.println("HWID: " + hwid); System.out.println("HWID: " + hwid);
``` ```
> 单项硬件信息获取失败时自动跳过该组件(如无序列号的硬件);全部失败时返回 `"UNKNOWN_HWID"`。
> MAC 地址优先取非回环、非虚拟网卡,避免虚拟网卡导致 HWID 漂移。
### 7. 版本信息 (`version`) ### 7. 版本信息 (`version`)
提供项目版本信息及编译时 Java/OpenJDK 版本常量。 提供项目版本信息及编译时 Java/OpenJDK 版本常量。
@@ -304,6 +316,8 @@ System.out.println("HWID: " + hwid);
| `version.GetVersion()` | 获取项目版本号 | | `version.GetVersion()` | 获取项目版本号 |
| `version.GetJavaVersion()` | 获取编译时 Java 版本 | | `version.GetJavaVersion()` | 获取编译时 Java 版本 |
> `GetVersion()` 打包为 JAR 时读取 MANIFEST 中的 `Implementation-Version`,类路径运行(如测试)时回退到内置常量。
```java ```java
import top.gtb520.java.gnu.java.utils.library.version; import top.gtb520.java.gnu.java.utils.library.version;
@@ -332,10 +346,10 @@ main.version.getOpenJDK64BitServerVM(); // OpenJDK 64-Bit Server VM
| 方法 | 说明 | | 方法 | 说明 |
|-------------------------------|------------------------------------------| |-------------------------------|------------------------------------------|
| `RunEnv.getJarPath()` | 获取当前运行的 JAR 路径 | | `RunEnv.getJarPath()` | 获取当前运行的 JAR 路径(非 JAR 运行时返回类路径目录) |
| `RunEnv.getTempDir()` | 获取系统临时目录路径 | | `RunEnv.getTempDir()` | 获取系统临时目录路径 |
| `RunEnv.newTempDir()` | 在系统临时目录下新建时间戳子目录,返回路径;已存在则返回 `null` | | `RunEnv.newTempDir()` | 在系统临时目录下新建唯一临时子目录,返回路径;创建失败抛出 `RuntimeException` |
| `RunEnv.newJarThisTempDir()` | 在 JAR 所在目录下新建时间戳子目录,返回路径;已存在则返回 `null` | | `RunEnv.newJarThisTempDir()` | 在 JAR 所在目录下新建唯一临时子目录,返回路径;JAR 路径无父目录时返回 `null` |
**Java 运行时环境 (`JavaRunTimeEnv`):** **Java 运行时环境 (`JavaRunTimeEnv`):**
@@ -390,9 +404,19 @@ mvn compile
mvn package mvn package
``` ```
## 测试
项目使用 JUnit 5 编写测试(共 53 个用例),运行:
```bash
mvn test
```
测试覆盖压缩/解压往返、Zip-Slip 恶意压缩包防护、AES/DES/RSA 加解密往返(含 8MB 大文件流式测试与错误密钥不损坏密文)、随机数边界(含 `Integer.MAX_VALUE` 上界)、彩色输出格式叠加与注入防护、临时目录创建与 HWID 稳定性。
## 使用 ## 使用
编译成功后,将 `target/gnu_java_utils_library-1.7-SNAPSHOT.jar` 放入项目依赖中,或在 Releases 中下载最新版 JAR 包。 编译成功后,将 `target/gnu_java_utils_library-1.8-SNAPSHOT-Java21.jar` 放入项目依赖中,或在 Releases 中下载最新版 JAR 包。
**pom.xml 引入本地依赖:** **pom.xml 引入本地依赖:**
@@ -401,9 +425,9 @@ mvn package
<dependency> <dependency>
<groupId>top.gtb520.java.gnu</groupId> <groupId>top.gtb520.java.gnu</groupId>
<artifactId>gnu_java_utils_library</artifactId> <artifactId>gnu_java_utils_library</artifactId>
<version>1.7-SNAPSHOT</version> <version>1.8-SNAPSHOT-Java21</version>
<scope>system</scope> <scope>system</scope>
<systemPath>${project.basedir}/lib/gnu_java_utils_library-1.7-SNAPSHOT.jar</systemPath> <systemPath>${project.basedir}/lib/gnu_java_utils_library-1.8-SNAPSHOT-Java21.jar</systemPath>
</dependency> </dependency>
</dependencies> </dependencies>
``` ```
@@ -414,10 +438,17 @@ mvn package
- **压缩时**:源路径不存在 - **压缩时**:源路径不存在
- **解压时**:目标路径不是有效的目录 - **解压时**:目标路径不是有效的目录
- **解压时**:压缩包包含越界路径(如 `../`)或符号/硬链接条目(防 Zip-Slip 攻击)
随机数方法在以下情况会抛出 `IllegalArgumentException`: 随机数方法在以下情况会抛出 `IllegalArgumentException`:
- **起始值大于结束值时** - **起始值大于结束值时**
- **随机字符串长度为负数时**
加密/解密方法的错误约定:
- **入参非法**(文件/密钥不存在或为 `null`):返回 `null`
- **加解密实际失败**(密钥错误、密文损坏等):抛出 `RuntimeException`
## 许可证 ## 许可证
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!--
最小化 Checkstyle 配置:
当前仅启用不涉及命名/布局风格的基础检查,保证 Java 21 项目在 verify 阶段通过。
后续可按需逐步追加规则(如 LineLength、Naming 等)。
-->
<module name="Checker">
<property name="charset" value="UTF-8"/>
<!-- 禁止使用制表符缩进 -->
<module name="FileTabCharacter"/>
<!-- 文件末尾需要换行 -->
<module name="NewlineAtEndOfFile"/>
<module name="TreeWalker">
<!-- 未使用的 import -->
<module name="UnusedImports"/>
<!-- 冗余的 import -->
<module name="RedundantImport"/>
<!-- 禁止星号通配 import -->
<module name="AvoidStarImport"/>
</module>
</module>
+56 -2
View File
@@ -6,7 +6,7 @@
<groupId>top.gtb520.java.gnu</groupId> <groupId>top.gtb520.java.gnu</groupId>
<artifactId>gnu_java_utils_library</artifactId> <artifactId>gnu_java_utils_library</artifactId>
<version>1.7-SNAPSHOT</version> <version>1.8-SNAPSHOT-Java21</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>GNU Java Utils Library</name> <name>GNU Java Utils Library</name>
@@ -22,7 +22,7 @@
</licenses> </licenses>
<properties> <properties>
<maven.compiler.release>25</maven.compiler.release> <maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.13.0</junit.version> <junit.version>5.13.0</junit.version>
<surefire.version>3.6.0</surefire.version> <surefire.version>3.6.0</surefire.version>
@@ -64,6 +64,60 @@
</archive> </archive>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<configLocation>checkstyle.xml</configLocation>
<!-- <encoding>UTF-8</encoding>-->
<failOnViolation>true</failOnViolation>
</configuration>
<executions>
<execution>
<id>checkstyle-check</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.8.6.6</version>
<configuration>
<effort>Max</effort>
<threshold>Medium</threshold>
<failOnError>true</failOnError>
<xmlOutput>true</xmlOutput>
<excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile>
</configuration>
<executions>
<execution>
<id>spotbugs-check</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.12.0</version>
<configuration>
<show>private</show>
<nohelp>true</nohelp>
<doclint>none</doclint>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter xmlns="https://github.com/spotbugs/filter/4.8.6"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://github.com/spotbugs/filter/4.8.6 https://raw.githubusercontent.com/spotbugs/spotbugs/4.8.6/spotbugs/etc/findbugsfilter.xsd">
<!--
遗留命名规范:以下类/方法沿用了历史 API 命名(小写类名、PascalCase 方法名),
重命名属于破坏性变更,暂不处理,待后续大版本统一规范后移除本排除。
-->
<Match>
<Class name="~.*\.(compress|echo|version|defaults|getHWID)$"/>
<Bug pattern="NM_CLASS_NAMING_CONVENTION"/>
</Match>
<Match>
<Class name="~.*\.(CreateFile|EncryptionAndDecryption|echo)$"/>
<Bug pattern="NM_METHOD_NAMING_CONVENTION"/>
</Match>
<!--
REC_CATCH_EXCEPTION:CompressPNG.PNG 的兜底 catch(Exception) 为有意设计
(压缩失败统一返回 null),该检查噪音较高,全局排除。
-->
<Match>
<Bug pattern="REC_CATCH_EXCEPTION"/>
</Match>
</FindBugsFilter>
@@ -52,7 +52,11 @@ public class compress {
if (entry.isDirectory()) { if (entry.isDirectory()) {
Files.createDirectories(targetPath); Files.createDirectories(targetPath);
} else { } else {
Files.createDirectories(targetPath.getParent()); Path parent = targetPath.getParent();
if (parent == null) {
throw new IllegalArgumentException("无法确定条目父目录: " + entry.getName());
}
Files.createDirectories(parent);
Files.copy(tarIn, targetPath); Files.copy(tarIn, targetPath);
} }
} }
@@ -115,7 +119,11 @@ public class compress {
} }
}); });
} else { } else {
TarArchiveEntry entry = new TarArchiveEntry(sourcePath, sourcePath.getFileName().toString()); Path fileName = sourcePath.getFileName();
if (fileName == null) {
throw new IllegalArgumentException("无法从源路径获取文件名: " + sourcePath);
}
TarArchiveEntry entry = new TarArchiveEntry(sourcePath, fileName.toString());
tarOut.putArchiveEntry(entry); tarOut.putArchiveEntry(entry);
Files.copy(sourcePath, tarOut); Files.copy(sourcePath, tarOut);
tarOut.closeArchiveEntry(); tarOut.closeArchiveEntry();
@@ -164,7 +172,11 @@ public class compress {
} }
}); });
} else { } else {
ZipArchiveEntry entry = new ZipArchiveEntry(sourcePath.getFileName().toString()); Path fileName = sourcePath.getFileName();
if (fileName == null) {
throw new IllegalArgumentException("无法从源路径获取文件名: " + sourcePath);
}
ZipArchiveEntry entry = new ZipArchiveEntry(fileName.toString());
zipOut.putArchiveEntry(entry); zipOut.putArchiveEntry(entry);
Files.copy(sourcePath, zipOut); Files.copy(sourcePath, zipOut);
zipOut.closeArchiveEntry(); zipOut.closeArchiveEntry();
@@ -200,7 +212,11 @@ public class compress {
if (entry.isDirectory()) { if (entry.isDirectory()) {
Files.createDirectories(targetPath); Files.createDirectories(targetPath);
} else { } else {
Files.createDirectories(targetPath.getParent()); Path parent = targetPath.getParent();
if (parent == null) {
throw new IllegalArgumentException("无法确定条目父目录: " + entry.getName());
}
Files.createDirectories(parent);
Files.copy(zipIn, targetPath); Files.copy(zipIn, targetPath);
} }
} }
@@ -53,6 +53,9 @@ public class EncryptionAndDecryption {
private static final String DES_ECB = "DES/ECB/PKCS5Padding"; private static final String DES_ECB = "DES/ECB/PKCS5Padding";
private static final String RSA_WRAP = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; private static final String RSA_WRAP = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
/** 共享的安全随机数发生器,避免每次加密都新建 SecureRandom 实例 */
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
/** /**
* 文件加密。加密后覆盖原文件(原子替换),密钥保存到 keyPath 目录下,密钥名为 原文件名.key。 * 文件加密。加密后覆盖原文件(原子替换),密钥保存到 keyPath 目录下,密钥名为 原文件名.key。
* *
@@ -135,7 +138,7 @@ public class EncryptionAndDecryption {
private static void encryptAesGcm(File file, SecretKey key) throws Exception { private static void encryptAesGcm(File file, SecretKey key) throws Exception {
byte[] iv = new byte[GCM_IV_BYTES]; byte[] iv = new byte[GCM_IV_BYTES];
new SecureRandom().nextBytes(iv); SECURE_RANDOM.nextBytes(iv);
Cipher cipher = buildCipher(AES_GCM, Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_BITS, iv)); Cipher cipher = buildCipher(AES_GCM, Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_BITS, iv));
Path tmp = tempSibling(file); Path tmp = tempSibling(file);
@@ -172,7 +175,7 @@ public class EncryptionAndDecryption {
// 随机 AES-256 会话密钥,RSA-OAEP 包裹后存入文件头 // 随机 AES-256 会话密钥,RSA-OAEP 包裹后存入文件头
SecretKey sessionKey = generateKey("AES", AES_KEY_BITS); SecretKey sessionKey = generateKey("AES", AES_KEY_BITS);
byte[] iv = new byte[GCM_IV_BYTES]; byte[] iv = new byte[GCM_IV_BYTES];
new SecureRandom().nextBytes(iv); SECURE_RANDOM.nextBytes(iv);
Cipher wrapCipher = Cipher.getInstance(RSA_WRAP); Cipher wrapCipher = Cipher.getInstance(RSA_WRAP);
wrapCipher.init(Cipher.WRAP_MODE, publicKey); wrapCipher.init(Cipher.WRAP_MODE, publicKey);
@@ -279,6 +282,9 @@ public class EncryptionAndDecryption {
*/ */
private static Path tempSibling(File file) throws IOException { private static Path tempSibling(File file) throws IOException {
Path dir = file.toPath().toAbsolutePath().getParent(); Path dir = file.toPath().toAbsolutePath().getParent();
if (dir == null) {
throw new IOException("无法确定文件所在目录: " + file);
}
return Files.createTempFile(dir, file.getName() + ".", ".tmp"); return Files.createTempFile(dir, file.getName() + ".", ".tmp");
} }
@@ -3,11 +3,11 @@ package top.gtb520.java.gnu.java.utils.library;
public class version { public class version {
// 项目的版本信息,不代表实时运行环境 // 项目的版本信息,不代表实时运行环境
// 与 pom.xml 保持一致;构建 JAR 时优先读取 MANIFEST 的 Implementation-Version // 与 pom.xml 保持一致;构建 JAR 时优先读取 MANIFEST 的 Implementation-Version
public static final String VERSION = "1.7"; public static final String VERSION = "1.8";
public static final String JAVA_VERSION = "25"; public static final String JAVA_VERSION = "21";
public static final String OpenJDK_VERSION = "25.0.0.0.1"; public static final String OpenJDK_VERSION = "21";
public static final String OpenJDK_Runtime_Environment = "25.0.0.0.1+36-GA"; public static final String OpenJDK_Runtime_Environment = "21+35-2513";
public static final String OpenJDK_64Bit_Server_VM = "25.0.0.0.1+36-GA"; public static final String OpenJDK_64Bit_Server_VM = "21+35-2513";
public String GetVersion() { public String GetVersion() {
// 打包为 JAR 时读取 MANIFEST 中的 Implementation-Version,类路径运行(如测试)时回退到常量 // 打包为 JAR 时读取 MANIFEST 中的 Implementation-Version,类路径运行(如测试)时回退到常量