package com.fzzy.api.utils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.ArrayUtils; import javax.crypto.Cipher; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.HashMap; import java.util.Map; @Slf4j public class RSAUtils { public static final String KEY_ALGORITHM = "RSA"; private static final String PUBLIC_KEY = "RSAPublicKey"; private static final String PRIVATE_KEY = "RSAPrivateKey"; private static final String CHARSET = "utf-8"; private static final int KEYSIZE = 2048;// 密钥位数 private static final int RESERVE_BYTES = 11; private static final String ECB_PADDING = "RSA/ECB/PKCS1Padding"; // 2048 bits 的 RSA 密钥对,最大解密密文大小 private static final int MAX_DECRYPT_BLOCK = KEYSIZE / 8; // 2048 bits 的 RSA 密钥对,最大加密明文大小 private static final int MAX_ENCRYPT_BLOCK = MAX_DECRYPT_BLOCK - RESERVE_BYTES; // 生成密钥对 public static Map initKey(int keysize) throws Exception { KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM); // 设置密钥对的 bit 数,越大越安全 keyPairGen.initialize(keysize); KeyPair keyPair = keyPairGen.generateKeyPair(); // 获取公钥 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 获取私钥 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); Map keyMap = new HashMap<>(2); keyMap.put(PUBLIC_KEY, publicKey); keyMap.put(PRIVATE_KEY, privateKey); return keyMap; } // 获取公钥字符串 public static String getPublicKeyStr(Map keyMap) { // 获得 map 中的公钥对象,转为 key 对象 Key key = (Key) keyMap.get(PUBLIC_KEY); // 编码返回字符串 return encryptBASE64(key.getEncoded()); } // 获取私钥字符串 public static String getPrivateKeyStr(Map keyMap) { // 获得 map 中的私钥对象,转为 key 对象 Key key = (Key) keyMap.get(PRIVATE_KEY); // 编码返回字符串 return encryptBASE64(key.getEncoded()); } // 获取公钥 public static PublicKey getPublicKey(String publicKeyString) throws Exception { byte[] publicKeyByte = Base64.getDecoder().decode(publicKeyString); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyByte); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); return keyFactory.generatePublic(keySpec); } // 获取私钥 public static PrivateKey getPrivateKey(String privateKeyString) throws Exception { byte[] privateKeyByte = Base64.getDecoder().decode(privateKeyString); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyByte); KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); return keyFactory.generatePrivate(keySpec); } /** * BASE64 编码返回加密字符串 * * @param key 需要编码的字节数组 * @return 编码后的字符串 */ public static String encryptBASE64(byte[] key) { return new String(Base64.getEncoder().encode(key)); } /** * BASE64 解码,返回字节数组 * * @param key 待解码的字符串 * @return 解码后的字节数组 */ public static byte[] decryptBASE64(String key) { return Base64.getDecoder().decode(key); } /** * 公钥加密 * * @param text 待加密的明文字符串 * @param publicKeyStr 公钥 * @return 加密后的密文 */ public static String encrypt(String text, String publicKeyStr) { try { log.info("明文字符串为:[{}]", text); Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKeyStr)); // URLEncoder编码解决中文乱码问题 byte[] data = text.getBytes(StandardCharsets.UTF_8); // 加密时超过117字节就报错。为此采用分段加密的办法来加密 byte[] enBytes = null; for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) { // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码 byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK)); enBytes = ArrayUtils.addAll(enBytes, doFinal); } return Base64.getEncoder().encodeToString(enBytes); } catch (Exception e) { throw new RuntimeException("加密字符串[" + text + "]时遇到异常", e); } } /** * 私钥解密 * * @param secretText 待解密的密文字符串 * @param privateKeyStr 私钥 * @return 解密后的明文 */ public static String decrypt(String secretText, String privateKeyStr) { try { // 生成私钥 Cipher cipher = Cipher.getInstance(KEY_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKeyStr)); // 密文解码 byte[] data = Base64.getDecoder().decode(secretText); // 返回UTF-8编码的解密信息 int inputLen = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } out.close(); return out.toString(CHARSET); } catch (Exception e) { throw new RuntimeException("解密字符串[" + secretText + "]时遇到异常", e); } } /** * 私钥加密 * * @param text * @param privateInfoStr * @return */ public static String encryptData(String text, String privateInfoStr) { try { log.info("明文字符串为:[{}]", text); Cipher cipher = Cipher.getInstance(ECB_PADDING); cipher.init(Cipher.ENCRYPT_MODE, getPrivateKey(privateInfoStr)); // URLEncoder编码解决中文乱码问题 byte[] data = text.getBytes(StandardCharsets.UTF_8); byte[] enBytes = null; for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) { // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码 byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK)); enBytes = ArrayUtils.addAll(enBytes, doFinal); } return Base64.getEncoder().encodeToString(enBytes); } catch (Exception e) { throw new RuntimeException("加密字符串[" + text + "]时遇到异常", e); } } /** * 公钥解密 * * @param secretText * @param publicInfoStr * @return */ public static String decryptData(String secretText, String publicInfoStr) { try { //解密 Cipher cipher = Cipher.getInstance(ECB_PADDING); cipher.init(Cipher.DECRYPT_MODE, getPublicKey(publicInfoStr)); // 密文解码 byte[] data = Base64.getDecoder().decode(secretText); // 返回UTF-8编码的解密信息 int inputLen = data.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 对数据分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(data, offSet, inputLen - offSet); } out.write(cache, 0, cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } out.close(); return out.toString(CHARSET); } catch (Exception e) { throw new RuntimeException("解密字符串[" + secretText + "]时遇到异常", e); } } }