RSA 工具包

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 加密工具
 * 

* 公钥加密/私钥加密/公钥解密/私钥解密 * 私钥签名/公钥验签 * 密钥生成/密钥转换 *

* ******************************************* * 字符串格式的密钥在没有特殊说明时都为BASE64编码格式 * * 字符串格式的签名在没有特殊说明时都为BASE64编码格式 * * 字符串格式的密文在没有特殊说明时都为BASE64编码格式 * * ******************************************* *

* 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 *

* 参考 * 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); } // 以下是密钥加密解密通用依赖方法 ---------- ---------- ---------- ---------- ---------- /** * 密钥加密 *

* 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); } } /** * 密钥解密 *

* 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); } }

你可能感兴趣的:(RSA 工具包)