mirror of
https://gitee.com/jzsw-it/yexuejc-base.git
synced 2025-12-29 13:29:28 +08:00
Compare commits
12 Commits
1415200b1a
...
1.5.4-jre1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b75cee8916 | ||
|
|
d1f0cd695e | ||
|
|
e9c65f8866 | ||
|
|
f2c904869f | ||
|
|
b0fd28d208 | ||
|
|
982f3aa418 | ||
|
|
8b43d99e11 | ||
|
|
31040ad0e9 | ||
|
|
9f5ecefc43 | ||
|
|
beb72c8009 | ||
|
|
0403c7c693 | ||
|
|
cf8231017d |
24
.gitea/workflows/package.yaml
Normal file
24
.gitea/workflows/package.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
name: yexuejc-base package jre11
|
||||
run-name: Deploy to ${{ inputs.deploy_target }} by @${{ github.actor }} 🚀
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- jre11
|
||||
|
||||
jobs:
|
||||
package_job:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
|
||||
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
|
||||
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
|
||||
- name: Check out repository code
|
||||
uses: advanced-security/maven-dependency-submission-action@v4
|
||||
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
|
||||
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
|
||||
- name: List files in the repository
|
||||
run: |
|
||||
ls ${{ gitea.workspace }}
|
||||
mvn -v
|
||||
mvn clean package
|
||||
- run: echo "🍏 This job's status is ${{ job.status }}."
|
||||
30
UPDATE.md
30
UPDATE.md
@@ -1,13 +1,33 @@
|
||||
yexuejc-base 更新记录
|
||||
------------------
|
||||
|
||||
#### version :1.5.3-jre11
|
||||
**time: ** <br/>
|
||||
#### version :1.5.4-jre11
|
||||
**time: 2025-8-27 19:45:33** <br/>
|
||||
**branch:** jre11 <br/>
|
||||
**update:** <br/>
|
||||
1. [FileUtil.java](src/main/java/com/yexuejc/base/util/FileUtil.java)增加读取大文件自定义方法和单纯读取方法
|
||||
2. JsonUtil 增加objToMap;优化obj2Json
|
||||
3. DateUtil 标准化日期时间的转换函数
|
||||
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** <br/>
|
||||
**branch:** jre11 <br/>
|
||||
**update:** <br/>
|
||||
1. [FileUtil](src/main/java/com/yexuejc/base/util/FileUtil.java) 增加读取大文件自定义方法和单纯读取方法
|
||||
2. [JsonUtil](src/main/java/com/yexuejc/base/util/JsonUtil.java) 增加objToMap;优化obj2Json
|
||||
3. [DateUtil](src/main/java/com/yexuejc/base/util/DateUtil.java) 标准化日期时间的转换函数
|
||||
4. [AES](src/main/java/com/yexuejc/base/encrypt/AES.java) 兼容ECB(虽然不再建议利用)
|
||||
5. [StrUtil](src/main/java/com/yexuejc/base/util/StrUtil.java) 增加国家代码二进制相互转换
|
||||
---
|
||||
|
||||
#### version :1.5.2-jre11
|
||||
|
||||
11
pom.xml
11
pom.xml
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>top.yexuejc</groupId>
|
||||
<artifactId>yexuejc-base</artifactId>
|
||||
<version>1.5.2-jre11</version>
|
||||
<version>1.5.4-jre11</version>
|
||||
|
||||
<name>yexuejc-base</name>
|
||||
<url>https://github.com/yexuejc/yexuejc-base</url>
|
||||
@@ -48,7 +48,7 @@
|
||||
<validation-api.version>3.0.2</validation-api.version>
|
||||
|
||||
<commons-io.version>2.11.0</commons-io.version>
|
||||
<bcprov-jdk18on.version>1.78</bcprov-jdk18on.version>
|
||||
<bouncycastle-jdk18on.version>1.81</bouncycastle-jdk18on.version>
|
||||
<guava.version>33.1.0-jre</guava.version>
|
||||
<apache-poi.version>5.2.5</apache-poi.version>
|
||||
<jackson.version>2.17.0</jackson.version>
|
||||
@@ -94,7 +94,12 @@
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>${bcprov-jdk18on.version}</version>
|
||||
<version>${bouncycastle-jdk18on.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk18on</artifactId>
|
||||
<version>${bouncycastle-jdk18on.version}</version>
|
||||
</dependency>
|
||||
<!--com.yexuejc.base.file.FileInput 读文件-->
|
||||
<dependency>
|
||||
|
||||
@@ -7,15 +7,17 @@ package com.yexuejc.base.constant;
|
||||
* @date 2023/05/17 13:45
|
||||
*/
|
||||
public class DateConsts {
|
||||
public static final String BAR = "-";
|
||||
public static final CharSequence DATE_KEY_AM = "AM";
|
||||
public static final CharSequence DATE_KEY_PM = "PM";
|
||||
public static final String DATE_TIMESTAMP_LINUX = "M/dd/yy, h:mm a";
|
||||
public static final CharSequence SLASH = "/";
|
||||
public static final CharSequence COLON = ":";
|
||||
public static final String DATE_YYYY_MM_DD_SLASH = "yyyy/MM/dd";
|
||||
public static final CharSequence DATE_KEY_T = "T";
|
||||
public static final String DATE_KEY_T = "T";
|
||||
public static final String DATE_KEY_AM = "AM";
|
||||
public static final String DATE_KEY_PM = "PM";
|
||||
public static final String DATE_KEY_Z = "Z";
|
||||
public static final String DATE_TIMESTAMP_LINUX = "M/dd/yy, h:mm a";
|
||||
public static final String DATE_YYYY_MM_DD_SLASH = "yyyy/MM/dd";
|
||||
public static final String DATE_TIMESTAMP = "yyyy/MM/dd H:m:s";
|
||||
public static final String DATE_YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
public static final String DATE_PATTERN = "yyyy-MM-dd";
|
||||
public static final String TIME_PATTERN = "HH:mm:ss";
|
||||
public static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
|
||||
public static final String DATE_TIME_MS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";
|
||||
public static final String ISO_8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ssxxx";
|
||||
public static final String ISO_8601_MS_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSSxxx";
|
||||
}
|
||||
|
||||
40
src/main/java/com/yexuejc/base/constant/ExpCode.java
Normal file
40
src/main/java/com/yexuejc/base/constant/ExpCode.java
Normal file
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,9 @@ package com.yexuejc.base.constant;
|
||||
* @author: maxf
|
||||
* @date: 2017/12/27 16:47
|
||||
*/
|
||||
public class RespsConsts {
|
||||
public class RespConsts {
|
||||
|
||||
private RespsConsts() {
|
||||
private RespConsts() {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.yexuejc.base.constant;
|
||||
|
||||
/**
|
||||
* 常用符号常量
|
||||
*
|
||||
* @author maxiaofeng
|
||||
* @date 2025/8/25 15:15
|
||||
*/
|
||||
public class SymbolicConstant {
|
||||
/**常量:换行符*/
|
||||
public static final String NEW_LINE = "\n";
|
||||
public static final String EMPTY = "";
|
||||
public static final String EQUAL = "=";
|
||||
public static final String COMMA = ",";
|
||||
public static final String BAR = "-";
|
||||
public static final String SLASH = "/";
|
||||
public static final String COLON = ":";
|
||||
|
||||
private SymbolicConstant() {
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.yexuejc.base.constant.DateConsts;
|
||||
import com.yexuejc.base.constant.SymbolicConstant;
|
||||
import com.yexuejc.base.util.StrUtil;
|
||||
|
||||
/**
|
||||
@@ -26,16 +27,16 @@ public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
|
||||
if (StrUtil.isEmpty(timeString)) {
|
||||
return null;
|
||||
}
|
||||
if (timeString.contains(DateConsts.BAR)) {
|
||||
if (timeString.contains(SymbolicConstant.BAR)) {
|
||||
return LocalDate.parse(timeString, DateTimeFormatter.ISO_DATE);
|
||||
} else if (timeString.contains(DateConsts.DATE_KEY_AM)
|
||||
|| timeString.contains(DateConsts.DATE_KEY_PM)) {
|
||||
return LocalDate.parse(timeString,
|
||||
DateTimeFormatter.ofPattern(DateConsts.DATE_TIMESTAMP_LINUX, Locale.ENGLISH));
|
||||
} else if (timeString.contains(DateConsts.SLASH) && timeString.contains(DateConsts.COLON)) {
|
||||
} else if (timeString.contains(SymbolicConstant.SLASH) && timeString.contains(SymbolicConstant.COLON)) {
|
||||
return LocalDate.parse(timeString.substring(0, 10),
|
||||
DateTimeFormatter.ofPattern(DateConsts.DATE_YYYY_MM_DD_SLASH));
|
||||
} else if (timeString.contains(DateConsts.SLASH)) {
|
||||
} else if (timeString.contains(SymbolicConstant.SLASH)) {
|
||||
return LocalDate.parse(timeString, DateTimeFormatter.ofPattern(DateConsts.DATE_YYYY_MM_DD_SLASH));
|
||||
} else {
|
||||
return LocalDate.parse(timeString, DateTimeFormatter.BASIC_ISO_DATE);
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.yexuejc.base.constant.DateConsts;
|
||||
import com.yexuejc.base.constant.SymbolicConstant;
|
||||
import com.yexuejc.base.util.StrUtil;
|
||||
|
||||
/**
|
||||
@@ -33,11 +34,11 @@ public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
|
||||
DateTimeFormatter.ofPattern(DateConsts.DATE_TIMESTAMP_LINUX, Locale.ENGLISH));
|
||||
} else if (timeString.endsWith(DateConsts.DATE_KEY_Z)) {
|
||||
return LocalDateTime.parse(timeString, DateTimeFormatter.ISO_INSTANT);
|
||||
} else if (timeString.contains(DateConsts.SLASH)) {
|
||||
} else if (timeString.contains(SymbolicConstant.SLASH)) {
|
||||
return LocalDateTime.parse(timeString, DateTimeFormatter.ofPattern(DateConsts.DATE_TIMESTAMP));
|
||||
} else {
|
||||
return LocalDateTime.parse(timeString,
|
||||
DateTimeFormatter.ofPattern(DateConsts.DATE_YYYY_MM_DD_HH_MM_SS));
|
||||
DateTimeFormatter.ofPattern(DateConsts.DATE_TIME_PATTERN));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.yexuejc.base.constant.DateConsts;
|
||||
import com.yexuejc.base.constant.SymbolicConstant;
|
||||
import com.yexuejc.base.util.StrUtil;
|
||||
|
||||
/**
|
||||
@@ -34,12 +35,12 @@ public class TimestampDeserializer extends JsonDeserializer<Timestamp> {
|
||||
DateTimeFormatter.ofPattern(DateConsts.DATE_TIMESTAMP_LINUX, Locale.ENGLISH)));
|
||||
} else if (timeString.endsWith(DateConsts.DATE_KEY_Z)) {
|
||||
return Timestamp.valueOf(LocalDateTime.parse(timeString, DateTimeFormatter.ISO_INSTANT));
|
||||
} else if (timeString.contains(DateConsts.SLASH)) {
|
||||
} else if (timeString.contains(SymbolicConstant.SLASH)) {
|
||||
return Timestamp.valueOf(
|
||||
LocalDateTime.parse(timeString, DateTimeFormatter.ofPattern(DateConsts.DATE_TIMESTAMP)));
|
||||
} else {
|
||||
return Timestamp.valueOf(LocalDateTime.parse(timeString,
|
||||
DateTimeFormatter.ofPattern(DateConsts.DATE_YYYY_MM_DD_HH_MM_SS)));
|
||||
DateTimeFormatter.ofPattern(DateConsts.DATE_TIME_PATTERN)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
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,44 +32,64 @@ public class AES {
|
||||
/**
|
||||
* 加密模式
|
||||
*/
|
||||
public static enum ALGORITHM {
|
||||
// @formatter:off
|
||||
public enum ALGORITHM {
|
||||
//算法/模式/填充 16字节加密后数据长度 不满16字节加密后长度
|
||||
//AES/CBC/NoPadding 16 不支持
|
||||
AES_CBC_NoPadding("AES/CBC/NoPadding"),
|
||||
//AES/CBC/PKCS5Padding 32 16
|
||||
AES_CBC_PKCS5Padding("AES/CBC/PKCS5Padding"),
|
||||
//AES/CBC/PKCS7Padding 32 16
|
||||
AES_CBC_PKCS7Padding("AES/CBC/PKCS7Padding"),
|
||||
//AES/CBC/ISO10126Padding 32 16
|
||||
AES_CBC_ISO10126Padding("AES/CBC/ISO10126Padding"),
|
||||
//AES/CBC/ZeroPadding 16 16
|
||||
AES_CBC_ZeroPadding("AES/CBC/ZeroPadding"),
|
||||
//AES/CFB/NoPadding 16 原始数据长度
|
||||
AES_CFB_NoPadding("AES/CFB/NoPadding"),
|
||||
//AES/CFB/PKCS5Padding 32 16
|
||||
//AES/CFB/PKCS5Padding 16 不支持
|
||||
AES_CFB_PKCS5Padding("AES/CFB/PKCS5Padding"),
|
||||
//AES/CFB/ISO10126Padding 32 16
|
||||
//AES/CFB/PKCS7Padding 16 不支持
|
||||
AES_CFB_PKCS7Padding("AES/CFB/PKCS7Padding"),
|
||||
//AES/CFB/ISO10126Padding 16 不支持
|
||||
AES_CFB_ISO10126Padding("AES/CFB/ISO10126Padding"),
|
||||
//AES/CFB/ZeroPadding 16 不支持
|
||||
AES_CFB_ZeroPadding("AES/CFB/ZeroPadding"),
|
||||
//AES/ECB/NoPadding 16 不支持
|
||||
AES_ECB_NoPadding("AES/ECB/NoPadding"),
|
||||
//AES/ECB/PKCS5Padding 32 16
|
||||
//AES/ECB/PKCS5Padding 32 不支持
|
||||
AES_ECB_PKCS5Padding("AES/ECB/PKCS5Padding"),
|
||||
//AES/ECB/PKCS7Padding 32 16
|
||||
AES_ECB_PKCS7Padding("AES/ECB/PKCS7Padding"),
|
||||
//AES/ECB/ISO10126Padding 32 16
|
||||
AES_ECB_ISO10126Padding("AES/ECB/ISO10126Padding"),
|
||||
//AES/ECB/ZeroPadding 16 16
|
||||
AES_ECB_ZeroPadding("AES/ECB/ZeroPadding"),
|
||||
//AES/OFB/NoPadding 16 原始数据长度
|
||||
AES_OFB_NoPadding("AES/OFB/NoPadding"),
|
||||
//AES/OFB/PKCS5Padding 32 16
|
||||
AES_OFB_PKCS5Padding("AES/OFB/PKCS5Padding"),
|
||||
//AES/OFB/PKCS7Padding 32 16
|
||||
AES_OFB_PKCS7Padding("AES/OFB/PKCS7Padding"),
|
||||
//AES/OFB/ISO10126Padding 32 16
|
||||
AES_OFB_ISO10126Padding("AES/OFB/ISO10126Padding"),
|
||||
//AES/OFB/ZeroPadding 16 16
|
||||
AES_OFB_ZeroPadding("AES/OFB/ZeroPadding"),
|
||||
//AES/PCBC/NoPadding 16 不支持
|
||||
AES_PCBC_NoPadding("AES/PCBC/NoPadding"),
|
||||
//AES/PCBC/PKCS5Padding 32 16
|
||||
AES_PCBC_PKCS5Padding("AES/PCBC/PKCS5Padding"),
|
||||
//AES/PCBC/ISO10126Padding 32 16
|
||||
AES_PCBC_ISO10126Padding("AES/PCBC/ISO10126Padding");
|
||||
public String name;
|
||||
AES_PCBC_ISO10126Padding("AES/PCBC/ISO10126Padding"),
|
||||
//AES/CTR/NoPadding 16 不支持
|
||||
AES_CTR_NoPadding("AES/CTR/NoPadding");
|
||||
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";
|
||||
@@ -80,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;
|
||||
@@ -93,13 +116,15 @@ public class AES {
|
||||
byte[] plaintext = new byte[plaintextLength];
|
||||
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
|
||||
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(charset), AES_ALGORITHM);
|
||||
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes(charset));
|
||||
IvParameterSpec ivspec = null;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,9 +138,12 @@ 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 = new IvParameterSpec(iv.getBytes(charset));
|
||||
IvParameterSpec ivspec = null;
|
||||
if (!algorithm.code.contains("ECB")) {
|
||||
ivspec = new IvParameterSpec(iv.getBytes(charset));
|
||||
}
|
||||
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
|
||||
byte[] original = cipher.doFinal(encrypted);
|
||||
return new String(original, charset).trim();
|
||||
@@ -124,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;
|
||||
|
||||
@@ -1,20 +1,37 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import java.io.*;
|
||||
import java.security.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
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;
|
||||
import com.yexuejc.base.util.StrUtil;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
|
||||
/**
|
||||
* RSA加解密 配置模式
|
||||
@@ -25,35 +42,77 @@ import java.util.logging.Logger;
|
||||
* @date: 2018/5/15 14:39
|
||||
*/
|
||||
public class RSA {
|
||||
private static Logger log = Logger.getLogger(RSA.class.getName());
|
||||
public static final String CHARSET = "UTF-8";
|
||||
public static final String RSA_ALGORITHM = "RSA";
|
||||
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;
|
||||
/**
|
||||
* 算法名称
|
||||
* <pre>
|
||||
* RSA - RSA算法
|
||||
* DSA - 数字签名算法
|
||||
* EC - 椭圆曲线算法
|
||||
* Ed25519 - Edwards-curve数字签名算法
|
||||
* Ed448 - 另一种Edwards-curve算法
|
||||
* X25519 - X25519密钥协商算法
|
||||
* X448 - X448密钥协商算法
|
||||
* </pre>
|
||||
* <p>使用方式:</p>
|
||||
* <p></p>
|
||||
*/
|
||||
public String algorithm = "RSA";
|
||||
/**
|
||||
* 加密方式
|
||||
* <h5>示例:</h5>
|
||||
* <pre>
|
||||
* RSA 可选择isChangeSign 是否每次改变加密结果
|
||||
* RSA/None/NoPadding 不改变加密结果
|
||||
* RSA/ECB/PKCS1Padding 改变加密结果
|
||||
* </pre>
|
||||
* <p>
|
||||
* <h5>支持的算法:</h5>
|
||||
* <li>对称加密:AES、DES、DESede(3DES)、Blowfish、RC4等</li>
|
||||
* <li>非对称加密:RSA、EC(椭圆曲线)</li>
|
||||
* <li>其他:ARCFOUR、ChaCha20等</li>
|
||||
* <h5>常见工作模式:</h5>
|
||||
* <li>ECB(电子密码本,不推荐)</li>
|
||||
* <li>CBC(密码块链接,需IV)</li>
|
||||
* <li>GCM(Galois/Counter模式,推荐用于AES)</li>
|
||||
* <li>CTR(计数器模式)</li>
|
||||
* <li>OFB、CFB(较少使用)</li>
|
||||
* <h5>常见填充方案:</h5>
|
||||
* <li>PKCS5Padding(对称加密常用)</li>
|
||||
* <li>PKCS1Padding(RSA专用)</li>
|
||||
* <li>OAEPWithSHA-256AndMGF1Padding(RSA的OAEP填充)</li>
|
||||
* <li>NoPadding(无填充,需数据长度对齐)</li>
|
||||
* </p>
|
||||
* <hr>
|
||||
* <p><b>AES的(算法/模式/填充)组合 参照 {@link AES.ALGORITHM}<b/></p>
|
||||
*/
|
||||
public static String RSA_ALGORITHM_ECB = "RSA";
|
||||
public String transformation = "RSA";
|
||||
/**
|
||||
* 是否每次改变加密结果
|
||||
* 只针对于RSA_ALGORITHM_ECB = "RSA"有效
|
||||
* 只针对于 transformation = "RSA"有效
|
||||
*/
|
||||
public static boolean isChangeSign = true;
|
||||
public boolean isChangeSign = true;
|
||||
/**
|
||||
* 是否使用 Base64URL 方式加密 默认正常加密
|
||||
* <pre>
|
||||
* 关于 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',
|
||||
@@ -62,11 +121,11 @@ public class RSA {
|
||||
* };
|
||||
* </pre>
|
||||
*/
|
||||
public static boolean encodeBase64URLSafe = false;
|
||||
public boolean encodeBase64URLSafe = false;
|
||||
/**
|
||||
* 签名算法
|
||||
*/
|
||||
public static SignAlgorithm signAlgorithm = SignAlgorithm.SHA1withRSA;
|
||||
public final SignAlgorithm signAlgorithm = SignAlgorithm.SHA256withRSA;
|
||||
|
||||
/**
|
||||
* 生成密钥对
|
||||
@@ -75,7 +134,7 @@ public class RSA {
|
||||
* @param base64URLSafe 是否生成 base64URL 格式的密钥:默认false
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, String> initKeys(int keySize, boolean base64URLSafe) {
|
||||
public Map<String, String> initKeys(int keySize, boolean base64URLSafe) {
|
||||
encodeBase64URLSafe = base64URLSafe;
|
||||
return initKeys(keySize);
|
||||
}
|
||||
@@ -86,13 +145,13 @@ public class RSA {
|
||||
* @param keySize 生成长度
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, String> initKeys(int keySize) {
|
||||
public Map<String, String> 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对象,密钥长度
|
||||
@@ -103,8 +162,8 @@ public class RSA {
|
||||
Key publicKey = keyPair.getPublic();
|
||||
//得到私钥
|
||||
Key privateKey = keyPair.getPrivate();
|
||||
String privateKeyStr = null;
|
||||
String publicKeyStr = null;
|
||||
String privateKeyStr;
|
||||
String publicKeyStr;
|
||||
if (encodeBase64URLSafe) {
|
||||
publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded());
|
||||
privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded());
|
||||
@@ -112,24 +171,42 @@ public class RSA {
|
||||
publicKeyStr = Base64.encodeBase64String(publicKey.getEncoded());
|
||||
privateKeyStr = Base64.encodeBase64String(privateKey.getEncoded());
|
||||
}
|
||||
Map<String, String> keyPairMap = new HashMap<String, String>(2);
|
||||
Map<String, String> keyPairMap = new HashMap<>(2);
|
||||
keyPairMap.put("publicKey", publicKeyStr);
|
||||
keyPairMap.put("privateKey", privateKeyStr);
|
||||
return keyPairMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成密钥文件
|
||||
* <p>会在指定路径下生成
|
||||
* <b>private.key</b>和<b>public.key</b>
|
||||
* </p>
|
||||
*
|
||||
* @param filePath 密钥文件路径
|
||||
* @throws BaseException
|
||||
*/
|
||||
public void initKey4File(String filePath) throws BaseException {
|
||||
Map<String, String> 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));
|
||||
} catch (IOException e) {
|
||||
throw new BaseException(e, ExpCode.CREATE_KEY_FILE_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到公钥
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,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);
|
||||
}
|
||||
@@ -166,18 +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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,14 +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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,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);
|
||||
}
|
||||
@@ -217,36 +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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,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);
|
||||
@@ -290,21 +380,15 @@ public class RSA {
|
||||
i++;
|
||||
offSet = i * maxBlock;
|
||||
}
|
||||
|
||||
return out.toByteArray();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
|
||||
throw new BaseException(e, ExpCode.DATA_DECODE_PARAM_FAILED, maxBlock);
|
||||
}
|
||||
byte[] resultDatas = out.toByteArray();
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
return resultDatas;
|
||||
}
|
||||
|
||||
private static Signature signature;
|
||||
|
||||
/**
|
||||
* 私钥签名:默认算法SHA1withRSA
|
||||
* 私钥签名:默认算法SHA256withRSA
|
||||
* <p>
|
||||
* 签名算法 {@link SignAlgorithm}
|
||||
* </p>
|
||||
@@ -313,48 +397,38 @@ public class RSA {
|
||||
* @param privateKey 签名私钥
|
||||
* @param base64URLSafe 是否生成 base64URL 格式的密钥:默认false
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static String sign(String plaintext, RSAPrivateKey privateKey, boolean base64URLSafe) throws NoSuchAlgorithmException {
|
||||
public String sign(String plaintext, RSAPrivateKey privateKey, boolean base64URLSafe) throws BaseException {
|
||||
encodeBase64URLSafe = base64URLSafe;
|
||||
return sign(plaintext, privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥签名:默认算法SHA1withRSA
|
||||
* 私钥签名:默认算法SHA256withRSA
|
||||
* <p>
|
||||
* 签名算法 {@link SignAlgorithm}
|
||||
* </p>
|
||||
*
|
||||
* @param plaintext 签名字符串
|
||||
* @param privateKey 签名私钥
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @return 签名串
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static String sign(String plaintext, RSAPrivateKey privateKey) throws NoSuchAlgorithmException {
|
||||
signature = Signature.getInstance(signAlgorithm.getValue());
|
||||
String signBase64Str = "";
|
||||
|
||||
public String sign(String plaintext, RSAPrivateKey privateKey) throws BaseException {
|
||||
try {
|
||||
Signature signature = Signature.getInstance(signAlgorithm.getValue());
|
||||
String signBase64Str = "";
|
||||
signature.initSign(privateKey);
|
||||
try {
|
||||
signature.update(plaintext.getBytes(CHARSET));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("签名字符串[" + plaintext + "]的数据时发生异常", e);
|
||||
}
|
||||
signature.update(plaintext.getBytes(CHARSET));
|
||||
if (encodeBase64URLSafe) {
|
||||
signBase64Str = Base64.encodeBase64URLSafeString(signature.sign());
|
||||
} else {
|
||||
signBase64Str = Base64.encodeBase64String(signature.sign());
|
||||
}
|
||||
return signBase64Str;
|
||||
} catch (InvalidKeyException var6) {
|
||||
var6.printStackTrace();
|
||||
throw new RuntimeException("签名字符串[" + plaintext + "]的数据时发生异常", var6);
|
||||
} catch (SignatureException var7) {
|
||||
var7.printStackTrace();
|
||||
throw new RuntimeException("签名字符串[" + plaintext + "]的数据时发生异常", var7);
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, ExpCode.SIGN_PARAM_FAILED, plaintext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,24 +438,92 @@ public class RSA {
|
||||
* @param plaintext 原串
|
||||
* @param signStr 签名串
|
||||
* @param publicKey 公钥
|
||||
* @return
|
||||
* @throws UnsupportedEncodingException
|
||||
* @return true:校验成功 / false:校验失败
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static boolean verify(String plaintext, String signStr, RSAPublicKey publicKey) throws UnsupportedEncodingException, NoSuchAlgorithmException {
|
||||
signature = Signature.getInstance(signAlgorithm.getValue());
|
||||
boolean isValid = false;
|
||||
public boolean verify(String plaintext, String signStr, RSAPublicKey publicKey) throws BaseException {
|
||||
try {
|
||||
Signature signature = Signature.getInstance(signAlgorithm.getValue());
|
||||
signature.initVerify(publicKey);
|
||||
signature.update(plaintext.getBytes(CHARSET));
|
||||
isValid = signature.verify(Base64.decodeBase64(signStr));
|
||||
} catch (InvalidKeyException var6) {
|
||||
var6.printStackTrace();
|
||||
throw new RuntimeException("校验签名字符串[" + plaintext + "]的数据时发生异常", var6);
|
||||
} catch (SignatureException var7) {
|
||||
var7.printStackTrace();
|
||||
throw new RuntimeException("校验签名字符串[" + plaintext + "]的数据时发生异常", var7);
|
||||
return signature.verify(Base64.decodeBase64(signStr));
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, ExpCode.VERIFY_PARAM_FAILED, plaintext);
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
/**
|
||||
* 公钥校验签名
|
||||
* <p>校验格式:</p>
|
||||
* <pre>
|
||||
* [HTTP-METHOD] [Response-URI]
|
||||
* [Client-Id].[Response-Time].[Response-Body]
|
||||
* </pre>
|
||||
*
|
||||
* @param uri 请求地址
|
||||
* @param respHeader 请求响应头
|
||||
* @param data 数据
|
||||
* @param publicKeyPath 公钥文件地址
|
||||
* @return true:校验成功 / false:校验失败
|
||||
* @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}"
|
||||
.replace("{uri}", uri)
|
||||
.replace("{clientId}", respHeader.getClientId())
|
||||
.replace("{respTime}", respHeader.geRespTime())
|
||||
.replace("{body}", data);
|
||||
// @formatter:on
|
||||
String sign = respHeader.getSignature();
|
||||
if (sign.contains(SymbolicConstant.COMMA)) {
|
||||
sign = Arrays.stream(sign.split(SymbolicConstant.COMMA))
|
||||
.map(s -> s.split(SymbolicConstant.EQUAL))
|
||||
.filter(subSplit -> subSplit.length > 1 && RequestHeader.SIGNATURE.equalsIgnoreCase(subSplit[0]))
|
||||
.map(subSplit -> subSplit[1])
|
||||
.findFirst()
|
||||
.orElse(sign);
|
||||
}
|
||||
log.log(System.Logger.Level.DEBUG, "签名内容:=====\n{0}\n=====", signContent);
|
||||
return verify(signContent, sign, RSA2.getPublicKey(publicKeyPath));
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, ExpCode.VERIFY_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 签名
|
||||
* <p> 签名格式:</p>
|
||||
* <pre>
|
||||
* [HTTP-METHOD] [Request-URI]
|
||||
* [Client-Id].[Request-Time].[Request-Body]
|
||||
* </pre>
|
||||
*
|
||||
* @param uri 请求地址
|
||||
* @param requestHeader 请求头
|
||||
* @param data 数据
|
||||
* @param privateKeyPath 私钥文件地址
|
||||
* @return 签名串
|
||||
* @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}"
|
||||
.replace("{uri}", uri)
|
||||
.replace("{clientId}", requestHeader.getClientId())
|
||||
.replace("{reqTime}", requestHeader.getReqTime())
|
||||
.replace("{body}", data);
|
||||
// @formatter:on
|
||||
log.log(System.Logger.Level.DEBUG, "签名内容:=====\n{0}\n=====", signContent);
|
||||
return sign(signContent, RSA2.getPrivateKeyFromPKCS1(privateKeyPath), true);
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, "签名时发生异常");
|
||||
}
|
||||
}
|
||||
|
||||
private RSA() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,42 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
import com.yexuejc.base.util.StrUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.*;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Security;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Base64;
|
||||
import 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;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.openssl.PEMKeyPair;
|
||||
import org.bouncycastle.openssl.PEMParser;
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
||||
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
|
||||
import org.bouncycastle.operator.InputDecryptorProvider;
|
||||
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
|
||||
|
||||
/**
|
||||
* RSA加解密 证书模式
|
||||
* 依赖 {@link RSA}
|
||||
@@ -21,9 +47,19 @@ import java.util.Enumeration;
|
||||
* @date: 2018/5/15 14:37
|
||||
*/
|
||||
public class RSA2 {
|
||||
private static final System.Logger log = System.getLogger(RSA2.class.getName());
|
||||
|
||||
public static final String CHARSET = "UTF-8";
|
||||
public static final String RSA_ALGORITHM = "RSA";
|
||||
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_PUBLIC = "-----BEGIN PUBLIC KEY-----";
|
||||
public static final String PEM_FILE_END_PUBLIC = "-----END PUBLIC KEY-----";
|
||||
public static final String PEM_FILE_START_PREFIX = "-----BEGIN";
|
||||
|
||||
/**
|
||||
* 得到公钥
|
||||
@@ -32,11 +68,24 @@ public class RSA2 {
|
||||
* @throws Exception
|
||||
*/
|
||||
public static RSAPublicKey getPublicKey(String filepath) throws CertificateException, FileNotFoundException {
|
||||
//通过证书,获取公钥
|
||||
CertificateFactory cf = null;
|
||||
cf = CertificateFactory.getInstance("X.509");
|
||||
Certificate c = cf.generateCertificate(new FileInputStream(filepath));
|
||||
return (RSAPublicKey) c.getPublicKey();
|
||||
try (FileInputStream pubKeyIn = new FileInputStream(filepath)) {
|
||||
byte[] certData = pubKeyIn.readAllBytes();
|
||||
// 检查是否为PEM格式
|
||||
String certString = new String(certData, StandardCharsets.UTF_8);
|
||||
if (certString.contains(PEM_FILE_START_PREFIX)) {
|
||||
// 这可能不是证书文件,而是公钥文件
|
||||
return getPublicKey4Pem(certString);
|
||||
}
|
||||
//通过证书,获取公钥
|
||||
try (ByteArrayInputStream bis = new ByteArrayInputStream(certData)) {
|
||||
return getPublicKey(bis);
|
||||
} catch (Exception e) {
|
||||
// 在尝试一次读取
|
||||
return getPublicKey4Pem(certString);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new CertificateException("无法解析证书: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,12 +97,88 @@ public class RSA2 {
|
||||
*/
|
||||
public static RSAPublicKey getPublicKey(InputStream pubKeyIn) throws CertificateException {
|
||||
//通过证书,获取公钥
|
||||
CertificateFactory cf = null;
|
||||
cf = CertificateFactory.getInstance("X.509");
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
Certificate c = cf.generateCertificate(pubKeyIn);
|
||||
return (RSAPublicKey) c.getPublicKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从PKCS12证书中获取公钥
|
||||
*
|
||||
* @param pubKeyPath 密钥文件路径
|
||||
* @param alias 别名
|
||||
* @param password 密码
|
||||
* @return RSAPublicKey
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static RSAPublicKey getPublicKeyFromPKCS12(String pubKeyPath, String alias, String password) throws BaseException {
|
||||
try (FileInputStream fis = new FileInputStream(pubKeyPath)) {
|
||||
return getPublicKeyFromPKCS12(fis, alias, password);
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, ExpCode.PUBLIC_READ_P12_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从PKCS12证书中获取公钥
|
||||
*
|
||||
* @param pubKeyIn 密钥文件流
|
||||
* @param alias 别名
|
||||
* @param password 密码
|
||||
* @return RSAPublicKey
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static RSAPublicKey getPublicKeyFromPKCS12(InputStream pubKeyIn, String alias, String password) throws BaseException {
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance(KEY_PKCS12);
|
||||
keyStore.load(pubKeyIn, password.toCharArray());
|
||||
Certificate cert = keyStore.getCertificate(alias);
|
||||
return (RSAPublicKey) cert.getPublicKey();
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, ExpCode.PUBLIC_READ_P12_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从PEM格式的公钥文件中提取公钥
|
||||
*
|
||||
* @param pemContent PEM格式的内容
|
||||
* @return RSAPublicKey
|
||||
* @throws CertificateException
|
||||
*/
|
||||
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", "");
|
||||
|
||||
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", "");
|
||||
|
||||
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();
|
||||
if (!pemContent.equals(trimmedString)) {
|
||||
byte[] keyBytes = Base64.getDecoder().decode(trimmedString);
|
||||
KeyFactory kf = KeyFactory.getInstance(KEY_RSA);
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
|
||||
return (RSAPublicKey) kf.generatePublic(keySpec);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new CertificateException("无法从PEM内容解析公钥: " + e.getMessage(), e);
|
||||
}
|
||||
throw new CertificateException("无法从PEM内容解析公钥");
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取JKS格式的key(私钥)keystore格式
|
||||
*
|
||||
@@ -61,14 +186,10 @@ public class RSA2 {
|
||||
* @param alias 证书别名
|
||||
* @param password 证书密码
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws KeyStoreException
|
||||
* @throws IOException
|
||||
* @throws CertificateException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKey(String filepath, String alias, String password) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, UnrecoverableKeyException {
|
||||
return getPrivateKey(filepath, alias, password, "JKS");
|
||||
public static RSAPrivateKey getPrivateKey(String filepath, String alias, String password) throws BaseException {
|
||||
return getPrivateKey(filepath, alias, password, KEY_JKS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,14 +199,10 @@ public class RSA2 {
|
||||
* @param alias 证书别名
|
||||
* @param password 证书密码
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws KeyStoreException
|
||||
* @throws IOException
|
||||
* @throws CertificateException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKey(InputStream priKeyIn, String alias, String password) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, UnrecoverableKeyException {
|
||||
return getPrivateKey(priKeyIn, alias, password, "JKS");
|
||||
public static RSAPrivateKey getPrivateKey(InputStream priKeyIn, String alias, String password) throws BaseException {
|
||||
return getPrivateKey(priKeyIn, alias, password, KEY_JKS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,14 +212,10 @@ public class RSA2 {
|
||||
* @param alias 证书别名 可空
|
||||
* @param password 证书密码
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws KeyStoreException
|
||||
* @throws IOException
|
||||
* @throws CertificateException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS12(String filepath, String alias, String password) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, UnrecoverableKeyException {
|
||||
return getPrivateKey(filepath, alias, password, "PKCS12");
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS12(String filepath, String alias, String password) throws BaseException {
|
||||
return getPrivateKey(filepath, alias, password, KEY_PKCS12);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,14 +225,54 @@ public class RSA2 {
|
||||
* @param alias 证书别名 可空
|
||||
* @param password 证书密码
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws KeyStoreException
|
||||
* @throws IOException
|
||||
* @throws CertificateException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS12(InputStream priKeyIn, String alias, String password) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, UnrecoverableKeyException {
|
||||
return getPrivateKey(priKeyIn, alias, password, "PKCS12");
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS12(InputStream priKeyIn, String alias, String password) throws BaseException {
|
||||
return getPrivateKey(priKeyIn, alias, password, KEY_PKCS12);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取PKCS8格式的key(私钥)pem格式
|
||||
*
|
||||
* @param filepath 私钥路径
|
||||
* @return
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS8(String filepath, String password) throws BaseException {
|
||||
return getPrivateKey(filepath, null, password, KEY_PKCS8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取PKCS8格式的key(私钥)pem格式
|
||||
*
|
||||
* @param priKeyIn 私钥文件流
|
||||
* @return
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS8(InputStream priKeyIn, String password) throws BaseException {
|
||||
return getPrivateKey(priKeyIn, null, password, KEY_PKCS8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取PKCS8格式的key(私钥)pem格式
|
||||
*
|
||||
* @param filepath 私钥路径
|
||||
* @return
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS1(String filepath) throws BaseException {
|
||||
return getPrivateKey(filepath, null, null, KEY_PKCS1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取PKCS8格式的key(私钥)pem格式
|
||||
*
|
||||
* @param priKeyIn 私钥文件流
|
||||
* @return
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS1(InputStream priKeyIn) throws BaseException {
|
||||
return getPrivateKey(priKeyIn, null, null, KEY_PKCS1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,60 +283,109 @@ public class RSA2 {
|
||||
* @param password 证书密码
|
||||
* @param type 证书格式
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws KeyStoreException
|
||||
* @throws IOException
|
||||
* @throws CertificateException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKey(String filepath, String alias, String password, String type) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, UnrecoverableKeyException {
|
||||
KeyStore ks = KeyStore.getInstance(type);
|
||||
FileInputStream fileInputStream = null;
|
||||
public static RSAPrivateKey getPrivateKey(String filepath, String alias, String password, String type) throws BaseException {
|
||||
try (FileInputStream fileInputStream = new FileInputStream(filepath)) {
|
||||
return getPrivateKey(fileInputStream, alias, password, type);
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, ExpCode.PRIVATE_READ_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取key(私钥)
|
||||
*
|
||||
* @param priKeyIn 私钥文件流
|
||||
* @param alias 证书别名 可空
|
||||
* @param password 证书密码
|
||||
* @param type 证书格式
|
||||
* @return
|
||||
* @throws BaseException
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKey(InputStream priKeyIn, String alias, String password, String type) throws BaseException {
|
||||
try {
|
||||
fileInputStream = new FileInputStream(filepath);
|
||||
ks.load(fileInputStream, password.toCharArray());
|
||||
if (StrUtil.isEmpty(alias)) {
|
||||
Enumeration<?> aliases = ks.aliases();
|
||||
if (aliases != null) {
|
||||
if (aliases.hasMoreElements()) {
|
||||
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);
|
||||
if (keyString.contains(PEM_FILE_START_PREFIX)) {
|
||||
// 标准PEM格式内容
|
||||
return getPrivateKeyByPKCS8(keyString, password);
|
||||
}
|
||||
// 非标准PEM格式内容,尝试直接读取
|
||||
String trimmedString = keyString.replaceAll("\\s", "").trim();
|
||||
if (!keyString.equals(trimmedString)) {
|
||||
keyBytes = Base64.getDecoder().decode(trimmedString);
|
||||
}
|
||||
// 验证数据是否有效
|
||||
if (keyBytes.length == 0) {
|
||||
throw new BaseException(ExpCode.PRIVATE_READ_FAILED);
|
||||
}
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
|
||||
KeyFactory kf = KeyFactory.getInstance(KEY_RSA);
|
||||
return (RSAPrivateKey) kf.generatePrivate(keySpec);
|
||||
} else {
|
||||
KeyStore ks = KeyStore.getInstance(type);
|
||||
ks.load(priKeyIn, password.toCharArray());
|
||||
if (StrUtil.isEmpty(alias)) {
|
||||
Enumeration<?> aliases = ks.aliases();
|
||||
if (aliases != null && aliases.hasMoreElements()) {
|
||||
alias = (String) aliases.nextElement();
|
||||
}
|
||||
}
|
||||
return (RSAPrivateKey) ks.getKey(alias, password.toCharArray());
|
||||
}
|
||||
} finally {
|
||||
if (fileInputStream != null) {
|
||||
fileInputStream.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, ExpCode.PRIVATE_READ_FAILED);
|
||||
}
|
||||
return (RSAPrivateKey) ks.getKey(alias, password.toCharArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取key(私钥)
|
||||
* 解密并解析加密的PKCS#8私钥
|
||||
* <p>需要依赖于bcpkix-jdk18on,用于pkcs8的密码验证</p>
|
||||
*
|
||||
* @param priKeyIn 私钥文件流
|
||||
* @param alias 证书别名 可空
|
||||
* @param password 证书密码
|
||||
* @param type 证书格式
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws KeyStoreException
|
||||
* @throws IOException
|
||||
* @throws CertificateException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @param pemContent PEM格式的加密私钥内容
|
||||
* @param password 密码
|
||||
* @return RSAPrivateKey
|
||||
* @throws Exception
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKey(InputStream priKeyIn, String alias, String password, String type) throws NoSuchAlgorithmException, KeyStoreException, IOException, CertificateException, UnrecoverableKeyException {
|
||||
KeyStore ks = KeyStore.getInstance(type);
|
||||
ks.load(priKeyIn, password.toCharArray());
|
||||
if (StrUtil.isEmpty(alias)) {
|
||||
Enumeration<?> aliases = ks.aliases();
|
||||
if (aliases != null) {
|
||||
if (aliases.hasMoreElements()) {
|
||||
alias = (String) aliases.nextElement();
|
||||
}
|
||||
}
|
||||
private static RSAPrivateKey getPrivateKeyByPKCS8(String pemContent, String password) throws Exception {
|
||||
if (Security.getProvider(BOUNCY_CASTLE_PROVIDER_KEY) == null) {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
try (PEMParser pemParser = new PEMParser(new StringReader(pemContent))) {
|
||||
Object object = pemParser.readObject();
|
||||
|
||||
// 处理PKCS#1格式的RSA私钥
|
||||
if (object instanceof org.bouncycastle.asn1.pkcs.RSAPrivateKey) {
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
|
||||
PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(object);
|
||||
return (RSAPrivateKey) converter.getPrivateKey(privateKeyInfo);
|
||||
} else if (object instanceof PEMKeyPair) {
|
||||
// 处理PKCS#1格式的RSA私钥
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
|
||||
return (RSAPrivateKey) converter.getPrivateKey(((PEMKeyPair) object).getPrivateKeyInfo());
|
||||
} else if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
|
||||
// 处理加密的PKCS#8私钥
|
||||
PKCS8EncryptedPrivateKeyInfo encryptedInfo = (PKCS8EncryptedPrivateKeyInfo) object;
|
||||
InputDecryptorProvider provider = new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider(BOUNCY_CASTLE_PROVIDER_KEY)
|
||||
.build(password.toCharArray());
|
||||
|
||||
PrivateKeyInfo privateKeyInfo = encryptedInfo.decryptPrivateKeyInfo(provider);
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
|
||||
return (RSAPrivateKey) converter.getPrivateKey(privateKeyInfo);
|
||||
} else if (object instanceof PrivateKeyInfo) {
|
||||
// 处理未加密的PKCS#8私钥
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
|
||||
return (RSAPrivateKey) converter.getPrivateKey((PrivateKeyInfo) object);
|
||||
}
|
||||
throw new IllegalArgumentException("不支持的PEM格式");
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, "获取私钥失败");
|
||||
}
|
||||
return (RSAPrivateKey) ks.getKey(alias, password.toCharArray());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,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("JKS");
|
||||
cover(fis, out, oPwd, nPwd, inputKeyStore, "PKCS12");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -256,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("PKCS12");
|
||||
cover(fis, out, oPwd, nPwd, inputKeyStore, "JKS");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,7 +487,7 @@ public class RSA2 {
|
||||
Enumeration<String> 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);
|
||||
@@ -301,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);
|
||||
}
|
||||
}
|
||||
|
||||
130
src/main/java/com/yexuejc/base/exception/BaseException.java
Normal file
130
src/main/java/com/yexuejc/base/exception/BaseException.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package com.yexuejc.base.exception;
|
||||
|
||||
import com.yexuejc.base.constant.ExpCode;
|
||||
import com.yexuejc.base.constant.SymbolicConstant;
|
||||
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 long serialVersionUID = 1L;
|
||||
/** 异常消息CODE */
|
||||
protected final String errorCode;
|
||||
/** 异常消息内容 */
|
||||
@SuppressWarnings("java:S1165")
|
||||
protected String errorMessage;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param cause 异常对象
|
||||
*/
|
||||
public BaseException(Throwable cause) {
|
||||
this(cause, ExpCode.SYSTEM_ERROR, (Object) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param cause 异常对象
|
||||
* @param errorCode 异常消息CODE
|
||||
*/
|
||||
public BaseException(Throwable cause, String errorCode) {
|
||||
this(cause, errorCode, (Object) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param errorCode 异常消息CODE
|
||||
*/
|
||||
public BaseException(String errorCode) {
|
||||
this((Throwable) null, errorCode, (Object) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param errorCode 异常消息CODE
|
||||
* @param args 异常消息参数
|
||||
*/
|
||||
public BaseException(String errorCode, Object... args) {
|
||||
this(null, errorCode, args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param cause 异常对象
|
||||
* @param errorCode 异常消息CODE
|
||||
* @param args 异常消息的参数
|
||||
*/
|
||||
public BaseException(Throwable cause, String errorCode, Object... args) {
|
||||
super(cause);
|
||||
// 消息内容
|
||||
if (StrUtil.isEmpty(errorCode)) {
|
||||
errorCode = ExpCode.SYSTEM_ERROR;
|
||||
}
|
||||
this.errorCode = errorCode;
|
||||
this.errorMessage = getMessageByCode(errorCode, args);
|
||||
if (cause instanceof BaseException) {
|
||||
BaseException comExp = (BaseException) cause;
|
||||
if (StrUtil.isNotEmpty(comExp.errorMessage)) {
|
||||
this.errorMessage = StrUtil.isEmpty(this.errorMessage) ? comExp.errorMessage :
|
||||
this.errorMessage + SymbolicConstant.NEW_LINE + comExp.errorMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异常消息CODE
|
||||
*
|
||||
* @return 异常消息CODE
|
||||
*/
|
||||
public String getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取异常消息内容
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getMessage() {
|
||||
if (StrUtil.isNotEmpty(super.getMessage())) {
|
||||
return super.getMessage();
|
||||
}
|
||||
return getErrorCode().equals(getFaultMessage()) ? getFaultMessage() : getErrorCode() + ":" + getFaultMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异常消息内容
|
||||
*
|
||||
* @return 异常消息内容,不包含异常信息
|
||||
*/
|
||||
public String getFaultMessage() {
|
||||
return this.errorMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息内容
|
||||
*
|
||||
* @param errorCode 异常消息CODE
|
||||
* @param args 异常消息的参数
|
||||
* @return
|
||||
*/
|
||||
protected String getMessageByCode(String errorCode, Object... args) {
|
||||
return MsgUtil.getMsg(errorCode, args);
|
||||
}
|
||||
|
||||
}
|
||||
51
src/main/java/com/yexuejc/base/http/RequestHeader.java
Normal file
51
src/main/java/com/yexuejc/base/http/RequestHeader.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package com.yexuejc.base.http;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.yexuejc.base.util.DateTimeUtil;
|
||||
|
||||
public class RequestHeader extends HashMap<String, String> {
|
||||
public static final String CLIENT_ID = "Client-Id";
|
||||
public static final String REQUEST_TIME = "Request-Time";
|
||||
public static final String RESPONSE_TIME = "Response-Time";
|
||||
public static final String SIGNATURE = "Signature";
|
||||
|
||||
public RequestHeader(String clientId) {
|
||||
put("Content-Type", "application/json");
|
||||
put(CLIENT_ID, clientId);
|
||||
}
|
||||
|
||||
public static RequestHeader requestBuilder(String clientId) {
|
||||
RequestHeader requestHeader = new RequestHeader(clientId);
|
||||
// 2019-05-28T12:12:12+08:00
|
||||
requestHeader.put(REQUEST_TIME, DateTimeUtil.formatIso8601BySystemZone());
|
||||
return requestHeader;
|
||||
}
|
||||
|
||||
public static RequestHeader responseBuilder(String clientId, String time, String signature) {
|
||||
RequestHeader requestHeader = new RequestHeader(clientId);
|
||||
requestHeader.put(RESPONSE_TIME, time);
|
||||
requestHeader.put(SIGNATURE, signature);
|
||||
return requestHeader;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return get(CLIENT_ID);
|
||||
}
|
||||
|
||||
public String getReqTime() {
|
||||
return get(REQUEST_TIME);
|
||||
}
|
||||
|
||||
public void setSignature(String signature) {
|
||||
put(SIGNATURE, signature);
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return get(SIGNATURE);
|
||||
}
|
||||
|
||||
public String geRespTime() {
|
||||
return get(RESPONSE_TIME);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.yexuejc.base.http;
|
||||
|
||||
import com.yexuejc.base.constant.RespsConsts;
|
||||
import com.yexuejc.base.constant.RespConsts;
|
||||
import com.yexuejc.base.util.JsonUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
@@ -90,27 +90,27 @@ public class Resps<T> implements Serializable {
|
||||
}
|
||||
|
||||
public static Resps success(String[] msg) {
|
||||
return new Resps(RespsConsts.CODE_SUCCESS, msg);
|
||||
return new Resps(RespConsts.CODE_SUCCESS, msg);
|
||||
}
|
||||
|
||||
public static Resps success(String msg) {
|
||||
return new Resps(RespsConsts.CODE_SUCCESS, msg);
|
||||
return new Resps(RespConsts.CODE_SUCCESS, msg);
|
||||
}
|
||||
|
||||
public static Resps success() {
|
||||
return new Resps(RespsConsts.CODE_SUCCESS, RespsConsts.MSG_SUCCESS_OPERATE);
|
||||
return new Resps(RespConsts.CODE_SUCCESS, RespConsts.MSG_SUCCESS_OPERATE);
|
||||
}
|
||||
|
||||
public static Resps error() {
|
||||
return new Resps(RespsConsts.CODE_ERROR, RespsConsts.MSG_ERROT_OPERATE);
|
||||
return new Resps(RespConsts.CODE_ERROR, RespConsts.MSG_ERROT_OPERATE);
|
||||
}
|
||||
|
||||
public static Resps error(String msg) {
|
||||
return new Resps(RespsConsts.CODE_ERROR, msg);
|
||||
return new Resps(RespConsts.CODE_ERROR, msg);
|
||||
}
|
||||
|
||||
public static Resps error(String[] msg) {
|
||||
return new Resps(RespsConsts.CODE_ERROR, msg);
|
||||
return new Resps(RespConsts.CODE_ERROR, msg);
|
||||
}
|
||||
|
||||
public static Resps error(String code, String msg) {
|
||||
@@ -122,15 +122,15 @@ public class Resps<T> implements Serializable {
|
||||
}
|
||||
|
||||
public static Resps fail() {
|
||||
return new Resps(RespsConsts.CODE_FAIL, RespsConsts.MSG_FAIL_OPERATE);
|
||||
return new Resps(RespConsts.CODE_FAIL, RespConsts.MSG_FAIL_OPERATE);
|
||||
}
|
||||
|
||||
public static Resps fail(String msg) {
|
||||
return new Resps(RespsConsts.CODE_FAIL, msg);
|
||||
return new Resps(RespConsts.CODE_FAIL, msg);
|
||||
}
|
||||
|
||||
public static Resps fail(String[] msg) {
|
||||
return new Resps(RespsConsts.CODE_FAIL, msg);
|
||||
return new Resps(RespConsts.CODE_FAIL, msg);
|
||||
}
|
||||
|
||||
public static Resps fail(String code, String msg) {
|
||||
|
||||
19
src/main/java/com/yexuejc/base/service/MessageService.java
Normal file
19
src/main/java/com/yexuejc/base/service/MessageService.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.yexuejc.base.service;
|
||||
|
||||
/**
|
||||
* 消息获取接口
|
||||
*
|
||||
* @author yexuejc
|
||||
* @date 2025/8/25 15:33
|
||||
*/
|
||||
public interface MessageService {
|
||||
/**
|
||||
* 通过异常CODE和参数获取对应的错误信息(多语言实现)
|
||||
*
|
||||
* @param code 异常CODE
|
||||
* @param args 参数
|
||||
* @return 错误信息
|
||||
*/
|
||||
String getMessage(String code, Object... args);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
package com.yexuejc.base.util;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.time.*;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.TemporalAdjuster;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.Date;
|
||||
|
||||
import com.yexuejc.base.constant.DateConsts;
|
||||
|
||||
|
||||
/**
|
||||
* 新版时间日期出来 工具
|
||||
@@ -15,10 +23,6 @@ import java.util.Date;
|
||||
* @date: 2018/3/27 10:44
|
||||
*/
|
||||
public class DateTimeUtil {
|
||||
public static String DATE_PATTERN = "yyyy-MM-dd";
|
||||
public static String TIME_PATTERN = "HH:mm:ss";
|
||||
public static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
|
||||
public static String DATE_TIME_MS_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";
|
||||
|
||||
private DateTimeUtil() {
|
||||
}
|
||||
@@ -116,8 +120,8 @@ public class DateTimeUtil {
|
||||
* @return
|
||||
*/
|
||||
public static LocalDate getWeek4First(LocalDate date) {
|
||||
TemporalAdjuster firstOfWeek = TemporalAdjusters.ofDateAdjuster(localDate ->
|
||||
localDate.minusDays(localDate.getDayOfWeek().getValue() - DayOfWeek.MONDAY.getValue()));
|
||||
TemporalAdjuster firstOfWeek = TemporalAdjusters.ofDateAdjuster(localDate -> localDate.minusDays(localDate.getDayOfWeek()
|
||||
.getValue() - DayOfWeek.MONDAY.getValue()));
|
||||
return date.with(firstOfWeek);
|
||||
}
|
||||
|
||||
@@ -137,8 +141,9 @@ public class DateTimeUtil {
|
||||
* @return
|
||||
*/
|
||||
public static LocalDate getWeek4Last(LocalDate date) {
|
||||
TemporalAdjuster lastOfWeek = TemporalAdjusters.ofDateAdjuster(localDate ->
|
||||
localDate.plusDays(DayOfWeek.SUNDAY.getValue() - localDate.getDayOfWeek().getValue()));
|
||||
TemporalAdjuster lastOfWeek =
|
||||
TemporalAdjusters.ofDateAdjuster(localDate -> localDate.plusDays(DayOfWeek.SUNDAY.getValue() - localDate.getDayOfWeek()
|
||||
.getValue()));
|
||||
return date.with(lastOfWeek);
|
||||
}
|
||||
|
||||
@@ -163,7 +168,9 @@ public class DateTimeUtil {
|
||||
*/
|
||||
public static Date parseDate(LocalDate localDate) {
|
||||
ZoneId zone = ZoneId.systemDefault();
|
||||
Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
|
||||
Instant instant = localDate.atStartOfDay()
|
||||
.atZone(zone)
|
||||
.toInstant();
|
||||
return Date.from(instant);
|
||||
}
|
||||
|
||||
@@ -200,7 +207,8 @@ public class DateTimeUtil {
|
||||
public static Date parseDate(LocalDate localDate, LocalTime localTime) {
|
||||
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
|
||||
ZoneId zone = ZoneId.systemDefault();
|
||||
Instant instant = localDateTime.atZone(zone).toInstant();
|
||||
Instant instant = localDateTime.atZone(zone)
|
||||
.toInstant();
|
||||
return Date.from(instant);
|
||||
}
|
||||
|
||||
@@ -213,7 +221,8 @@ public class DateTimeUtil {
|
||||
public static ZonedDateTime parseZonedDateTime(Date date) {
|
||||
Instant instant = date.toInstant();
|
||||
ZoneId zoneId = ZoneId.systemDefault();
|
||||
return instant.atZone(zoneId).withZoneSameInstant(zoneId);
|
||||
return instant.atZone(zoneId)
|
||||
.withZoneSameInstant(zoneId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,7 +234,8 @@ public class DateTimeUtil {
|
||||
public static LocalDateTime parseLocalDateTime(Date date) {
|
||||
Instant instant = date.toInstant();
|
||||
ZoneId zoneId = ZoneId.systemDefault();
|
||||
return instant.atZone(zoneId).toLocalDateTime();
|
||||
return instant.atZone(zoneId)
|
||||
.toLocalDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,7 +247,8 @@ public class DateTimeUtil {
|
||||
public static LocalDate parseLocalDate(Date date) {
|
||||
Instant instant = date.toInstant();
|
||||
ZoneId zoneId = ZoneId.systemDefault();
|
||||
return instant.atZone(zoneId).toLocalDate();
|
||||
return instant.atZone(zoneId)
|
||||
.toLocalDate();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,7 +315,8 @@ public class DateTimeUtil {
|
||||
* @return
|
||||
*/
|
||||
public static LocalDateTime parserUTC(Long timestamp) {
|
||||
if (String.valueOf(timestamp).length() == 10) {
|
||||
if (String.valueOf(timestamp)
|
||||
.length() == 10) {
|
||||
timestamp = timestamp * 1000;
|
||||
}
|
||||
return parseLocalDateTime13(timestamp, ZoneId.of("UTC"));
|
||||
@@ -318,7 +330,8 @@ public class DateTimeUtil {
|
||||
*/
|
||||
public static long parseLong(LocalDateTime localDateTime) {
|
||||
ZoneId zone = ZoneId.systemDefault();
|
||||
Instant instant = localDateTime.atZone(zone).toInstant();
|
||||
Instant instant = localDateTime.atZone(zone)
|
||||
.toInstant();
|
||||
return instant.toEpochMilli();
|
||||
}
|
||||
|
||||
@@ -330,7 +343,8 @@ public class DateTimeUtil {
|
||||
*/
|
||||
public static long parseLong(LocalDate localDate) {
|
||||
ZoneId zone = ZoneId.systemDefault();
|
||||
Instant instant = localDate.atStartOfDay(zone).toInstant();
|
||||
Instant instant = localDate.atStartOfDay(zone)
|
||||
.toInstant();
|
||||
return instant.toEpochMilli();
|
||||
}
|
||||
|
||||
@@ -355,7 +369,7 @@ public class DateTimeUtil {
|
||||
*/
|
||||
public static String format(LocalDate dateTime, String pattern) {
|
||||
if (StrUtil.isEmpty(pattern)) {
|
||||
pattern = DATE_PATTERN;
|
||||
pattern = DateConsts.DATE_PATTERN;
|
||||
}
|
||||
DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
|
||||
return df.format(dateTime);
|
||||
@@ -381,7 +395,32 @@ public class DateTimeUtil {
|
||||
*/
|
||||
public static String format(LocalDateTime dateTime, String pattern) {
|
||||
if (StrUtil.isEmpty(pattern)) {
|
||||
pattern = DATE_TIME_PATTERN;
|
||||
pattern = DateConsts.DATE_TIME_PATTERN;
|
||||
}
|
||||
DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
|
||||
return df.format(dateTime);
|
||||
}
|
||||
/**
|
||||
* 格式化时间 <br/>
|
||||
* 格式 yyyy-MM-dd HH:mm:ss
|
||||
*
|
||||
* @param dateTime
|
||||
* @return
|
||||
*/
|
||||
public static String format(OffsetDateTime dateTime) {
|
||||
return format(dateTime, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间
|
||||
*
|
||||
* @param dateTime
|
||||
* @param pattern 格式 默认:yyyy-MM-dd HH:mm:ss
|
||||
* @return
|
||||
*/
|
||||
public static String format(OffsetDateTime dateTime, String pattern) {
|
||||
if (StrUtil.isEmpty(pattern)) {
|
||||
pattern = DateConsts.DATE_TIME_PATTERN;
|
||||
}
|
||||
DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
|
||||
return df.format(dateTime);
|
||||
@@ -425,7 +464,39 @@ public class DateTimeUtil {
|
||||
currentZone = ZoneId.of(currentZoneId);
|
||||
}
|
||||
ZoneId targetZone = ZoneId.of(targetZoneId);
|
||||
return date.atZone(currentZone).withZoneSameInstant(targetZone).toLocalDateTime();
|
||||
return date.atZone(currentZone)
|
||||
.withZoneSameInstant(targetZone)
|
||||
.toLocalDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 ISO8601 格式时间
|
||||
*
|
||||
* @return 2019-05-28T12:12:12+08:00
|
||||
*/
|
||||
public static String formatIso8601BySystemZone() {
|
||||
return DateTimeUtil.format(OffsetDateTime.now(), DateConsts.ISO_8601_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间转换成ISO8601格式
|
||||
*
|
||||
* @param localDateTime 时间
|
||||
* @return 2019-05-28T12:12:12+08:00
|
||||
*/
|
||||
public static String formatIso8601BySystemZone(LocalDateTime localDateTime) {
|
||||
return DateTimeUtil.format(localDateTime, DateConsts.ISO_8601_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间转换成ISO8601格式
|
||||
*
|
||||
* @param timestamp 时间戳
|
||||
* @param zoneId 时区
|
||||
* @return 2019-05-28T12:12:12+08:00
|
||||
*/
|
||||
public static String formatIso8601(long timestamp, ZoneId zoneId) {
|
||||
return DateTimeUtil.format(parseLocalDateTime13(timestamp, zoneId), DateConsts.ISO_8601_PATTERN);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -252,7 +252,7 @@ public class JsonUtil {
|
||||
/**
|
||||
* Json字符串转换为Java对象
|
||||
*
|
||||
* @param in 输入流
|
||||
* @param in 输入流
|
||||
* @param parametrized 容器类
|
||||
* @param parameterClasses 实际类
|
||||
* @return
|
||||
@@ -347,6 +347,30 @@ public class JsonUtil {
|
||||
return json2Obj(obj2Json(pojo), Map.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* json -> Map<String, String> 转换
|
||||
*
|
||||
* @param json JSON
|
||||
* @return Map
|
||||
*/
|
||||
public static Map<String, String> jsonToMapString(String json) {
|
||||
return objToMap(json, String.class, String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Bean 对象转换为 Map
|
||||
* <p>
|
||||
* 示例:Map<String, RequestBean> map = JacksonMapperConfig.objToMap(data, String.class, RequestBean.class);
|
||||
* </p>
|
||||
*
|
||||
* @param pojo Bean 数据
|
||||
* @return Map 数据
|
||||
*/
|
||||
|
||||
public static <K, V> Map<K, V> objToMap(Object pojo, Class<K> kCls, Class<V> vCls) {
|
||||
return json2Obj(obj2Json(pojo), Map.class, kCls, vCls);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化输出
|
||||
*
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package com.yexuejc.base.util;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.io.Decoders;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import io.jsonwebtoken.security.SecureDigestAlgorithm;
|
||||
|
||||
/**
|
||||
* jwt工具类
|
||||
@@ -34,35 +36,46 @@ public class JwtUtil {
|
||||
/**
|
||||
* 参数配置:设置一次即可,多次设置会覆盖之前的
|
||||
*
|
||||
* @param key 加密key 默认:h%OG8Y3WgA5AN7&6Ke7I#C1XvneW0N8a
|
||||
* @param type 加密类型:默认JWT
|
||||
* @param iss token发行商: 默认yexuejc.top
|
||||
* @param type 加密类型:默认JWT
|
||||
* @param iss token发行商: 默认yexuejc.top
|
||||
* @param key 加密key 默认:h%OG8Y3WgA5AN7&6Ke7I#C1XvneW0N8a
|
||||
* @param algorithm 加密方式
|
||||
* @return
|
||||
*/
|
||||
public static JwtUtil config(String key, String type, String iss) {
|
||||
public static JwtUtil config(String type, String iss, String key, SecureDigestAlgorithm<SecretKey, SecretKey> algorithm) {
|
||||
JwtUtil jwtUtil = instace();
|
||||
jwtUtil.JWT_SIGNATURE_KEY = key;
|
||||
jwtUtil.JWT_HEADER_TYP = type;
|
||||
jwtUtil.JWT_CLAIMS_ISS = iss;
|
||||
if (null != key) {
|
||||
jwtUtil.jwtSignatureKey = key;
|
||||
}
|
||||
if (null != type) {
|
||||
jwtUtil.jwtHeaderTyp = type;
|
||||
}
|
||||
if (null != iss) {
|
||||
jwtUtil.jwtClaimsIss = iss;
|
||||
}
|
||||
if (null != algorithm) {
|
||||
jwtUtil.algorithm = algorithm;
|
||||
}
|
||||
return jwtUtil;
|
||||
}
|
||||
|
||||
public static class Instace {
|
||||
private static class Instace {
|
||||
private static JwtUtil jwtUtil = new JwtUtil();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密用KEY
|
||||
*/
|
||||
private String JWT_SIGNATURE_KEY = "h%OG8Y3WgA5AN7&6Ke7I#C1XvneW0N8a";
|
||||
private String jwtSignatureKey = "h%OG8Y3WgA5AN7&6Ke7I#C1XvneW0N8a";
|
||||
/**
|
||||
* token类型
|
||||
*/
|
||||
private String JWT_HEADER_TYP = "JWT";
|
||||
private String jwtHeaderTyp = "JWT";
|
||||
/**
|
||||
* token发行商
|
||||
*/
|
||||
private String JWT_CLAIMS_ISS = "yexuejc.top";
|
||||
private String jwtClaimsIss = "yexuejc.top";
|
||||
private SecureDigestAlgorithm<SecretKey, SecretKey> algorithm = Jwts.SIG.HS512;
|
||||
|
||||
/**
|
||||
* 加密内容生成token
|
||||
@@ -71,27 +84,36 @@ public class JwtUtil {
|
||||
* @return
|
||||
*/
|
||||
public String compact(Object subjectObj) {
|
||||
String subject = null;
|
||||
if (subjectObj instanceof String) {
|
||||
subject = (String) subjectObj;
|
||||
} else {
|
||||
subject = JsonUtil.obj2Json(subjectObj);
|
||||
return compact(null, subjectObj);
|
||||
}
|
||||
|
||||
public String compact(String subId, Object subjectObj) {
|
||||
String subject = subId;
|
||||
Map<String, Object> subMap = JsonUtil.objToMap(subjectObj, String.class, Object.class);
|
||||
if (StrUtil.isEmpty(subject)) {
|
||||
if (subjectObj instanceof String) {
|
||||
subject = (String) subjectObj;
|
||||
} else if (StrUtil.isNotEmpty(subMap)) {
|
||||
Object sid = subMap.get("subId");
|
||||
Object s = subMap.get("sub");
|
||||
subject = StrUtil.isNotEmpty(sid) ? String.valueOf(sid) : String.valueOf(s);
|
||||
}
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
return Jwts.builder()
|
||||
// 设置token的唯一标识ID(claims.jti)
|
||||
.id(StrUtil.genUUID())
|
||||
// 设置token类型(header.typ)
|
||||
.header().add("typ", JWT_HEADER_TYP)
|
||||
.and()
|
||||
.header().add("typ", jwtHeaderTyp).and()
|
||||
// 设置token发行时间为当前时间(claims.iat)
|
||||
.issuedAt(now)
|
||||
.issuedAt(now).claims(JsonUtil.objToMap(subjectObj, String.class, Object.class))
|
||||
// 设置token发行商/发行者(claims.iss)
|
||||
.issuer(JWT_CLAIMS_ISS)
|
||||
.issuer(jwtClaimsIss)
|
||||
// 设置token用户定义主体(claims.sub)
|
||||
.subject(subject)
|
||||
// 设置算法签名,(密钥,加密算法)
|
||||
.signWith(getSecretKey(), Jwts.SIG.HS512)
|
||||
.signWith(getSecretKey(), algorithm)
|
||||
// 生成token
|
||||
.compact();
|
||||
}
|
||||
@@ -114,20 +136,27 @@ public class JwtUtil {
|
||||
* @return
|
||||
*/
|
||||
public <T> T parse(String token, Class<T> cls) {
|
||||
return JsonUtil.json2Obj(parseStr(token), cls);
|
||||
return JsonUtil.json2Obj(JsonUtil.obj2Json(parseStr(token)), cls);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密token为字符串
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public String parseStr(String token) {
|
||||
return Jwts.parser().verifyWith(getSecretKey()).build().parseSignedClaims(token).getPayload().getSubject();
|
||||
public Claims parseStr(String token) {
|
||||
return Jwts.parser().verifyWith(getSecretKey()).build().parseSignedClaims(token).getPayload();
|
||||
}
|
||||
|
||||
private SecretKey getSecretKey() {
|
||||
byte[] bytes = Decoders.BASE64.decode(JWT_SIGNATURE_KEY);
|
||||
int minLen = 32;
|
||||
if (jwtSignatureKey.length() % minLen != 0) {
|
||||
// 补齐32的倍数,左补0
|
||||
String format = "%" + ((jwtSignatureKey.length() / minLen + 1) * minLen) + "s";
|
||||
jwtSignatureKey = String.format(format, jwtSignatureKey).replace(' ', '0');
|
||||
}
|
||||
byte[] bytes = jwtSignatureKey.getBytes(StandardCharsets.UTF_8);
|
||||
return Keys.hmacShaKeyFor(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
95
src/main/java/com/yexuejc/base/util/MsgUtil.java
Normal file
95
src/main/java/com/yexuejc/base/util/MsgUtil.java
Normal file
@@ -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获取多语言消息
|
||||
* <pre>
|
||||
* 使用示例:
|
||||
* 1.getMsgBySystemLocale("ENCRYPTION_FAILED", "张三")
|
||||
* 2.getMsgBySystemLocale("{0},你好", "张三")
|
||||
* </pre>
|
||||
*
|
||||
* @param code 消息CODE
|
||||
* @param args 参数
|
||||
* @return {@link MessageService}实现中对应返回的文本,获取不到再使用系统语言获取对应的文本,获取不到直接格式化返回
|
||||
* <h5>
|
||||
* 实现{@link MessageService}接口
|
||||
* </h5>
|
||||
* <pre>
|
||||
* {@code
|
||||
* public class DefMessageServiceImpl implements MessageService {
|
||||
* @Override
|
||||
* public String getMessage(String code, Object... args) {
|
||||
* return ResourceBundle.getBundle("i18n/messages", Locale.getDefault()).getString(code);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
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<MessageService> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过系统语言获取多语言消息
|
||||
* <pre>
|
||||
* 使用示例:
|
||||
* 1.getMsgBySystemLocale("ENCRYPTION_FAILED", "张三")
|
||||
* 2.getMsgBySystemLocale("{0},你好", "张三")
|
||||
* </pre>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,15 +4,7 @@ import java.lang.reflect.Array;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -45,7 +37,8 @@ public final class StrUtil {
|
||||
return ((Optional<?>) obj).isEmpty();
|
||||
} else if (obj instanceof CharSequence) {
|
||||
return ((CharSequence) obj).length() == 0;
|
||||
} else if (obj.getClass().isArray()) {
|
||||
} else if (obj.getClass()
|
||||
.isArray()) {
|
||||
return Array.getLength(obj) == 0;
|
||||
} else if (obj instanceof Collection) {
|
||||
return ((Collection<?>) obj).isEmpty();
|
||||
@@ -64,7 +57,9 @@ public final class StrUtil {
|
||||
* @return
|
||||
*/
|
||||
public static String genUUID() {
|
||||
return UUID.randomUUID().toString().replaceAll("-", "");
|
||||
return UUID.randomUUID()
|
||||
.toString()
|
||||
.replaceAll("-", "");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,7 +94,9 @@ public final class StrUtil {
|
||||
* @return
|
||||
*/
|
||||
public static String genNum() {
|
||||
int hashCode = UUID.randomUUID().toString().hashCode();
|
||||
int hashCode = UUID.randomUUID()
|
||||
.toString()
|
||||
.hashCode();
|
||||
StringBuilder num = new StringBuilder();
|
||||
if (hashCode < 0) {
|
||||
hashCode = -hashCode;
|
||||
@@ -107,7 +104,8 @@ public final class StrUtil {
|
||||
} else {
|
||||
num.append("1");
|
||||
}
|
||||
return num.append(String.format("%010d", hashCode)).substring(0, 8);
|
||||
return num.append(String.format("%010d", hashCode))
|
||||
.substring(0, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,7 +302,10 @@ public final class StrUtil {
|
||||
for (String key : keys) {
|
||||
Object value = sortedParams.get(key);
|
||||
if (isNotEmpty(key) && isNotEmpty(value)) {
|
||||
content.append(index == 0 ? "" : "&").append(key).append("=").append(value);
|
||||
content.append(index == 0 ? "" : "&")
|
||||
.append(key)
|
||||
.append("=")
|
||||
.append(value);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
@@ -425,14 +426,17 @@ public final class StrUtil {
|
||||
if (cause != null) {
|
||||
StringBuilder msg = new StringBuilder();
|
||||
if (isNotEmpty(cause.getMessage())) {
|
||||
msg.append(cause.getMessage()).append(NEW_LINE);
|
||||
msg.append(cause.getMessage())
|
||||
.append(NEW_LINE);
|
||||
}
|
||||
String causedMsg = printStackTrace(cause, (eMessage) -> {
|
||||
if (isNotEmpty(eMessage)) {
|
||||
msg.append(eMessage).append(NEW_LINE);
|
||||
msg.append(eMessage)
|
||||
.append(NEW_LINE);
|
||||
}
|
||||
});
|
||||
return msg.append(causedMsg).toString();
|
||||
return msg.append(causedMsg)
|
||||
.toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -452,25 +456,157 @@ public final class StrUtil {
|
||||
private static String printStackTrace(Throwable cause, Consumer<String> consumer) {
|
||||
if (cause != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String cClass = cause.getClass().getName();
|
||||
String cClass = cause.getClass()
|
||||
.getName();
|
||||
String eMessage = cause.getMessage();
|
||||
StackTraceElement[] stackTrace = cause.getStackTrace();
|
||||
Throwable caused = cause.getCause();
|
||||
while (caused != null) {
|
||||
cClass = caused.getClass().getName();
|
||||
cClass = caused.getClass()
|
||||
.getName();
|
||||
eMessage = caused.getMessage();
|
||||
stackTrace = caused.getStackTrace();
|
||||
caused = caused.getCause();
|
||||
consumer.accept(eMessage);
|
||||
}
|
||||
sb.append("Caused by: ").append(cClass).append(": ").append(eMessage).append(NEW_LINE);
|
||||
sb.append("Caused by: ")
|
||||
.append(cClass)
|
||||
.append(": ")
|
||||
.append(eMessage)
|
||||
.append(NEW_LINE);
|
||||
for (StackTraceElement element : stackTrace) {
|
||||
sb.append("\tat ");
|
||||
sb.append(String.format(ERROR_MESSAGE_FORMAT, element.getClassName(), element.getMethodName(), element.getFileName(), element.getLineNumber()));
|
||||
sb.append(String.format(ERROR_MESSAGE_FORMAT, element.getClassName(), element.getMethodName(), element.getFileName(),
|
||||
element.getLineNumber()));
|
||||
sb.append(NEW_LINE);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 不是空对象时执行
|
||||
*
|
||||
* @param checkObj 判定对象
|
||||
* @param consumer 执行器
|
||||
* @param <T> 判定对象
|
||||
*/
|
||||
public static <T> void notNullExecute(T checkObj, Consumer<T> consumer) {
|
||||
if (null != checkObj) {
|
||||
consumer.accept(checkObj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 不是空内容时执行
|
||||
*
|
||||
* @param checkObj 判定对象
|
||||
* @param consumer 执行器
|
||||
* @param <T> 判定对象
|
||||
*/
|
||||
public static <T> void notEmptyExecute(T checkObj, Consumer<T> consumer) {
|
||||
if (isNotEmpty(checkObj)) {
|
||||
consumer.accept(checkObj);
|
||||
}
|
||||
}
|
||||
|
||||
private static final List<String> COUNTRY_CODE = Arrays.asList("JPN", "KOR", "THA", "SGP", "CHN", "TWN", "HKG", "MAC", "999");
|
||||
|
||||
/**
|
||||
* 国名を国コードに変換
|
||||
*
|
||||
* @param country JPN:日本、KOR:韓国、THA:タイ、SGP:シンガポール、CHN:中国内陸、TWN:中国台湾、HKG:中国香港、MAC:マカオ、999:その他、0:不明
|
||||
* @return code byte <pre>
|
||||
* 1 0 1 0 1 1 1 1 1 <br>
|
||||
* 日本 韓国 タイ シンガポール 中国内陸 台湾 香港 マカオ その他
|
||||
* <br>
|
||||
* 右→左:0位:その他、1位:マカオ、2位:香港、3位:台湾、4位:中国内陸、5位:シンガポール、6位:タイ、7位:韓国、8位:日本
|
||||
* <br> 1:当該国で表示、0:当該国表示しない
|
||||
* </pre>
|
||||
*/
|
||||
public static byte countryToCodeByte(String country) {
|
||||
String code = countryToCode(country);
|
||||
return (byte) Integer.parseInt(code, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 国家代码转換成二进制
|
||||
*
|
||||
* @param country JPN:日本、KOR:韓国、THA:泰国、CHN:中国内陸、SGP:新加坡、TWN:中国台湾、HKG:中国香港、MAC:中国澳门、999:其他、0:不明
|
||||
* @return <pre>
|
||||
* 1 0 1 0 1 1 1 1 1 <br>
|
||||
* 日本 韓国 泰国 新加坡 中国内陸 台湾 香港 澳门 其他
|
||||
* <br>
|
||||
* 右→左:0位:其他、1位:中国澳门、2位:中国香港、3位:中国台湾、4位:中国内陸、5位:新加坡、6位:泰国、7位:韓国、8位:日本
|
||||
* <br> 1:在该国表示、0:不表示该国
|
||||
* </pre>
|
||||
*/
|
||||
public static String countryToCode(String country) {
|
||||
int index = COUNTRY_CODE.indexOf(country);
|
||||
if (index == -1) {
|
||||
return "000000000";
|
||||
}
|
||||
int bn = 1 << (COUNTRY_CODE.size() - 1 - index);
|
||||
// 转成二进制
|
||||
String bs = Integer.toBinaryString(bn);
|
||||
// 为了保证长度一致,前面补0
|
||||
return String.format("%09d", Integer.parseInt(bs));
|
||||
}
|
||||
|
||||
/**
|
||||
* 国家代码二进制转国家代码
|
||||
*
|
||||
* @param countryCode 国家代码二进制转:010000000
|
||||
* <pre>
|
||||
* 1 0 1 0 1 1 1 1 1 <br>
|
||||
* 日本 韓国 泰国 新加坡 中国内陸 台湾 香港 澳门 其他
|
||||
* <br>
|
||||
* 右→左:0位:其他、1位:中国澳门、2位:中国香港、3位:中国台湾、4位:中国内陸、5位:新加坡、6位:泰国、7位:韓国、8位:日本
|
||||
* <br> 1:在该国表示、0:不表示该国
|
||||
* </pre>
|
||||
* @return JPN:日本、KOR:韓国、THA:泰国、CHN:中国内陸、SGP:新加坡、TWN:中国台湾、HKG:中国香港、MAC:中国澳门、999:其他、0:不明
|
||||
*/
|
||||
public static String getCountryByCode(String countryCode) {
|
||||
int i = Integer.parseInt(countryCode, 2);
|
||||
int index = Integer.numberOfTrailingZeros(i);
|
||||
if (index > COUNTRY_CODE.size()) {
|
||||
return "O";
|
||||
}
|
||||
return COUNTRY_CODE.get(COUNTRY_CODE.size() - 1 - index);
|
||||
}
|
||||
|
||||
/**
|
||||
* nationalCodeは想定国エリア範囲内存在するかどうか
|
||||
* 想定国エリア範囲:"JPN", "KOR", "THA", "SGP", "CHN", "TWN", "HKG", "MAC", "999"
|
||||
*
|
||||
* @param nationCode
|
||||
* @return 存在:true;存在しない:false
|
||||
*/
|
||||
public static boolean containsCountry(String nationCode) {
|
||||
if (isNotEmpty(nationCode)) {
|
||||
return COUNTRY_CODE.contains(nationCode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将长字符串分割为多行
|
||||
*
|
||||
* @param str 原始字符串
|
||||
* @param chunkSize 每行长度
|
||||
* @return 分割后的字符串
|
||||
*/
|
||||
public static String chunkString(String str, int chunkSize) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < str.length(); i += chunkSize) {
|
||||
int end = Math.min(str.length(), i + chunkSize);
|
||||
builder.append(str, i, end);
|
||||
if (end != str.length()) {
|
||||
builder.append("\n");
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
13
src/main/resources/i18n/msg_en_US.properties
Normal file
13
src/main/resources/i18n/msg_en_US.properties
Normal file
@@ -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.
|
||||
13
src/main/resources/i18n/msg_ja_JP.properties
Normal file
13
src/main/resources/i18n/msg_ja_JP.properties
Normal file
@@ -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
|
||||
13
src/main/resources/i18n/msg_ko_KR.properties
Normal file
13
src/main/resources/i18n/msg_ko_KR.properties
Normal file
@@ -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.
|
||||
13
src/main/resources/i18n/msg_zh_CN.properties
Normal file
13
src/main/resources/i18n/msg_zh_CN.properties
Normal file
@@ -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
|
||||
13
src/main/resources/i18n/msg_zh_TW.properties
Normal file
13
src/main/resources/i18n/msg_zh_TW.properties
Normal file
@@ -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
|
||||
55
src/test/java/com/yexuejc/base/encrypt/AESTest.java
Normal file
55
src/test/java/com/yexuejc/base/encrypt/AESTest.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
|
||||
public class AESTest {
|
||||
|
||||
@Test
|
||||
public void testEncrypt() throws Exception {
|
||||
String data = "Hello World!";
|
||||
AES aes = AES.builder()
|
||||
.setAlgorithm(AES.ALGORITHM.AES_CBC_PKCS5Padding)
|
||||
.setKey("hj7x89H$yuBI0456")
|
||||
.setIv("NIfb&95GUY86Gfgh")
|
||||
.setCharset(StandardCharsets.UTF_8);
|
||||
String encrypted = aes.encrypt(data);
|
||||
assertNotNull(encrypted);
|
||||
assertFalse(encrypted.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecrypt() throws Exception {
|
||||
String data = "p0x0vK5T6OOy69+p9cgI/9xfeoi/f0t6NO7HbLsUON4=";
|
||||
AES aes = AES.builder()
|
||||
.setAlgorithm(AES.ALGORITHM.AES_CBC_PKCS5Padding)
|
||||
.setKey("hj7x89H$yuBI0456")
|
||||
.setIv("NIfb&95GUY86Gfgh")
|
||||
.setCharset(StandardCharsets.UTF_8);
|
||||
String decrypted = aes.decrypt(data);
|
||||
assertNotNull(decrypted);
|
||||
assertFalse(decrypted.isEmpty());
|
||||
assertEquals("Hello World!", decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptAndDecrypt() throws Exception {
|
||||
String data = "张三";
|
||||
AES aes = AES.builder()
|
||||
.setAlgorithm(AES.ALGORITHM.AES_OFB_ISO10126Padding)
|
||||
.setKey("hj7x89H$yuBI0456")
|
||||
.setIv("NIfb&95GUY86Gfgh")
|
||||
.setCharset(StandardCharsets.UTF_8);
|
||||
String encrypt = aes.encrypt(data);
|
||||
System.out.println("加密:" + encrypt);
|
||||
String decrypt = aes.decrypt(encrypt);
|
||||
System.out.println("解密:" + decrypt);
|
||||
}
|
||||
|
||||
}
|
||||
17
src/test/java/com/yexuejc/base/encrypt/RSATest.java
Normal file
17
src/test/java/com/yexuejc/base/encrypt/RSATest.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
26
src/test/java/com/yexuejc/base/util/JwtUtilTest.java
Normal file
26
src/test/java/com/yexuejc/base/util/JwtUtilTest.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.yexuejc.base.util;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author maxiaofeng
|
||||
* @date 2024/9/1 15:23
|
||||
*/
|
||||
class JwtUtilTest {
|
||||
|
||||
@Test
|
||||
void compact() {
|
||||
Map map = JsonUtil.json2Obj("{\n" + " \"payload\": {\n" + " \"sub\": \"0fqgx9h1d9eijd07umlyn1ez52jvkkkb883j\",\n" + " " +
|
||||
"\"iss\": \"www" + ".donki.com\",\n" + " \"aud\": \"D27A80C9AF01488F9A1152B8C20D93BA\",\n" + " \"exp\": 1725260806," +
|
||||
"\n" + " " + "\"iat\": 1725174406,\n" + " \"address\": {\n" + " \"country\": \"南朝鮮\"\n" + " },\n" + " " + "\"email_verified\": true,\n" + " \"birthdate\": \"1993/11/16\",\n" + " \"news_letter\": \"1\",\n" + " " + "\"gender\": \"other\",\n" + " \"member_code\": \"3511623793483553889787\",\n" + " \"preferred_username\": " + "\"3923490434\",\n" + " \"nonce\": \"mjhjSTmtVRNWPBtOQXZx\",\n" + " \"country_code\": \"CHN\",\n" + " " + "\"language_code\": \"zh-TW\",\n" + " \"updated_at\": 1722404351,\n" + " \"nickname\": \"3923490434\",\n" + " " + "\"customer_flag\": \"0\",\n" + " \"service_term_version\": \"2\",\n" + " \"email\": \"1828844769@qq.com\",\n" + " " + " \"auth_time\": 1725173983\n" + " }\n" + "}", Map.class);
|
||||
JwtUtil jwtUtil = JwtUtil.config("JWT", "www.donki.com", "5326D79EDEF344C3B3431F268DD2564C", Jwts.SIG.HS256);
|
||||
String jwtStr = jwtUtil.compact(map.get("payload"));
|
||||
System.out.println(jwtStr);
|
||||
|
||||
Map<?, ?> parse = jwtUtil.parse(jwtStr);
|
||||
System.out.println(JsonUtil.obj2Json(parse));
|
||||
}
|
||||
}
|
||||
23
src/test/java/com/yexuejc/base/util/MsgUtilTest.java
Normal file
23
src/test/java/com/yexuejc/base/util/MsgUtilTest.java
Normal file
@@ -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}", "张三"));
|
||||
}
|
||||
}
|
||||
139
src/test/java/com/yexuejc/base/util/StrUtilTest.java
Normal file
139
src/test/java/com/yexuejc/base/util/StrUtilTest.java
Normal file
@@ -0,0 +1,139 @@
|
||||
package com.yexuejc.base.util;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class StrUtilTest {
|
||||
|
||||
@Test
|
||||
void isEmpty() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNotEmpty() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void genUUID() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGenUUID() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void genNum() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void toHex() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void toMD5() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void toSHA256() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void toSHA() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void iso2utf() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void isNumeric() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void codeId() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void decodeId() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseUrlencoded() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSignContent() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void replaceMobile() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void mapSort() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void setStr() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void underlineToCamel() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void camelToUnderline() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void printStackTraceAndMessage() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void printStackTrace() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void notNullExecute() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void notEmptyExecute() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void countryToCode() {
|
||||
Assertions.assertEquals(StrUtil.countryToCode("JPN"), "100000000");
|
||||
Assertions.assertEquals(StrUtil.countryToCode("KOR"), "010000000");
|
||||
Assertions.assertEquals(StrUtil.countryToCode("THA"), "001000000");
|
||||
Assertions.assertEquals(StrUtil.countryToCode("SGP"), "000100000");
|
||||
Assertions.assertEquals(StrUtil.countryToCode("CHN"), "000010000");
|
||||
Assertions.assertEquals(StrUtil.countryToCode("TWN"), "000001000");
|
||||
Assertions.assertEquals(StrUtil.countryToCode("HKG"), "000000100");
|
||||
Assertions.assertEquals(StrUtil.countryToCode("MAC"), "000000010");
|
||||
Assertions.assertEquals(StrUtil.countryToCode("999"), "000000001");
|
||||
Assertions.assertEquals(StrUtil.countryToCode("O"), "000000000");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCountryByCode() {
|
||||
Assertions.assertEquals(StrUtil.getCountryByCode("100000000"), "JPN");
|
||||
Assertions.assertEquals(StrUtil.getCountryByCode("010000000"), "KOR");
|
||||
Assertions.assertEquals(StrUtil.getCountryByCode("001000000"), "THA");
|
||||
Assertions.assertEquals(StrUtil.getCountryByCode("000100000"), "SGP");
|
||||
Assertions.assertEquals(StrUtil.getCountryByCode("000010000"), "CHN");
|
||||
Assertions.assertEquals(StrUtil.getCountryByCode("000001000"), "TWN");
|
||||
Assertions.assertEquals(StrUtil.getCountryByCode("000000100"), "HKG");
|
||||
Assertions.assertEquals(StrUtil.getCountryByCode("000000010"), "MAC");
|
||||
Assertions.assertEquals(StrUtil.getCountryByCode("000000001"), "999");
|
||||
Assertions.assertEquals(StrUtil.getCountryByCode("000000000"), "O");
|
||||
Assertions.assertEquals(StrUtil.getCountryByCode("100000000000"), "O");
|
||||
Assertions.assertEquals(StrUtil.getCountryByCode("-100000000000"), "O");
|
||||
}
|
||||
|
||||
@Test
|
||||
void countryToCodeByte() {
|
||||
System.out.println(String.format("%9s", Integer.toBinaryString(StrUtil.countryToCodeByte("CHN") & 0xFF)).replace(" ", "0"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
com.yexuejc.base.service.DefMessageServiceImpl
|
||||
1
src/test/resources/i18n/messages_en_US.properties
Normal file
1
src/test/resources/i18n/messages_en_US.properties
Normal file
@@ -0,0 +1 @@
|
||||
ENCRYPTION_PARAM_FAILED=Custom multilingual: Exception occurred while encrypting string [{0}].
|
||||
1
src/test/resources/i18n/messages_ja_JP.properties
Normal file
1
src/test/resources/i18n/messages_ja_JP.properties
Normal file
@@ -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
|
||||
1
src/test/resources/i18n/messages_ko_KR.properties
Normal file
1
src/test/resources/i18n/messages_ko_KR.properties
Normal file
@@ -0,0 +1 @@
|
||||
ENCRYPTION_PARAM_FAILED=\uCEE4\uC2A4\uD140 \uB2E4\uAD6D\uC5B4: \uBB38\uC790\uC5F4 [{0}] \uC554\uD638\uD654 \uC911 \uC608\uC678 \uBC1C\uC0DD
|
||||
1
src/test/resources/i18n/messages_zh_CN.properties
Normal file
1
src/test/resources/i18n/messages_zh_CN.properties
Normal file
@@ -0,0 +1 @@
|
||||
ENCRYPTION_PARAM_FAILED=\u81EA\u5B9A\u4E49\u591A\u8BED\u8A00\uFF1A\u52A0\u5BC6\u5B57\u7B26\u4E32[{0}]\u9047\u5230\u5F02\u5E38
|
||||
1
src/test/resources/i18n/messages_zh_TW.properties
Normal file
1
src/test/resources/i18n/messages_zh_TW.properties
Normal file
@@ -0,0 +1 @@
|
||||
ENCRYPTION_PARAM_FAILED=\u81EA\u8A02\u591A\u8A9E\u8A00\uFF1A\u52A0\u5BC6\u5B57\u4E32[{0}]\u767C\u751F\u7570\u5E38\u3002
|
||||
Reference in New Issue
Block a user