From b75cee891683802927e36abd62daa2d4c81b7e51 Mon Sep 17 00:00:00 2001
From: maxf
Date: Wed, 27 Aug 2025 20:09:47 +0800
Subject: [PATCH] =?UTF-8?q?[update]=201.5.4-jre11=20=E5=A4=9A=E9=A1=B9?=
=?UTF-8?q?=E6=9B=B4=E6=96=B0=EF=BC=8C=E5=8F=82=E7=85=A7:UPDATE.md?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
UPDATE.md | 18 ++
pom.xml | 2 +-
.../com/yexuejc/base/constant/ExpCode.java | 40 +++
.../java/com/yexuejc/base/encrypt/AES.java | 36 ++-
.../java/com/yexuejc/base/encrypt/RSA.java | 248 +++++++++++-------
.../java/com/yexuejc/base/encrypt/RSA2.java | 86 +++---
.../yexuejc/base/exception/BaseException.java | 31 +--
.../java/com/yexuejc/base/util/MsgUtil.java | 95 +++++++
src/main/resources/i18n/msg_en_US.properties | 13 +
src/main/resources/i18n/msg_ja_JP.properties | 13 +
src/main/resources/i18n/msg_ko_KR.properties | 13 +
src/main/resources/i18n/msg_zh_CN.properties | 13 +
src/main/resources/i18n/msg_zh_TW.properties | 13 +
.../com/yexuejc/base/encrypt/RSATest.java | 17 ++
.../base/exception/BaseExceptionTest.java | 45 ++++
.../base/service/DefMessageServiceImpl.java | 13 +
.../com/yexuejc/base/util/MsgUtilTest.java | 23 ++
.../com.yexuejc.base.service.MessageService | 1 +
.../resources/i18n/messages_en_US.properties | 1 +
.../resources/i18n/messages_ja_JP.properties | 1 +
.../resources/i18n/messages_ko_KR.properties | 1 +
.../resources/i18n/messages_zh_CN.properties | 1 +
.../resources/i18n/messages_zh_TW.properties | 1 +
23 files changed, 525 insertions(+), 200 deletions(-)
create mode 100644 src/main/java/com/yexuejc/base/constant/ExpCode.java
create mode 100644 src/main/java/com/yexuejc/base/util/MsgUtil.java
create mode 100644 src/main/resources/i18n/msg_en_US.properties
create mode 100644 src/main/resources/i18n/msg_ja_JP.properties
create mode 100644 src/main/resources/i18n/msg_ko_KR.properties
create mode 100644 src/main/resources/i18n/msg_zh_CN.properties
create mode 100644 src/main/resources/i18n/msg_zh_TW.properties
create mode 100644 src/test/java/com/yexuejc/base/encrypt/RSATest.java
create mode 100644 src/test/java/com/yexuejc/base/exception/BaseExceptionTest.java
create mode 100644 src/test/java/com/yexuejc/base/service/DefMessageServiceImpl.java
create mode 100644 src/test/java/com/yexuejc/base/util/MsgUtilTest.java
create mode 100644 src/test/resources/META-INF/services/com.yexuejc.base.service.MessageService
create mode 100644 src/test/resources/i18n/messages_en_US.properties
create mode 100644 src/test/resources/i18n/messages_ja_JP.properties
create mode 100644 src/test/resources/i18n/messages_ko_KR.properties
create mode 100644 src/test/resources/i18n/messages_zh_CN.properties
create mode 100644 src/test/resources/i18n/messages_zh_TW.properties
diff --git a/UPDATE.md b/UPDATE.md
index aea927e..014f56b 100644
--- a/UPDATE.md
+++ b/UPDATE.md
@@ -1,6 +1,24 @@
yexuejc-base 更新记录
------------------
+#### version :1.5.4-jre11
+**time: 2025-8-27 19:45:33**
+**branch:** jre11
+**update:**
+1. [AES](src/main/java/com/yexuejc/base/encrypt/AES.java) 优化枚举,优化异常
+2. [RSA](src/main/java/com/yexuejc/base/encrypt/RSA.java)
+ * 从静态修改为单例
+ * 变量命名优化
+ * 增加verifyByApi和signByApi:针对通用API规范请求的签名验签
+ * 优化异常
+3. [RSA2](src/main/java/com/yexuejc/base/encrypt/RSA2.java)
+ * 获取公私玥兼容PKCS1,PKCS8,PKCS12,JKS
+ * PKCS1,PKCS8兼容标准密钥和非标准密钥(不带=====头)
+ * 优化异常
+4. [BaseException](src/main/java/com/yexuejc/base/exception/BaseException.java) 增加异常统一处理,支持多语言配置
+5. [MsgUtil](src/main/java/com/yexuejc/base/util/MsgUtil.java) 多语言消息获取处理,支持Java SPI配置和获取系统默认语言
+---
+
#### version :1.5.3-jre11
**time: 2025-4-27 16:37:29**
**branch:** jre11
diff --git a/pom.xml b/pom.xml
index 9da31b1..5f64347 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
top.yexuejc
yexuejc-base
- 1.5.3-jre11
+ 1.5.4-jre11
yexuejc-base
https://github.com/yexuejc/yexuejc-base
diff --git a/src/main/java/com/yexuejc/base/constant/ExpCode.java b/src/main/java/com/yexuejc/base/constant/ExpCode.java
new file mode 100644
index 0000000..67a4230
--- /dev/null
+++ b/src/main/java/com/yexuejc/base/constant/ExpCode.java
@@ -0,0 +1,40 @@
+package com.yexuejc.base.constant;
+
+/**
+ * 统一异常code
+ *
+ * @author maxiaofeng
+ * @date 2025/8/27 15:30
+ */
+public class ExpCode {
+
+ /** 系统异常。 */
+ public static final String SYSTEM_ERROR = "SYSTEM_ERROR";
+ /** 加密字符串[{0}]遇到异常。 */
+ public static final String ENCRYPTION_PARAM_FAILED = "ENCRYPTION_PARAM_FAILED";
+ /** 加密失败。 */
+ public static final String ENCRYPTION_FAILED = "ENCRYPTION_FAILED";
+ /** 解密字符串[{0}]遇到异常。 */
+ public static final String DECRYPTION_PARAM_FAILED = "DECRYPTION_PARAM_FAILED";
+ /** 生成密钥文件失败。 */
+ public static final String CREATE_KEY_FILE_FAILED = "CREATE_KEY_FILE_FAILED";
+ /** 加解密阀值为[{0}]的数据时发生异常。 */
+ public static final String DATA_DECODE_PARAM_FAILED = "DATA_DECODE_PARAM_FAILED";
+ /** 签名字符串[{0}]的数据时发生异常。 */
+ public static final String SIGN_PARAM_FAILED = "SIGN_PARAM_FAILED";
+ /** 校验签名字符串[{0}]时发生异常。 */
+ public static final String VERIFY_PARAM_FAILED = "VERIFY_PARAM_FAILED";
+ /** 校验签名时发生异常。 */
+ public static final String VERIFY_FAILED = "VERIFY_FAILED";
+ /** 签名时发生异常 */
+ public static final String SIGN_FAILED = "SIGN_FAILED";
+ /** 证书转换失败 */
+ public static final String CA_CONVERT_FAILED = "CA_CONVERT_FAILED";
+ /** 私钥读取失败。 */
+ public static final String PRIVATE_READ_FAILED = "PRIVATE_READ_FAILED";
+ /** 无法从PKCS12证书中获取公钥。 */
+ public static final String PUBLIC_READ_P12_FAILED = "PUBLIC_READ_P12_FAILED";
+
+ private ExpCode() {
+ }
+}
diff --git a/src/main/java/com/yexuejc/base/encrypt/AES.java b/src/main/java/com/yexuejc/base/encrypt/AES.java
index 7e02d14..2addffd 100644
--- a/src/main/java/com/yexuejc/base/encrypt/AES.java
+++ b/src/main/java/com/yexuejc/base/encrypt/AES.java
@@ -7,6 +7,9 @@ import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
+import com.yexuejc.base.constant.ExpCode;
+import com.yexuejc.base.exception.BaseException;
+
/**
* AES加解密
*
@@ -29,7 +32,8 @@ public class AES {
/**
* 加密模式
*/
- public static enum ALGORITHM {
+ // @formatter:off
+ public enum ALGORITHM {
//算法/模式/填充 16字节加密后数据长度 不满16字节加密后长度
//AES/CBC/NoPadding 16 不支持
AES_CBC_NoPadding("AES/CBC/NoPadding"),
@@ -79,12 +83,13 @@ public class AES {
AES_PCBC_ISO10126Padding("AES/PCBC/ISO10126Padding"),
//AES/CTR/NoPadding 16 不支持
AES_CTR_NoPadding("AES/CTR/NoPadding");
- public String name;
+ public final String code;
- ALGORITHM(String name) {
- this.name = name;
+ ALGORITHM(String code) {
+ this.code = code;
}
}
+ // @formatter:on
private ALGORITHM algorithm = ALGORITHM.AES_CBC_NoPadding;
private String key = "hj7x89H$yuBI0456";
@@ -98,10 +103,10 @@ public class AES {
* @return 密文
* @Description AES算法加密明文
*/
- public String encrypt(String data) throws Exception {
+ public String encrypt(String data) throws BaseException {
try {
- Cipher cipher = Cipher.getInstance(algorithm.name);
+ Cipher cipher = Cipher.getInstance(algorithm.code);
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes(charset);
int plaintextLength = dataBytes.length;
@@ -112,15 +117,14 @@ public class AES {
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(charset), AES_ALGORITHM);
IvParameterSpec ivspec = null;
- if(!algorithm.name.contains("ECB")){
+ if (!algorithm.code.contains("ECB")) {
ivspec = new IvParameterSpec(iv.getBytes(charset));
}
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
- e.printStackTrace();
- return null;
+ throw new BaseException(e, ExpCode.ENCRYPTION_FAILED);
}
}
@@ -134,10 +138,10 @@ public class AES {
public String decrypt(String data) throws Exception {
try {
byte[] encrypted = Base64.getDecoder().decode(data);
- Cipher cipher = Cipher.getInstance(algorithm.name);
+ Cipher cipher = Cipher.getInstance(algorithm.code);
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(charset), AES_ALGORITHM);
IvParameterSpec ivspec = null;
- if(!algorithm.name.contains("ECB")){
+ if (!algorithm.code.contains("ECB")) {
ivspec = new IvParameterSpec(iv.getBytes(charset));
}
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
@@ -148,16 +152,6 @@ public class AES {
return null;
}
}
-//
-// public static void main(String[] args) throws Exception {
-// String str = " 奥萨蒂 asd8阿斯顿8asd ";
-// AES.builder().setAlgorithm(ALGORITHM.AES_CBC_ISO10126Padding);
-// AES.builder().setKey("DEsx89H$yuBI0456");
-// String encrypt = AES.builder().encrypt(str);
-// System.out.println(encrypt);
-// String decrypt = AES.builder().decrypt(encrypt);
-// System.out.println(">>>" + decrypt + "<<<");
-// }
public ALGORITHM getAlgorithm() {
return algorithm;
diff --git a/src/main/java/com/yexuejc/base/encrypt/RSA.java b/src/main/java/com/yexuejc/base/encrypt/RSA.java
index 9772451..b6327ec 100644
--- a/src/main/java/com/yexuejc/base/encrypt/RSA.java
+++ b/src/main/java/com/yexuejc/base/encrypt/RSA.java
@@ -25,6 +25,7 @@ import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
+import com.yexuejc.base.constant.ExpCode;
import com.yexuejc.base.constant.SymbolicConstant;
import com.yexuejc.base.exception.BaseException;
import com.yexuejc.base.http.RequestHeader;
@@ -41,35 +42,77 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
* @date: 2018/5/15 14:39
*/
public class RSA {
- private static System.Logger log = System.getLogger(RSA.class.getName());
+ public static RSA builder() {
+ return RSA.Instace.rsa;
+ }
+
+ private static class Instace {
+ private static RSA rsa = new RSA();
+ }
+
+ private static final System.Logger log = System.getLogger(RSA.class.getName());
public static final Charset CHARSET = StandardCharsets.UTF_8;
- public static final String RSA_ALGORITHM = "RSA";
+ /**
+ * 算法名称
+ *
+ * RSA - RSA算法
+ * DSA - 数字签名算法
+ * EC - 椭圆曲线算法
+ * Ed25519 - Edwards-curve数字签名算法
+ * Ed448 - 另一种Edwards-curve算法
+ * X25519 - X25519密钥协商算法
+ * X448 - X448密钥协商算法
+ *
+ * 使用方式:
+ *
+ */
+ public String algorithm = "RSA";
/**
* 加密方式
+ * 示例:
*
* RSA 可选择isChangeSign 是否每次改变加密结果
* RSA/None/NoPadding 不改变加密结果
* RSA/ECB/PKCS1Padding 改变加密结果
*
+ *
+ *
支持的算法:
+ * 对称加密:AES、DES、DESede(3DES)、Blowfish、RC4等
+ * 非对称加密:RSA、EC(椭圆曲线)
+ * 其他:ARCFOUR、ChaCha20等
+ * 常见工作模式:
+ * ECB(电子密码本,不推荐)
+ * CBC(密码块链接,需IV)
+ * GCM(Galois/Counter模式,推荐用于AES)
+ * CTR(计数器模式)
+ * OFB、CFB(较少使用)
+ * 常见填充方案:
+ * PKCS5Padding(对称加密常用)
+ * PKCS1Padding(RSA专用)
+ * OAEPWithSHA-256AndMGF1Padding(RSA的OAEP填充)
+ * NoPadding(无填充,需数据长度对齐)
+ *
+ *
+ * AES的(算法/模式/填充)组合 参照 {@link AES.ALGORITHM}
*/
- public static final String RSA_ALGORITHM_ECB = "RSA";
+ public String transformation = "RSA";
/**
* 是否每次改变加密结果
- * 只针对于RSA_ALGORITHM_ECB = "RSA"有效
+ * 只针对于 transformation = "RSA"有效
*/
- public static final boolean isChangeSign = true;
+ public boolean isChangeSign = true;
/**
* 是否使用 Base64URL 方式加密 默认正常加密
*
* 关于 Base64URL 和正常加密的区别:Base64URL会把 '+', '/' 转换成 '-', '_' 来防止请求时url上的转义
- * private static final byte[] STANDARD_ENCODE_TABLE = {
+ * {
* 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
* 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
* 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
* 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
* '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
* };
- * private static final byte[] URL_SAFE_ENCODE_TABLE = {
+ * {
* 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
* 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
* 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
@@ -78,11 +121,11 @@ public class RSA {
* };
*
*/
- public static boolean encodeBase64URLSafe = false;
+ public boolean encodeBase64URLSafe = false;
/**
* 签名算法
*/
- public static final SignAlgorithm signAlgorithm = SignAlgorithm.SHA256withRSA;
+ public final SignAlgorithm signAlgorithm = SignAlgorithm.SHA256withRSA;
/**
* 生成密钥对
@@ -91,7 +134,7 @@ public class RSA {
* @param base64URLSafe 是否生成 base64URL 格式的密钥:默认false
* @return
*/
- public static Map initKeys(int keySize, boolean base64URLSafe) {
+ public Map initKeys(int keySize, boolean base64URLSafe) {
encodeBase64URLSafe = base64URLSafe;
return initKeys(keySize);
}
@@ -102,13 +145,13 @@ public class RSA {
* @param keySize 生成长度
* @return
*/
- public static Map initKeys(int keySize) {
+ public Map initKeys(int keySize) {
//为RSA算法创建一个KeyPairGenerator对象
KeyPairGenerator kpg;
try {
- kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
+ kpg = KeyPairGenerator.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
- throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");
+ throw new IllegalArgumentException("No such algorithm-->[" + algorithm + "]");
}
//初始化KeyPairGenerator对象,密钥长度
@@ -143,15 +186,13 @@ public class RSA {
* @param filePath 密钥文件路径
* @throws BaseException
*/
- public static void initKey4File(String filePath) throws BaseException {
+ public void initKey4File(String filePath) throws BaseException {
Map keys = initKeys(2048, false);
try {
- Files.write(Paths.get(filePath, "private.key"), StrUtil.chunkString(keys.get("privateKey"), 64)
- .getBytes(CHARSET));
- Files.write(Paths.get(filePath, "public.key"), StrUtil.chunkString(keys.get("publicKey"), 64)
- .getBytes(CHARSET));
+ Files.write(Paths.get(filePath, "private.key"), StrUtil.chunkString(keys.get("privateKey"), 64).getBytes(CHARSET));
+ Files.write(Paths.get(filePath, "public.key"), StrUtil.chunkString(keys.get("publicKey"), 64).getBytes(CHARSET));
} catch (IOException e) {
- throw new BaseException(e, "生成密钥文件失败");
+ throw new BaseException(e, ExpCode.CREATE_KEY_FILE_FAILED);
}
}
@@ -161,12 +202,11 @@ public class RSA {
* @param publicKey 密钥字符串(经过base64编码)
* @throws Exception
*/
- public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ public RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//通过X509编码的Key指令获得公钥对象
- KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
+ KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
- RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
- return key;
+ return (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
}
/**
@@ -175,12 +215,11 @@ public class RSA {
* @param privateKey 密钥字符串(经过base64编码)
* @throws Exception
*/
- public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ public RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//通过PKCS#8编码的Key指令获得私钥对象
- KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
+ KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
- RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
- return key;
+ return (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
}
/**
@@ -191,7 +230,7 @@ public class RSA {
* @param base64URLSafe 是否生成 base64URL 格式的密钥:默认false
* @return
*/
- public static String publicEncrypt(String data, RSAPublicKey publicKey, boolean base64URLSafe) {
+ public String publicEncrypt(String data, RSAPublicKey publicKey, boolean base64URLSafe) throws BaseException {
encodeBase64URLSafe = base64URLSafe;
return publicEncrypt(data, publicKey);
}
@@ -203,20 +242,8 @@ public class RSA {
* @param publicKey 公钥
* @return
*/
- public static String publicEncrypt(String data, RSAPublicKey publicKey) {
- try {
- Cipher cipher = getCipher();
- cipher.init(Cipher.ENCRYPT_MODE, publicKey);
- if (encodeBase64URLSafe) {
- return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus()
- .bitLength()));
- } else {
- return Base64.encodeBase64String(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus()
- .bitLength()));
- }
- } catch (Exception e) {
- throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
- }
+ public String publicEncrypt(String data, RSAPublicKey publicKey) throws BaseException {
+ return encrypt(data, publicKey);
}
/**
@@ -226,15 +253,8 @@ public class RSA {
* @param privateKey
* @return
*/
- public static String privateDecrypt(String data, RSAPrivateKey privateKey) {
- try {
- Cipher cipher = getCipher();
- cipher.init(Cipher.DECRYPT_MODE, privateKey);
- return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), privateKey.getModulus()
- .bitLength()), CHARSET);
- } catch (Exception e) {
- throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
- }
+ public String privateDecrypt(String data, RSAPrivateKey privateKey) throws BaseException {
+ return decrypt(data, privateKey);
}
/**
@@ -245,7 +265,7 @@ public class RSA {
* @param base64URLSafe 是否生成 base64URL 格式的密钥:默认false
* @return
*/
- public static String privateEncrypt(String data, RSAPrivateKey privateKey, boolean base64URLSafe) {
+ public String privateEncrypt(String data, RSAPrivateKey privateKey, boolean base64URLSafe) throws BaseException {
encodeBase64URLSafe = base64URLSafe;
return privateEncrypt(data, privateKey);
}
@@ -257,39 +277,64 @@ public class RSA {
* @param privateKey 公钥
* @return
*/
- public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
+ public String privateEncrypt(String data, RSAPrivateKey privateKey) throws BaseException {
+ return encrypt(data, privateKey);
+ }
+
+ /**
+ * 使用密钥加密,支持公玥,私玥
+ *
+ * @param data 待加密的数据
+ * @param key 公钥或私钥
+ * @return 加密后的数据
+ * @throws BaseException
+ */
+ private String encrypt(String data, Key key) throws BaseException {
try {
+ // @formatter:off
Cipher cipher = getCipher();
- cipher.init(Cipher.ENCRYPT_MODE, privateKey);
+ cipher.init(Cipher.ENCRYPT_MODE, key);
if (encodeBase64URLSafe) {
- return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus()
- .bitLength()));
+ return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET),
+ key instanceof RSAPublicKey ? ((RSAPublicKey) key).getModulus().bitLength() :
+ ((RSAPrivateKey) key).getModulus().bitLength()));
} else {
- return Base64.encodeBase64String(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus()
- .bitLength()));
+ return Base64.encodeBase64String(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET),
+ key instanceof RSAPublicKey ?((RSAPublicKey) key).getModulus().bitLength() :
+ ((RSAPrivateKey) key).getModulus().bitLength()));
}
+ // @formatter:on
} catch (Exception e) {
- throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
+ throw new BaseException(e, ExpCode.ENCRYPTION_PARAM_FAILED, data);
}
}
+ private String decrypt(String data, Key key) throws BaseException {
+ try {
+ Cipher cipher = getCipher();
+ cipher.init(Cipher.DECRYPT_MODE, key);
+ // @formatter:off
+ return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data),
+ key instanceof RSAPublicKey ?((RSAPublicKey) key).getModulus().bitLength() :
+ ((RSAPrivateKey) key).getModulus().bitLength())
+ , CHARSET);
+ // @formatter:on
+ } catch (Exception e) {
+ throw new BaseException(e, ExpCode.DECRYPTION_PARAM_FAILED, data);
+ }
+ }
+
+
/**
* 公钥解密
*
- * @param data
- * @param publicKey
- * @return
+ * @param data 待解密数据
+ * @param publicKey 公钥
+ * @return 解密后的数据
*/
- public static String publicDecrypt(String data, RSAPublicKey publicKey) {
- try {
- Cipher cipher = getCipher();
- cipher.init(Cipher.DECRYPT_MODE, publicKey);
- return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus()
- .bitLength()), CHARSET);
- } catch (Exception e) {
- throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
- }
+ public String publicDecrypt(String data, RSAPublicKey publicKey) throws BaseException {
+ return decrypt(data, publicKey);
}
/**
@@ -300,29 +345,31 @@ public class RSA {
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
*/
- private static Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
+ private Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
Cipher cipher;
- if ("RSA".equals(RSA_ALGORITHM_ECB) && isChangeSign) {
- cipher = Cipher.getInstance(RSA_ALGORITHM_ECB);
+ if ("RSA".equals(transformation) && isChangeSign) {
+ // 每次改变加密结果
+ cipher = Cipher.getInstance(transformation);
} else {
Security.addProvider(new BouncyCastleProvider());
- cipher = Cipher.getInstance(RSA_ALGORITHM_ECB, "BC");
+ // 算法/模式/填充组合;提供者名称(如 "BC" 表示Bouncy Castle)
+ cipher = Cipher.getInstance(transformation, "BC");
}
return cipher;
}
- private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
+ private byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) throws BaseException {
int maxBlock = 0;
if (opmode == Cipher.DECRYPT_MODE) {
maxBlock = keySize / 8;
} else {
maxBlock = keySize / 8 - 11;
}
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- int offSet = 0;
- byte[] buff;
- int i = 0;
- try {
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ int offSet = 0;
+ byte[] buff;
+ int i = 0;
+
while (datas.length > offSet) {
if (datas.length - offSet > maxBlock) {
buff = cipher.doFinal(datas, offSet, maxBlock);
@@ -333,18 +380,12 @@ public class RSA {
i++;
offSet = i * maxBlock;
}
- } catch (Exception e) {
- throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
- }
- byte[] resultDatas = out.toByteArray();
- try {
- out.close();
- } catch (IOException e) {
- }
- return resultDatas;
- }
- private static Signature signature;
+ return out.toByteArray();
+ } catch (Exception e) {
+ throw new BaseException(e, ExpCode.DATA_DECODE_PARAM_FAILED, maxBlock);
+ }
+ }
/**
* 私钥签名:默认算法SHA256withRSA
@@ -358,7 +399,7 @@ public class RSA {
* @return
* @throws BaseException
*/
- public static String sign(String plaintext, RSAPrivateKey privateKey, boolean base64URLSafe) throws BaseException {
+ public String sign(String plaintext, RSAPrivateKey privateKey, boolean base64URLSafe) throws BaseException {
encodeBase64URLSafe = base64URLSafe;
return sign(plaintext, privateKey);
}
@@ -374,9 +415,9 @@ public class RSA {
* @return 签名串
* @throws BaseException
*/
- public static String sign(String plaintext, RSAPrivateKey privateKey) throws BaseException {
+ public String sign(String plaintext, RSAPrivateKey privateKey) throws BaseException {
try {
- signature = Signature.getInstance(signAlgorithm.getValue());
+ Signature signature = Signature.getInstance(signAlgorithm.getValue());
String signBase64Str = "";
signature.initSign(privateKey);
signature.update(plaintext.getBytes(CHARSET));
@@ -387,7 +428,7 @@ public class RSA {
}
return signBase64Str;
} catch (Exception e) {
- throw new BaseException(e, "签名字符串[" + plaintext + "]的数据时发生异常");
+ throw new BaseException(e, ExpCode.SIGN_PARAM_FAILED, plaintext);
}
}
@@ -400,14 +441,14 @@ public class RSA {
* @return true:校验成功 / false:校验失败
* @throws BaseException
*/
- public static boolean verify(String plaintext, String signStr, RSAPublicKey publicKey) throws BaseException {
+ public boolean verify(String plaintext, String signStr, RSAPublicKey publicKey) throws BaseException {
try {
- signature = Signature.getInstance(signAlgorithm.getValue());
+ Signature signature = Signature.getInstance(signAlgorithm.getValue());
signature.initVerify(publicKey);
signature.update(plaintext.getBytes(CHARSET));
return signature.verify(Base64.decodeBase64(signStr));
} catch (Exception e) {
- throw new BaseException(e, "校验签名字符串[" + plaintext + "]的数据时发生异常");
+ throw new BaseException(e, ExpCode.VERIFY_PARAM_FAILED, plaintext);
}
}
@@ -426,7 +467,7 @@ public class RSA {
* @return true:校验成功 / false:校验失败
* @throws BaseException 签名校验异常
*/
- public static boolean verifyByApi(String uri, RequestHeader respHeader, String data, String publicKeyPath) throws BaseException {
+ public boolean verifyByApi(String uri, RequestHeader respHeader, String data, String publicKeyPath) throws BaseException {
try {
// @formatter:off
String signContent = "POST {uri}\n{clientId}.{respTime}.{body}"
@@ -447,7 +488,7 @@ public class RSA {
log.log(System.Logger.Level.DEBUG, "签名内容:=====\n{0}\n=====", signContent);
return verify(signContent, sign, RSA2.getPublicKey(publicKeyPath));
} catch (Exception e) {
- throw new BaseException(e, "校验签名时发生异常");
+ throw new BaseException(e, ExpCode.VERIFY_FAILED);
}
}
@@ -467,7 +508,7 @@ public class RSA {
* @return 签名串
* @throws BaseException 签名异常
*/
- public static String signByApi(String uri, RequestHeader requestHeader, String data, String privateKeyPath) throws BaseException {
+ public String signByApi(String uri, RequestHeader requestHeader, String data, String privateKeyPath) throws BaseException {
try {
// @formatter:off
String signContent = "POST {uri}\n{clientId}.{reqTime}.{body}"
@@ -482,4 +523,7 @@ public class RSA {
throw new BaseException(e, "签名时发生异常");
}
}
+
+ private RSA() {
+ }
}
diff --git a/src/main/java/com/yexuejc/base/encrypt/RSA2.java b/src/main/java/com/yexuejc/base/encrypt/RSA2.java
index 2cf6d7c..343cf6e 100644
--- a/src/main/java/com/yexuejc/base/encrypt/RSA2.java
+++ b/src/main/java/com/yexuejc/base/encrypt/RSA2.java
@@ -25,6 +25,7 @@ import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Enumeration;
+import com.yexuejc.base.constant.ExpCode;
import com.yexuejc.base.exception.BaseException;
import com.yexuejc.base.util.StrUtil;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
@@ -46,22 +47,18 @@ import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
* @date: 2018/5/15 14:37
*/
public class RSA2 {
+ private static final System.Logger log = System.getLogger(RSA2.class.getName());
public static final String KEY_RSA = "RSA";
public static final String KEY_PKCS1 = "PKCS81";
public static final String KEY_PKCS8 = "PKCS8";
public static final String KEY_PKCS12 = "PKCS12";
public static final String KEY_JKS = "JKS";
+ public static final String BOUNCY_CASTLE_PROVIDER_KEY = "BC";
public static final String PEM_FILE_START_CE = "-----BEGIN CERTIFICATE-----";
public static final String PEM_FILE_END_CE = "-----END CERTIFICATE-----";
- public static final String PEM_FILE_START_EN = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
- public static final String PEM_FILE_END_EN = "-----END ENCRYPTED PRIVATE KEY-----";
public static final String PEM_FILE_START_PUBLIC = "-----BEGIN PUBLIC KEY-----";
public static final String PEM_FILE_END_PUBLIC = "-----END PUBLIC KEY-----";
- public static final String PEM_FILE_START_PRIVATE = "-----BEGIN PRIVATE KEY-----";
- public static final String PEM_FILE_END_PRIVATE = "-----END PRIVATE KEY-----";
- public static final String PEM_FILE_START_RSA = "-----BEGIN RSA PRIVATE KEY-----";
- public static final String PEM_FILE_END_RSA = "-----END RSA PRIVATE KEY-----";
public static final String PEM_FILE_START_PREFIX = "-----BEGIN";
/**
@@ -118,7 +115,7 @@ public class RSA2 {
try (FileInputStream fis = new FileInputStream(pubKeyPath)) {
return getPublicKeyFromPKCS12(fis, alias, password);
} catch (Exception e) {
- throw new BaseException(e, "无法从PKCS12证书中获取公钥");
+ throw new BaseException(e, ExpCode.PUBLIC_READ_P12_FAILED);
}
}
@@ -138,7 +135,7 @@ public class RSA2 {
Certificate cert = keyStore.getCertificate(alias);
return (RSAPublicKey) cert.getPublicKey();
} catch (Exception e) {
- throw new BaseException(e, "无法从PKCS12证书中获取公钥");
+ throw new BaseException(e, ExpCode.PUBLIC_READ_P12_FAILED);
}
}
@@ -152,33 +149,25 @@ public class RSA2 {
private static RSAPublicKey getPublicKey4Pem(String pemContent) throws CertificateException {
try {
if (pemContent.contains(PEM_FILE_START_CE)) {
- String base64Cert = pemContent.replace(PEM_FILE_START_CE, "")
- .replace(PEM_FILE_END_CE, "")
- .replaceAll("\\s", "");
+ String base64Cert = pemContent.replace(PEM_FILE_START_CE, "").replace(PEM_FILE_END_CE, "").replaceAll("\\s", "");
- byte[] certBytes = Base64.getDecoder()
- .decode(base64Cert);
+ byte[] certBytes = Base64.getDecoder().decode(base64Cert);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate c = cf.generateCertificate(new ByteArrayInputStream(certBytes));
return (RSAPublicKey) c.getPublicKey();
}
// 如果是公钥PEM格式
else if (pemContent.contains(PEM_FILE_START_PUBLIC)) {
- String base64Key = pemContent.replace(PEM_FILE_START_PUBLIC, "")
- .replace(PEM_FILE_END_PUBLIC, "")
- .replaceAll("\\s", "");
+ String base64Key = pemContent.replace(PEM_FILE_START_PUBLIC, "").replace(PEM_FILE_END_PUBLIC, "").replaceAll("\\s", "");
- byte[] keyBytes = Base64.getDecoder()
- .decode(base64Key);
+ byte[] keyBytes = Base64.getDecoder().decode(base64Key);
KeyFactory kf = KeyFactory.getInstance(KEY_RSA);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
return (RSAPublicKey) kf.generatePublic(keySpec);
} else {
- String trimmedString = pemContent.replaceAll("\\s", "")
- .trim();
+ String trimmedString = pemContent.replaceAll("\\s", "").trim();
if (!pemContent.equals(trimmedString)) {
- byte[] keyBytes = Base64.getDecoder()
- .decode(trimmedString);
+ byte[] keyBytes = Base64.getDecoder().decode(trimmedString);
KeyFactory kf = KeyFactory.getInstance(KEY_RSA);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
return (RSAPublicKey) kf.generatePublic(keySpec);
@@ -300,7 +289,7 @@ public class RSA2 {
try (FileInputStream fileInputStream = new FileInputStream(filepath)) {
return getPrivateKey(fileInputStream, alias, password, type);
} catch (Exception e) {
- throw new BaseException(e, "私钥读取失败。");
+ throw new BaseException(e, ExpCode.PRIVATE_READ_FAILED);
}
}
@@ -316,6 +305,9 @@ public class RSA2 {
*/
public static RSAPrivateKey getPrivateKey(InputStream priKeyIn, String alias, String password, String type) throws BaseException {
try {
+ if (StrUtil.isEmpty(password)) {
+ password = "";
+ }
if (KEY_PKCS8.equalsIgnoreCase(type) || KEY_PKCS1.equalsIgnoreCase(type)) {
byte[] keyBytes = priKeyIn.readAllBytes();
String keyString = new String(keyBytes, StandardCharsets.UTF_8);
@@ -324,15 +316,13 @@ public class RSA2 {
return getPrivateKeyByPKCS8(keyString, password);
}
// 非标准PEM格式内容,尝试直接读取
- String trimmedString = keyString.replaceAll("\\s", "")
- .trim();
+ String trimmedString = keyString.replaceAll("\\s", "").trim();
if (!keyString.equals(trimmedString)) {
- keyBytes = Base64.getDecoder()
- .decode(trimmedString);
+ keyBytes = Base64.getDecoder().decode(trimmedString);
}
// 验证数据是否有效
if (keyBytes.length == 0) {
- throw new BaseException("Private key data is empty");
+ throw new BaseException(ExpCode.PRIVATE_READ_FAILED);
}
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance(KEY_RSA);
@@ -349,12 +339,13 @@ public class RSA2 {
return (RSAPrivateKey) ks.getKey(alias, password.toCharArray());
}
} catch (Exception e) {
- throw new BaseException(e, "私钥读取失败。");
+ throw new BaseException(e, ExpCode.PRIVATE_READ_FAILED);
}
}
/**
* 解密并解析加密的PKCS#8私钥
+ * 需要依赖于bcpkix-jdk18on,用于pkcs8的密码验证
*
* @param pemContent PEM格式的加密私钥内容
* @param password 密码
@@ -362,7 +353,7 @@ public class RSA2 {
* @throws Exception
*/
private static RSAPrivateKey getPrivateKeyByPKCS8(String pemContent, String password) throws Exception {
- if (Security.getProvider("BC") == null) {
+ if (Security.getProvider(BOUNCY_CASTLE_PROVIDER_KEY) == null) {
Security.addProvider(new BouncyCastleProvider());
}
try (PEMParser pemParser = new PEMParser(new StringReader(pemContent))) {
@@ -380,10 +371,10 @@ public class RSA2 {
} else if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
// 处理加密的PKCS#8私钥
PKCS8EncryptedPrivateKeyInfo encryptedInfo = (PKCS8EncryptedPrivateKeyInfo) object;
- InputDecryptorProvider decryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider("BC")
+ InputDecryptorProvider provider = new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider(BOUNCY_CASTLE_PROVIDER_KEY)
.build(password.toCharArray());
- PrivateKeyInfo privateKeyInfo = encryptedInfo.decryptPrivateKeyInfo(decryptorProvider);
+ PrivateKeyInfo privateKeyInfo = encryptedInfo.decryptPrivateKeyInfo(provider);
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
return (RSAPrivateKey) converter.getPrivateKey(privateKeyInfo);
} else if (object instanceof PrivateKeyInfo) {
@@ -405,17 +396,14 @@ public class RSA2 {
* @param oPwd 原证书密码
* @param nPwd 新证书密码(为空同原证书密码一致)
*/
- public static void cover2Pfx(String inPath, String outPath, String oPwd, String nPwd) {
- try {
- FileInputStream fis = new FileInputStream(inPath);
- FileOutputStream out = new FileOutputStream(outPath);
+ public static void cover2Pfx(String inPath, String outPath, String oPwd, String nPwd) throws BaseException {
+ try (FileInputStream fis = new FileInputStream(inPath); FileOutputStream out = new FileOutputStream(outPath)) {
if (nPwd == null) {
nPwd = oPwd;
}
cover2Pfx(fis, out, oPwd.toCharArray(), nPwd.toCharArray());
- out.close();
} catch (Exception e) {
- e.printStackTrace();
+ throw new BaseException(e, ExpCode.CA_CONVERT_FAILED);
}
}
@@ -427,12 +415,12 @@ public class RSA2 {
* @param oPwd 原证书密码
* @param nPwd 新证书密码(为空同原证书密码一致)
*/
- public static void cover2Pfx(FileInputStream fis, FileOutputStream out, char[] oPwd, char[] nPwd) {
+ public static void cover2Pfx(FileInputStream fis, FileOutputStream out, char[] oPwd, char[] nPwd) throws BaseException {
try {
KeyStore inputKeyStore = KeyStore.getInstance(KEY_JKS);
cover(fis, out, oPwd, nPwd, inputKeyStore, KEY_PKCS12);
} catch (Exception e) {
- e.printStackTrace();
+ throw new BaseException(e, ExpCode.CA_CONVERT_FAILED);
}
}
@@ -444,17 +432,14 @@ public class RSA2 {
* @param oPwd 原证书密码
* @param nPwd 新证书密码(为空同原证书密码一致)
*/
- public static void cover2keyStore(String inPath, String outPath, String oPwd, String nPwd) {
- try {
- FileInputStream fis = new FileInputStream(inPath);
- FileOutputStream out = new FileOutputStream(outPath);
+ public static void cover2keyStore(String inPath, String outPath, String oPwd, String nPwd) throws BaseException {
+ try (FileInputStream fis = new FileInputStream(inPath); FileOutputStream out = new FileOutputStream(outPath)) {
if (nPwd == null) {
nPwd = oPwd;
}
cover2keyStore(fis, out, oPwd.toCharArray(), nPwd.toCharArray());
- out.close();
} catch (Exception e) {
- e.printStackTrace();
+ throw new BaseException(e, ExpCode.CA_CONVERT_FAILED);
}
}
@@ -467,12 +452,12 @@ public class RSA2 {
* @param oPwd 原证书密码
* @param nPwd 新证书密码(为空同原证书密码一致)
*/
- public static void cover2keyStore(FileInputStream fis, FileOutputStream out, char[] oPwd, char[] nPwd) {
+ public static void cover2keyStore(FileInputStream fis, FileOutputStream out, char[] oPwd, char[] nPwd) throws BaseException {
try {
KeyStore inputKeyStore = KeyStore.getInstance(KEY_PKCS12);
cover(fis, out, oPwd, nPwd, inputKeyStore, KEY_JKS);
} catch (Exception e) {
- e.printStackTrace();
+ throw new BaseException(e, ExpCode.CA_CONVERT_FAILED);
}
}
@@ -502,7 +487,7 @@ public class RSA2 {
Enumeration enums = inputKeyStore.aliases();
while (enums.hasMoreElements()) {
String keyAlias = enums.nextElement();
- System.out.println("alias=[" + keyAlias + "]");
+ log.log(System.Logger.Level.DEBUG, "alias=[{0}]", keyAlias);
if (inputKeyStore.isKeyEntry(keyAlias)) {
Key key = inputKeyStore.getKey(keyAlias, oPwd);
Certificate[] certChain = inputKeyStore.getCertificateChain(keyAlias);
@@ -512,7 +497,4 @@ public class RSA2 {
outputKeyStore.store(out, nPwd);
}
-// public static void main(String[] args) {
-// cover2Pfx("D:\\mykeystore.keystore", "D:\\m1.pfx", "123456", null);
-// }
}
diff --git a/src/main/java/com/yexuejc/base/exception/BaseException.java b/src/main/java/com/yexuejc/base/exception/BaseException.java
index db3d71b..f03830a 100644
--- a/src/main/java/com/yexuejc/base/exception/BaseException.java
+++ b/src/main/java/com/yexuejc/base/exception/BaseException.java
@@ -1,20 +1,18 @@
package com.yexuejc.base.exception;
-import java.text.MessageFormat;
-import java.util.ServiceLoader;
-
-import com.yexuejc.base.constant.RespConsts;
+import com.yexuejc.base.constant.ExpCode;
import com.yexuejc.base.constant.SymbolicConstant;
-import com.yexuejc.base.service.MessageService;
+import com.yexuejc.base.util.MsgUtil;
import com.yexuejc.base.util.StrUtil;
/**
* 异常处理
+ *
* @author yexuejc
* @date 2019/8/25 15:38
*/
public class BaseException extends Exception {
- private static final System.Logger LOG = System.getLogger(BaseException.class.getName());
+
/** 序列化 */
private static final long serialVersionUID = 1L;
/** 异常消息CODE */
@@ -29,7 +27,7 @@ public class BaseException extends Exception {
* @param cause 异常对象
*/
public BaseException(Throwable cause) {
- this(cause, RespConsts.CODE_ERROR, (Object) null);
+ this(cause, ExpCode.SYSTEM_ERROR, (Object) null);
}
/**
@@ -73,7 +71,7 @@ public class BaseException extends Exception {
super(cause);
// 消息内容
if (StrUtil.isEmpty(errorCode)) {
- errorCode = RespConsts.CODE_ERROR;
+ errorCode = ExpCode.SYSTEM_ERROR;
}
this.errorCode = errorCode;
this.errorMessage = getMessageByCode(errorCode, args);
@@ -126,22 +124,7 @@ public class BaseException extends Exception {
* @return
*/
protected String getMessageByCode(String errorCode, Object... args) {
- try {
- // 使用 ServiceLoader 动态加载 MessageService 实现
- ServiceLoader loader = ServiceLoader.load(MessageService.class);
- for (MessageService service : loader) {
- if (service != null) {
- return service.getMessage(errorCode, args);
- }
- }
- // 如果没有找到实现,使用默认处理方式
- LOG.log(System.Logger.Level.DEBUG, "No MessageService implementation found, using default message format.");
- return MessageFormat.format(errorCode, args);
- } catch (Exception e) {
- LOG.log(System.Logger.Level.WARNING, "Error getting message for code: " + errorCode + ", using default format.", e);
- return MessageFormat.format(errorCode, args);
- }
+ return MsgUtil.getMsg(errorCode, args);
}
-
}
diff --git a/src/main/java/com/yexuejc/base/util/MsgUtil.java b/src/main/java/com/yexuejc/base/util/MsgUtil.java
new file mode 100644
index 0000000..ac9dbec
--- /dev/null
+++ b/src/main/java/com/yexuejc/base/util/MsgUtil.java
@@ -0,0 +1,95 @@
+package com.yexuejc.base.util;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.ServiceLoader;
+
+import com.yexuejc.base.service.MessageService;
+
+/**
+ * 消息工具类
+ *
+ * @author maxiaofeng
+ * @date 2025/8/27 15:51
+ */
+public class MsgUtil {
+ private static final System.Logger LOG = System.getLogger(MsgUtil.class.getName());
+
+
+ /**
+ * 通过code获取多语言消息
+ *
+ * 使用示例:
+ * 1.getMsgBySystemLocale("ENCRYPTION_FAILED", "张三")
+ * 2.getMsgBySystemLocale("{0},你好", "张三")
+ *
+ *
+ * @param code 消息CODE
+ * @param args 参数
+ * @return {@link MessageService}实现中对应返回的文本,获取不到再使用系统语言获取对应的文本,获取不到直接格式化返回
+ *
+ * 实现{@link MessageService}接口
+ *
+ *
+ * {@code
+ * public class DefMessageServiceImpl implements MessageService {
+ * @Override
+ * public String getMessage(String code, Object... args) {
+ * return ResourceBundle.getBundle("i18n/messages", Locale.getDefault()).getString(code);
+ * }
+ * }
+ * }
+ *
+ */
+ public static String getMsg(String code, Object... args) {
+ try {
+ // 使用 ServiceLoader 动态加载 MessageService 实现
+ // 可以接入SpringBoot使用
+ // 1.在你的项目 src/main/resources 目录下创建 META-INF/services 文件夹
+ // 2.在该文件夹中创建名为 com.yexuejc.base.service.MessageService 的文件
+ // 3.文件内容填写实现类的全限定名:比如:top.yexuejc.ensi.service.DefMessageServiceImpl
+ ServiceLoader loader = ServiceLoader.load(MessageService.class, Thread.currentThread().getContextClassLoader());
+ for (MessageService service : loader) {
+ if (service != null) {
+ return service.getMessage(code, args);
+ }
+ }
+ // 如果没有找到实现,使用默认处理方式
+ LOG.log(System.Logger.Level.WARNING, "No MessageService implementation found, using default message format.");
+ return getMsgBySystemLocale(code, args);
+ } catch (Exception e) {
+ LOG.log(System.Logger.Level.WARNING, "Error getting message for code: " + code + ", using default format.", e);
+ return getMsgBySystemLocale(code, args);
+ }
+ }
+
+ /**
+ * 通过系统语言获取多语言消息
+ *
+ * 使用示例:
+ * 1.getMsgBySystemLocale("ENCRYPTION_FAILED", "张三")
+ * 2.getMsgBySystemLocale("{0},你好", "张三")
+ *
+ *
+ * @param code 消息CODE
+ * @param args 参数
+ * @return 系统语言对应的文本,获取不到直接格式化返回
+ */
+ public static String getMsgBySystemLocale(String code, Object... args) {
+ try {
+ // 获取系统语言
+ Locale locale = Locale.getDefault();
+ // 加载对应语言的资源文件
+ ResourceBundle bundle = ResourceBundle.getBundle("i18n/msg", locale);
+ // 根据 code 获取消息文本
+ String message = bundle.getString(code);
+ // 格式化参数
+ return MessageFormat.format(message, args);
+ } catch (Exception e) {
+ // 没有获取到,直接格式化
+ return MessageFormat.format(code, args);
+ }
+ }
+
+}
diff --git a/src/main/resources/i18n/msg_en_US.properties b/src/main/resources/i18n/msg_en_US.properties
new file mode 100644
index 0000000..475fc1b
--- /dev/null
+++ b/src/main/resources/i18n/msg_en_US.properties
@@ -0,0 +1,13 @@
+SYSTEM_ERROR=System exception.
+ENCRYPTION_PARAM_FAILED=Exception occurred while encrypting string [{0}].
+ENCRYPTION_FAILED=Encryption failed.
+DECRYPTION_PARAM_FAILED=Exception occurred while decrypting string [{0}].
+CREATE_KEY_FILE_FAILED=Failed to generate key file.
+DATA_DECODE_PARAM_FAILED=Exception occurred while encrypting/decrypting data with threshold [{0}].
+SIGN_PARAM_FAILED=Exception occurred while signing string [{0}] data.
+VERIFY_PARAM_FAILED=Exception occurred while verifying signature string [{0}].
+VERIFY_FAILED=Exception occurred while verifying signature.
+SIGN_FAILED=Exception occurred while signing.
+CA_CONVERT_FAILED=Certificate conversion failed.
+PRIVATE_READ_FAILED=Private key read failed.
+PUBLIC_READ_P12_FAILED=Failed to obtain public key from PKCS12 certificate.
diff --git a/src/main/resources/i18n/msg_ja_JP.properties b/src/main/resources/i18n/msg_ja_JP.properties
new file mode 100644
index 0000000..f74cbe8
--- /dev/null
+++ b/src/main/resources/i18n/msg_ja_JP.properties
@@ -0,0 +1,13 @@
+SYSTEM_ERROR=\u30B7\u30B9\u30C6\u30E0\u7570\u5E38\u304C\u767A\u751F\u3059\u308B\u3002
+ENCRYPTION_PARAM_FAILED=\u6587\u5B57\u5217[{0}]\u306E\u6697\u53F7\u5316\u4E2D\u306B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
+ENCRYPTION_FAILED=\u6697\u53F7\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002
+DECRYPTION_PARAM_FAILED=\u6587\u5B57\u5217[{0}]\u306E\u5FA9\u53F7\u5316\u4E2D\u306B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
+CREATE_KEY_FILE_FAILED=\u30AD\u30FC \u30D5\u30A1\u30A4\u30EB\u306E\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002
+DATA_DECODE_PARAM_FAILED=\u95BE\u5024[{0}]\u306E\u30C7\u30FC\u30BF\u306E\u6697\u53F7\u5316/\u5FA9\u53F7\u5316\u4E2D\u306B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
+SIGN_PARAM_FAILED=\u6587\u5B57\u5217[{0}]\u30C7\u30FC\u30BF\u306E\u7F72\u540D\u4E2D\u306B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
+VERIFY_PARAM_FAILED=\u7F72\u540D\u6587\u5B57\u5217[{0}]\u306E\u691C\u8A3C\u4E2D\u306B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
+VERIFY_FAILED=\u7F72\u540D\u306E\u691C\u8A3C\u4E2D\u306B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
+SIGN_FAILED=\u7F72\u540D\u4E2D\u306B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
+CA_CONVERT_FAILED=\u8A3C\u660E\u66F8\u306E\u5909\u63DB\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002
+PRIVATE_READ_FAILED=\u79D8\u5BC6\u9375\u306E\u8AAD\u307F\u53D6\u308A\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002
+PUBLIC_READ_P12_FAILED=PKCS12 \u8A3C\u660E\u66F8\u304B\u3089\u516C\u958B\u9375\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002
diff --git a/src/main/resources/i18n/msg_ko_KR.properties b/src/main/resources/i18n/msg_ko_KR.properties
new file mode 100644
index 0000000..8616565
--- /dev/null
+++ b/src/main/resources/i18n/msg_ko_KR.properties
@@ -0,0 +1,13 @@
+SYSTEM_ERROR=\uC2DC\uC2A4\uD15C \uC608\uC678 \uBC1C\uC0DD.
+ENCRYPTION_PARAM_FAILED=\uBB38\uC790\uC5F4[{0}] \uC554\uD638\uD654 \uC911 \uC608\uC678 \uBC1C\uC0DD.
+ENCRYPTION_FAILED=\uC554\uD638\uD654 \uC2E4\uD328.
+DECRYPTION_PARAM_FAILED=\uBB38\uC790\uC5F4[{0}] \uBCF5\uD638\uD654 \uC911 \uC608\uC678 \uBC1C\uC0DD.
+CREATE_KEY_FILE_FAILED=\uD0A4 \uD30C\uC77C \uC0DD\uC131 \uC2E4\uD328.
+DATA_DECODE_PARAM_FAILED=\uC784\uACC4\uAC12[{0}] \uB370\uC774\uD130 \uC554\uD638\uD654/\uBCF5\uD638\uD654 \uC911 \uC608\uC678 \uBC1C\uC0DD.
+SIGN_PARAM_FAILED=\uBB38\uC790\uC5F4[{0}] \uB370\uC774\uD130 \uC11C\uBA85 \uC911 \uC608\uC678 \uBC1C\uC0DD.
+VERIFY_PARAM_FAILED=\uC11C\uBA85 \uBB38\uC790\uC5F4[{0}] \uAC80\uC99D \uC911 \uC608\uC678 \uBC1C\uC0DD.
+VERIFY_FAILED=\uC11C\uBA85 \uAC80\uC99D \uC911 \uC608\uC678 \uBC1C\uC0DD.
+SIGN_FAILED=\uC11C\uBA85 \uC911 \uC608\uC678 \uBC1C\uC0DD.
+CA_CONVERT_FAILED=\uC778\uC99D\uC11C \uBCC0\uD658 \uC2E4\uD328.
+PRIVATE_READ_FAILED=\uAC1C\uC778 \uD0A4 \uC77D\uAE30 \uC2E4\uD328.
+PUBLIC_READ_P12_FAILED=PKCS12 \uC778\uC99D\uC11C\uC5D0\uC11C \uACF5\uAC1C \uD0A4\uB97C \uAC00\uC838\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
diff --git a/src/main/resources/i18n/msg_zh_CN.properties b/src/main/resources/i18n/msg_zh_CN.properties
new file mode 100644
index 0000000..584f207
--- /dev/null
+++ b/src/main/resources/i18n/msg_zh_CN.properties
@@ -0,0 +1,13 @@
+SYSTEM_ERROR=\u7CFB\u7EDF\u5F02\u5E38\u3002
+ENCRYPTION_PARAM_FAILED=\u52A0\u5BC6\u5B57\u7B26\u4E32[{0}]\u9047\u5230\u5F02\u5E38\u3002
+ENCRYPTION_FAILED=\u52A0\u5BC6\u5931\u8D25\u3002
+DECRYPTION_PARAM_FAILED=\u89E3\u5BC6\u5B57\u7B26\u4E32[{0}]\u9047\u5230\u5F02\u5E38\u3002
+CREATE_KEY_FILE_FAILED=\u751F\u6210\u5BC6\u94A5\u6587\u4EF6\u5931\u8D25\u3002
+DATA_DECODE_PARAM_FAILED=\u52A0\u89E3\u5BC6\u9600\u503C\u4E3A[{0}]\u7684\u6570\u636E\u65F6\u53D1\u751F\u5F02\u5E38\u3002
+SIGN_PARAM_FAILED=\u7B7E\u540D\u5B57\u7B26\u4E32[{0}]\u7684\u6570\u636E\u65F6\u53D1\u751F\u5F02\u5E38\u3002
+VERIFY_PARAM_FAILED=\u6821\u9A8C\u7B7E\u540D\u5B57\u7B26\u4E32[{0}]\u65F6\u53D1\u751F\u5F02\u5E38\u3002
+VERIFY_FAILED=\u6821\u9A8C\u7B7E\u540D\u65F6\u53D1\u751F\u5F02\u5E38\u3002
+SIGN_FAILED=\u7B7E\u540D\u65F6\u53D1\u751F\u5F02\u5E38\u3002
+CA_CONVERT_FAILED=\u8BC1\u4E66\u8F6C\u6362\u5931\u8D25\u3002
+PRIVATE_READ_FAILED=\u79C1\u94A5\u8BFB\u53D6\u5931\u8D25\u3002
+PUBLIC_READ_P12_FAILED=\u65E0\u6CD5\u4ECEPKCS12\u8BC1\u4E66\u4E2D\u83B7\u53D6\u516C\u94A5\u3002
\ No newline at end of file
diff --git a/src/main/resources/i18n/msg_zh_TW.properties b/src/main/resources/i18n/msg_zh_TW.properties
new file mode 100644
index 0000000..b62778f
--- /dev/null
+++ b/src/main/resources/i18n/msg_zh_TW.properties
@@ -0,0 +1,13 @@
+SYSTEM_ERROR=\u7CFB\u7D71\u7570\u5E38\u3002
+ENCRYPTION_PARAM_FAILED=\u52A0\u5BC6\u5B57\u4E32[{0}]\u767C\u751F\u7570\u5E38\u3002
+ENCRYPTION_FAILED=\u52A0\u5BC6\u5931\u6557\u3002
+DECRYPTION_PARAM_FAILED=\u89E3\u5BC6\u5B57\u4E32[{0}]\u767C\u751F\u7570\u5E38\u3002
+CREATE_KEY_FILE_FAILED=\u7522\u751F\u91D1\u9470\u6A94\u6848\u5931\u6557\u3002
+DATA_DECODE_PARAM_FAILED=\u52A0\u89E3\u5BC6\u95BE\u503C\u70BA[{0}]\u7684\u8CC7\u6599\u6642\u767C\u751F\u7570\u5E38\u3002
+SIGN_PARAM_FAILED=\u7C3D\u540D\u5B57\u4E32[{0}]\u7684\u8CC7\u6599\u6642\u767C\u751F\u7570\u5E38\u3002
+VERIFY_PARAM_FAILED=\u9A57\u8B49\u7C3D\u540D\u5B57\u4E32[{0}]\u6642\u767C\u751F\u7570\u5E38\u3002
+VERIFY_FAILED=\u9A57\u8B49\u7C3D\u7AE0\u6642\u767C\u751F\u7570\u5E38\u3002
+SIGN_FAILED=\u7C3D\u7AE0\u6642\u767C\u751F\u7570\u5E38\u3002
+CA_CONVERT_FAILED=\u6191\u8B49\u8F49\u63DB\u5931\u6557\u3002
+PRIVATE_READ_FAILED=\u79C1\u9470\u8B80\u53D6\u5931\u6557\u3002
+PUBLIC_READ_P12_FAILED=\u7121\u6CD5\u5F9EPKCS12\u6191\u8B49\u4E2D\u53D6\u5F97\u516C\u9470\u3002
diff --git a/src/test/java/com/yexuejc/base/encrypt/RSATest.java b/src/test/java/com/yexuejc/base/encrypt/RSATest.java
new file mode 100644
index 0000000..ec0f5ce
--- /dev/null
+++ b/src/test/java/com/yexuejc/base/encrypt/RSATest.java
@@ -0,0 +1,17 @@
+package com.yexuejc.base.encrypt;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ * @author maxiaofeng
+ * @date 2025/8/26 9:43
+ */
+class RSATest {
+
+ @Test
+ void initKeys() {
+ RSA.builder().algorithm = "EC";
+ RSA.builder().initKeys(1024);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/yexuejc/base/exception/BaseExceptionTest.java b/src/test/java/com/yexuejc/base/exception/BaseExceptionTest.java
new file mode 100644
index 0000000..fa5b923
--- /dev/null
+++ b/src/test/java/com/yexuejc/base/exception/BaseExceptionTest.java
@@ -0,0 +1,45 @@
+package com.yexuejc.base.exception;
+
+import com.yexuejc.base.constant.ExpCode;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ *
+ * @author maxiaofeng
+ * @date 2025/8/27 19:26
+ */
+class BaseExceptionTest {
+
+ @Test
+ void getErrorCode() {
+ BaseException exp = Assertions.assertThrows(BaseException.class, () -> new TestClass().test());
+ String errorCode = exp.getErrorCode();
+ Assertions.assertEquals(ExpCode.SYSTEM_ERROR, errorCode);
+ System.out.println("ErrorCode " + errorCode);
+ }
+
+ @Test
+ void getMessage() {
+ BaseException exp = Assertions.assertThrows(BaseException.class, () -> new TestClass().test());
+ System.out.println("Error Message :" + exp.getMessage());
+ }
+
+ @Test
+ void getFaultMessage() {
+ BaseException exp = Assertions.assertThrows(BaseException.class, () -> new TestClass().test());
+ System.out.println("Fault Message :" + exp.getFaultMessage());
+ }
+
+ static class TestClass {
+ public void test() throws BaseException {
+ try {
+ int i = 0;
+ int b = 0;
+ int c = i / b;
+ } catch (Exception e) {
+ throw new BaseException(ExpCode.SYSTEM_ERROR);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/yexuejc/base/service/DefMessageServiceImpl.java b/src/test/java/com/yexuejc/base/service/DefMessageServiceImpl.java
new file mode 100644
index 0000000..5d67212
--- /dev/null
+++ b/src/test/java/com/yexuejc/base/service/DefMessageServiceImpl.java
@@ -0,0 +1,13 @@
+package com.yexuejc.base.service;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+public class DefMessageServiceImpl implements MessageService {
+ @Override
+ public String getMessage(String code, Object... args) {
+ String str = ResourceBundle.getBundle("i18n/messages", Locale.getDefault()).getString(code);
+ return MessageFormat.format(str, args);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/yexuejc/base/util/MsgUtilTest.java b/src/test/java/com/yexuejc/base/util/MsgUtilTest.java
new file mode 100644
index 0000000..58ae55c
--- /dev/null
+++ b/src/test/java/com/yexuejc/base/util/MsgUtilTest.java
@@ -0,0 +1,23 @@
+package com.yexuejc.base.util;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * 一个一个的执行,一起执行要报错
+ * @author maxiaofeng
+ * @date 2025/8/27 19:16
+ */
+class MsgUtilTest {
+
+ @Test
+ void getMsg() {
+ System.out.println(MsgUtil.getMsg("ENCRYPTION_PARAM_FAILED", "张三"));
+ }
+
+ @Test
+ void getMsgBySystemLocale() {
+ System.out.println(MsgUtil.getMsgBySystemLocale("ENCRYPTION_PARAM_FAILED", "张三"));
+ System.out.println(MsgUtil.getMsgBySystemLocale("你好:{0}", "张三"));
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/META-INF/services/com.yexuejc.base.service.MessageService b/src/test/resources/META-INF/services/com.yexuejc.base.service.MessageService
new file mode 100644
index 0000000..609571d
--- /dev/null
+++ b/src/test/resources/META-INF/services/com.yexuejc.base.service.MessageService
@@ -0,0 +1 @@
+com.yexuejc.base.service.DefMessageServiceImpl
\ No newline at end of file
diff --git a/src/test/resources/i18n/messages_en_US.properties b/src/test/resources/i18n/messages_en_US.properties
new file mode 100644
index 0000000..341e286
--- /dev/null
+++ b/src/test/resources/i18n/messages_en_US.properties
@@ -0,0 +1 @@
+ENCRYPTION_PARAM_FAILED=Custom multilingual: Exception occurred while encrypting string [{0}].
\ No newline at end of file
diff --git a/src/test/resources/i18n/messages_ja_JP.properties b/src/test/resources/i18n/messages_ja_JP.properties
new file mode 100644
index 0000000..e6f223a
--- /dev/null
+++ b/src/test/resources/i18n/messages_ja_JP.properties
@@ -0,0 +1 @@
+ENCRYPTION_PARAM_FAILED=\u30AB\u30B9\u30BF\u30E0\u591A\u8A00\u8A9E: \u6587\u5B57\u5217[{0}]\u306E\u6697\u53F7\u5316\u4E2D\u306B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002
\ No newline at end of file
diff --git a/src/test/resources/i18n/messages_ko_KR.properties b/src/test/resources/i18n/messages_ko_KR.properties
new file mode 100644
index 0000000..a4b8216
--- /dev/null
+++ b/src/test/resources/i18n/messages_ko_KR.properties
@@ -0,0 +1 @@
+ENCRYPTION_PARAM_FAILED=\uCEE4\uC2A4\uD140 \uB2E4\uAD6D\uC5B4: \uBB38\uC790\uC5F4 [{0}] \uC554\uD638\uD654 \uC911 \uC608\uC678 \uBC1C\uC0DD
diff --git a/src/test/resources/i18n/messages_zh_CN.properties b/src/test/resources/i18n/messages_zh_CN.properties
new file mode 100644
index 0000000..b857b3f
--- /dev/null
+++ b/src/test/resources/i18n/messages_zh_CN.properties
@@ -0,0 +1 @@
+ENCRYPTION_PARAM_FAILED=\u81EA\u5B9A\u4E49\u591A\u8BED\u8A00\uFF1A\u52A0\u5BC6\u5B57\u7B26\u4E32[{0}]\u9047\u5230\u5F02\u5E38
\ No newline at end of file
diff --git a/src/test/resources/i18n/messages_zh_TW.properties b/src/test/resources/i18n/messages_zh_TW.properties
new file mode 100644
index 0000000..4643665
--- /dev/null
+++ b/src/test/resources/i18n/messages_zh_TW.properties
@@ -0,0 +1 @@
+ENCRYPTION_PARAM_FAILED=\u81EA\u8A02\u591A\u8A9E\u8A00\uFF1A\u52A0\u5BC6\u5B57\u4E32[{0}]\u767C\u751F\u7570\u5E38\u3002
\ No newline at end of file