
edi.rule.config.JSRuleSecurity Maven / Gradle / Ivy
package edi.rule.config;
import edi.rule.util.ZSString;
import edi.rule.work.custom.JSRuleException;
import edi.rule.work.enums.JSRuleSecurityEnum;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.function.BiFunction;
import java.util.function.Consumer;
/**
* @author 摩拉克斯
* >
*/
@Slf4j
public class JSRuleSecurity {
private static final String provider = "BC";
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
*
判断当前系统是否是安全模式,true是,false不是
* @param type 待判断的安全模式
* */
public static boolean isSecurityMode(JSRuleSecurityEnum type){
return JSRuleSecurityEnum.NONE != type;
}
/**
*
判断当前系统是否是安全模式,true是,false不是
* */
public static boolean isSecurityMode(){
return isSecurityMode(JSRuleContext.getSecurity());
}
/**
*
获取签名原文,后台配置的秘钥与文本进行组合
* @param plainText 文本
* @return 返回组合后的签名文本
* */
public static String joinSignKey(String plainText){
if (ZSString.isBlank(JSRuleContext.getProperties().security.signKey)){
return plainText;
}
return plainText+JSRuleContext.getProperties().security.signKey;
}
/*--------------------------------AES--------------------------------*/
public static String aesEncrypt(String plainText){
try {
return Base64.getEncoder().encodeToString(getAESCipher(Cipher.ENCRYPT_MODE).doFinal(plainText.getBytes(StandardCharsets.UTF_8)));
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new JSRuleException(e);
}
}
public static String aesDecrypt(String encryptText) {
try {
return new String(getAESCipher(Cipher.DECRYPT_MODE).doFinal(Base64.getDecoder().decode(encryptText)));
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new JSRuleException(e);
}
}
public static SecretKey aesGenerate(){
return AESParam.keyGenerator.generateKey();
}
public static String aesGenerateStr(){
return Base64.getEncoder().encodeToString(aesGenerate().getEncoded());
}
static void initAES(String secretKey,String vector){
AESParam.secretKey = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8),AESParam.keyInstance);
AESParam.ivSpec = new IvParameterSpec(vector.getBytes(StandardCharsets.UTF_8));
}
private static final class AESParam {
private static final String algorithm = "AES/CBC/PKCS5Padding";
private static final String keyInstance = "AES";
private static final KeyGenerator keyGenerator;
private static IvParameterSpec ivSpec;
private static SecretKeySpec secretKey;
static{
try {
keyGenerator = KeyGenerator.getInstance(keyInstance);
keyGenerator.init(128);
} catch (NoSuchAlgorithmException e) {
throw new JSRuleException(e);
}
}
}
private static Cipher getAESCipher(int mode){
try {
Cipher cipher = Cipher.getInstance(AESParam.algorithm);
cipher.init(mode,AESParam.secretKey,AESParam.ivSpec);
return cipher;
} catch (NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException e) {
throw new JSRuleException(e);
}
}
/*--------------------------------SM2--------------------------------*/
/**
*
公钥加密
* @param plainText 原文
* */
public static String sm2Encrypt(String plainText) {
return base64Encrypt(plainText,SM2Param.publicKey,getCipher(SM2Param.transformation),(d,c)->{
try {
return c.doFinal(d);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new JSRuleException(e);
}
});
}
/**
*
私钥解密
* @param encryptText 为公钥加密密文
* */
public static String sm2Decrypt(String encryptText) {
return base64Decrypt(encryptText,SM2Param.privateKey,getCipher(SM2Param.transformation),(d,c)->{
try {
return c.doFinal(d);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new JSRuleException(e);
}
});
}
/**
*
私钥签名
* @param text 文本
* */
public static String sm2Sign(String text) {
return base64Sign(joinSignKey(text),SM2Param.privateKey,SM2Param.algorithm);
}
public static String sm2Sign(String text, String signKey) {
return base64Sign(text+signKey,SM2Param.privateKey,SM2Param.algorithm);
}
/**
*
公钥验签,自动取后台配置的秘钥对原文文本进行组合,得到组合文本后再与签名后的密文进行比对验签
* @param text 待做校验的原文文本,可以是密文也可以不是
* @param signText 需要比对验证的签名文本(进行签名算法后得到的文本)
* @return true校验签名通过,false校验签名不通过
* */
public static boolean sm2Verify(String text,String signText) {
return base64Verify(joinSignKey(text),signText,SM2Param.publicKey,SM2Param.algorithm);
}
public static boolean sm2Verify(String text,String signKey,String signText) {
return base64Verify(text+signKey,signText,SM2Param.publicKey,SM2Param.algorithm);
}
/**
*
生成sm2秘钥对儿>
* */
public static KeyPair sm2Generate() {
try{
// 实例化KeyPairGenerator对象,并指定算法为EC(椭圆曲线),提供者为BC(Bouncy Castle)
KeyPairGenerator kpg = KeyPairGenerator.getInstance(SM2Param.keyInstance, provider);
//设置椭圆曲线参数为sm2p256v1,这是SM2算法所使用的特定椭圆曲线,初始化KeyPairGenerator对象,使用随机数生成器以增加密钥的随机性
kpg.initialize(new ECGenParameterSpec("sm2p256v1"), new SecureRandom());
return kpg.generateKeyPair();
} catch (Exception e) {
throw new JSRuleException(e);
}
}
/**
*
初始化SM2对象
* */
static void initSM2(String pubQKeyBase64,String prvDKeyBase64){
SM2Param.pubQKeyBase64 = pubQKeyBase64;
SM2Param.prvDKeyBase64 = prvDKeyBase64;
SM2Param.publicKey = initPublicKey(SM2Param.pubQKeyBase64,SM2Param.keyFactory);
SM2Param.privateKey = initPrivateKey(SM2Param.prvDKeyBase64,SM2Param.keyFactory);
}
private static final class SM2Param {
private static final String algorithm = "SM3withSM2";
private static final String transformation = "SM2";
private static final String keyInstance = "EC";
private static final KeyFactory keyFactory;
private static String pubQKeyBase64;
private static String prvDKeyBase64;
private static PublicKey publicKey;
private static PrivateKey privateKey;
static {
try {
keyFactory = KeyFactory.getInstance(SM2Param.keyInstance, new BouncyCastleProvider());
} catch (Exception e) {
throw new JSRuleException(e);
}
}
}
/*--------------------------------RSA--------------------------------*/
public static String rsaEncrypt(String plainText) {
return base64Encrypt(plainText,RSAParam.publicKey,getCipher(RSAParam.transformation), JSRuleSecurity::rsaEncryptSection);
}
public static String rsaDecrypt(String encryptText) {
return base64Decrypt(encryptText,RSAParam.privateKey,getCipher(RSAParam.transformation), JSRuleSecurity::rsaDecryptSection);
}
private static byte[] rsaEncryptSection(byte[] data,Cipher cipher){
return writeRsaSection(data,cipher,RSAParam.rsaEncryptSize);
}
private static byte[] rsaDecryptSection(byte[] data,Cipher cipher){
return writeRsaSection(data,cipher,RSAParam.rsaDecryptSize);
}
private static byte[] writeRsaSection(byte[] data,Cipher cipher,int size){
try(ByteArrayOutputStream out = new ByteArrayOutputStream()){
int length = data.length;int offSet = 0;byte[] cache;int i = 0;
while (length - offSet > 0) {
if (length - offSet > size) {
cache = cipher.doFinal(data, offSet, size);
} else {
cache = cipher.doFinal(data, offSet, length - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * size;
}
byte[] encryptedData = out.toByteArray();
out.close();
return encryptedData;
} catch (IOException | IllegalBlockSizeException | BadPaddingException e) {
throw new JSRuleException(e);
}
}
public static String rsaSign(String text) {
return base64Sign(joinSignKey(text),RSAParam.privateKey,RSAParam.algorithm);
}
public static String rsaSign(String text,String signKey) {
return base64Sign(text+signKey,RSAParam.privateKey,RSAParam.algorithm);
}
public static boolean rsaVerify(String text,String signText) {
return base64Verify(joinSignKey(text),signText,RSAParam.publicKey,RSAParam.algorithm);
}
public static boolean rsaVerify(String text,String signKey,String signText) {
return base64Verify(text+signKey,signText,RSAParam.publicKey,RSAParam.algorithm);
}
public static KeyPair rsaGenerate() {
try{
KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSAParam.keyInstance, provider);
kpg.initialize(RSAParam.rsaKeySize);
return kpg.generateKeyPair();
} catch (Exception e) {
throw new JSRuleException(e);
}
}
static void initRSA(String pubQKeyBase64,String prvDKeyBase64){
RSAParam.pubQKeyBase64 = pubQKeyBase64;
RSAParam.prvDKeyBase64 = prvDKeyBase64;
RSAParam.publicKey = initPublicKey(RSAParam.pubQKeyBase64,RSAParam.keyFactory);
RSAParam.privateKey = initPrivateKey(RSAParam.prvDKeyBase64,RSAParam.keyFactory);
}
private static final class RSAParam {
private static final String algorithm = "SHA256withRSA";
//PKCS1Padding通常用于非对称加密,PKCS5Padding通常用于对称加密
private static final String transformation = "RSA";
private static final String keyInstance = "RSA";
private static final KeyFactory keyFactory;
private static String pubQKeyBase64;
private static String prvDKeyBase64;
private static PublicKey publicKey;
private static PrivateKey privateKey;
private static final int rsaKeySize = JSRuleContext.getProperties().security.rsaKeySize;
private static final int rsaDecryptSize = rsaKeySize/8;
private static final int rsaEncryptSize = rsaDecryptSize-1;
static {
try {
keyFactory = KeyFactory.getInstance(RSAParam.keyInstance);
} catch (Exception e) {
throw new JSRuleException(e);
}
}
}
/*由于Cipher是有状态的,因此每次加解密都需要重新创建Cipher对象*/
private static Cipher getCipher(String transformation){
try {
return Cipher.getInstance(transformation,provider);
} catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
throw new JSRuleException(e);
}
}
private static String base64Encrypt(String plainText,Key key,Cipher cipher,BiFunction doFinal) {
return Base64.getEncoder().encodeToString(encrypt(plainText.getBytes(StandardCharsets.UTF_8),key,cipher,doFinal));
}
private static String base64Decrypt(String encryptText,Key key,Cipher cipher,BiFunction doFinal) {
return new String(decrypt(Base64.getDecoder().decode(encryptText),key,cipher,doFinal));
}
private static String base64Sign(String text,PrivateKey privateKey,String algorithm) {
return Base64.getEncoder().encodeToString(sign(text.getBytes(StandardCharsets.UTF_8),privateKey,algorithm));
}
private static boolean base64Verify(String text,String signText,PublicKey publicKey,String algorithm) {
return verify(text,Base64.getDecoder().decode(signText),publicKey,algorithm);
}
private static PublicKey initPublicKey(String pubQKeyBase64,KeyFactory keyFactory) {
try {
return keyFactory.generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(pubQKeyBase64)));
} catch (InvalidKeySpecException e) {
throw new JSRuleException(e);
}
}
private static PrivateKey initPrivateKey(String prvDKeyBase64,KeyFactory keyFactory) {
try {
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(prvDKeyBase64)));
} catch (InvalidKeySpecException e) {
throw new JSRuleException(e);
}
}
private static byte[] cipherFinal(byte[] data, int mode, Key key, Cipher cipher,BiFunction doFinal){
try{
cipher.init(mode, key);
}catch (Exception e){
throw new JSRuleException(e);
}
return doFinal.apply(data,cipher);
}
private static byte[] encrypt(byte[] data,Key key, Cipher cipher, BiFunction doFinal) {
return cipherFinal(data,Cipher.ENCRYPT_MODE, key,cipher,doFinal);
}
private static byte[] decrypt(byte[] encryptData,Key key,Cipher cipher, BiFunction doFinal) {
return cipherFinal(encryptData,Cipher.DECRYPT_MODE, key,cipher,doFinal);
}
private static byte[] sign(byte[] data,PrivateKey privateKey,String algorithm) {
try {
return getSignature(data,signature->{
try {
signature.initSign(privateKey);
} catch (InvalidKeyException e) {
throw new JSRuleException(e);
}
},algorithm).sign();
} catch (SignatureException e) {
throw new JSRuleException(e);
}
}
private static boolean verify(String data, byte[] signatureBytes,PublicKey publicKey,String algorithm) {
try {
return getSignature(data.getBytes(StandardCharsets.UTF_8),signature->{
try {
signature.initVerify(publicKey);
} catch (InvalidKeyException e) {
throw new JSRuleException(e);
}
},algorithm).verify(signatureBytes);
} catch (SignatureException e) {
throw new JSRuleException(e.getMessage());
}
}
private static Signature getSignature(byte[] data, Consumer behavior,String algorithm) {
try{
// 初始化签名对象,指定使用SM3withSM2算法和BC提供者。
Signature signature = Signature.getInstance(algorithm, provider);
behavior.accept(signature);
signature.update(data);
return signature;
} catch (Exception e) {
throw new JSRuleException(e.getMessage());
}
}
}