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