CreatedAt: 20200813
JDK Version: Oracle JDK 1.8.0_202
package com.mrathena.toolkit;
import com.mrathena.exception.ServiceException;
import javax.crypto.Cipher;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* RSA 加密工具
* <p>
* 公钥加密/私钥加密/公钥解密/私钥解密
* 私钥签名/公钥验签
* 密钥生成/密钥转换
* <p>
* *******************************************
* 字符串格式的密钥在没有特殊说明时都为BASE64编码格式 *
* 字符串格式的签名在没有特殊说明时都为BASE64编码格式 *
* 字符串格式的密文在没有特殊说明时都为BASE64编码格式 *
* *******************************************
* <p>
* Java Cryptography Architecture
* Standard Algorithm Name Documentation for JDK 8
* Java密码体系结构 JDK 8的标准算法名称文档
* https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
* https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyPairGenerator
* https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#KeyFactory
* https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#algspec
* https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher
* https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Signature
* <p>
* 参考
* java RSA加密解密实现(含分段加密)
* https://www.cnblogs.com/jiafuwei/p/7054500.html
* JDK安全模块JCE核心Cipher使用详解
* https://blog.csdn.net/zcmain/article/details/90640797
* 加密与安全:非对称加密算法 RSA 1024 公钥、秘钥、明文和密文长度
* https://blog.csdn.net/liwei16611/article/details/83751851
*/
public final class RSAKit {
private RSAKit() {}
/**
* 加密算法
*/
private static final String ALGORITHM = "RSA";
/**
* 默认密钥长度(位),RSA密钥长度必须是8的倍数且必须大于等于512,建议1024起步(1024以下已有破解先例)
*/
private static final int DEFAULT_KEY_SIZE = 1024;
/**
* 单例 KeyFactory, 用于转换字符串公私钥到标准公私钥
*/
private static volatile KeyFactory instance;
/**
* UTF-8编码
*/
private static final Charset UTF8 = StandardCharsets.UTF_8;
// 以下是密钥相关方法 ---------- ---------- ---------- ---------- ----------
/**
* 生成公私钥对
*
* @param keySize 密钥长度, RSA密钥长度必须是8的倍数且必须大于等于512,建议1024起步(1024以下已有破解先例)
*/
public static KeyPair generateKeyPair(int keySize) {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
keyPairGenerator.initialize(keySize);
return keyPairGenerator.generateKeyPair();
} catch (Throwable cause) {
throw new ServiceException(cause);
}
}
/**
* 生成公私钥对(1024位)
*/
public static KeyPair generateKeyPair() {
return generateKeyPair(DEFAULT_KEY_SIZE);
}
/**
* 生成公私钥对(2048位)
*/
public static KeyPair generateKeyPairWithKeySize2048() {
return generateKeyPair(2048);
}
/**
* 生成公私钥对(4096位)
*/
public static KeyPair generateKeyPairWithKeySize4096() {
return generateKeyPair(4096);
}
/**
* 单例获取 KeyFactory
*/
private static KeyFactory getKeyFactory() {
try {
if (null == instance) {
synchronized (RSAKit.class) {
if (null == instance) {
instance = KeyFactory.getInstance(ALGORITHM);
}
}
}
return instance;
} catch (Throwable cause) {
throw new ServiceException(cause);
}
}
/**
* 字符串公钥转标准公钥
*/
public static PublicKey toPublicKey(String publicKeyStr) {
try {
return getKeyFactory().generatePublic(new X509EncodedKeySpec(decode(publicKeyStr)));
} catch (Throwable cause) {
throw new ServiceException(cause);
}
}
/**
* 字符串私钥转标准私钥
*/
public static PrivateKey toPrivateKey(String privateKeyStr) {
try {
return getKeyFactory().generatePrivate(new PKCS8EncodedKeySpec(decode(privateKeyStr)));
} catch (Throwable cause) {
throw new ServiceException(cause);
}
}
/**
* 标准密钥转字符串密钥
*/
public static String toKeyStr(Key key) {
return encode(key.getEncoded());
}
// 以下是公钥加密相关方法 ---------- ---------- ---------- ---------- ----------
/**
* 公钥加密
*/
public static byte[] encryptByPublicKey(PublicKey publicKey, byte[] data) {
return encryptByKey(publicKey, data);
}
/**
* 公钥加密
*/
public static byte[] encryptByPublicKey(PublicKey publicKey, String data) {
return encryptByKey(publicKey, data.getBytes(UTF8));
}
/**
* 公钥加密
*/
public static byte[] encryptByPublicKey(String publicKeyStr, byte[] data) {
return encryptByKey(toPublicKey(publicKeyStr), data);
}
/**
* 公钥加密
*/
public static byte[] encryptByPublicKey(String publicKeyStr, String data) {
return encryptByKey(toPublicKey(publicKeyStr), data.getBytes(UTF8));
}
/**
* 公钥加密
*/
public static String encryptToStringByPublicKey(PublicKey publicKey, byte[] data) {
return encode(encryptByKey(publicKey, data));
}
/**
* 公钥加密
*/
public static String encryptToStringByPublicKey(PublicKey publicKey, String data) {
return encode(encryptByKey(publicKey, data.getBytes(UTF8)));
}
/**
* 公钥加密
*/
public static String encryptToStringByPublicKey(String publicKeyStr, byte[] data) {
return encode(encryptByKey(toPublicKey(publicKeyStr), data));
}
/**
* 公钥加密
*/
public static String encryptToStringByPublicKey(String publicKeyStr, String data) {
return encode(encryptByKey(toPublicKey(publicKeyStr), data.getBytes(UTF8)));
}
// 以下是公钥解密相关方法 ---------- ---------- ---------- ---------- ----------
/**
* 公钥解密
*/
public static byte[] decryptByPublicKey(PublicKey publicKey, byte[] data) {
return decryptByKey(publicKey, data);
}
/**
* 公钥解密
*/
public static byte[] decryptByPublicKey(PublicKey publicKey, String data) {
return decryptByKey(publicKey, decode(data));
}
/**
* 公钥解密
*/
public static byte[] decryptByPublicKey(String publicKeyStr, byte[] data) {
return decryptByKey(toPublicKey(publicKeyStr), data);
}
/**
* 公钥解密
*/
public static byte[] decryptByPublicKey(String publicKeyStr, String data) {
return decryptByKey(toPublicKey(publicKeyStr), decode(data));
}
/**
* 公钥解密
*/
public static String decryptToStringByPublicKey(PublicKey publicKey, byte[] data) {
return new String(decryptByKey(publicKey, data), UTF8);
}
/**
* 公钥解密
*/
public static String decryptToStringByPublicKey(PublicKey publicKey, String data) {
return new String(decryptByKey(publicKey, decode(data)), UTF8);
}
/**
* 公钥解密
*/
public static String decryptToStringByPublicKey(String publicKeyStr, byte[] data) {
return new String(decryptByKey(toPublicKey(publicKeyStr), data), UTF8);
}
/**
* 公钥解密
*/
public static String decryptToStringByPublicKey(String publicKeyStr, String data) {
return new String(decryptByKey(toPublicKey(publicKeyStr), decode(data)), UTF8);
}
// 以下是私钥加密相关方法 ---------- ---------- ---------- ---------- ----------
/**
* 私钥加密
*/
public static byte[] encryptByPrivateKey(PrivateKey privateKey, byte[] data) {
return encryptByKey(privateKey, data);
}
/**
* 私钥加密
*/
public static byte[] encryptByPrivateKey(PrivateKey privateKey, String data) {
return encryptByKey(privateKey, data.getBytes(UTF8));
}
/**
* 私钥加密
*/
public static byte[] encryptByPrivateKey(String privateKeyStr, byte[] data) {
return encryptByKey(toPrivateKey(privateKeyStr), data);
}
/**
* 私钥加密
*/
public static byte[] encryptByPrivateKey(String privateKeyStr, String data) {
return encryptByKey(toPrivateKey(privateKeyStr), data.getBytes(UTF8));
}
/**
* 私钥加密
*/
public static String encryptToStringByPrivateKey(PrivateKey privateKey, byte[] data) {
return encode(encryptByKey(privateKey, data));
}
/**
* 私钥加密
*/
public static String encryptToStringByPrivateKey(PrivateKey privateKey, String data) {
return encode(encryptByKey(privateKey, data.getBytes(UTF8)));
}
/**
* 私钥加密
*/
public static String encryptToStringByPrivateKey(String privateKeyStr, byte[] data) {
return encode(encryptByKey(toPrivateKey(privateKeyStr), data));
}
/**
* 私钥加密
*/
public static String encryptToStringByPrivateKey(String privateKeyStr, String data) {
return encode(encryptByKey(toPrivateKey(privateKeyStr), data.getBytes(UTF8)));
}
// 以下是私钥解密相关方法 ---------- ---------- ---------- ---------- ----------
/**
* 私钥解密
*/
public static byte[] decryptByPrivateKey(PrivateKey privateKey, byte[] data) {
return decryptByKey(privateKey, data);
}
/**
* 私钥解密
*/
public static byte[] decryptByPrivateKey(PrivateKey privateKey, String data) {
return decryptByKey(privateKey, decode(data));
}
/**
* 私钥解密
*/
public static byte[] decryptByPrivateKey(String privateKeyStr, byte[] data) {
return decryptByKey(toPrivateKey(privateKeyStr), data);
}
/**
* 私钥解密
*/
public static byte[] decryptByPrivateKey(String privateKeyStr, String data) {
return decryptByKey(toPrivateKey(privateKeyStr), decode(data));
}
/**
* 私钥解密
*/
public static String decryptToStringByPrivateKey(PrivateKey privateKey, byte[] data) {
return new String(decryptByKey(privateKey, data), UTF8);
}
/**
* 私钥解密
*/
public static String decryptToStringByPrivateKey(PrivateKey privateKey, String data) {
return new String(decryptByKey(privateKey, decode(data)), UTF8);
}
/**
* 私钥解密
*/
public static String decryptToStringByPrivateKey(String privateKeyStr, byte[] data) {
return new String(decryptByKey(toPrivateKey(privateKeyStr), data), UTF8);
}
/**
* 私钥解密
*/
public static String decryptToStringByPrivateKey(String privateKeyStr, String data) {
return new String(decryptByKey(toPrivateKey(privateKeyStr), decode(data)), UTF8);
}
// 以下是密钥加密解密通用依赖方法 ---------- ---------- ---------- ---------- ----------
/**
* 密钥加密
* <p>
* RSA加密时最大分段长度
* 512 - 512/8-11=64-11=53
* 1024 - 1024/8-11=128-11=117
* 2048 - 2048/8-11=256-11=245
* 4096 - 4096/8-11=512-11=501
* 加密时,最大分段长度,是密钥长度的八分之一减十一,加密分段你长度有一个可选范围
* 1024可以使用117,2048也可以使用117,但最大可使用245,4096也可以使用117,但最大可使用501
*/
private static byte[] encryptByKey(Key key, byte[] data) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
return encryptByCipher(cipher, getBlockLength(key) / 8 - 11, data);
} catch (Throwable cause) {
throw new ServiceException(cause);
}
}
/**
* 密钥解密
* <p>
* RSA解密时分段长度
* 512 - 512/8=64
* 1024 - 1024/8=128
* 2048 - 2048/8=256
* 4096 - 4096/8=512
* 这个长度是固定的,正好是密钥长度的八分之一,不像加密时是一个范围
*/
private static byte[] decryptByKey(Key key, byte[] data) {
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
return encryptByCipher(cipher, getBlockLength(key) / 8, data);
} catch (Throwable cause) {
throw new ServiceException(cause);
}
}
/**
* 获取加解密基础分段长度
*/
private static int getBlockLength(Key key) {
try {
if (key instanceof PublicKey) {
// 获取公钥长度(位)
return getKeyFactory().getKeySpec(key, RSAPublicKeySpec.class).getModulus().toString(2).length();
} else {
// 获取私钥长度(位)
return getKeyFactory().getKeySpec(key, RSAPrivateKeySpec.class).getModulus().toString(2).length();
}
} catch (Throwable cause) {
throw new ServiceException(cause);
}
}
/**
* 加密解密流程
* 加密过程使用二维数组存储分块加密结果,最终转换为一维数组存储完整加密结果
* 也可使用ByteArrayOutputStream的write()方法把分块解密结果写到流,toByteArray()方法转换称为完成加密结果
*
* @param data 数据
* @param cipher 密码器
* @param maxBlockLength 加密/解密块大小, 加密117, 解密128
* @return 加密/解密结果
*/
private static byte[] encryptByCipher(Cipher cipher, int maxBlockLength, byte[] data) {
try {
int lines = data.length % maxBlockLength == 0 ? data.length / maxBlockLength : data.length / maxBlockLength + 1;
byte[][] tempArray = new byte[lines][];
for (int i = 0; i < lines; i++) {
int offset = i * maxBlockLength;
tempArray[i] = cipher.doFinal(data, offset, i == lines - 1 ? data.length - offset : maxBlockLength);
}
int tempArrayTotalLength = 0;
for (byte[] bytes : tempArray) {
tempArrayTotalLength = tempArrayTotalLength + bytes.length;
}
byte[] targetArray = new byte[tempArrayTotalLength];
int index = 0;
for (byte[] byteArray : tempArray) {
for (byte aByte : byteArray) {
targetArray[index] = aByte;
index++;
}
}
return targetArray;
} catch (Throwable cause) {
throw new ServiceException(cause);
}
}
// 以下是私钥签名相关方法 ---------- ---------- ---------- ---------- ----------
/**
* 私钥签名
*
* @param privateKey 私钥
* @param algorithm 签名算法,NONEwithRSA,MD2withRSA,MD5withRSA,SHA1withRSA,SHA224withRSA,SHA256withRSA,SHA384withRSA,SHA512withRSA,SHA512/224withRSA,SHA512/256withRSA
* @param data 待签名数据
*/
private static byte[] sign(PrivateKey privateKey, String algorithm, byte[] data) {
try {
Signature signature = Signature.getInstance(algorithm);
signature.initSign(privateKey);
signature.update(data);
return signature.sign();
} catch (Throwable cause) {
throw new ServiceException(cause);
}
}
/**
* 私钥签名
*/
public static String signToString(PrivateKey privateKey, String algorithm, byte[] data) {
return encode(sign(privateKey, algorithm, data));
}
/**
* 私钥签名
*/
public static String signToString(String privateKeyStr, String algorithm, byte[] data) {
return encode(sign(toPrivateKey(privateKeyStr), algorithm, data));
}
/**
* 私钥签名
*/
public static String signToString(PrivateKey privateKey, String algorithm, String data) {
return encode(sign(privateKey, algorithm, data.getBytes(UTF8)));
}
/**
* 私钥签名
*/
public static String signToString(String privateKeyStr, String algorithm, String data) {
return encode(sign(toPrivateKey(privateKeyStr), algorithm, data.getBytes(UTF8)));
}
// 以下是公钥验签相关方法 ---------- ---------- ---------- ---------- ----------
/**
* 公钥验签
*
* @param publicKey 公钥
* @param algorithm 签名算法,NONEwithRSA,MD2withRSA,MD5withRSA,SHA1withRSA,SHA224withRSA,SHA256withRSA,SHA384withRSA,SHA512withRSA,SHA512/224withRSA,SHA512/256withRSA
* @param data 待验签数据
* @param sign 签名字符串(BASE64编码)
*/
public static boolean verify(PublicKey publicKey, String algorithm, byte[] data, String sign) {
try {
Signature signature = Signature.getInstance(algorithm);
signature.initVerify(publicKey);
signature.update(data);
return signature.verify(decode(sign));
} catch (Throwable cause) {
throw new ServiceException(cause);
}
}
/**
* 公钥验签
*/
public static boolean verify(String publicKeyStr, String algorithm, byte[] data, String sign) {
return verify(toPublicKey(publicKeyStr), algorithm, data, sign);
}
/**
* 公钥验签
*/
public static boolean verify(PublicKey publicKey, String algorithm, String data, String sign) {
return verify(publicKey, algorithm, data.getBytes(UTF8), sign);
}
/**
* 公钥验签
*/
public static boolean verify(String publicKeyStr, String algorithm, String data, String sign) {
return verify(toPublicKey(publicKeyStr), algorithm, data.getBytes(UTF8), sign);
}
/**
* 编码
*/
private static String encode(byte[] data) {
return Base64.getEncoder().encodeToString(data);
}
/**
* 解码
*/
private static byte[] decode(String data) {
return Base64.getDecoder().decode(data);
}
}