YanFrp/src/main/java/top/octopusyan/utils/EncryptionUtil.java

480 lines
16 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package top.octopusyan.utils;
import cn.hutool.crypto.ProviderFactory;
import cn.hutool.crypto.SecureUtil;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
/**
* @author : octopus yan
* @email : octopus_yan@foxmail.com
* @description : 常用加密工具类
* @create : 2021-03-18 19:03
*/
public class EncryptionUtil {
private static final String defaultPassword = "yanfrp";
private static Logger logger = LoggerFactory.getLogger(EncryptionUtil.class);
private static BASE64Encoder BASE64Encoder;
private static MessageDigest MessageDigest_SHA1;
private static MessageDigest MessageDigest_MD5;
private static Cipher Cipher_ECB_MOB;
private static Cipher Cipher_ALGORITHM_MODE_PADDING;
private static Signature Signature_SHA256_RSA;
/**
* 密钥算法
*/
private static final String DES = "DES";
private static final String AES = "AES";
private static final String RES = "RES";
private static final String RSA = "RSA";
/**
* 加解密算法/工作模式/填充方式
*/
private static final String ECB_MOB = "DES/ECB/PKCS5Padding";
// private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS7Padding";
private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS5Padding";
private static final Charset UTF_8 = StandardCharsets.UTF_8;
private static final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F'};
private static final char hexDigitsLower[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c',
'd', 'e', 'f'};
public static void init() {
BASE64Encoder = new BASE64Encoder();
try {
MessageDigest_SHA1 = MessageDigest.getInstance("SHA-1");
MessageDigest_MD5 = MessageDigest.getInstance("MD5");
Cipher_ECB_MOB = Cipher.getInstance(ECB_MOB);
Cipher_ALGORITHM_MODE_PADDING = Cipher.getInstance(ALGORITHM_MODE_PADDING);
Signature_SHA256_RSA = Signature.getInstance("SHA256WithRSA");
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
}
public static String getDefaultPassword() {
return getSHA(defaultPassword + getMessageDigestString("frp." + defaultPassword, hexDigits, MessageDigest_SHA1));
}
public static String decodeAppData(String str) {
try {
return DESdecode(str, getDefaultPassword());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String encodeAppData(String str) {
try {
return DESencode(str, getDefaultPassword());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String getSha1(String str) {
if (str == null || str.length() == 0) {
return null;
}
return getMessageDigestString(str, hexDigitsLower, MessageDigest_SHA1);
}
public static String getSHA(String spara) {
String sRtn;
byte[] plainText = spara.getBytes(UTF_8);
// 开始使用算法
MessageDigest_SHA1.update(plainText);
// 输出算法运算结果
sRtn = BASE64Encoder.encode(MessageDigest_SHA1.digest());
return sRtn;
}
/**
* DES加密
*
* @param data 加密内容
* @param password 秘钥
*/
public static String DESencode(String data, String password) throws Exception {
byte[] pasByte;
Key key = getDESKey(password);
Cipher_ECB_MOB.init(Cipher.ENCRYPT_MODE, key);
pasByte = Cipher_ECB_MOB.doFinal(data.getBytes(UTF_8));
return Base64.encode(pasByte);
}
/**
* DES解密
*
* @param data 加密内容
* @param password 秘钥
* @return
*/
public static String DESdecode(String data, String password) throws Exception {
byte[] pasByte;
Key key = getDESKey(password);
Cipher_ECB_MOB.init(Cipher.DECRYPT_MODE, key);
byte[] x = Base64.decode(data);
pasByte = Cipher_ECB_MOB.doFinal(x);
return new String(pasByte, UTF_8);
}
private static Key getDESKey(String password) throws Exception {
byte[] DESkey = password.getBytes(UTF_8);
DESKeySpec keySpec = new DESKeySpec(DESkey);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
return keyFactory.generateSecret(keySpec);
}
/**
* AES加密
*
* @param data 加密内容
* @param password 加密密码
*/
public static String AESencode(String data, String password) throws Exception {
byte[] result;
Security.addProvider(ProviderFactory.createBouncyCastleProvider());
// 创建密码器
// 初始化为加密模式的密码
Cipher_ALGORITHM_MODE_PADDING.init(Cipher.ENCRYPT_MODE, getSecretKey(password));
// 加密
result = Cipher_ALGORITHM_MODE_PADDING.doFinal(data.getBytes());
return Base64.encode(result);
}
/**
* AES解密
*
* @param base64Data 解密内容
* @param password 解密密码
*/
public static String AESdecode(String base64Data, String password) throws Exception {
byte[] result;
Security.addProvider(ProviderFactory.createBouncyCastleProvider());
// 使用密钥初始化,设置为解密模式
Cipher_ALGORITHM_MODE_PADDING.init(Cipher.DECRYPT_MODE, getSecretKey(password));
// 执行操作
result = Cipher_ALGORITHM_MODE_PADDING.doFinal(Base64.decode(base64Data));
return new String(result, UTF_8);
}
/**
* 生成AES加密秘钥
*
* @return
*/
private static SecretKeySpec getSecretKey(String password) {
return new SecretKeySpec(SecureUtil.md5(password).toLowerCase().getBytes(), AES);
}
/**
* 对字符串 MD5 无盐值加密
*
* @param plainText 传入要加密的字符串
* @return MD5加密后生成32位(小写字母 + 数字)字符串
*/
public static String MD5Lower(String plainText) {
// 使用指定的字节更新摘要
MessageDigest_MD5.update(plainText.getBytes());
// digest()最后确定返回md5 hash值返回值为8位字符串。因为md5 hash值是16位的hex值实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值用字符串来表示得到字符串形式的hash值。1 固定值
return new BigInteger(1, MessageDigest_MD5.digest()).toString(16);
}
/**
* 对字符串 MD5 加密
*
* @param plainText 传入要加密的字符串
* @return MD5加密后生成32位(大写字母 + 数字)字符串
*/
public static String MD5Upper(String plainText) {
// 使用指定的字节更新摘要
MessageDigest_MD5.update(plainText.getBytes());
// 获得密文
byte[] mdResult = MessageDigest_MD5.digest();
// 把密文转换成十六进制的字符串形式
return getHexString(hexDigitsLower, mdResult);
}
/**
* 对字符串 MD5 加盐值加密
*
* @param plainText 传入要加密的字符串
* @param saltValue 传入要加的盐值
* @return MD5加密后生成32位(小写字母 + 数字)字符串
*/
public static String MD5Lower(String plainText, String saltValue) {
// 使用指定的字节更新摘要
MessageDigest_MD5.update(plainText.getBytes());
MessageDigest_MD5.update(saltValue.getBytes());
// digest()最后确定返回md5 hash值返回值为8位字符串。因为md5 hash值是16位的hex值实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值用字符串来表示得到字符串形式的hash值。1 固定值
return new BigInteger(1, MessageDigest_MD5.digest()).toString(16);
}
/**
* 对字符串 MD5 加盐值加密
*
* @param plainText 传入要加密的字符串
* @param saltValue 传入要加的盐值
* @return MD5加密后生成32位(大写字母 + 数字)字符串
*/
public static String MD5Upper(String plainText, String saltValue) {
// 使用指定的字节更新摘要
MessageDigest_MD5.update(plainText.getBytes());
MessageDigest_MD5.update(saltValue.getBytes());
// 获得密文
byte[] mdResult = MessageDigest_MD5.digest();
// 把密文转换成十六进制的字符串形式
return getHexString(hexDigitsLower, mdResult);
}
/**
* MD5加密后生成32位(小写字母+数字)字符串
* 同 MD5Lower() 一样
*/
public static String MD5(String plainText) {
return getMessageDigestString(plainText, hexDigitsLower, MessageDigest_MD5);
}
/**
* MD5加密后生成16位(小写字母+数字)字符串
* 同 MD5Lower() 一样
*/
public static String MD5_16(String plainText) {
return getMessageDigestString(plainText, hexDigitsLower, MessageDigest_MD5).substring(8, 24);
}
/**
* 校验MD5码
*
* @param text 要校验的字符串
* @param md5 md5值
* @return 校验结果
*/
public static boolean MD5valid(String text, String md5) throws Exception {
return md5.equals(MD5(text)) || md5.equals(MD5(text).toUpperCase());
}
/**
* 校验MD5码
*
* @param text 要校验的字符串
* @param md5 md5值
* @param salt 加密盐
* @return 校验结果
*/
public static boolean MD5valid(String text, String md5, String salt) {
return md5.equals(MD5Lower(text, salt)) || md5.equals(MD5Upper(text, salt).toUpperCase());
}
/**
* RSA2签名
*
* @param content 待签名的字符串
* @param privateKey rsa私钥字符串
* @param charset 字符集编码
* @return 签名结果
*/
public static String rsaSign(String content, String privateKey, String charset) throws Exception {
PrivateKey priKey = getPrivateKeyFromPKCS8(RSA, new ByteArrayInputStream(privateKey.getBytes()));
Signature_SHA256_RSA.initSign(priKey);
if (StringUtils.isBlank(charset)) {
Signature_SHA256_RSA.update(content.getBytes());
} else {
Signature_SHA256_RSA.update(content.getBytes(charset));
}
byte[] signed = Signature_SHA256_RSA.sign();
return new String(Base64.encode(signed));
}
/**
* RSA2验签
*
* @param content 被签名的内容
* @param sign 签名后的结果
* @param publicKey rsa公钥
* @param charset 字符集编码
* @return 验签结果
*/
public static boolean doCheck(String content, String sign, String publicKey, String charset) throws Exception {
PublicKey pubKey = getPublicKeyFromX509(RSA, new ByteArrayInputStream(publicKey.getBytes()));
Signature_SHA256_RSA.initVerify(pubKey);
Signature_SHA256_RSA.update(getContentBytes(content, charset));
return Signature_SHA256_RSA.verify(Base64.decode(sign));
}
/**
* 获取私钥对象
*
* @param algorithm 签名方式
* @param ins 私钥流
* @return
* @throws Exception
*/
private static PrivateKey getPrivateKeyFromPKCS8(String algorithm, InputStream ins) throws Exception {
if (ins == null || StringUtils.isEmpty(algorithm)) {
return null;
}
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
byte[] encodedKey = readText(ins, UTF_8, true).getBytes();
encodedKey = Base64.decode(Arrays.toString(encodedKey));
return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
}
/**
* 获取公钥对象
*
* @param algorithm 签名方式
* @param ins 公钥流
* @return
* @throws Exception
*/
private static PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
StringWriter writer = new StringWriter();
io(new InputStreamReader(ins), writer, true, true);
byte[] encodedKey = writer.toString().getBytes();
// 先base64解码
encodedKey = Base64.decode(Arrays.toString(encodedKey));
return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
}
/**
* 获取字符串对应编码的字节
*
* @param content 字符串内容
* @param charset 字符集编码
* @return
* @throws UnsupportedEncodingException
*/
private static byte[] getContentBytes(String content, String charset) throws UnsupportedEncodingException {
if (StringUtils.isEmpty(charset)) {
return content.getBytes();
}
return content.getBytes(charset);
}
/**
* 将指定输入流的所有文本全部读出到一个字符串中
*
* @param in 输入流
* @param charset 字符集编码
* @param closeIn 是否关闭流
* @return
* @throws IOException
*/
private static String readText(InputStream in, Charset charset, boolean closeIn) throws IOException {
Reader reader = charset == null ? new InputStreamReader(in) : new InputStreamReader(in, charset);
return readText(reader, closeIn);
}
/**
* 将指定<code>Reader</code>的所有文本全部读出到一个字符串中
*
* @param in 输入流
* @param closeIn 是否关闭流
* @return
* @throws IOException
*/
private static String readText(Reader in, boolean closeIn) throws IOException {
StringWriter out = new StringWriter();
io(in, out, closeIn, true);
return out.toString();
}
/**
* 从输入流读取内容,写入到输出流中
*
* @param in 输入流
* @param out 输出流
* @param closeIn 是否关闭流
* @param closeOut 是否关闭流
* @throws IOException
*/
private static void io(Reader in, Writer out, boolean closeIn, boolean closeOut) {
int bufferSize = 8192 >> 1;
char[] buffer = new char[bufferSize];
int amount;
try {
while ((amount = in.read(buffer)) >= 0) {
out.write(buffer, 0, amount);
}
out.flush();
} catch (Exception e) {
logger.error("从输入流读取内容,写入到输出流中异常:{}", e.getMessage(), e);
} finally {
if (closeIn) {
try {
in.close();
} catch (IOException e) {
logger.error("从输入流读取内容,写入到输出流中异常:{}", e.getMessage(), e);
}
}
if (closeOut) {
try {
out.close();
} catch (IOException e) {
logger.error("从输入流读取内容,写入到输出流中异常:{}", e.getMessage(), e);
}
}
}
}
private static String getMessageDigestString(String str, char[] hexDigits, MessageDigest messageDigest) {
messageDigest.update(str.getBytes(UTF_8));
byte[] md = messageDigest.digest();
return getHexString(hexDigits, md);
}
private static String getHexString(char[] hexDigits, byte[] md) {
int j = md.length;
char[] buf = new char[j * 2];
int k = 0;
for (byte byteO : md) {
buf[k++] = hexDigits[byteO >>> 4 & 0xf];
buf[k++] = hexDigits[byteO & 0xf];
}
return new String(buf);
}
}