RSA 帮助类

  1. import lombok.extern.slf4j.Slf4j;
  2. import javax.crypto.Cipher;
  3. import java.io.ByteArrayOutputStream;
  4. import java.security.*;
  5. import java.security.interfaces.RSAPrivateKey;
  6. import java.security.interfaces.RSAPublicKey;
  7. import java.security.spec.PKCS8EncodedKeySpec;
  8. import java.security.spec.X509EncodedKeySpec;
  9. import java.util.Base64;
  10. import java.util.HashMap;
  11. import java.util.Map;
  12. /**
  13. * 公私钥工具类
  14. */
  15. @Slf4j
  16. public class RsaUtils {
  17. /** RSA算法标识 */
  18. public static final String RSA = "RSA";
  19. /** 算法 */
  20. public static final String ENCRYP_TYPE = "SHA256WithRSA";
  21. /** UTF-8编码 */
  22. public static final String CHARSET_UTF8 = "utf-8";
  23. /** 公钥字段名 */
  24. public static final String GEN_PUBLIC_KEY = "public_Key";
  25. /** 私钥字段名 */
  26. public static final String GEN_PRIVATE_KEY = "private_key";
  27. /**
  28. * RSA密钥长度必须是64的倍数,在512~65536之间。默认是1024
  29. */
  30. public static final Integer KEY_LENGTH = 512;
  31. /**
  32. * RSA最大加密明文大小
  33. */
  34. private static final int MAX_ENCRYPT_BLOCK = 53;
  35. /**
  36. * RSA最大解密密文大小
  37. */
  38. private static final int MAX_DECRYPT_BLOCK = 64;
  39. /**
  40. * 生成公私钥
  41. *
  42. * @return 公私钥集合
  43. */
  44. public static Map<String, String> generatorRsaKey() {
  45. Map<String, String> map = new HashMap(2);
  46. KeyPairGenerator keyPairGen;
  47. try {
  48. keyPairGen = KeyPairGenerator.getInstance(RSA);
  49. // 初始化密钥对生成器,密钥大小为1024位
  50. keyPairGen.initialize(KEY_LENGTH, new SecureRandom());
  51. // 生成一个密钥对,保存在keyPair中
  52. KeyPair keyPair = keyPairGen.generateKeyPair();
  53. // 得到私钥
  54. RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
  55. // 得到公钥
  56. RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
  57. // 公钥
  58. map.put(GEN_PUBLIC_KEY, new String(Base64.getEncoder().encode(publicKey.getEncoded())));
  59. // 私钥
  60. map.put(GEN_PRIVATE_KEY, new String(Base64.getEncoder().encode(privateKey.getEncoded())));
  61. } catch (Exception e) {
  62. log.error("init rsa key error!", e);
  63. throw new GatewayRuntimeException(ResultCode.GENERATOR_KEY_ERROR);
  64. }
  65. return map;
  66. }
  67. /**
  68. * 创建私钥
  69. *
  70. * @param privateKeyStr
  71. * @return
  72. */
  73. public static PrivateKey createPrivateKey(String privateKeyStr) {
  74. try {
  75. byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr.getBytes(CHARSET_UTF8));
  76. // 生成私匙
  77. KeyFactory kf = KeyFactory.getInstance(RSA);
  78. PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
  79. return kf.generatePrivate(keySpec);
  80. } catch (Exception e) {
  81. log.error("init private key error!", e);
  82. throw new GatewayRuntimeException(ResultCode.CREATE_PRIVATE_KEY_ERROR);
  83. }
  84. }
  85. /**
  86. * 创建公钥
  87. *
  88. * @param publicKeyStr
  89. * @return
  90. */
  91. public static PublicKey createPublicKey(String publicKeyStr) {
  92. try {
  93. byte[] publickeyBytes = Base64.getDecoder().decode(publicKeyStr.getBytes(CHARSET_UTF8));
  94. X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publickeyBytes);
  95. KeyFactory keyFactory = KeyFactory.getInstance(RSA);
  96. return keyFactory.generatePublic(x509EncodedKeySpec);
  97. } catch (Exception e) {
  98. log.error("init public key error!", e);
  99. throw new GatewayRuntimeException(ResultCode.CREATE_PUBLIC_KEY_ERROR);
  100. }
  101. }
  102. /**
  103. * 加签
  104. *
  105. * @param signContent 签名内容
  106. * @param privateKey 私钥
  107. * @return
  108. */
  109. public static String doSign(String signContent, PrivateKey privateKey) {
  110. try {
  111. Signature signature = Signature.getInstance(ENCRYP_TYPE);
  112. signature.initSign(privateKey);
  113. signature.update(signContent.getBytes(CHARSET_UTF8));
  114. byte[] sign = signature.sign();
  115. return new String(Base64.getEncoder().encode(sign), CHARSET_UTF8);
  116. } catch (Exception e) {
  117. log.error("generator sign error!", e);
  118. throw new GatewayRuntimeException(ResultCode.DATA_SIGNATURE_ERROR);
  119. }
  120. }
  121. /**
  122. * 验签
  123. *
  124. * @param content 内容
  125. * @param sign 签名
  126. * @param publicKey 公钥
  127. * @return
  128. */
  129. public static boolean verifySign(String content, String sign, PublicKey publicKey) {
  130. try {
  131. Signature sig = Signature.getInstance(ENCRYP_TYPE);
  132. sig.initVerify(publicKey);
  133. byte[] signs = Base64.getDecoder().decode(sign.getBytes(CHARSET_UTF8));
  134. sig.update(content.getBytes(CHARSET_UTF8));
  135. return sig.verify(signs);
  136. } catch (Exception e) {
  137. log.error("verify sign error!", e);
  138. throw new GatewayRuntimeException(ResultCode.ILLEGAL_SIGN);
  139. }
  140. }
  141. /**
  142. * RSA加密
  143. *
  144. * @param content 内容
  145. * @param publicKey 公钥
  146. * @return
  147. */
  148. public static String encrypt(String content, PublicKey publicKey) {
  149. try {
  150. Cipher cipher = Cipher.getInstance(RSA);
  151. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
  152. return new String(Base64.getEncoder().encode(cipher.doFinal(content.getBytes(CHARSET_UTF8))), CHARSET_UTF8);
  153. } catch (Exception e) {
  154. log.error("rsa encrypt error!", e);
  155. throw new GatewayRuntimeException(ResultCode.ENCODE_DATE_ERROR);
  156. }
  157. }
  158. /**
  159. * RSA解密
  160. *
  161. * @param content 内容
  162. * @param privateKey 私钥
  163. * @return
  164. */
  165. public static String decrypt(String content, PrivateKey privateKey) {
  166. try {
  167. //64位解码加密后的字符串
  168. byte[] inputByte = Base64.getDecoder().decode(content.getBytes(CHARSET_UTF8));
  169. //RSA解密
  170. Cipher cipher = Cipher.getInstance(RSA);
  171. cipher.init(Cipher.DECRYPT_MODE, privateKey);
  172. return new String(cipher.doFinal(inputByte));
  173. } catch (Exception e) {
  174. log.error("rsa decrypt error!", e);
  175. throw new GatewayRuntimeException(ResultCode.DNCODE_DATE_ERROR);
  176. }
  177. }
  178. /** */
  179. /**
  180. * <P>
  181. * 私钥解密
  182. * </p>
  183. *
  184. * @param encryptedData 已加密数据
  185. * @param privateKey 私钥(BASE64编码)
  186. * @return
  187. * @throws Exception
  188. */
  189. public static byte[] decryptByPrivateKey(byte[] encryptedData, PrivateKey privateKey) throws Exception {
  190. /*byte[] keyBytes = Base64.getDecoder().decode(privateKey);
  191. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
  192. Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
  193. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);*/
  194. Cipher cipher = Cipher.getInstance(RSA);
  195. cipher.init(Cipher.DECRYPT_MODE, privateKey);
  196. int inputLen = encryptedData.length;
  197. ByteArrayOutputStream out = new ByteArrayOutputStream();
  198. int offSet = 0;
  199. byte[] cache;
  200. int i = 0;
  201. // 对数据分段解密
  202. while (inputLen - offSet > 0) {
  203. if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
  204. cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
  205. } else {
  206. cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
  207. }
  208. out.write(cache, 0, cache.length);
  209. i++;
  210. offSet = i * MAX_DECRYPT_BLOCK;
  211. }
  212. byte[] decryptedData = out.toByteArray();
  213. out.close();
  214. return decryptedData;
  215. }
  216. /** */
  217. /**
  218. * <p>
  219. * 公钥解密
  220. * </p>
  221. *
  222. * @param encryptedData 已加密数据
  223. * @param publicKey 公钥(BASE64编码)
  224. * @return
  225. * @throws Exception
  226. */
  227. public static byte[] decryptByPublicKey(byte[] encryptedData, PublicKey publicKey) throws Exception {
  228. /*byte[] keyBytes = Base64.getDecoder().decode(publicKey);
  229. X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
  230. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
  231. Key publicK = keyFactory.generatePublic(x509KeySpec);
  232. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());*/
  233. Cipher cipher = Cipher.getInstance(RSA);
  234. cipher.init(Cipher.DECRYPT_MODE, publicKey);
  235. int inputLen = encryptedData.length;
  236. try(ByteArrayOutputStream out = new ByteArrayOutputStream()){
  237. int offSet = 0;
  238. byte[] cache;
  239. int i = 0;
  240. // 对数据分段解密
  241. while (inputLen - offSet > 0) {
  242. if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
  243. cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
  244. } else {
  245. cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
  246. }
  247. out.write(cache, 0, cache.length);
  248. i++;
  249. offSet = i * MAX_DECRYPT_BLOCK;
  250. }
  251. return out.toByteArray();
  252. }
  253. }
  254. /** */
  255. /**
  256. * <p>
  257. * 公钥加密
  258. * </p>
  259. *
  260. * @param data 源数据
  261. * @param publicKey 公钥(BASE64编码)
  262. * @return
  263. * @throws Exception
  264. */
  265. public static byte[] encryptByPublicKey(byte[] data, PublicKey publicKey) throws Exception {
  266. /*byte[] keyBytes = Base64.getDecoder().decode(publicKey);
  267. X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
  268. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
  269. Key publicK = keyFactory.generatePublic(x509KeySpec);
  270. // 对数据加密
  271. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());*/
  272. Cipher cipher = Cipher.getInstance(RSA);
  273. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
  274. int inputLen = data.length;
  275. ByteArrayOutputStream out = new ByteArrayOutputStream();
  276. int offSet = 0;
  277. byte[] cache;
  278. int i = 0;
  279. // 对数据分段加密
  280. while (inputLen - offSet > 0) {
  281. if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
  282. cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
  283. } else {
  284. cache = cipher.doFinal(data, offSet, inputLen - offSet);
  285. }
  286. out.write(cache, 0, cache.length);
  287. i++;
  288. offSet = i * MAX_ENCRYPT_BLOCK;
  289. }
  290. byte[] encryptedData = out.toByteArray();
  291. out.close();
  292. return encryptedData;
  293. }
  294. /** */
  295. /**
  296. * <p>
  297. * 私钥加密
  298. * </p>
  299. *
  300. * @param data 源数据
  301. * @param privateKey 私钥(BASE64编码)
  302. * @return
  303. * @throws Exception
  304. */
  305. public static byte[] encryptByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
  306. /*byte[] keyBytes = Base64.getDecoder().decode(privateKey);
  307. PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
  308. KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
  309. Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
  310. Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());*/
  311. Cipher cipher = Cipher.getInstance(RSA);
  312. cipher.init(Cipher.ENCRYPT_MODE, privateKey);
  313. int inputLen = data.length;
  314. ByteArrayOutputStream out = new ByteArrayOutputStream();
  315. int offSet = 0;
  316. byte[] cache;
  317. int i = 0;
  318. // 对数据分段加密
  319. while (inputLen - offSet > 0) {
  320. if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
  321. cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
  322. } else {
  323. cache = cipher.doFinal(data, offSet, inputLen - offSet);
  324. }
  325. out.write(cache, 0, cache.length);
  326. i++;
  327. offSet = i * MAX_ENCRYPT_BLOCK;
  328. }
  329. byte[] encryptedData = out.toByteArray();
  330. out.close();
  331. return encryptedData;
  332. }
  333. /**
  334. * 公钥加密(分段)
  335. *
  336. * @param data
  337. * @param publicKey
  338. * @return
  339. */
  340. public static String encryptByPublicKeyToString(String data, PublicKey publicKey) throws Exception {
  341. byte[] inputByte = data.getBytes(CHARSET_UTF8);
  342. byte[] result = encryptByPublicKey(inputByte,publicKey);
  343. return new String(Base64.getEncoder().encode(result), CHARSET_UTF8);
  344. }
  345. /**
  346. * 私钥解密(分段)
  347. *
  348. * @param data
  349. * @param privateKey
  350. * @return
  351. */
  352. public static String decryptByPrivateKeyToString(String data, PrivateKey privateKey) throws Exception {
  353. byte[] inputByte = Base64.getDecoder().decode(data.getBytes(CHARSET_UTF8));
  354. byte[] result = decryptByPrivateKey(inputByte,privateKey);
  355. return new String(result, CHARSET_UTF8);
  356. }
  357. }

AES帮助类

import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class AESUtils {
    private static final String ALGORITHM = "AES/ECB/PKCS5Padding";
    public static final String AES = "AES";
    public static final String CHARSET_UTF8 = "utf-8";

    public AESUtils() {
    }

    public static String encrypt(String content, String key) throws Exception {
        byte[] raw = key.getBytes(CHARSET_UTF8);
        SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(1, skeySpec);
        byte[] encrypted = cipher.doFinal(content.getBytes(CHARSET_UTF8));
        return new String(Base64.getEncoder().encode(encrypted), CHARSET_UTF8);
    }

    public static String dcrypt(String content, String key) throws Exception {
        byte[] raw = key.getBytes(CHARSET_UTF8);
        SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(2, skeySpec);
        byte[] encrypted1 = Base64.getDecoder().decode(content);
        byte[] original = cipher.doFinal(encrypted1);
        return new String(original, CHARSET_UTF8);
    }
}

image.png
指定位数 ,128 ,192, 256

测试代码

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * @Author: Luis
 * @Description:
 * @Date: 2021/5/24 16:30
 */
public class PerformanceTest {




    /**
     * 获取随机字符串,由数字、大小写字母组成
     *
     * @param bytes:生成的字符串的位数
     * @return
     * @author
     */
    public static String getRandomStr(int bytes) {
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < bytes; i++) {
            //随机判断判断该字符是数字还是字母
            String choice = random.nextInt(2) % 2 == 0 ? "char" : "num";
            if ("char".equalsIgnoreCase(choice)) {
                //随机判断是大写字母还是小写字母
                int start = random.nextInt(2) % 2 == 0 ? 65 : 97;
                sb.append((char) (start + random.nextInt(26)));
            } else if ("num".equalsIgnoreCase(choice)) {
                sb.append(random.nextInt(10));
            }
        }
        return sb.toString();
    }


    /**
     *
         // 1024 1k
         // 1024 * 1024 1M
         // 1024 * 1024 * 5 5M
         // 1024 * 1024 * 10 10M
         // 1024 * 1024 * 100 100M
         // 1024 * 1024 * 1024 1G
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {

        //testRsa();
        //testPerformanceAES();
        testPerformanceRSA();
    }

    public static void testPerformanceAES() throws Exception {

        int size;
        int num = 100; // 次数

        System.out.println("----------验证AES算法加解密性能-----------");

        size = 1024; // 1kb
        performanceAES(size,num);

        size = 1024 * 64; // 64 kb
        performanceAES(size,num);

        size = 1024 * 128; // 128 kb
        performanceAES(size,num);

        size = 1024 * 256; // 256 kb
        performanceAES(size,num);

        size = 1024 * 512;  // 512 kb
        performanceAES(size,num);

        size = 1024 * 1024;  // 1 mb
        performanceAES(size,num);

        size = 1024 * 1024 * 3;  // 3 mb
        performanceAES(size,num);

        size = 1024 * 1024 * 5;  // 5 mb
        performanceAES(size,num);

        System.out.println("------------------------------------------");
    }


    public static void testPerformanceRSA() throws Exception {

        int size;
        int num = 100; // 次数

        System.out.println("----------验证AES算法加解密性能-----------");

        size = 1024; // 1kb
        performanceRSA(size,num);

        size = 1024 * 64; // 64 kb
        performanceRSA(size,num);

        size = 1024 * 128; // 128 kb
        performanceRSA(size,num);

        size = 1024 * 256; // 256 kb
        performanceRSA(size,num);

        size = 1024 * 512;  // 512 kb
        performanceRSA(size,num);

        size = 1024 * 1024;  // 1 mb
        performanceRSA(size,num);

        size = 1024 * 1024 * 3;  // 3 mb
        performanceRSA(size,num);

        size = 1024 * 1024 * 5;  // 5 mb
        performanceRSA(size,num);

        System.out.println("------------------------------------------");
    }

    /**
     * 验证AES 算法
     *
     * @param size
     * @param num
     */
    private static void performanceAES(int size, int num) throws Exception {
        long encryptCostTime = 0,dcryptCostTime = 0;
        String key = UUIDUtils.genReqNo().substring(0, 16);
        List<String> encryptDatas = new ArrayList();
        for (int i = 0; i < num; i++) {
            String content = getRandomStr(size);
            long encryptStartTime = System.currentTimeMillis();
            String encryptStr = AESUtils.encrypt(content,key);
            encryptCostTime = encryptCostTime + (System.currentTimeMillis() - encryptStartTime);
            encryptDatas.add(encryptStr);
        }
        System.out.println(String.format("------AES加密数据大小:%s,次数:%s,总耗时:%s,平均耗时:%s ",size,num,encryptCostTime,(encryptCostTime/num)));
        for(String encryptStr : encryptDatas){
            long dcryptStartTime = System.currentTimeMillis();
            AESUtils.dcrypt(encryptStr,key);
            dcryptCostTime = dcryptCostTime + (System.currentTimeMillis() - dcryptStartTime);
        }
        System.out.println(String.format("------AES解密数据大小:%s,次数:%s,总耗时:%s,平均耗时:%s ",size,num,dcryptCostTime,(dcryptCostTime/num)));
    }

    /**
     * 验证AES 算法
     *
     * @param size
     * @param num
     */
    private static void performanceRSA(int size, int num) throws Exception {

        Map<String, String> rsaKeys = RsaUtils.generatorRsaKey();
        PrivateKey privateKey = RsaUtils.createPrivateKey(rsaKeys.get(GEN_PRIVATE_KEY));
        PublicKey publicKey = RsaUtils.createPublicKey(rsaKeys.get(GEN_PUBLIC_KEY));
        long encryptCostTime = 0,dcryptCostTime = 0;
        List<String> encryptDatas = new ArrayList();
        for (int i = 0; i < num; i++) {
            String content = getRandomStr(size);
            long encryptStartTime = System.currentTimeMillis();
            //RsaUtils.encrypt(content,publicKey);
            String encryptStr = RsaUtils.encryptByPublicKeyToString(content,publicKey);
            encryptCostTime = encryptCostTime + (System.currentTimeMillis() - encryptStartTime);
            encryptDatas.add(encryptStr);
        }
        System.out.println(String.format("------RSA加密数据大小:%s,次数:%s,总耗时:%s,平均耗时:%s ",size,num,encryptCostTime,(encryptCostTime/num)));
        for(String encryptStr : encryptDatas){
            long dcryptStartTime = System.currentTimeMillis();
            //RsaUtils.decrypt(encryptStr,privateKey);
            RsaUtils.decryptByPrivateKeyToString(encryptStr,privateKey);
            dcryptCostTime = dcryptCostTime + (System.currentTimeMillis() - dcryptStartTime);
        }
        System.out.println(String.format("------RSA解密数据大小:%s,次数:%s,总耗时:%s,平均耗时:%s ",size,num,dcryptCostTime,(dcryptCostTime/num)));
    }

    private static void testRsa() throws Exception {


        Map<String, String> rsaKeys = RsaUtils.generatorRsaKey();
        PrivateKey privateKey = RsaUtils.createPrivateKey(rsaKeys.get(GEN_PRIVATE_KEY));
        PublicKey publicKey = RsaUtils.createPublicKey(rsaKeys.get(GEN_PUBLIC_KEY));

        String content = getRandomStr(100);
        System.out.println("orig message = "+ content);
        String encryptStr = RsaUtils.encryptByPublicKeyToString(content,publicKey);
        System.out.println("encrypt message = "+ encryptStr);
        String decryptStr = RsaUtils.decryptByPrivateKeyToString(encryptStr,privateKey);
        System.out.println("decrypt message = "+ decryptStr);

    }
}

性能测试

AES加解密

image.png

RSA加解密

image.png

RSA密钥长度 最大加密内容大小
512 53 bytes
1024 117 bytes
2048 245 bytes
65536 16384 bits

结论

  • 解密要比加密耗时长,另外加密后的报文大小大于未加密的报文
  • RSA默认一次加密的内容长度不太够,如果使用需要对内容进行分段加密
  • AES加解密的效率明显优于RSA算法

资料