mirror of
https://gitee.com/jzsw-it/yexuejc-base.git
synced 2025-12-28 12:59:26 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c993cc6858 | ||
|
|
7f8c46fa9b |
@@ -5,9 +5,9 @@ github:https://github.com/yexuejc/yexuejc-base
|
||||
gitee:https://gitee.com/jzsw-it/yexuejc-base
|
||||
|
||||
### 说明
|
||||
1. 支持环境:java11(1.5.0开始支持java11,请使用`1.5.x-jre11`版本)
|
||||
1. 支持环境:java21(1.5.0开始支持java11,请使用`1.5.x-jre11`版本,1.6.0开始支持java21,请使用`1.6.x-jre21`版本)
|
||||
2. 该工具包基于springboot提取,按理说适用于所有java工程
|
||||
7. 从`1.5.0`开始,版本分为`1.5.0-jre8`和`1.5.0-jre11`,分别对于jre8和jre11使用(后续逐渐放弃jre8)
|
||||
7. 从`1.5.0`开始,版本分为`1.5.0-jre8`和`1.5.0-jre11`和`1.6.0-jre21`,分别对于jre8和jre11和jre21使用(后续逐渐全面使用jre21)
|
||||
|
||||
### 使用
|
||||
|
||||
@@ -17,7 +17,7 @@ pom.xml
|
||||
<dependency>
|
||||
<groupId>top.yexuejc</groupId>
|
||||
<artifactId>yexuejc-base</artifactId>
|
||||
<version>1.5.2-jre11</version>
|
||||
<version>1.6.0-jre21</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
yexuejc-base 更新记录
|
||||
------------------
|
||||
#### version :1.6.0-jre21
|
||||
**time: 2025-12-11 18:23:15** <br/>
|
||||
**branch:** jre21 <br/>
|
||||
**update:** <br/>
|
||||
1. 全面升级到JDK21
|
||||
|
||||
---
|
||||
|
||||
#### version :1.5.7-jre11
|
||||
**time: 2025-12-11 18:10:47** <br/>
|
||||
**branch:** jre11 <br/>
|
||||
|
||||
4
pom.xml
4
pom.xml
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>top.yexuejc</groupId>
|
||||
<artifactId>yexuejc-base</artifactId>
|
||||
<version>1.5.7-jre11</version>
|
||||
<version>1.6.0-jre21</version>
|
||||
|
||||
<name>yexuejc-base</name>
|
||||
<url>https://github.com/yexuejc/yexuejc-base</url>
|
||||
@@ -39,7 +39,7 @@
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
<java.version>21</java.version>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.verbose>true</maven.compiler.verbose>
|
||||
|
||||
@@ -18,8 +18,8 @@ public class DateConsts {
|
||||
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";
|
||||
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";
|
||||
public static final String DATE_YYYYMMDD_PATTERN = "yyyyMMdd";
|
||||
|
||||
}
|
||||
|
||||
@@ -16,12 +16,13 @@ import com.yexuejc.base.util.StrUtil;
|
||||
*
|
||||
* @author maxf
|
||||
* @class-name AES
|
||||
* @description
|
||||
* @description 提供AES对称加密解密功能,支持多种加密模式和填充方式
|
||||
* @date 2022/11/11 15:36
|
||||
*/
|
||||
public class AES {
|
||||
/**
|
||||
* 创建新的AES实例
|
||||
*
|
||||
* @return 新的AES实例
|
||||
*/
|
||||
public static AES builder() {
|
||||
@@ -105,7 +106,7 @@ public class AES {
|
||||
* 加密
|
||||
*
|
||||
* @param data 明文
|
||||
* @return 密文
|
||||
* @return 密文(Base64编码格式)
|
||||
* @throws BaseException 加密异常
|
||||
* @Description AES算法加密明文
|
||||
*/
|
||||
@@ -113,23 +114,15 @@ public class AES {
|
||||
validateKeyAndIv();
|
||||
validateInput(data, "加密数据");
|
||||
try {
|
||||
|
||||
Cipher cipher = Cipher.getInstance(algorithm.code);
|
||||
int blockSize = cipher.getBlockSize();
|
||||
byte[] dataBytes = data.getBytes(charset);
|
||||
int plaintextLength = dataBytes.length;
|
||||
if (plaintextLength % blockSize != 0) {
|
||||
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
|
||||
}
|
||||
byte[] plaintext = new byte[plaintextLength];
|
||||
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
|
||||
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(charset), AES_ALGORITHM);
|
||||
IvParameterSpec ivspec = null;
|
||||
// ECB模式不需要初始化向量
|
||||
if (!algorithm.code.contains("ECB")) {
|
||||
ivspec = new IvParameterSpec(iv.getBytes(charset));
|
||||
}
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
|
||||
byte[] encrypted = cipher.doFinal(plaintext);
|
||||
byte[] encrypted = cipher.doFinal(data.getBytes(charset));
|
||||
return Base64.getEncoder().encodeToString(encrypted);
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, ExpCode.ENCRYPTION_FAILED);
|
||||
@@ -139,7 +132,7 @@ public class AES {
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param data 密文
|
||||
* @param data 密文(Base64编码格式)
|
||||
* @return 明文
|
||||
* @throws BaseException 解密异常
|
||||
* @Description AES算法解密密文
|
||||
@@ -152,6 +145,7 @@ public class AES {
|
||||
Cipher cipher = Cipher.getInstance(algorithm.code);
|
||||
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(charset), AES_ALGORITHM);
|
||||
IvParameterSpec ivspec = null;
|
||||
// ECB模式不需要初始化向量
|
||||
if (!algorithm.code.contains("ECB")) {
|
||||
ivspec = new IvParameterSpec(iv.getBytes(charset));
|
||||
}
|
||||
@@ -159,10 +153,16 @@ public class AES {
|
||||
byte[] original = cipher.doFinal(encrypted);
|
||||
return new String(original, charset).trim();
|
||||
} catch (Exception e) {
|
||||
// 统一异常处理,防止信息泄露
|
||||
throw new BaseException(e, ExpCode.DECRYPTION_PARAM_FAILED, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前使用的加密算法
|
||||
*
|
||||
* @return 当前算法枚举值
|
||||
*/
|
||||
public ALGORITHM getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
@@ -172,30 +172,64 @@ public class AES {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密密钥
|
||||
*
|
||||
* @return 加密密钥
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置加密密钥
|
||||
*
|
||||
* @param key 加密密钥,长度必须是16、24或32字节
|
||||
* @return 当前AES实例,支持链式调用
|
||||
*/
|
||||
public AES setKey(String key) {
|
||||
validateKeyLength(key);
|
||||
this.key = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取初始化向量(IV)
|
||||
*
|
||||
* @return 初始化向量
|
||||
*/
|
||||
public String getIv() {
|
||||
return iv;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置初始化向量(IV)
|
||||
*
|
||||
* @param iv 初始化向量,长度必须是16字节
|
||||
* @return 当前AES实例,支持链式调用
|
||||
*/
|
||||
public AES setIv(String iv) {
|
||||
validateIvLength(iv);
|
||||
this.iv = iv;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符集编码
|
||||
*
|
||||
* @return 字符集编码
|
||||
*/
|
||||
public Charset getCharset() {
|
||||
return charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置字符集编码
|
||||
*
|
||||
* @param charset 字符集编码
|
||||
* @return 当前AES实例,支持链式调用
|
||||
* @throws IllegalArgumentException 当字符集为空时抛出
|
||||
*/
|
||||
public AES setCharset(Charset charset) {
|
||||
if (charset == null) {
|
||||
throw new IllegalArgumentException("字符集不能为空");
|
||||
@@ -218,6 +252,9 @@ public class AES {
|
||||
|
||||
/**
|
||||
* 验证密钥长度
|
||||
*
|
||||
* @param key 待验证的密钥
|
||||
* @throws IllegalArgumentException 当密钥为空或长度不符合要求时抛出
|
||||
*/
|
||||
private void validateKeyLength(String key) {
|
||||
if (StrUtil.isEmpty(key)) {
|
||||
@@ -231,6 +268,9 @@ public class AES {
|
||||
|
||||
/**
|
||||
* 验证IV长度
|
||||
*
|
||||
* @param iv 待验证的初始化向量
|
||||
* @throws IllegalArgumentException 当IV为空或长度不符合要求时抛出
|
||||
*/
|
||||
private void validateIvLength(String iv) {
|
||||
if (StrUtil.isEmpty(iv)) {
|
||||
@@ -253,6 +293,8 @@ public class AES {
|
||||
|
||||
/**
|
||||
* 构造函数 - 允许创建多个实例
|
||||
* <p>
|
||||
* 允许创建多个实例,避免单例模式的状态共享问题
|
||||
*/
|
||||
public AES() {
|
||||
// 允许创建多个实例,避免单例模式的状态共享问题
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
import com.yexuejc.base.constant.ExpCode;
|
||||
import com.yexuejc.base.exception.BaseException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.DESedeKeySpec;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Key;
|
||||
|
||||
import com.yexuejc.base.constant.ExpCode;
|
||||
import com.yexuejc.base.exception.BaseException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
|
||||
/**
|
||||
@@ -17,39 +22,45 @@ import java.security.Key;
|
||||
*
|
||||
* @author maxf
|
||||
* @ClassName ThreeDES
|
||||
* @Description
|
||||
* @Description 提供3DES对称加密解密功能,使用CBC模式和PKCS5填充
|
||||
* @date 2018/9/3 17:09
|
||||
*/
|
||||
public class DES3 {
|
||||
private DES3() {
|
||||
}
|
||||
|
||||
public static String IV = "1234567-";
|
||||
public static String ENCODING = "utf-8";
|
||||
private static final Charset CHARSET = StandardCharsets.UTF_8;
|
||||
private static final String ALGORITHM = "desede";
|
||||
private static final String TRANSFORMATION = "desede/CBC/PKCS5Padding";
|
||||
|
||||
/**
|
||||
* DESCBC加密
|
||||
*
|
||||
* @param src 数据源
|
||||
* @param key 密钥
|
||||
* @param key 密钥,长度至少24位
|
||||
* @return 返回加密后的数据
|
||||
* @throws BaseException
|
||||
* @throws BaseException 加密异常
|
||||
*/
|
||||
public static String encryptDesCbc(final String src, final String key) throws BaseException {
|
||||
if (key.length() < 24) {
|
||||
if (key == null || key.length() < 24) {
|
||||
throw new BaseException(ExpCode.INVALID_KEY_LENGTH);
|
||||
}
|
||||
try {
|
||||
Key deskey = null;
|
||||
DESedeKeySpec spec = new DESedeKeySpec(key.getBytes());
|
||||
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
|
||||
deskey = keyfactory.generateSecret(spec);
|
||||
|
||||
Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
|
||||
IvParameterSpec ips = new IvParameterSpec(IV.getBytes());
|
||||
Key deskey = generateKey(key);
|
||||
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
||||
// 使用更安全的随机IV
|
||||
byte[] ivBytes = new byte[8];
|
||||
new SecureRandom().nextBytes(ivBytes);
|
||||
IvParameterSpec ips = new IvParameterSpec(ivBytes);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
|
||||
byte[] encryptData = cipher.doFinal(src.getBytes(ENCODING));
|
||||
return Base64.encodeBase64URLSafeString(encryptData);
|
||||
byte[] encryptData = cipher.doFinal(src.getBytes(CHARSET));
|
||||
|
||||
// 将IV与加密数据一起返回
|
||||
byte[] result = new byte[ivBytes.length + encryptData.length];
|
||||
System.arraycopy(ivBytes, 0, result, 0, ivBytes.length);
|
||||
System.arraycopy(encryptData, 0, result, ivBytes.length, encryptData.length);
|
||||
|
||||
return Base64.encodeBase64URLSafeString(result);
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, ExpCode.ENCRYPTION_FAILED);
|
||||
}
|
||||
@@ -59,49 +70,70 @@ public class DES3 {
|
||||
* DESCBC解密
|
||||
*
|
||||
* @param src 数据源
|
||||
* @param key 密钥
|
||||
* @param key 密钥,长度至少24位
|
||||
* @return 返回解密后的原始数据
|
||||
* @throws BaseException
|
||||
* @throws BaseException 解密异常
|
||||
*/
|
||||
public static String decryptDesCbc(final String src, final String key) throws BaseException {
|
||||
if (key.length() < 24) {
|
||||
if (key == null || key.length() < 24) {
|
||||
throw new BaseException(ExpCode.INVALID_KEY_LENGTH);
|
||||
}
|
||||
try {
|
||||
Key deskey = null;
|
||||
DESedeKeySpec spec = new DESedeKeySpec(key.getBytes());
|
||||
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
|
||||
deskey = keyfactory.generateSecret(spec);
|
||||
Cipher cipher = Cipher.getInstance("desede/CBC/PKCS5Padding");
|
||||
IvParameterSpec ips = new IvParameterSpec(IV.getBytes());
|
||||
Key deskey = generateKey(key);
|
||||
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
|
||||
|
||||
// 从加密数据中提取IV
|
||||
byte[] srcBytes = Base64.decodeBase64(src);
|
||||
byte[] ivBytes = new byte[8];
|
||||
byte[] encryptedData = new byte[srcBytes.length - 8];
|
||||
System.arraycopy(srcBytes, 0, ivBytes, 0, 8);
|
||||
System.arraycopy(srcBytes, 8, encryptedData, 0, encryptedData.length);
|
||||
|
||||
IvParameterSpec ips = new IvParameterSpec(ivBytes);
|
||||
cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
|
||||
|
||||
byte[] decryptData = cipher.doFinal(Base64.decodeBase64(src));
|
||||
|
||||
return new String(decryptData, ENCODING);
|
||||
byte[] decryptData = cipher.doFinal(encryptedData);
|
||||
return new String(decryptData, CHARSET);
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, ExpCode.DECRYPTION_PARAM_FAILED, src);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成3DES密钥
|
||||
*
|
||||
* @param key 密钥字符串
|
||||
* @return Key对象
|
||||
* @throws InvalidKeyException 密钥生成异常
|
||||
* @throws NoSuchAlgorithmException 密钥生成异常
|
||||
* @throws InvalidKeySpecException 密钥生成异常
|
||||
*/
|
||||
private static Key generateKey(String key) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
DESedeKeySpec spec = new DESedeKeySpec(key.getBytes(CHARSET));
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
|
||||
return keyFactory.generateSecret(spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充,不是8的倍数会填充成8的倍数
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
* @param str 待填充的字符串
|
||||
* @return 填充后的字符串
|
||||
*/
|
||||
public static String padding(String str) {
|
||||
byte[] oldByteArray;
|
||||
oldByteArray = str.getBytes(StandardCharsets.UTF_8);
|
||||
int numberToPad = 8 - oldByteArray.length % 8;
|
||||
byte[] oldByteArray = str.getBytes(CHARSET);
|
||||
int len = 8;
|
||||
int numberToPad = len - oldByteArray.length % len;
|
||||
if (numberToPad == len) {
|
||||
// 如果已经是8的倍数,则不填充
|
||||
numberToPad = 0;
|
||||
}
|
||||
byte[] newByteArray = new byte[oldByteArray.length + numberToPad];
|
||||
System.arraycopy(oldByteArray, 0, newByteArray, 0,
|
||||
oldByteArray.length);
|
||||
System.arraycopy(oldByteArray, 0, newByteArray, 0, oldByteArray.length);
|
||||
for (int i = oldByteArray.length; i < newByteArray.length; ++i) {
|
||||
newByteArray[i] = 0;
|
||||
}
|
||||
return new String(newByteArray, StandardCharsets.UTF_8);
|
||||
return new String(newByteArray, CHARSET);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,20 +37,25 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
* RSA加解密 配置模式
|
||||
*
|
||||
* @ClassName: RSA
|
||||
* @Description:
|
||||
* @Description: 提供RSA加密、解密、签名和验证功能的工具类
|
||||
* @author: maxf
|
||||
* @date: 2018/5/15 14:39
|
||||
*/
|
||||
public class RSA {
|
||||
/** 私钥文件名常量 */
|
||||
private static final String PRIVATE_KEY_FILE = "private.key";
|
||||
/** 公钥文件名常量 */
|
||||
private static final String PUBLIC_KEY_FILE = "public.key";
|
||||
|
||||
public static RSA builder() {
|
||||
return RSA.Instace.rsa;
|
||||
return Instance.RSA;
|
||||
}
|
||||
|
||||
private static class Instace {
|
||||
private static RSA rsa = new RSA();
|
||||
private static class Instance {
|
||||
private static final RSA RSA = new RSA();
|
||||
}
|
||||
|
||||
private static final System.Logger log = System.getLogger(RSA.class.getName());
|
||||
private static final System.Logger LOGGER = System.getLogger(RSA.class.getName());
|
||||
public static final Charset CHARSET = StandardCharsets.UTF_8;
|
||||
/**
|
||||
* 算法名称
|
||||
@@ -66,7 +71,7 @@ public class RSA {
|
||||
* <p>使用方式:</p>
|
||||
* <p></p>
|
||||
*/
|
||||
public String algorithm = "RSA";
|
||||
public volatile String algorithm = "RSA";
|
||||
/**
|
||||
* 加密方式
|
||||
* <h5>示例:</h5>
|
||||
@@ -95,12 +100,12 @@ public class RSA {
|
||||
* <hr>
|
||||
* <p><b>AES的(算法/模式/填充)组合 参照 {@link ALGORITHM}<b/></p>
|
||||
*/
|
||||
public String transformation = "RSA";
|
||||
public volatile String transformation = "RSA";
|
||||
/**
|
||||
* 是否每次改变加密结果
|
||||
* 只针对于 transformation = "RSA"有效
|
||||
*/
|
||||
public boolean isChangeSign = true;
|
||||
public volatile boolean isChangeSign = true;
|
||||
/**
|
||||
* 是否使用 Base64URL 方式加密 默认正常加密
|
||||
* <pre>
|
||||
@@ -121,7 +126,7 @@ public class RSA {
|
||||
* };
|
||||
* </pre>
|
||||
*/
|
||||
public boolean encodeBase64URLSafe = false;
|
||||
public volatile boolean encodeBase64URLSafe = false;
|
||||
/**
|
||||
* 签名算法
|
||||
*/
|
||||
@@ -168,18 +173,23 @@ public class RSA {
|
||||
*
|
||||
* @param keySize 生成长度
|
||||
* @param base64URLSafe 是否生成 base64URL 格式的密钥:默认false
|
||||
* @return
|
||||
* @return 包含公钥和私钥的Map,key为"publicKey"和"privateKey"
|
||||
*/
|
||||
public Map<String, String> initKeys(int keySize, boolean base64URLSafe) {
|
||||
encodeBase64URLSafe = base64URLSafe;
|
||||
return initKeys(keySize);
|
||||
boolean originalEncodeBase64URLSafe = this.encodeBase64URLSafe;
|
||||
try {
|
||||
this.encodeBase64URLSafe = base64URLSafe;
|
||||
return initKeys(keySize);
|
||||
} finally {
|
||||
this.encodeBase64URLSafe = originalEncodeBase64URLSafe;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成密钥对
|
||||
*
|
||||
* @param keySize 生成长度
|
||||
* @return
|
||||
* @return 包含公钥和私钥的Map,key为"publicKey"和"privateKey"
|
||||
*/
|
||||
public Map<String, String> initKeys(int keySize) {
|
||||
//为RSA算法创建一个KeyPairGenerator对象
|
||||
@@ -220,13 +230,13 @@ public class RSA {
|
||||
* </p>
|
||||
*
|
||||
* @param filePath 密钥文件路径
|
||||
* @throws BaseException
|
||||
* @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));
|
||||
Files.writeString(Paths.get(filePath, PRIVATE_KEY_FILE), StrUtil.chunkString(keys.get("privateKey"), 64),CHARSET);
|
||||
Files.writeString(Paths.get(filePath, PUBLIC_KEY_FILE), StrUtil.chunkString(keys.get("publicKey"), 64),CHARSET);
|
||||
} catch (IOException e) {
|
||||
throw new BaseException(e, ExpCode.CREATE_KEY_FILE_FAILED);
|
||||
}
|
||||
@@ -236,7 +246,9 @@ public class RSA {
|
||||
* 得到公钥
|
||||
*
|
||||
* @param publicKey 密钥字符串(经过base64编码)
|
||||
* @throws Exception
|
||||
* @return RSAPublicKey对象
|
||||
* @throws NoSuchAlgorithmException 算法不存在异常
|
||||
* @throws InvalidKeySpecException 无效的密钥规范异常
|
||||
*/
|
||||
public RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
//通过X509编码的Key指令获得公钥对象
|
||||
@@ -249,7 +261,9 @@ public class RSA {
|
||||
* 得到私钥
|
||||
*
|
||||
* @param privateKey 密钥字符串(经过base64编码)
|
||||
* @throws Exception
|
||||
* @return RSAPrivateKey对象
|
||||
* @throws NoSuchAlgorithmException 算法不存在异常
|
||||
* @throws InvalidKeySpecException 无效的密钥规范异常
|
||||
*/
|
||||
public RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
//通过PKCS#8编码的Key指令获得私钥对象
|
||||
@@ -264,11 +278,17 @@ public class RSA {
|
||||
* @param data 加密原串数据
|
||||
* @param publicKey 公钥
|
||||
* @param base64URLSafe 是否生成 base64URL 格式的密钥:默认false
|
||||
* @return
|
||||
* @return 加密后的数据
|
||||
* @throws BaseException 加密异常
|
||||
*/
|
||||
public String publicEncrypt(String data, RSAPublicKey publicKey, boolean base64URLSafe) throws BaseException {
|
||||
encodeBase64URLSafe = base64URLSafe;
|
||||
return publicEncrypt(data, publicKey);
|
||||
boolean originalEncodeBase64URLSafe = this.encodeBase64URLSafe;
|
||||
try {
|
||||
this.encodeBase64URLSafe = base64URLSafe;
|
||||
return publicEncrypt(data, publicKey);
|
||||
} finally {
|
||||
this.encodeBase64URLSafe = originalEncodeBase64URLSafe;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,7 +296,8 @@ public class RSA {
|
||||
*
|
||||
* @param data 加密原串数据
|
||||
* @param publicKey 公钥
|
||||
* @return
|
||||
* @return 加密后的数据
|
||||
* @throws BaseException 加密异常
|
||||
*/
|
||||
public String publicEncrypt(String data, RSAPublicKey publicKey) throws BaseException {
|
||||
return encrypt(data, publicKey);
|
||||
@@ -285,9 +306,10 @@ public class RSA {
|
||||
/**
|
||||
* 私钥解密
|
||||
*
|
||||
* @param data
|
||||
* @param privateKey
|
||||
* @return
|
||||
* @param data 待解密数据
|
||||
* @param privateKey 私钥
|
||||
* @return 解密后的数据
|
||||
* @throws BaseException 解密异常
|
||||
*/
|
||||
public String privateDecrypt(String data, RSAPrivateKey privateKey) throws BaseException {
|
||||
return decrypt(data, privateKey);
|
||||
@@ -297,69 +319,92 @@ public class RSA {
|
||||
* 私钥加密
|
||||
*
|
||||
* @param data 加密原串数据
|
||||
* @param privateKey 公钥
|
||||
* @param privateKey 私钥
|
||||
* @param base64URLSafe 是否生成 base64URL 格式的密钥:默认false
|
||||
* @return
|
||||
* @return 加密后的数据
|
||||
* @throws BaseException 加密异常
|
||||
*/
|
||||
public String privateEncrypt(String data, RSAPrivateKey privateKey, boolean base64URLSafe) throws BaseException {
|
||||
encodeBase64URLSafe = base64URLSafe;
|
||||
return privateEncrypt(data, privateKey);
|
||||
boolean originalEncodeBase64URLSafe = this.encodeBase64URLSafe;
|
||||
try {
|
||||
this.encodeBase64URLSafe = base64URLSafe;
|
||||
return privateEncrypt(data, privateKey);
|
||||
} finally {
|
||||
this.encodeBase64URLSafe = originalEncodeBase64URLSafe;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥加密
|
||||
*
|
||||
* @param data 加密原串数据
|
||||
* @param privateKey 公钥
|
||||
* @return
|
||||
* @param privateKey 私钥
|
||||
* @return 加密后的数据
|
||||
* @throws BaseException 加密异常
|
||||
*/
|
||||
public String privateEncrypt(String data, RSAPrivateKey privateKey) throws BaseException {
|
||||
return encrypt(data, privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用密钥加密,支持公玥,私玥
|
||||
* 使用密钥加密,支持公钥,私钥
|
||||
*
|
||||
* @param data 待加密的数据
|
||||
* @param key 公钥或私钥
|
||||
* @return 加密后的数据
|
||||
* @throws BaseException
|
||||
* @throws BaseException 加密异常
|
||||
*/
|
||||
private String encrypt(String data, Key key) throws BaseException {
|
||||
try {
|
||||
// @formatter:off
|
||||
Cipher cipher = getCipher();
|
||||
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||
int keyLength = getKeyLength(key);
|
||||
if (encodeBase64URLSafe) {
|
||||
return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET),
|
||||
key instanceof RSAPublicKey ? ((RSAPublicKey) key).getModulus().bitLength() :
|
||||
((RSAPrivateKey) key).getModulus().bitLength()));
|
||||
return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), keyLength));
|
||||
} else {
|
||||
return Base64.encodeBase64String(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET),
|
||||
key instanceof RSAPublicKey ?((RSAPublicKey) key).getModulus().bitLength() :
|
||||
((RSAPrivateKey) key).getModulus().bitLength()));
|
||||
return Base64.encodeBase64String(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), keyLength));
|
||||
}
|
||||
// @formatter:on
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, ExpCode.ENCRYPTION_PARAM_FAILED, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用密钥解密
|
||||
*
|
||||
* @param data 待解密的数据
|
||||
* @param key 公钥或私钥
|
||||
* @return 解密后的数据
|
||||
* @throws BaseException 解密异常
|
||||
*/
|
||||
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
|
||||
int keyLength = getKeyLength(key);
|
||||
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), keyLength), CHARSET);
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, ExpCode.DECRYPTION_PARAM_FAILED, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取密钥长度
|
||||
*
|
||||
* @param key 密钥
|
||||
* @return 密钥长度
|
||||
* @throws IllegalArgumentException 当密钥类型不是RSA公钥或私钥时抛出
|
||||
*/
|
||||
private int getKeyLength(Key key) {
|
||||
// 根据密钥类型获取对应的模数位长度
|
||||
if (key instanceof RSAPublicKey publicKey) {
|
||||
return publicKey.getModulus().bitLength();
|
||||
} else if(key instanceof RSAPrivateKey privateKey) {
|
||||
return privateKey.getModulus().bitLength();
|
||||
} else {
|
||||
throw new IllegalArgumentException("密钥类型不支持: " + key.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥解密
|
||||
@@ -367,6 +412,7 @@ public class RSA {
|
||||
* @param data 待解密数据
|
||||
* @param publicKey 公钥
|
||||
* @return 解密后的数据
|
||||
* @throws BaseException 解密异常
|
||||
*/
|
||||
|
||||
public String publicDecrypt(String data, RSAPublicKey publicKey) throws BaseException {
|
||||
@@ -376,16 +422,17 @@ public class RSA {
|
||||
/**
|
||||
* 获取 Cipher
|
||||
*
|
||||
* @return
|
||||
* @throws NoSuchPaddingException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws NoSuchProviderException
|
||||
* @return Cipher对象
|
||||
* @throws NoSuchPaddingException 无此填充异常
|
||||
* @throws NoSuchAlgorithmException 无此算法异常
|
||||
* @throws NoSuchProviderException 无此提供者异常
|
||||
*/
|
||||
private Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
|
||||
Cipher cipher;
|
||||
if (transformation.startsWith("RSA") && isChangeSign) {
|
||||
// 每次改变加密结果
|
||||
if (ALGORITHM.RSA_ECB_SHA3_256.code.equals(transformation)) {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
cipher = Cipher.getInstance(transformation, "BC");
|
||||
} else {
|
||||
cipher = Cipher.getInstance(transformation);
|
||||
@@ -398,6 +445,16 @@ public class RSA {
|
||||
return cipher;
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA分段编解码
|
||||
*
|
||||
* @param cipher 加密器
|
||||
* @param opmode 操作模式(加密/解密)
|
||||
* @param datas 待处理数据
|
||||
* @param keySize 密钥长度
|
||||
* @return 处理后的数据
|
||||
* @throws BaseException 编解码异常
|
||||
*/
|
||||
private byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) throws BaseException {
|
||||
int maxBlock = 0;
|
||||
if (opmode == Cipher.DECRYPT_MODE) {
|
||||
@@ -405,6 +462,11 @@ public class RSA {
|
||||
} else {
|
||||
maxBlock = keySize / 8 - 11;
|
||||
}
|
||||
|
||||
if (maxBlock <= 0) {
|
||||
throw new BaseException("密钥大小无效: " + keySize, ExpCode.DATA_DECODE_PARAM_FAILED, keySize);
|
||||
}
|
||||
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
int offSet = 0;
|
||||
byte[] buff;
|
||||
@@ -435,12 +497,17 @@ public class RSA {
|
||||
* @param plaintext 签名字符串
|
||||
* @param privateKey 签名私钥
|
||||
* @param base64URLSafe 是否生成 base64URL 格式的密钥:默认false
|
||||
* @return
|
||||
* @throws BaseException
|
||||
* @return 签名串
|
||||
* @throws BaseException 签名异常
|
||||
*/
|
||||
public String sign(String plaintext, RSAPrivateKey privateKey, boolean base64URLSafe) throws BaseException {
|
||||
encodeBase64URLSafe = base64URLSafe;
|
||||
return sign(plaintext, privateKey);
|
||||
boolean originalEncodeBase64URLSafe = this.encodeBase64URLSafe;
|
||||
try {
|
||||
this.encodeBase64URLSafe = base64URLSafe;
|
||||
return sign(plaintext, privateKey);
|
||||
} finally {
|
||||
this.encodeBase64URLSafe = originalEncodeBase64URLSafe;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -452,7 +519,7 @@ public class RSA {
|
||||
* @param plaintext 签名字符串
|
||||
* @param privateKey 签名私钥
|
||||
* @return 签名串
|
||||
* @throws BaseException
|
||||
* @throws BaseException 签名异常
|
||||
*/
|
||||
public String sign(String plaintext, RSAPrivateKey privateKey) throws BaseException {
|
||||
try {
|
||||
@@ -478,7 +545,7 @@ public class RSA {
|
||||
* @param signStr 签名串
|
||||
* @param publicKey 公钥
|
||||
* @return true:校验成功 / false:校验失败
|
||||
* @throws BaseException
|
||||
* @throws BaseException 校验异常
|
||||
*/
|
||||
public boolean verify(String plaintext, String signStr, RSAPublicKey publicKey) throws BaseException {
|
||||
try {
|
||||
@@ -517,20 +584,30 @@ public class RSA {
|
||||
// @formatter:on
|
||||
String sign = respHeader.getSignature();
|
||||
if (sign.contains(SymbolicConsts.COMMA)) {
|
||||
sign = Arrays.stream(sign.split(SymbolicConsts.COMMA))
|
||||
.map(s -> s.split(SymbolicConsts.EQUAL))
|
||||
.filter(subSplit -> subSplit.length > 1 && RequestHeader.SIGNATURE.equalsIgnoreCase(subSplit[0]))
|
||||
.map(subSplit -> subSplit[1])
|
||||
.findFirst()
|
||||
.orElse(sign);
|
||||
sign = extractSignature(sign);
|
||||
}
|
||||
log.log(System.Logger.Level.DEBUG, "签名内容:=====\n{0}\n=====", signContent);
|
||||
LOGGER.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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从复合签名字符串中提取签名值
|
||||
*
|
||||
* @param sign 复合签名字符串
|
||||
* @return 提取的签名值
|
||||
*/
|
||||
private String extractSignature(String sign) {
|
||||
// 按逗号分割签名字符串,然后查找以SIGNATURE开头的部分
|
||||
return Arrays.stream(sign.split(SymbolicConsts.COMMA))
|
||||
.map(s -> s.split(SymbolicConsts.EQUAL))
|
||||
.filter(subSplit -> subSplit.length > 1 && RequestHeader.SIGNATURE.equalsIgnoreCase(subSplit[0]))
|
||||
.map(subSplit -> subSplit[1])
|
||||
.findFirst()
|
||||
.orElse(sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名
|
||||
@@ -556,13 +633,13 @@ public class RSA {
|
||||
.replace("{reqTime}", requestHeader.getReqTime())
|
||||
.replace("{body}", data);
|
||||
// @formatter:on
|
||||
log.log(System.Logger.Level.DEBUG, "签名内容:=====\n{0}\n=====", signContent);
|
||||
LOGGER.log(System.Logger.Level.DEBUG, "签名内容:=====\n{0}\n=====", signContent);
|
||||
return sign(signContent, RSA2.getPrivateKeyFromPKCS1(privateKeyPath), true);
|
||||
} catch (Exception e) {
|
||||
throw new BaseException(e, "签名时发生异常");
|
||||
throw new BaseException(e, ExpCode.SIGN_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
private RSA() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
|
||||
* 依赖 {@link RSA}
|
||||
*
|
||||
* @ClassName: RSA2
|
||||
* @Description:
|
||||
* @Description: 提供基于证书的RSA加解密功能,支持多种证书格式(JKS、PKCS12、PKCS8等)
|
||||
* @author: maxf
|
||||
* @date: 2018/5/15 14:37
|
||||
*/
|
||||
@@ -65,7 +65,9 @@ public class RSA2 {
|
||||
* 得到公钥
|
||||
*
|
||||
* @param filepath 密钥文件路径
|
||||
* @throws Exception
|
||||
* @return RSAPublicKey 公钥对象
|
||||
* @throws CertificateException 证书异常
|
||||
* @throws FileNotFoundException 文件未找到异常
|
||||
*/
|
||||
public static RSAPublicKey getPublicKey(String filepath) throws CertificateException, FileNotFoundException {
|
||||
try (FileInputStream pubKeyIn = new FileInputStream(filepath)) {
|
||||
@@ -92,8 +94,8 @@ public class RSA2 {
|
||||
* 得到公钥
|
||||
*
|
||||
* @param pubKeyIn 密钥文件流
|
||||
* @return
|
||||
* @throws CertificateException
|
||||
* @return RSAPublicKey 公钥对象
|
||||
* @throws CertificateException 证书异常
|
||||
*/
|
||||
public static RSAPublicKey getPublicKey(InputStream pubKeyIn) throws CertificateException {
|
||||
//通过证书,获取公钥
|
||||
@@ -108,8 +110,8 @@ public class RSA2 {
|
||||
* @param pubKeyPath 密钥文件路径
|
||||
* @param alias 别名
|
||||
* @param password 密码
|
||||
* @return RSAPublicKey
|
||||
* @throws BaseException
|
||||
* @return RSAPublicKey 公钥对象
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static RSAPublicKey getPublicKeyFromPKCS12(String pubKeyPath, String alias, String password) throws BaseException {
|
||||
try (FileInputStream fis = new FileInputStream(pubKeyPath)) {
|
||||
@@ -125,8 +127,8 @@ public class RSA2 {
|
||||
* @param pubKeyIn 密钥文件流
|
||||
* @param alias 别名
|
||||
* @param password 密码
|
||||
* @return RSAPublicKey
|
||||
* @throws BaseException
|
||||
* @return RSAPublicKey 公钥对象
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static RSAPublicKey getPublicKeyFromPKCS12(InputStream pubKeyIn, String alias, String password) throws BaseException {
|
||||
try {
|
||||
@@ -143,8 +145,8 @@ public class RSA2 {
|
||||
* 从PEM格式的公钥文件中提取公钥
|
||||
*
|
||||
* @param pemContent PEM格式的内容
|
||||
* @return RSAPublicKey
|
||||
* @throws CertificateException
|
||||
* @return RSAPublicKey 公钥对象
|
||||
* @throws CertificateException 证书异常
|
||||
*/
|
||||
private static RSAPublicKey getPublicKey4Pem(String pemContent) throws CertificateException {
|
||||
try {
|
||||
@@ -185,8 +187,8 @@ public class RSA2 {
|
||||
* @param filepath 私钥路径
|
||||
* @param alias 证书别名
|
||||
* @param password 证书密码
|
||||
* @return
|
||||
* @throws BaseException
|
||||
* @return RSAPrivateKey 私钥对象
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKey(String filepath, String alias, String password) throws BaseException {
|
||||
return getPrivateKey(filepath, alias, password, KEY_JKS);
|
||||
@@ -198,8 +200,8 @@ public class RSA2 {
|
||||
* @param priKeyIn 私钥文件流
|
||||
* @param alias 证书别名
|
||||
* @param password 证书密码
|
||||
* @return
|
||||
* @throws BaseException
|
||||
* @return RSAPrivateKey 私钥对象
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKey(InputStream priKeyIn, String alias, String password) throws BaseException {
|
||||
return getPrivateKey(priKeyIn, alias, password, KEY_JKS);
|
||||
@@ -211,8 +213,8 @@ public class RSA2 {
|
||||
* @param filepath 私钥路径
|
||||
* @param alias 证书别名 可空
|
||||
* @param password 证书密码
|
||||
* @return
|
||||
* @throws BaseException
|
||||
* @return RSAPrivateKey 私钥对象
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS12(String filepath, String alias, String password) throws BaseException {
|
||||
return getPrivateKey(filepath, alias, password, KEY_PKCS12);
|
||||
@@ -224,8 +226,8 @@ public class RSA2 {
|
||||
* @param priKeyIn 私钥文件流
|
||||
* @param alias 证书别名 可空
|
||||
* @param password 证书密码
|
||||
* @return
|
||||
* @throws BaseException
|
||||
* @return RSAPrivateKey 私钥对象
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS12(InputStream priKeyIn, String alias, String password) throws BaseException {
|
||||
return getPrivateKey(priKeyIn, alias, password, KEY_PKCS12);
|
||||
@@ -235,8 +237,8 @@ public class RSA2 {
|
||||
* 读取PKCS8格式的key(私钥)pem格式
|
||||
*
|
||||
* @param filepath 私钥路径
|
||||
* @return
|
||||
* @throws BaseException
|
||||
* @return RSAPrivateKey 私钥对象
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS8(String filepath, String password) throws BaseException {
|
||||
return getPrivateKey(filepath, null, password, KEY_PKCS8);
|
||||
@@ -246,8 +248,8 @@ public class RSA2 {
|
||||
* 读取PKCS8格式的key(私钥)pem格式
|
||||
*
|
||||
* @param priKeyIn 私钥文件流
|
||||
* @return
|
||||
* @throws BaseException
|
||||
* @return RSAPrivateKey 私钥对象
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS8(InputStream priKeyIn, String password) throws BaseException {
|
||||
return getPrivateKey(priKeyIn, null, password, KEY_PKCS8);
|
||||
@@ -257,8 +259,8 @@ public class RSA2 {
|
||||
* 读取PKCS8格式的key(私钥)pem格式
|
||||
*
|
||||
* @param filepath 私钥路径
|
||||
* @return
|
||||
* @throws BaseException
|
||||
* @return RSAPrivateKey 私钥对象
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS1(String filepath) throws BaseException {
|
||||
return getPrivateKey(filepath, null, null, KEY_PKCS1);
|
||||
@@ -268,8 +270,8 @@ public class RSA2 {
|
||||
* 读取PKCS8格式的key(私钥)pem格式
|
||||
*
|
||||
* @param priKeyIn 私钥文件流
|
||||
* @return
|
||||
* @throws BaseException
|
||||
* @return RSAPrivateKey 私钥对象
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKeyFromPKCS1(InputStream priKeyIn) throws BaseException {
|
||||
return getPrivateKey(priKeyIn, null, null, KEY_PKCS1);
|
||||
@@ -282,8 +284,8 @@ public class RSA2 {
|
||||
* @param alias 证书别名 可空
|
||||
* @param password 证书密码
|
||||
* @param type 证书格式
|
||||
* @return
|
||||
* @throws BaseException
|
||||
* @return RSAPrivateKey 私钥对象
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKey(String filepath, String alias, String password, String type) throws BaseException {
|
||||
try (FileInputStream fileInputStream = new FileInputStream(filepath)) {
|
||||
@@ -300,8 +302,8 @@ public class RSA2 {
|
||||
* @param alias 证书别名 可空
|
||||
* @param password 证书密码
|
||||
* @param type 证书格式
|
||||
* @return
|
||||
* @throws BaseException
|
||||
* @return RSAPrivateKey 私钥对象
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static RSAPrivateKey getPrivateKey(InputStream priKeyIn, String alias, String password, String type) throws BaseException {
|
||||
try {
|
||||
@@ -349,8 +351,8 @@ public class RSA2 {
|
||||
*
|
||||
* @param pemContent PEM格式的加密私钥内容
|
||||
* @param password 密码
|
||||
* @return RSAPrivateKey
|
||||
* @throws Exception
|
||||
* @return RSAPrivateKey 私钥对象
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
private static RSAPrivateKey getPrivateKeyByPKCS8(String pemContent, String password) throws Exception {
|
||||
if (Security.getProvider(BOUNCY_CASTLE_PROVIDER_KEY) == null) {
|
||||
@@ -361,25 +363,24 @@ public class RSA2 {
|
||||
|
||||
// 处理PKCS#1格式的RSA私钥
|
||||
if (object instanceof org.bouncycastle.asn1.pkcs.RSAPrivateKey) {
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BOUNCY_CASTLE_PROVIDER_KEY);
|
||||
PrivateKeyInfo privateKeyInfo = PrivateKeyInfo.getInstance(object);
|
||||
return (RSAPrivateKey) converter.getPrivateKey(privateKeyInfo);
|
||||
} else if (object instanceof PEMKeyPair) {
|
||||
// 处理PKCS#1格式的RSA私钥
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BOUNCY_CASTLE_PROVIDER_KEY);
|
||||
return (RSAPrivateKey) converter.getPrivateKey(((PEMKeyPair) object).getPrivateKeyInfo());
|
||||
} else if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
|
||||
} else if (object instanceof PKCS8EncryptedPrivateKeyInfo encryptedInfo) {
|
||||
// 处理加密的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();
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BOUNCY_CASTLE_PROVIDER_KEY);
|
||||
return (RSAPrivateKey) converter.getPrivateKey(privateKeyInfo);
|
||||
} else if (object instanceof PrivateKeyInfo) {
|
||||
// 处理未加密的PKCS#8私钥
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(BOUNCY_CASTLE_PROVIDER_KEY);
|
||||
return (RSAPrivateKey) converter.getPrivateKey((PrivateKeyInfo) object);
|
||||
}
|
||||
throw new IllegalArgumentException("不支持的PEM格式");
|
||||
@@ -395,6 +396,7 @@ public class RSA2 {
|
||||
* @param outPath 证书输出文件路径
|
||||
* @param oPwd 原证书密码
|
||||
* @param nPwd 新证书密码(为空同原证书密码一致)
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
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)) {
|
||||
@@ -414,6 +416,7 @@ public class RSA2 {
|
||||
* @param out 证书输出文件流[自行关闭->out.close()]
|
||||
* @param oPwd 原证书密码
|
||||
* @param nPwd 新证书密码(为空同原证书密码一致)
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static void cover2Pfx(FileInputStream fis, FileOutputStream out, char[] oPwd, char[] nPwd) throws BaseException {
|
||||
try {
|
||||
@@ -431,6 +434,7 @@ public class RSA2 {
|
||||
* @param outPath 证书输出文件路径
|
||||
* @param oPwd 原证书密码
|
||||
* @param nPwd 新证书密码(为空同原证书密码一致)
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
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)) {
|
||||
@@ -451,6 +455,7 @@ public class RSA2 {
|
||||
* @param out 证书输出文件流[自行关闭->out.close()]
|
||||
* @param oPwd 原证书密码
|
||||
* @param nPwd 新证书密码(为空同原证书密码一致)
|
||||
* @throws BaseException 基础异常
|
||||
*/
|
||||
public static void cover2keyStore(FileInputStream fis, FileOutputStream out, char[] oPwd, char[] nPwd) throws BaseException {
|
||||
try {
|
||||
@@ -470,11 +475,11 @@ public class RSA2 {
|
||||
* @param nPwd 新证书密码(为空同原证书密码一致)
|
||||
* @param inputKeyStore 输入格式
|
||||
* @param type 目标类型
|
||||
* @throws IOException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws CertificateException
|
||||
* @throws KeyStoreException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @throws IOException IO异常
|
||||
* @throws NoSuchAlgorithmException 无此算法异常
|
||||
* @throws CertificateException 证书异常
|
||||
* @throws KeyStoreException 密钥库异常
|
||||
* @throws UnrecoverableKeyException 无法恢复密钥异常
|
||||
*/
|
||||
public static void cover(FileInputStream fis, FileOutputStream out, char[] oPwd, char[] nPwd, KeyStore inputKeyStore, String type) throws IOException, NoSuchAlgorithmException, CertificateException, KeyStoreException, UnrecoverableKeyException {
|
||||
inputKeyStore.load(fis, oPwd);
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* RSA 加解密 工具模式
|
||||
*
|
||||
* @author maxf
|
||||
* @ClassName RSACoder
|
||||
* @Description
|
||||
* @Description RSA加解密工具类,提供基于公钥和私钥的加密解密功能
|
||||
* @date 2018/9/3 16:13
|
||||
*/
|
||||
public class RSACoder {
|
||||
@@ -21,175 +28,166 @@ public class RSACoder {
|
||||
public static final String KEY_ALGORITHM = "RSA";
|
||||
|
||||
/**
|
||||
* 解密<br>
|
||||
* 用公钥解密
|
||||
* 使用公钥解密数据
|
||||
*
|
||||
* @param data
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @param data 待解密的数据(BASE64编码的字符串)
|
||||
* @param key 公钥(BASE64编码)
|
||||
* @return 解密后的字节数组
|
||||
* @throws Exception 解密过程中可能出现的异常
|
||||
*/
|
||||
public static byte[] decryptByPublic(String data, String key)
|
||||
throws Exception {
|
||||
public static byte[] decryptByPublic(String data, String key) throws Exception {
|
||||
return getDataByPublicKey(data, key, Cipher.DECRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密<br>
|
||||
* 用私钥解密
|
||||
* 使用私钥解密数据
|
||||
*
|
||||
* @param data
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @param data 待解密的数据(BASE64编码的字符串)
|
||||
* @param key 私钥(BASE64编码)
|
||||
* @return 解密后的字节数组
|
||||
* @throws Exception 解密过程中可能出现的异常
|
||||
*/
|
||||
public static byte[] decryptByPrivateKey(String data, String key)
|
||||
throws Exception {
|
||||
public static byte[] decryptByPrivateKey(String data, String key) throws Exception {
|
||||
return getDataByPrivateKey(data, key, Cipher.DECRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密<br>
|
||||
* 用公钥加密
|
||||
* 使用公钥加密数据
|
||||
*
|
||||
* @param data
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @param data 待加密的数据(BASE64编码的字符串)
|
||||
* @param key 公钥(BASE64编码)
|
||||
* @return 加密后的字节数组
|
||||
* @throws Exception 加密过程中可能出现的异常
|
||||
*/
|
||||
public static byte[] encryptByPublicKey(String data, String key)
|
||||
throws Exception {
|
||||
public static byte[] encryptByPublicKey(String data, String key) throws Exception {
|
||||
return getDataByPublicKey(data, key, Cipher.ENCRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密<br>
|
||||
* 用私钥加密
|
||||
* 使用私钥加密数据
|
||||
*
|
||||
* @param data
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @param data 待加密的数据(BASE64编码的字符串)
|
||||
* @param key 私钥(BASE64编码)
|
||||
* @return 加密后的字节数组
|
||||
* @throws Exception 加密过程中可能出现的异常
|
||||
*/
|
||||
public static byte[] encryptByPrivateKey(String data, String key)
|
||||
throws Exception {
|
||||
public static byte[] encryptByPrivateKey(String data, String key) throws Exception {
|
||||
return getDataByPrivateKey(data, key, Cipher.ENCRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密<br>
|
||||
* 用公钥解密
|
||||
* 使用公钥解密数据
|
||||
*
|
||||
* @param data
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @param data 待解密的字节数组
|
||||
* @param key 公钥(BASE64编码)
|
||||
* @return 解密后的字节数组
|
||||
* @throws Exception 解密过程中可能出现的异常
|
||||
*/
|
||||
public static byte[] decryptByPublic(byte[] data, String key)
|
||||
throws Exception {
|
||||
public static byte[] decryptByPublic(byte[] data, String key) throws Exception {
|
||||
return getDataByPublicKey(data, key, Cipher.DECRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密<br>
|
||||
* 用私钥解密
|
||||
* 使用私钥解密数据
|
||||
*
|
||||
* @param data
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @param data 待解密的字节数组
|
||||
* @param key 私钥(BASE64编码)
|
||||
* @return 解密后的字节数组
|
||||
* @throws Exception 解密过程中可能出现的异常
|
||||
*/
|
||||
public static byte[] decryptByPrivateKey(byte[] data, String key)
|
||||
throws Exception {
|
||||
public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {
|
||||
return getDataByPrivateKey(data, key, Cipher.DECRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密<br>
|
||||
* 用公钥加密
|
||||
* 使用公钥加密数据
|
||||
*
|
||||
* @param data
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @param data 待加密的字节数组
|
||||
* @param key 公钥(BASE64编码)
|
||||
* @return 加密后的字节数组
|
||||
* @throws Exception 加密过程中可能出现的异常
|
||||
*/
|
||||
public static byte[] encryptByPublicKey(byte[] data, String key)
|
||||
throws Exception {
|
||||
public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {
|
||||
return getDataByPublicKey(data, key, Cipher.ENCRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密<br>
|
||||
* 用私钥加密
|
||||
* 使用私钥加密数据
|
||||
*
|
||||
* @param data
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @param data 待加密的字节数组
|
||||
* @param key 私钥(BASE64编码)
|
||||
* @return 加密后的字节数组
|
||||
* @throws Exception 加密过程中可能出现的异常
|
||||
*/
|
||||
public static byte[] encryptByPrivateKey(byte[] data, String key)
|
||||
throws Exception {
|
||||
public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {
|
||||
return getDataByPrivateKey(data, key, Cipher.ENCRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过公钥获得加解密数据
|
||||
*
|
||||
* @param data String
|
||||
* @param key String
|
||||
* @param mode int
|
||||
* @return
|
||||
* @param data 待处理的数据(BASE64编码的字符串)
|
||||
* @param key 公钥(BASE64编码)
|
||||
* @param mode 加密或解密模式(Cipher.ENCRYPT_MODE或Cipher.DECRYPT_MODE)
|
||||
* @return 处理后的字节数组
|
||||
* @throws Exception 处理过程中可能出现的异常
|
||||
*/
|
||||
public static byte[] getDataByPublicKey(String data, String key, int mode)
|
||||
throws Exception {
|
||||
public static byte[] getDataByPublicKey(String data, String key, int mode) throws Exception {
|
||||
return getDataByPublicKey(data.getBytes(), key, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过私钥获得加解密数据
|
||||
*
|
||||
* @param data String
|
||||
* @param key String
|
||||
* @param mode 加密或解密
|
||||
* @return
|
||||
* @param data 待处理的数据(BASE64编码的字符串)
|
||||
* @param key 私钥(BASE64编码)
|
||||
* @param mode 加密或解密模式(Cipher.ENCRYPT_MODE或Cipher.DECRYPT_MODE)
|
||||
* @return 处理后的字节数组
|
||||
* @throws Exception 处理过程中可能出现的异常
|
||||
*/
|
||||
public static byte[] getDataByPrivateKey(String data, String key, int mode)
|
||||
throws Exception {
|
||||
public static byte[] getDataByPrivateKey(String data, String key, int mode) throws Exception {
|
||||
return getDataByPrivateKey(data.getBytes(), key, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过公钥获得加解密数据
|
||||
*
|
||||
* @param data String
|
||||
* @param key String
|
||||
* @param mode int
|
||||
* @return
|
||||
* @param data 待处理的字节数组
|
||||
* @param key 公钥(BASE64编码)
|
||||
* @param mode 加密或解密模式(Cipher.ENCRYPT_MODE或Cipher.DECRYPT_MODE)
|
||||
* @return 处理后的字节数组
|
||||
* @throws Exception 处理过程中可能出现的异常
|
||||
*/
|
||||
public static byte[] getDataByPublicKey(byte[] data, String key, int mode)
|
||||
throws Exception {
|
||||
// 取得私钥
|
||||
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(key.getBytes("UTF-8")));
|
||||
public static byte[] getDataByPublicKey(byte[] data, String key, int mode) throws Exception {
|
||||
// 取得公钥
|
||||
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(key.getBytes(StandardCharsets.UTF_8)));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
Key privateKey = keyFactory.generatePublic(x509KeySpec);
|
||||
Key publicKey = keyFactory.generatePublic(x509KeySpec);
|
||||
// 对数据进行加密或解密
|
||||
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
|
||||
cipher.init(mode, privateKey);
|
||||
cipher.init(mode, publicKey);
|
||||
return cipher.doFinal(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过私钥获得加解密数据
|
||||
*
|
||||
* @param data String
|
||||
* @param key String
|
||||
* @param mode 加密或解密
|
||||
* @return
|
||||
* @param data 待处理的字节数组
|
||||
* @param key 私钥(BASE64编码)
|
||||
* @param mode 加密或解密模式(Cipher.ENCRYPT_MODE或Cipher.DECRYPT_MODE)
|
||||
* @return 处理后的字节数组
|
||||
* @throws NoSuchAlgorithmException 算法不存在异常
|
||||
* @throws InvalidKeySpecException 无效的密钥规范异常
|
||||
* @throws NoSuchPaddingException 无此填充异常
|
||||
* @throws InvalidKeyException 无效密钥异常
|
||||
* @throws IllegalBlockSizeException 非法块大小异常
|
||||
* @throws BadPaddingException 坏填充异常
|
||||
*/
|
||||
public static byte[] getDataByPrivateKey(byte[] data, String key, int mode)
|
||||
throws Exception {
|
||||
public static byte[] getDataByPrivateKey(byte[] data, String key,
|
||||
int mode) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException,
|
||||
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
||||
// 取得私钥
|
||||
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(key.getBytes("UTF-8")));
|
||||
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(key.getBytes(StandardCharsets.UTF_8)));
|
||||
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
|
||||
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
|
||||
// 对数据解密
|
||||
@@ -197,4 +195,4 @@ public class RSACoder {
|
||||
cipher.init(mode, privateKey);
|
||||
return cipher.doFinal(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.yexuejc.base.exception;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import com.yexuejc.base.constant.ExpCode;
|
||||
import com.yexuejc.base.constant.SymbolicConsts;
|
||||
import com.yexuejc.base.util.MsgUtil;
|
||||
@@ -14,6 +16,7 @@ import com.yexuejc.base.util.StrUtil;
|
||||
public class BaseException extends Exception {
|
||||
|
||||
/** 序列化 */
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
/** 异常消息CODE */
|
||||
protected final String errorCode;
|
||||
@@ -75,8 +78,7 @@ public class BaseException extends Exception {
|
||||
}
|
||||
this.errorCode = errorCode;
|
||||
this.errorMessage = getMessageByCode(errorCode, args);
|
||||
if (cause instanceof BaseException) {
|
||||
BaseException comExp = (BaseException) cause;
|
||||
if (cause instanceof BaseException comExp) {
|
||||
if (StrUtil.isNotEmpty(comExp.errorMessage)) {
|
||||
this.errorMessage = StrUtil.isEmpty(this.errorMessage) ? comExp.errorMessage :
|
||||
this.errorMessage + SymbolicConsts.NEW_LINE + comExp.errorMessage;
|
||||
|
||||
@@ -9,8 +9,10 @@ import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Scanner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -33,6 +35,10 @@ import org.apache.commons.io.IOUtils;
|
||||
* 12. {@link #read4CharStreams(InputStream, Charset)}<br>
|
||||
*/
|
||||
public class FileInput {
|
||||
private static final System.Logger LOGGER = System.getLogger(FileInput.class.getName());
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
private static final String IO_EXCEPTION_MSG = "读取流时发生IO异常";
|
||||
|
||||
/**
|
||||
* 读取IO流内容:byte方式
|
||||
*
|
||||
@@ -42,9 +48,24 @@ public class FileInput {
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String read4Byte(InputStream inputStream, Charset charset) throws IOException {
|
||||
byte[] bytes = new byte[inputStream.available()];
|
||||
inputStream.read(bytes);
|
||||
return new String(bytes, charset == null ? Charset.defaultCharset() : charset);
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
return baos.toString(getCharsetName(charset));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符集名称
|
||||
*
|
||||
* @param charset 字符集
|
||||
* @return 字符集名称
|
||||
*/
|
||||
private static String getCharsetName(Charset charset) {
|
||||
return (charset == null ? Charset.defaultCharset() : charset).name();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,10 +76,13 @@ public class FileInput {
|
||||
* @param lineSeparator 换行方式:默认跟随系统 {@link System#lineSeparator()}
|
||||
* @return
|
||||
*/
|
||||
public static String read4BufferedReader(InputStream inputStream, Charset charset, String lineSeparator) {
|
||||
return new BufferedReader(
|
||||
new InputStreamReader(inputStream, charset == null ? Charset.defaultCharset() : charset)
|
||||
).lines().collect(Collectors.joining(lineSeparator == null ? System.lineSeparator() : lineSeparator));
|
||||
public static String read4BufferedReader(InputStream inputStream, Charset charset, String lineSeparator) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset == null ? Charset.defaultCharset() : charset))) {
|
||||
return reader.lines().collect(Collectors.joining(lineSeparator == null ? System.lineSeparator() : lineSeparator));
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(System.Logger.Level.WARNING, IO_EXCEPTION_MSG, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,11 +93,13 @@ public class FileInput {
|
||||
* @param lineSeparator 换行方式:默认跟随系统 {@link System#lineSeparator()}
|
||||
* @return
|
||||
*/
|
||||
public static String read4BufferedReaderParallel(InputStream inputStream, Charset charset, String lineSeparator) {
|
||||
return new BufferedReader(
|
||||
new InputStreamReader(inputStream, charset == null ? Charset.defaultCharset() : charset)
|
||||
).lines().parallel()
|
||||
.collect(Collectors.joining(lineSeparator == null ? System.lineSeparator() : lineSeparator));
|
||||
public static String read4BufferedReaderParallel(InputStream inputStream, Charset charset, String lineSeparator) throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset == null ? Charset.defaultCharset() : charset))) {
|
||||
return reader.lines().parallel().collect(Collectors.joining(lineSeparator == null ? System.lineSeparator() : lineSeparator));
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(System.Logger.Level.WARNING, IO_EXCEPTION_MSG, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,9 +109,9 @@ public class FileInput {
|
||||
* @return
|
||||
*/
|
||||
public static String read4ScannerA(InputStream inputStream) {
|
||||
Scanner s = new Scanner(inputStream).useDelimiter("\\A");
|
||||
String str = s.hasNext() ? s.next() : "";
|
||||
return str;
|
||||
try (Scanner s = new Scanner(inputStream).useDelimiter("\\A")) {
|
||||
return s.hasNext() ? s.next() : "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +121,15 @@ public class FileInput {
|
||||
* @return
|
||||
*/
|
||||
public static String read4ScannerZ(InputStream inputStream) {
|
||||
return new Scanner(inputStream).useDelimiter("\\Z").next();
|
||||
Scanner s = new Scanner(inputStream).useDelimiter("\\Z");
|
||||
try {
|
||||
return s.hasNext() ? s.next() : "";
|
||||
} catch (NoSuchElementException e) {
|
||||
LOGGER.log(System.Logger.Level.WARNING, IO_EXCEPTION_MSG, e);
|
||||
throw e;
|
||||
} finally {
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,9 +142,15 @@ public class FileInput {
|
||||
public static String read4StringBuilder(InputStream inputStream, Charset charset) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, charset == null ? Charset.defaultCharset() : charset));
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line);
|
||||
String lineSeparator = System.lineSeparator();
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, charset == null ? Charset.defaultCharset() : charset))) {
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line).append(lineSeparator);
|
||||
}
|
||||
// 删除末尾多余的换行符
|
||||
if (sb.length() > 0) {
|
||||
sb.delete(sb.length() - lineSeparator.length(), sb.length());
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -184,17 +224,21 @@ public class FileInput {
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String read4CharStreams(InputStream inputStream, Charset charset) throws IOException, ClassNotFoundException {
|
||||
public static String read4CharStreams(InputStream inputStream,
|
||||
Charset charset) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException,
|
||||
IllegalAccessException {
|
||||
try {
|
||||
Class<?> charStreamsClass = Class.forName("com.google.common.io.CharStreams");
|
||||
Method toStringMethod = charStreamsClass.getMethod("toString", InputStreamReader.class);
|
||||
String method = "toString";
|
||||
Method toStringMethod = charStreamsClass.getMethod(method, Readable.class);
|
||||
return (String) toStringMethod.invoke(null, new InputStreamReader(inputStream, charset == null ? Charset.defaultCharset() : charset));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ClassNotFoundException("缺少依赖,请引入Guava");
|
||||
LOGGER.log(System.Logger.Level.DEBUG, "缺少依赖,请引入Guava");
|
||||
throw e;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("com.google.common.io.CharStreams.toString调用失败,请检查Guava版本", e);
|
||||
LOGGER.log(System.Logger.Level.DEBUG, "com.google.common.io.CharStreams.toString调用失败,请检查Guava版本");
|
||||
throw e;
|
||||
}
|
||||
// return com.google.common.io.CharStreams.toString(new InputStreamReader(inputStream, charset == null ? Charset.defaultCharset() : charset));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,50 +249,20 @@ public class FileInput {
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String read4ByteStreams(InputStream inputStream, Charset charset) throws IOException, ClassNotFoundException {
|
||||
public static String read4ByteStreams(InputStream inputStream,
|
||||
Charset charset) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException,
|
||||
IllegalAccessException {
|
||||
try {
|
||||
Class<?> charStreamsClass = Class.forName("com.google.common.io.ByteStreams");
|
||||
Method toStringMethod = charStreamsClass.getMethod("toByteArray", InputStreamReader.class);
|
||||
String method = "toByteArray";
|
||||
Method toStringMethod = charStreamsClass.getMethod(method, InputStreamReader.class);
|
||||
return (String) toStringMethod.invoke(null, new InputStreamReader(inputStream, charset == null ? Charset.defaultCharset() : charset));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ClassNotFoundException("缺少依赖,请引入Guava");
|
||||
LOGGER.log(System.Logger.Level.DEBUG, "缺少依赖,请引入Guava");
|
||||
throw e;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("com.google.common.io.ByteStreams.toByteArray调用失败,请检查Guava版本", e);
|
||||
LOGGER.log(System.Logger.Level.DEBUG, "com.google.common.io.ByteStreams.toByteArray调用失败,请检查Guava版本");
|
||||
throw e;
|
||||
}
|
||||
// return new String(com.google.common.io.ByteStreams.toByteArray(inputStream), charset == null ? Charset.defaultCharset() : charset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*public static void main(String[] args) {
|
||||
long size = FileUtil.size(new File("E:\\OS\\deepin-15.6-amd64\\DeepinCloudPrintServerInstaller_1.0.0.1.exe"));
|
||||
System.out.println(size);
|
||||
System.out.println(1024 * 1024 * 5);
|
||||
if (size > 1024 * 1024 * 5) {
|
||||
System.out.println("文件最大5M");
|
||||
return;
|
||||
}
|
||||
|
||||
long s1 = fileSize(new File("E:\\OS\\cn_windows_10_consumer_editions_version_1803_updated_march_2018_x64_dvd_12063766.iso"));
|
||||
System.out.println(s1);
|
||||
long s2 = fileSize4Stream(new File("E:\\OS\\cn_windows_10_consumer_editions_version_1803_updated_march_2018_x64_dvd_12063766.iso"));
|
||||
System.out.println(s2);
|
||||
|
||||
String s1 = base64(new File("C:\\Users\\Administrator\\Desktop\\a.html"));
|
||||
System.out.println(s1);
|
||||
|
||||
String s = sha1(new File("C:\\Users\\Administrator\\Desktop\\a.html"));
|
||||
String s2 = sha1ByBigFile(new File("C:\\Users\\Administrator\\Desktop\\a.html"));
|
||||
System.out.println(s);
|
||||
System.out.println(s2);
|
||||
|
||||
|
||||
String md5 = md5(new File("C:\\Users\\Administrator\\Desktop\\a.html"));
|
||||
String md52 = md5ByBigFile(new File("C:\\Users\\Administrator\\Desktop\\a.html"));
|
||||
System.out.println(md5);
|
||||
System.out.println(md52);
|
||||
|
||||
|
||||
String crc32 = crc32(new File("C:\\Users\\Administrator\\Desktop\\a.html"));
|
||||
System.out.println(crc32);
|
||||
}*/
|
||||
|
||||
@@ -5,10 +5,10 @@ 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.Temporal;
|
||||
import java.time.temporal.TemporalAdjuster;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.Date;
|
||||
@@ -168,9 +168,7 @@ 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);
|
||||
}
|
||||
|
||||
@@ -207,8 +205,7 @@ 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);
|
||||
}
|
||||
|
||||
@@ -221,8 +218,7 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,8 +230,7 @@ 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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,8 +242,7 @@ 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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,8 +309,7 @@ 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"));
|
||||
@@ -330,8 +323,7 @@ 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();
|
||||
}
|
||||
|
||||
@@ -343,49 +335,10 @@ 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();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 格式化时间 <br/>
|
||||
* 格式 yyyy-MM-dd HH:mm:ss
|
||||
*
|
||||
* @param dateTime
|
||||
* @return
|
||||
*/
|
||||
public static String format(LocalDate dateTime) {
|
||||
return format(dateTime, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间
|
||||
*
|
||||
* @param dateTime
|
||||
* @param pattern 格式 默认:yyyy-MM-dd
|
||||
* @return
|
||||
*/
|
||||
public static String format(LocalDate dateTime, String pattern) {
|
||||
if (StrUtil.isEmpty(pattern)) {
|
||||
pattern = DateConsts.DATE_PATTERN;
|
||||
}
|
||||
DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
|
||||
return df.format(dateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间 <br/>
|
||||
* 格式 yyyy-MM-dd HH:mm:ss
|
||||
*
|
||||
* @param dateTime
|
||||
* @return
|
||||
*/
|
||||
public static String format(LocalDateTime dateTime) {
|
||||
return format(dateTime, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间
|
||||
*
|
||||
@@ -393,13 +346,20 @@ public class DateTimeUtil {
|
||||
* @param pattern 格式 默认:yyyy-MM-dd HH:mm:ss
|
||||
* @return
|
||||
*/
|
||||
public static String format(LocalDateTime dateTime, String pattern) {
|
||||
public static String format(Temporal dateTime, String pattern) {
|
||||
if (StrUtil.isEmpty(pattern)) {
|
||||
pattern = DateConsts.DATE_TIME_PATTERN;
|
||||
if (dateTime instanceof LocalDate) {
|
||||
// yyyy-MM-dd
|
||||
pattern = DateConsts.DATE_PATTERN;
|
||||
} else {
|
||||
// yyyy-MM-dd HH:mm:ss
|
||||
pattern = DateConsts.DATE_TIME_PATTERN;
|
||||
}
|
||||
}
|
||||
DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
|
||||
return df.format(dateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间 <br/>
|
||||
* 格式 yyyy-MM-dd HH:mm:ss
|
||||
@@ -407,25 +367,10 @@ public class DateTimeUtil {
|
||||
* @param dateTime
|
||||
* @return
|
||||
*/
|
||||
public static String format(OffsetDateTime dateTime) {
|
||||
public static String format(Temporal 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取UTC(格林威治,标准)时间
|
||||
*
|
||||
@@ -464,9 +409,7 @@ 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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -475,7 +418,7 @@ public class DateTimeUtil {
|
||||
* @return 2019-05-28T12:12:12+08:00
|
||||
*/
|
||||
public static String formatIso8601BySystemZone() {
|
||||
return DateTimeUtil.format(OffsetDateTime.now(), DateConsts.ISO_8601_PATTERN);
|
||||
return DateTimeUtil.format(ZonedDateTime.now(), DateConsts.ISO_8601_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -485,7 +428,8 @@ public class DateTimeUtil {
|
||||
* @return 2019-05-28T12:12:12+08:00
|
||||
*/
|
||||
public static String formatIso8601BySystemZone(LocalDateTime localDateTime) {
|
||||
return DateTimeUtil.format(localDateTime, DateConsts.ISO_8601_PATTERN);
|
||||
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
|
||||
return DateTimeUtil.format(zonedDateTime, DateConsts.ISO_8601_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -499,8 +443,15 @@ public class DateTimeUtil {
|
||||
return DateTimeUtil.format(parseLocalDateTime13(timestamp, zoneId), DateConsts.ISO_8601_PATTERN);
|
||||
}
|
||||
|
||||
|
||||
/*public static void main(String[] args) throws ParseException {
|
||||
System.out.println(format(LocalDate.now(), DateConsts.DATE_PATTERN));
|
||||
System.out.println(format(LocalDateTime.now(), DateConsts.DATE_TIME_MS_PATTERN));
|
||||
System.out.println(format(OffsetDateTime.now(), DateConsts.ISO_8601_MS_PATTERN));
|
||||
System.out.println(format(ZonedDateTime.now(), DateConsts.ISO_8601_MS_PATTERN));
|
||||
System.out.println(format(LocalDate.now()));
|
||||
System.out.println(format(LocalDateTime.now()));
|
||||
System.out.println(format(OffsetDateTime.now()));
|
||||
System.out.println(format(ZonedDateTime.now()));
|
||||
System.out.println(parserUTC(1684140338161L));
|
||||
System.out.println(convertUTC(LocalDateTime.now()));
|
||||
Date date = DateUtil.str2dateTime("2023-05-15 08:28:05.327");
|
||||
|
||||
@@ -8,6 +8,8 @@ import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.yexuejc.base.constant.DateConsts;
|
||||
|
||||
/**
|
||||
* java.util.Date 时间工具类
|
||||
*
|
||||
@@ -166,6 +168,17 @@ public class DateUtil {
|
||||
return sdf.parse(dateStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间 <br/>
|
||||
* 格式 yyyy-MM-dd HH:mm:ss
|
||||
*
|
||||
* @param dateTime
|
||||
* @return
|
||||
*/
|
||||
public static String formatDate(Date dateTime) {
|
||||
return formatDate(dateTime, DateConsts.DATE_TIME_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的日期格式和地域格式化日期对象为字符串。
|
||||
* 如果未指定地域,则默认使用中文地域格式。
|
||||
|
||||
@@ -1,55 +1,207 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
import com.yexuejc.base.exception.BaseException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
/**
|
||||
* AES加密解密工具类测试
|
||||
*/
|
||||
class AESTest {
|
||||
|
||||
|
||||
public class AESTest {
|
||||
private static final String TEST_KEY = "1234567890123456"; // 16位密钥
|
||||
private static final String TEST_IV = "abcdefghijklmnop"; // 16位初始向量
|
||||
|
||||
@Test
|
||||
public void testEncrypt() throws Exception {
|
||||
String data = "Hello World!";
|
||||
void testBuilder() {
|
||||
AES aes = AES.builder();
|
||||
assertNotNull(aes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptAndDecrypt() throws BaseException {
|
||||
AES aes = AES.builder()
|
||||
.setKey(TEST_KEY)
|
||||
.setIv(TEST_IV);
|
||||
|
||||
String plainText = "Hello, World!";
|
||||
String encrypted = aes.encrypt(plainText);
|
||||
String decrypted = aes.decrypt(encrypted);
|
||||
|
||||
assertNotEquals(plainText, encrypted);
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDifferentAlgorithms() throws BaseException {
|
||||
String plainText = "This is a test message for different algorithms.";
|
||||
|
||||
// 测试CBC模式
|
||||
AES aesCbc = AES.builder()
|
||||
.setKey(TEST_KEY)
|
||||
.setIv(TEST_IV)
|
||||
.setAlgorithm(AES.ALGORITHM.AES_CBC_PKCS5Padding);
|
||||
|
||||
String encryptedCbc = aesCbc.encrypt(plainText);
|
||||
String decryptedCbc = aesCbc.decrypt(encryptedCbc);
|
||||
assertEquals(plainText, decryptedCbc);
|
||||
|
||||
// 测试ECB模式 (不需要IV)
|
||||
AES aesEcb = AES.builder()
|
||||
.setKey(TEST_KEY)
|
||||
.setAlgorithm(AES.ALGORITHM.AES_ECB_PKCS5Padding);
|
||||
|
||||
String encryptedEcb = aesEcb.encrypt(plainText);
|
||||
String decryptedEcb = aesEcb.decrypt(encryptedEcb);
|
||||
assertEquals(plainText, decryptedEcb);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetKeyWithInvalidLength() {
|
||||
AES aes = AES.builder();
|
||||
|
||||
// 测试空密钥
|
||||
IllegalArgumentException thrown1 = assertThrows(IllegalArgumentException.class, () -> aes.setKey(null));
|
||||
assertEquals("AES密钥不能为空", thrown1.getMessage());
|
||||
|
||||
// 测试长度不足的密钥
|
||||
IllegalArgumentException thrown2 = assertThrows(IllegalArgumentException.class, () -> aes.setKey("12345"));
|
||||
assertEquals("AES密钥长度必须是16、24或32字节", thrown2.getMessage());
|
||||
|
||||
// 测试长度正确的密钥
|
||||
assertDoesNotThrow(() -> aes.setKey(TEST_KEY));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetIvWithInvalidLength() {
|
||||
AES aes = AES.builder();
|
||||
|
||||
// 测试空IV
|
||||
IllegalArgumentException thrown1 = assertThrows(IllegalArgumentException.class, () -> aes.setIv(null));
|
||||
assertEquals("AES初始向量不能为空", thrown1.getMessage());
|
||||
|
||||
// 测试长度不足的IV
|
||||
IllegalArgumentException thrown2 = assertThrows(IllegalArgumentException.class, () -> aes.setIv("12345"));
|
||||
assertEquals("AES初始向量长度必须是16字节", thrown2.getMessage());
|
||||
|
||||
// 测试长度正确的IV
|
||||
assertDoesNotThrow(() -> aes.setIv(TEST_IV));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptWithoutSettingKey() {
|
||||
AES aes = AES.builder();
|
||||
|
||||
IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> aes.encrypt("test"));
|
||||
assertEquals("AES密钥未设置,请调用setKey()方法设置密钥", thrown.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptWithoutSettingIv() {
|
||||
AES aes = AES.builder()
|
||||
.setKey(TEST_KEY);
|
||||
|
||||
IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> aes.encrypt("test"));
|
||||
assertEquals("AES初始向量未设置,请调用setIv()方法设置IV", thrown.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptWithEmptyData() {
|
||||
AES aes = AES.builder()
|
||||
.setKey(TEST_KEY)
|
||||
.setIv(TEST_IV);
|
||||
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> aes.encrypt(""));
|
||||
assertEquals("加密数据不能为空", thrown.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecryptWithEmptyData() {
|
||||
AES aes = AES.builder()
|
||||
.setKey(TEST_KEY)
|
||||
.setIv(TEST_IV);
|
||||
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> aes.decrypt(""));
|
||||
assertEquals("解密数据不能为空", thrown.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetCharset() {
|
||||
AES aes = AES.builder();
|
||||
|
||||
// 测试正常设置字符集
|
||||
assertDoesNotThrow(() -> aes.setCharset(StandardCharsets.UTF_8));
|
||||
|
||||
// 测试设置null字符集
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> aes.setCharset(null));
|
||||
assertEquals("字符集不能为空", thrown.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGettersAndSetters() {
|
||||
AES aes = AES.builder();
|
||||
|
||||
// 测试算法设置和获取
|
||||
aes.setAlgorithm(AES.ALGORITHM.AES_CBC_NoPadding);
|
||||
assertEquals(AES.ALGORITHM.AES_CBC_NoPadding, aes.getAlgorithm());
|
||||
|
||||
// 测试密钥设置和获取
|
||||
aes.setKey(TEST_KEY);
|
||||
assertEquals(TEST_KEY, aes.getKey());
|
||||
|
||||
// 测试IV设置和获取
|
||||
aes.setIv(TEST_IV);
|
||||
assertEquals(TEST_IV, aes.getIv());
|
||||
|
||||
// 测试字符集获取
|
||||
assertEquals(StandardCharsets.UTF_8, aes.getCharset());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testChainCalls() {
|
||||
AES aes = AES.builder()
|
||||
.setKey(TEST_KEY)
|
||||
.setIv(TEST_IV)
|
||||
.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());
|
||||
|
||||
assertNotNull(aes.getKey());
|
||||
assertNotNull(aes.getIv());
|
||||
assertNotNull(aes.getAlgorithm());
|
||||
assertNotNull(aes.getCharset());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecrypt() throws Exception {
|
||||
String data = "p0x0vK5T6OOy69+p9cgI/9xfeoi/f0t6NO7HbLsUON4=";
|
||||
void testSpecialCharacters() throws BaseException {
|
||||
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);
|
||||
.setKey(TEST_KEY)
|
||||
.setIv(TEST_IV);
|
||||
|
||||
String plainText = "特殊字符测试: !@#$%^&*()_+-={}[]|\\:\";'<>?,./ 中文测试";
|
||||
String encrypted = aes.encrypt(plainText);
|
||||
String decrypted = aes.decrypt(encrypted);
|
||||
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptAndDecrypt() throws Exception {
|
||||
String data = "张三";
|
||||
void testLongText() throws BaseException {
|
||||
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);
|
||||
}
|
||||
.setKey(TEST_KEY)
|
||||
.setIv(TEST_IV);
|
||||
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
sb.append("这是一个用于测试长文本加密解密的文本。");
|
||||
}
|
||||
String plainText = sb.toString();
|
||||
|
||||
String encrypted = aes.encrypt(plainText);
|
||||
String decrypted = aes.decrypt(encrypted);
|
||||
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
}
|
||||
187
src/test/java/com/yexuejc/base/encrypt/DES3NewTest.java
Normal file
187
src/test/java/com/yexuejc/base/encrypt/DES3NewTest.java
Normal file
@@ -0,0 +1,187 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
import com.yexuejc.base.exception.BaseException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* DES3加密解密工具类测试(新版本)
|
||||
*/
|
||||
class DES3NewTest {
|
||||
|
||||
private static final String VALID_KEY = "123456789012345678901234"; // 24位有效密钥
|
||||
private static final String INVALID_KEY = "123456"; // 无效密钥,长度不足
|
||||
|
||||
@Test
|
||||
void testEncryptDesCbcWithValidKey() throws BaseException {
|
||||
String plainText = "Hello, World!";
|
||||
String encrypted = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
String decrypted = DES3.decryptDesCbc(encrypted, VALID_KEY);
|
||||
|
||||
assertNotNull(encrypted);
|
||||
assertNotEquals(plainText, encrypted); // 确保加密后的内容与原文不同
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptAndDecryptChineseCharacters() throws BaseException {
|
||||
String plainText = "你好,世界!";
|
||||
String encrypted = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
String decrypted = DES3.decryptDesCbc(encrypted, VALID_KEY);
|
||||
|
||||
assertNotNull(encrypted);
|
||||
assertNotEquals(plainText, encrypted);
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptAndDecryptSpecialCharacters() throws BaseException {
|
||||
String plainText = "!@#$%^&*()_+-=[]{}|;':\",./<>?";
|
||||
String encrypted = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
String decrypted = DES3.decryptDesCbc(encrypted, VALID_KEY);
|
||||
|
||||
assertNotNull(encrypted);
|
||||
assertNotEquals(plainText, encrypted);
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptDesCbcWithNullKey() {
|
||||
String plainText = "Hello, World!";
|
||||
|
||||
BaseException exception = assertThrows(BaseException.class, () -> {
|
||||
DES3.encryptDesCbc(plainText, null);
|
||||
});
|
||||
|
||||
assertEquals("INVALID_KEY_LENGTH", exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptDesCbcWithInvalidKey() {
|
||||
String plainText = "Hello, World!";
|
||||
|
||||
BaseException exception = assertThrows(BaseException.class, () -> {
|
||||
DES3.encryptDesCbc(plainText, INVALID_KEY);
|
||||
});
|
||||
|
||||
assertEquals("INVALID_KEY_LENGTH", exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecryptDesCbcWithNullKey() {
|
||||
String encryptedText = "someEncryptedText";
|
||||
|
||||
BaseException exception = assertThrows(BaseException.class, () -> {
|
||||
DES3.decryptDesCbc(encryptedText, null);
|
||||
});
|
||||
|
||||
assertEquals("INVALID_KEY_LENGTH", exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecryptDesCbcWithInvalidKey() {
|
||||
String encryptedText = "someEncryptedText";
|
||||
|
||||
BaseException exception = assertThrows(BaseException.class, () -> {
|
||||
DES3.decryptDesCbc(encryptedText, INVALID_KEY);
|
||||
});
|
||||
|
||||
assertEquals("INVALID_KEY_LENGTH", exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecryptWithInvalidEncryptedData() {
|
||||
String invalidEncryptedText = "invalidBase64String!";
|
||||
|
||||
BaseException exception = assertThrows(BaseException.class, () -> {
|
||||
DES3.decryptDesCbc(invalidEncryptedText, VALID_KEY);
|
||||
});
|
||||
|
||||
assertEquals("DECRYPTION_PARAM_FAILED", exception.getErrorCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptAndDecryptEmptyString() throws BaseException {
|
||||
String plainText = "";
|
||||
String encrypted = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
String decrypted = DES3.decryptDesCbc(encrypted, VALID_KEY);
|
||||
|
||||
assertNotNull(encrypted);
|
||||
assertNotEquals(plainText, encrypted);
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptAndDecryptLongText() throws BaseException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
sb.append("这是用于测试长文本加密解密的文本。");
|
||||
}
|
||||
String plainText = sb.toString();
|
||||
|
||||
String encrypted = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
String decrypted = DES3.decryptDesCbc(encrypted, VALID_KEY);
|
||||
|
||||
assertNotNull(encrypted);
|
||||
assertNotEquals(plainText, encrypted);
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPaddingFunction() {
|
||||
// 测试长度为7的字符串(需要填充1个字节)
|
||||
String input1 = "1234567";
|
||||
String padded1 = DES3.padding(input1);
|
||||
assertEquals(8, padded1.length());
|
||||
assertTrue(padded1.startsWith("1234567"));
|
||||
|
||||
// 测试长度为8的字符串(不需要填充)
|
||||
String input2 = "12345678";
|
||||
String padded2 = DES3.padding(input2);
|
||||
assertEquals(8, padded2.length()); // 长度已经是8的倍数,不需要额外填充
|
||||
assertTrue(padded2.startsWith("12345678"));
|
||||
|
||||
// 测试长度为9的字符串(需要填充7个字节)
|
||||
String input3 = "123456789";
|
||||
String padded3 = DES3.padding(input3);
|
||||
assertEquals(16, padded3.length());
|
||||
assertTrue(padded3.startsWith("123456789"));
|
||||
|
||||
// 测试空字符串(需要填充8个字节)
|
||||
String input4 = "";
|
||||
String padded4 = DES3.padding(input4);
|
||||
assertEquals(8, padded4.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDifferentEncryptionsProduceDifferentResults() throws BaseException {
|
||||
String plainText = "consistent test message";
|
||||
String encrypted1 = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
String encrypted2 = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
|
||||
// 同样的明文和密钥应该产生不同的密文(因为使用随机IV)
|
||||
assertNotEquals(encrypted1, encrypted2);
|
||||
|
||||
// 但是解密后应该能得到相同原文
|
||||
String decrypted1 = DES3.decryptDesCbc(encrypted1, VALID_KEY);
|
||||
String decrypted2 = DES3.decryptDesCbc(encrypted2, VALID_KEY);
|
||||
assertEquals(plainText, decrypted1);
|
||||
assertEquals(plainText, decrypted2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMinimumKeyLengthRequirement() {
|
||||
// 测试23位密钥(不够24位)
|
||||
String key23 = "12345678901234567890123";
|
||||
BaseException exception23 = assertThrows(BaseException.class, () -> {
|
||||
DES3.encryptDesCbc("test", key23);
|
||||
});
|
||||
assertEquals("INVALID_KEY_LENGTH", exception23.getErrorCode());
|
||||
|
||||
// 测试正好24位密钥(应该正常工作)
|
||||
assertDoesNotThrow(() -> {
|
||||
DES3.encryptDesCbc("test", VALID_KEY);
|
||||
});
|
||||
}
|
||||
}
|
||||
141
src/test/java/com/yexuejc/base/encrypt/DES3Test.java
Normal file
141
src/test/java/com/yexuejc/base/encrypt/DES3Test.java
Normal file
@@ -0,0 +1,141 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
import com.yexuejc.base.exception.BaseException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* DES3加密解密工具类测试
|
||||
*/
|
||||
class DES3Test {
|
||||
|
||||
private static final String VALID_KEY = "123456789012345678901234"; // 24位有效密钥
|
||||
private static final String INVALID_KEY = "123456"; // 无效密钥,长度不足
|
||||
|
||||
@Test
|
||||
void testEncryptDesCbcWithValidKey() throws BaseException {
|
||||
String plainText = "Hello, World!";
|
||||
String encrypted = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
String decrypted = DES3.decryptDesCbc(encrypted, VALID_KEY);
|
||||
|
||||
assertNotNull(encrypted);
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptAndDecryptChineseCharacters() throws BaseException {
|
||||
String plainText = "你好,世界!";
|
||||
String encrypted = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
String decrypted = DES3.decryptDesCbc(encrypted, VALID_KEY);
|
||||
|
||||
assertNotNull(encrypted);
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptAndDecryptSpecialCharacters() throws BaseException {
|
||||
String plainText = "!@#$%^&*()_+-=[]{}|;':\",./<>?";
|
||||
String encrypted = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
String decrypted = DES3.decryptDesCbc(encrypted, VALID_KEY);
|
||||
|
||||
assertNotNull(encrypted);
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptDesCbcWithInvalidKey() {
|
||||
String plainText = "Hello, World!";
|
||||
|
||||
BaseException exception = assertThrows(BaseException.class, () -> {
|
||||
DES3.encryptDesCbc(plainText, INVALID_KEY);
|
||||
});
|
||||
|
||||
assertEquals("INVALID_KEY_LENGTH:key的length不得小于24。", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecryptDesCbcWithInvalidKey() {
|
||||
String encryptedText = "someEncryptedText";
|
||||
|
||||
BaseException exception = assertThrows(BaseException.class, () -> {
|
||||
DES3.decryptDesCbc(encryptedText, INVALID_KEY);
|
||||
});
|
||||
|
||||
assertEquals("INVALID_KEY_LENGTH:key的length不得小于24。", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDecryptWithInvalidEncryptedData() {
|
||||
String invalidEncryptedText = "invalidBase64String!";
|
||||
|
||||
BaseException exception = assertThrows(BaseException.class, () -> {
|
||||
DES3.decryptDesCbc(invalidEncryptedText, VALID_KEY);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptAndDecryptEmptyString() throws BaseException {
|
||||
String plainText = "";
|
||||
String encrypted = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
String decrypted = DES3.decryptDesCbc(encrypted, VALID_KEY);
|
||||
|
||||
assertNotNull(encrypted);
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEncryptAndDecryptLongText() throws BaseException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
sb.append("这是用于测试长文本加密解密的文本。");
|
||||
}
|
||||
String plainText = sb.toString();
|
||||
|
||||
String encrypted = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
String decrypted = DES3.decryptDesCbc(encrypted, VALID_KEY);
|
||||
|
||||
assertNotNull(encrypted);
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPaddingFunction() {
|
||||
// 测试长度为7的字符串(需要填充1个字节)
|
||||
String input1 = "1234567";
|
||||
String padded1 = DES3.padding(input1);
|
||||
assertEquals(8, padded1.length());
|
||||
assertTrue(padded1.startsWith("1234567"));
|
||||
|
||||
// 测试长度为8的字符串(不需要填充)
|
||||
String input2 = "12345678";
|
||||
String padded2 = DES3.padding(input2);
|
||||
assertEquals(8, padded2.length());
|
||||
assertTrue(padded2.startsWith("12345678"));
|
||||
|
||||
// 测试长度为9的字符串(需要填充7个字节)
|
||||
String input3 = "123456789";
|
||||
String padded3 = DES3.padding(input3);
|
||||
assertEquals(16, padded3.length());
|
||||
assertTrue(padded3.startsWith("123456789"));
|
||||
|
||||
// 测试空字符串(需要填充8个字节)
|
||||
String input4 = "";
|
||||
String padded4 = DES3.padding(input4);
|
||||
assertEquals(0, padded4.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConsistentEncryption() throws BaseException {
|
||||
String plainText = "consistent test message";
|
||||
String encrypted1 = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
String encrypted2 = DES3.encryptDesCbc(plainText, VALID_KEY);
|
||||
assertNotEquals(encrypted1, encrypted2);
|
||||
|
||||
// 解密应该能得到原文
|
||||
String decrypted = DES3.decryptDesCbc(encrypted1, VALID_KEY);
|
||||
String decrypted2 = DES3.decryptDesCbc(encrypted2, VALID_KEY);
|
||||
assertEquals(plainText, decrypted);
|
||||
assertEquals(plainText, decrypted2);
|
||||
}
|
||||
}
|
||||
23
src/test/java/com/yexuejc/base/encrypt/PaddingDebugTest.java
Normal file
23
src/test/java/com/yexuejc/base/encrypt/PaddingDebugTest.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
public class PaddingDebugTest {
|
||||
public static void main(String[] args) {
|
||||
// 测试padding函数的行为
|
||||
System.out.println("Testing padding function:");
|
||||
|
||||
String input1 = "12345678";
|
||||
String padded1 = DES3.padding(input1);
|
||||
System.out.println("Input: '" + input1 + "' Length: " + input1.length());
|
||||
System.out.println("Output: '" + padded1 + "' Length: " + padded1.length());
|
||||
|
||||
String input2 = "1234567";
|
||||
String padded2 = DES3.padding(input2);
|
||||
System.out.println("Input: '" + input2 + "' Length: " + input2.length());
|
||||
System.out.println("Output: '" + padded2 + "' Length: " + padded2.length());
|
||||
|
||||
String input3 = "";
|
||||
String padded3 = DES3.padding(input3);
|
||||
System.out.println("Input: '" + input3 + "' Length: " + input3.length());
|
||||
System.out.println("Output: '" + padded3 + "' Length: " + padded3.length());
|
||||
}
|
||||
}
|
||||
179
src/test/java/com/yexuejc/base/encrypt/RSA2Test.java
Normal file
179
src/test/java/com/yexuejc/base/encrypt/RSA2Test.java
Normal file
@@ -0,0 +1,179 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
import com.yexuejc.base.exception.BaseException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* RSA2类的单元测试
|
||||
*/
|
||||
class RSA2Test {
|
||||
|
||||
/**
|
||||
* 测试获取公钥的方法 - 正常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldGenerateAndReadPublicKeySuccessfully() throws Exception {
|
||||
// 生成测试用的密钥对
|
||||
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
|
||||
generator.initialize(1024);
|
||||
KeyPair keyPair = generator.generateKeyPair();
|
||||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
||||
|
||||
// 注意:由于RSA2类依赖于实际的证书文件,
|
||||
// 我们只能测试一些基本的逻辑,而不能完全测试所有方法
|
||||
assertNotNull(publicKey);
|
||||
assertNotNull(privateKey);
|
||||
assertNotNull(publicKey.getModulus());
|
||||
assertNotNull(privateKey.getModulus());
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试获取私钥的方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowBaseExceptionWhenReadingNonExistentPrivateKey() {
|
||||
assertThrows(BaseException.class, () -> {
|
||||
RSA2.getPrivateKey("non-existent-file.pem", "alias", "password");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试获取公钥的方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowCertificateExceptionWhenReadingNonExistentPublicKey() {
|
||||
assertThrows(CertificateException.class, () -> {
|
||||
RSA2.getPublicKey("non-existent-file.crt");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试从PKCS12获取私钥的方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowBaseExceptionWhenReadingNonExistentPrivateKeyFromPKCS12() {
|
||||
assertThrows(BaseException.class, () -> {
|
||||
RSA2.getPrivateKeyFromPKCS12("non-existent-file.pfx", "alias", "password");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试从PKCS8获取私钥的方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowBaseExceptionWhenReadingNonExistentPrivateKeyFromPKCS8() {
|
||||
assertThrows(BaseException.class, () -> {
|
||||
RSA2.getPrivateKeyFromPKCS8("non-existent-file.pem", "password");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试从PKCS1获取私钥的方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowBaseExceptionWhenReadingNonExistentPrivateKeyFromPKCS1() {
|
||||
assertThrows(BaseException.class, () -> {
|
||||
RSA2.getPrivateKeyFromPKCS1("non-existent-file.pem");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试获取JKS私钥的方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowBaseExceptionWhenReadingNonExistentPrivateKeyFromJKS() {
|
||||
assertThrows(BaseException.class, () -> {
|
||||
RSA2.getPrivateKey("non-existent-file.jks", "alias", "password", "JKS");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试证书格式转换方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowBaseExceptionWhenConvertingNonExistentCertificate() {
|
||||
assertThrows(BaseException.class, () -> {
|
||||
RSA2.cover2Pfx("non-existent-file.jks", "output.pfx", "password", "password");
|
||||
});
|
||||
|
||||
assertThrows(BaseException.class, () -> {
|
||||
RSA2.cover2keyStore("non-existent-file.pfx", "output.jks", "password", "password");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试从输入流获取公钥的方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowCertificateExceptionWhenReadingPublicKeyFromInvalidInputStream() {
|
||||
InputStream invalidInputStream = new ByteArrayInputStream("invalid certificate data".getBytes());
|
||||
assertThrows(CertificateException.class, () -> {
|
||||
RSA2.getPublicKey(invalidInputStream);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试从PKCS12输入流获取公钥的方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowBaseExceptionWhenReadingPublicKeyFromInvalidPKCS12InputStream() {
|
||||
InputStream invalidInputStream = new ByteArrayInputStream("invalid certificate data".getBytes());
|
||||
assertThrows(BaseException.class, () -> {
|
||||
RSA2.getPublicKeyFromPKCS12(invalidInputStream, "alias", "password");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试从输入流获取私钥的方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowBaseExceptionWhenReadingPrivateKeyFromInvalidInputStream() {
|
||||
InputStream invalidInputStream = new ByteArrayInputStream("invalid private key data".getBytes());
|
||||
assertThrows(BaseException.class, () -> {
|
||||
RSA2.getPrivateKey(invalidInputStream, "alias", "password", "JKS");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试从PKCS12输入流获取私钥的方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowBaseExceptionWhenReadingPrivateKeyFromInvalidPKCS12InputStream() {
|
||||
InputStream invalidInputStream = new ByteArrayInputStream("invalid private key data".getBytes());
|
||||
assertThrows(BaseException.class, () -> {
|
||||
RSA2.getPrivateKeyFromPKCS12(invalidInputStream, "alias", "password");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试从PKCS8输入流获取私钥的方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowBaseExceptionWhenReadingPrivateKeyFromInvalidPKCS8InputStream() {
|
||||
InputStream invalidInputStream = new ByteArrayInputStream("invalid private key data".getBytes());
|
||||
assertThrows(BaseException.class, () -> {
|
||||
RSA2.getPrivateKeyFromPKCS8(invalidInputStream, "password");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试从PKCS1输入流获取私钥的方法 - 异常情况
|
||||
*/
|
||||
@Test
|
||||
void shouldThrowBaseExceptionWhenReadingPrivateKeyFromInvalidPKCS1InputStream() {
|
||||
InputStream invalidInputStream = new ByteArrayInputStream("invalid private key data".getBytes());
|
||||
assertThrows(BaseException.class, () -> {
|
||||
RSA2.getPrivateKeyFromPKCS1(invalidInputStream);
|
||||
});
|
||||
}
|
||||
}
|
||||
180
src/test/java/com/yexuejc/base/encrypt/RSATestNew.java
Normal file
180
src/test/java/com/yexuejc/base/encrypt/RSATestNew.java
Normal file
@@ -0,0 +1,180 @@
|
||||
package com.yexuejc.base.encrypt;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* RSA类的单元测试
|
||||
*/
|
||||
class RSATestNew {
|
||||
|
||||
private RSA rsa;
|
||||
private RSAPublicKey publicKey;
|
||||
private RSAPrivateKey privateKey;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
rsa = RSA.builder();
|
||||
|
||||
// 生成测试用的密钥对
|
||||
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
|
||||
generator.initialize(1024);
|
||||
KeyPair keyPair = generator.generateKeyPair();
|
||||
publicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
privateKey = (RSAPrivateKey) keyPair.getPrivate();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGenerateValidKeyMapWithDefaultSettings() {
|
||||
Map<String, String> keys = rsa.initKeys(1024);
|
||||
|
||||
assertNotNull(keys);
|
||||
assertTrue(keys.containsKey("publicKey"));
|
||||
assertTrue(keys.containsKey("privateKey"));
|
||||
|
||||
assertFalse(keys.get("publicKey").isEmpty());
|
||||
assertFalse(keys.get("privateKey").isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGenerateValidKeyMapWithBase64UrlSafeOption() {
|
||||
Map<String, String> keys = rsa.initKeys(1024, true);
|
||||
|
||||
assertNotNull(keys);
|
||||
assertTrue(keys.containsKey("publicKey"));
|
||||
assertTrue(keys.containsKey("privateKey"));
|
||||
|
||||
assertFalse(keys.get("publicKey").isEmpty());
|
||||
assertFalse(keys.get("privateKey").isEmpty());
|
||||
|
||||
// Base64URL安全字符串不应该包含+和/
|
||||
assertFalse(keys.get("publicKey").contains("+") || keys.get("publicKey").contains("/"));
|
||||
assertFalse(keys.get("privateKey").contains("+") || keys.get("privateKey").contains("/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParsePublicKeyCorrectly() throws Exception {
|
||||
// 使用RSA类生成密钥对进行测试
|
||||
Map<String, String> keys = rsa.initKeys(1024);
|
||||
String pubStr = keys.get("publicKey");
|
||||
|
||||
RSAPublicKey parsedPub = rsa.getPublicKey(pubStr);
|
||||
|
||||
// 验证解析出来的公钥不为空
|
||||
assertNotNull(parsedPub);
|
||||
assertNotNull(parsedPub.getModulus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParsePrivateKeyCorrectly() throws Exception {
|
||||
// 使用RSA类生成密钥对进行测试
|
||||
Map<String, String> keys = rsa.initKeys(1024);
|
||||
String priStr = keys.get("privateKey");
|
||||
|
||||
RSAPrivateKey parsedPri = rsa.getPrivateKey(priStr);
|
||||
|
||||
// 验证解析出来的私钥不为空
|
||||
assertNotNull(parsedPri);
|
||||
assertNotNull(parsedPri.getModulus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEncryptAndDecryptSuccessfullyUsingPublicPrivate() throws Exception {
|
||||
String plainText = "Hello RSA!";
|
||||
String encrypted = rsa.publicEncrypt(plainText, publicKey);
|
||||
String decrypted = rsa.privateDecrypt(encrypted, privateKey);
|
||||
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEncryptAndDecryptSuccessfullyUsingPrivatePublic() throws Exception {
|
||||
String plainText = "Hello RSA Private->Public!";
|
||||
String encrypted = rsa.privateEncrypt(plainText, privateKey);
|
||||
String decrypted = rsa.publicDecrypt(encrypted, publicKey);
|
||||
|
||||
assertEquals(plainText, decrypted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSignAndVerifySuccessfully() throws Exception {
|
||||
String message = "This is a signed message.";
|
||||
String signature = rsa.sign(message, privateKey);
|
||||
boolean verified = rsa.verify(message, signature, publicKey);
|
||||
|
||||
assertTrue(verified);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailVerificationWhenMessageModified() throws Exception {
|
||||
String originalMsg = "Original Message";
|
||||
String signature = rsa.sign(originalMsg, privateKey);
|
||||
boolean result = rsa.verify("Modified Message", signature, publicKey);
|
||||
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateKeyFilesWithoutException() throws Exception {
|
||||
Path tempDir = Files.createTempDirectory("rsa-test");
|
||||
|
||||
assertDoesNotThrow(() -> rsa.initKey4File(tempDir.toString()));
|
||||
|
||||
assertTrue(Files.exists(tempDir.resolve("private.key")));
|
||||
assertTrue(Files.exists(tempDir.resolve("public.key")));
|
||||
}
|
||||
|
||||
// 注释掉需要Mockito的测试用例,因为项目中没有引入Mockito依赖
|
||||
/*
|
||||
@Test
|
||||
void shouldThrowBaseExceptionOnFileWriteFailure() throws IOException {
|
||||
try (MockedStatic<Files> mockedFiles = mockStatic(Files.class)) {
|
||||
mockedFiles.when(() -> Files.writeString(any(Path.class), any(CharSequence.class)))
|
||||
.thenThrow(new IOException("Simulated IO error"));
|
||||
|
||||
assertThrows(BaseException.class, () -> rsa.initKey4File("/tmp"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleApiStyleSigningAndVerifying() throws Exception {
|
||||
// 准备测试数据
|
||||
String uri = "/api/test";
|
||||
String body = "{\"id\":1}";
|
||||
|
||||
RequestHeader header = mock(RequestHeader.class);
|
||||
when(header.getClientId()).thenReturn("client123");
|
||||
when(header.getReqTime()).thenReturn("20230101T120000Z");
|
||||
when(header.geRespTime()).thenReturn("20230101T120000Z");
|
||||
when(header.getSignature()).thenReturn("SIGNATURE=abc123");
|
||||
|
||||
// 使用Mock来模拟RSA2的行为
|
||||
try (MockedStatic<RSA2> mockedRSA2 = mockStatic(RSA2.class)) {
|
||||
mockedRSA2.when(() -> RSA2.getPublicKey(anyString())).thenReturn(publicKey);
|
||||
mockedRSA2.when(() -> RSA2.getPrivateKeyFromPKCS1(anyString())).thenReturn(privateKey);
|
||||
|
||||
// 测试签名
|
||||
String signature = rsa.signByApi(uri, header, body, "/path/to/private.pem");
|
||||
|
||||
// 测试验证
|
||||
boolean verified = rsa.verifyByApi(uri, header, body, "/path/to/public.pem");
|
||||
|
||||
assertNotNull(signature);
|
||||
assertTrue(verified);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
Reference in New Issue
Block a user