• Java实现加密(二)RSA加解密


    1.背景知识:

    请添加图片描述

    • 在密码学中,加密算法分为单向加密双向加密
      • 单向加密包括MD5、SHA等摘要算法,它们是不可逆的。
      • 双向加密包括对称加密和非对称加密。双向加密是可逆的,存在密文的密钥。
        • 对称加密是指加密和解密使用相同的密钥,包括AES加密、DES加密等。
        • 非对称加密是指加密和解密使用不同的密钥,包括RSA加密等。

    2.RSA简介

    RSA: 是一种应用比较广泛的非对称加密算法,是由MIT工作的三人姓名首字母命名的。

    • RSA算法主要依靠分解大素数的复杂性来实现其安全性,由于大素数之积难被分解,因此该密码就难被破解。(素数:质数的别称,一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则成为合数,规定1既不是质数也不是合数。)
    • 从1977年提出到现在已经四十余年,经历了各种攻击的考验,普遍认为是目前最优秀的公钥方案之一。

    请添加图片描述

    3.RSA原理

    3.1 基本原理

    ​ RSA基于一个十分简单的数论事实:将两个大素数相乘十分容易,但想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成为私钥。公钥是可以发布的,供任何人使用,私钥则为自己所有,供解密之用。

    3.2 RSA公私钥生成流程

    随机找两个质数P和Q,P与Q越大,越安全。(例如:61和53)

    计算P和Q的乘积n。(n=61*53=3233,n的长度就是密钥长度。3233写成二进制是110010100001,一共有12位,所以这个密钥就是12位。)

    计算n的欧拉函数φ(n)。(根据公式 φ(n) = (p-1)(q-1) 算出 φ(3233) = 60 * 52,即3120。)

    随机选择一个整数e,条件是1

    有一个整数d,可以使得ed 除以φ(n) 的余数为 1。(ed ≡ 1 (mod φ(n)),即17*2753 mode 3120=1)

    将n和e封装成公钥,n和d封装成私钥。(n=3233,e=17,d=2753,所以公钥就是:3233,17,私钥就是:3233, 2753。)

    3.3 RSA加密

    ​ 首先对明文进行比特串分组,使得每个分组对应的十进制数小于n,然后依次对每个分组m做一次加密,所有分组的密文构成的序列就是原始消息的加密结果,即m满足0<=m

    3.4 RSA解密

    对于密文0<=c

    4.RSA 加密算法的优缺点

    优点: RSA算法是国际标准算法,属于主流算法之一,应用广泛,兼容性比较广,能够适用于各种不同的系统之中,不容易出现限制问题。

    缺点: RSA算法加密长度为2048位,对于服务器的消耗是比较大的,计算速度也比较慢,效率偏低,一般只适用于处理小量数据。

    总结: 尽管RSA加密算法运行消耗大,效率低,但是由于其优秀兼容性和安全性,它依旧是使用最广泛的非对称加密算法。

    5.Java实现

    注意:RSA加密包含Base64加密、byte[]和十六进制的转换

    加解密核心步骤:
    1.初始化密钥对
    2.获取公钥字符串: 初始化后的密钥对 -> 取公钥 -> Base64编码 -> 公钥字符串。
    3.生成私钥字符串: 初始化后的密钥对 -> 取私钥 -> Base64编码 -> 私钥字符串。
    4.公钥加密: 明文 -> 获取公钥(公钥字符串 -> Base64解码 -> 公钥) -> 公钥加密 -> Hex编码 -> 密文。
    5.私钥解密: 密文 -> 获取私钥(私钥字符串 -> Base64解码 -> 私钥) -> 私钥解密 -> Hex解码 -> 明文。

    测试地址:
    1.RSA密钥对:http://web.chacuo.net/netrsakeypair
    2.RSA公钥加密:http://tool.chacuo.net/cryptrsapubkey
    2.RSA私钥解密:http://tool.chacuo.net/cryptrsaprikey

    5.1 RSAUtil.java 源码

    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    
    import javax.crypto.Cipher;
    import java.io.ByteArrayOutputStream;
    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;
    
    /**
     * RSA加密工具类
     *
     * @author ACGkaka
     * @since 2021-09-19 19:11:03
     */
    public class RSAUtil {
    
        /**
         * 密钥类实例化入参
         */
        private static final String KEY_ALGORITHM = "RSA";
        /**
         * Cipher类实例化入参
         */
        private static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";
        /**
         * 密钥对中公钥映射key
         */
        private static final String PUBLIC_KEY = "RSAPublicKey";
        /**
         * 密钥对中私钥映射key
         */
        private static final String PRIVATE_KEY = "RSAPrivateKey";
        /**
         * 签名类实例化入参
         */
        private static final String SIGNATURE_ALGORITHM = "MD5withRSA";
        /**
         * RSA最大加密明文大小
         */
        private static final int MAX_ENCRYPT_BLOCK = 117;
    
        /**
         * 初始化密钥对生成器时,指定密钥大小的整数值(安全漏洞,长度至少为2048)
         */
        private static final int KEY_PAIR_INIT_SIZE = 2048;
    
        /**
         * RSA最大解密密文大小,
         * RSA 位数 如果采用1024 上面最大加密和最大解密则须填写: 117 128
         * RSA 位数 如果采用2048 上面最大加密和最大解密则须填写: 245 256
         */
        private static final int MAX_DECRYPT_BLOCK = 256;
    
        private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    
        /**
         * 获取公钥字符串
         *
         * @param keyMap 密钥对
         * @return 公钥字符串
         * @throws Exception 异常
         */
        public static String getPublicKeyStr(Map<String, Object> keyMap) throws Exception {
            //获得map中的公钥对象 转为key对象
            Key key = (Key) keyMap.get(PUBLIC_KEY);
            //编码返回字符串
            return encryptBASE64(key.getEncoded());
        }
    
        /**
         * 获取私钥字符串
         *
         * @param keyMap 密钥对
         * @return 私钥字符串
         * @throws Exception 异常
         */
        public static String getPrivateKeyStr(Map<String, Object> keyMap) throws Exception {
            //获得map中的私钥对象 转为key对象
            Key key = (Key) keyMap.get(PRIVATE_KEY);
            //编码返回字符串
            return encryptBASE64(key.getEncoded());
        }
    
    
        /**
         * 获取公钥
         *
         * @param key 公钥字符串
         * @return 公钥
         * @throws Exception 异常
         */
        public static PublicKey getPublicKey(String key) throws Exception {
            byte[] keyBytes;
            keyBytes = decryptBASE64(key);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PublicKey publicKey = keyFactory.generatePublic(keySpec);
            return publicKey;
        }
    
        /**
         * 获取私钥
         *
         * @param key 私钥字符串
         * @return 私钥
         * @throws Exception 异常
         */
        public static PrivateKey getPrivateKey(String key) throws Exception {
            byte[] keyBytes;
            keyBytes = decryptBASE64(key);
            // 修复异常:java.security.InvalidKeyException: IOException : algid parse error, not a sequence
            Security.addProvider(new BouncyCastleProvider());
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
            return privateKey;
        }
    
        /**
         * Base64解码,返回byte[]
         *
         * @param key 待解码字符串
         * @return 解码后的byte[]
         */
        public static byte[] decryptBASE64(String key) {
            return Base64.getMimeDecoder().decode(key);
        }
    
        /**
         * 将byte[]进行Base64编码
         *
         * @param key 待编码的byte[]
         * @return 编码后的字符串
         */
        public static String encryptBASE64(byte[] key) {
            return Base64.getMimeEncoder().encodeToString(key);
        }
    
        /**
         * 生成签名
         *
         * @param data          待生成签名内容
         * @param privateKeyStr 私钥
         * @return 签名信息
         * @throws Exception 异常
         */
        public static String sign(byte[] data, String privateKeyStr) throws Exception {
            PrivateKey priK = getPrivateKey(new String(hexToBytes(privateKeyStr)));
            Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
            sig.initSign(priK);
            sig.update(data);
            return bytesToHex(sig.sign());
        }
    
        /**
         * 验证签名
         *
         * @param data         待验证原文
         * @param sign         待验证签名
         * @param publicKeyStr 公钥
         * @return 是否验证成功
         * @throws Exception 异常
         */
        public static boolean verify(byte[] data, String sign, String publicKeyStr) throws Exception {
            PublicKey pubK = getPublicKey(new String(hexToBytes(publicKeyStr)));
            Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
            sig.initVerify(pubK);
            sig.update(data);
            return sig.verify(hexToBytes(sign));
        }
    
        /**
         * RSA加密
         * @param plainText 待加密内容
         * @param publicKeyStr 公钥字符串
         * @return 加密后内容
         * @throws Exception 异常
         */
        public static String encrypt(byte[] plainText, String publicKeyStr) throws Exception {
            PublicKey publicKey = getPublicKey(publicKeyStr);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            int inputLen = plainText.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            int i = 0;
            byte[] cache;
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(plainText, offSet, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(plainText, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptText = out.toByteArray();
            out.close();
            return bytesToHex(encryptText);
        }
    
        /**
         * RSA解密
         * @param encryptTextHex 已加密内容
         * @param privateKeyStr 私钥字符串
         * @return 解密后内容
         * @throws Exception 异常
         */
        public static String decrypt(String encryptTextHex, String privateKeyStr) throws Exception {
            byte[] encryptText = hexToBytes(encryptTextHex);
            PrivateKey privateKey = getPrivateKey(privateKeyStr);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            int inputLen = encryptText.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(encryptText, offSet, MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(encryptText, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] plainText = out.toByteArray();
            out.close();
            return new String(plainText);
        }
    
        public static Map<String, Object> initKey() throws Exception {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            keyPairGen.initialize(KEY_PAIR_INIT_SIZE);
            KeyPair keyPair = keyPairGen.generateKeyPair();
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            Map<String, Object> keyMap = new HashMap<>(2);
            keyMap.put(PUBLIC_KEY, publicKey);
            keyMap.put(PRIVATE_KEY, privateKey);
            return keyMap;
        }
    
        /**
         * 将byte[]转换为16进制字符串
         *
         * @param bytes 待转换byte[]
         * @return 转换后的字符串
         */
        public static String bytesToHex(byte[] bytes) {
            //一个byte为8位,可用两个十六进制位标识
            char[] buf = new char[bytes.length * 2];
            int a = 0;
            int index = 0;
            for (byte b : bytes) { // 使用除与取余进行转换
                if (b < 0) {
                    a = 256 + b;
                } else {
                    a = b;
                }
    
                buf[index++] = HEX_CHAR[a / 16];
                buf[index++] = HEX_CHAR[a % 16];
            }
            return new String(buf);
        }
    
        /**
         * 将16进制字符串转换为byte[]
         *
         * @param str 待转换字符串
         * @return 转换后的byte[]
         */
        public static byte[] hexToBytes(String str) {
            if (str == null || "".equals(str.trim())) {
                return new byte[0];
            }
    
            byte[] bytes = new byte[str.length() / 2];
            for (int i = 0; i < str.length() / 2; i++) {
                String subStr = str.substring(i * 2, i * 2 + 2);
                bytes[i] = (byte) Integer.parseInt(subStr, 16);
            }
    
            return bytes;
        }
    
        public static void main(String[] args) throws Exception {
            // 初始化密钥对
            Map<String, Object> keyMap = initKey();
            // 获取公钥字符串
            String publicKey = getPublicKeyStr(keyMap);
            // 获取私钥字符串
            String privateKey = getPrivateKeyStr(keyMap);
    
            // 打印公钥、私钥
            System.out.println("公钥:(填充方式:PKCS1_PADDING,输出类型:base64,字符集:utf8编码)");
            System.out.println("-----BEGIN PUBLIC KEY-----");
            System.out.println(publicKey);
            System.out.println("-----END PUBLIC KEY-----");
            System.out.println("\n");
    
            System.out.println("私钥:(填充方式:PKCS1_PADDING,输出类型:base64,字符集:utf8编码)");
            System.out.println("-----BEGIN RSA PRIVATE KEY-----");
            System.out.println(privateKey);
            System.out.println("-----END RSA PRIVATE KEY-----");
            System.out.println("\n");
    
            // 待加密内容,例:123
            String s = "123";
            // 进行RSA加密
            String encrypt = encrypt(s.getBytes(), publicKey);
            // 打印加密后内容
            System.out.println("密文:(填充方式:PKCS1_PADDING,输出类型:hex,字符集:utf8编码)");
            System.out.println(encrypt);
            System.out.println("\n");
    
            // 进行RSA解密
            String decrypt = decrypt(encrypt, privateKey);
            // 打印解密后内容
            System.out.println("解密后明文: ");
            System.out.println(decrypt);
        }
    }
    
    

    5.2 执行结果

    公钥:(填充方式:PKCS1_PADDING,输出类型:base64,字符集:utf8编码)
    -----BEGIN PUBLIC KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwdN4/2H4J6RzLu7gHnGQgMN4odQuwmzo
    r1/upmaHlXQqew2Ac5ZAaDs7hKLg7sO2+wgMLK2iZSB3hBMe2L8CmXmRPKPJU+WG6SzWZmm6gGMu
    18O1rlMKKxl9/WTBk8nb00Ft30bdA7vl0aEUOdoEz7ls45bu/ZFZeCpmdk3Q4NEYEmO4OB4uvIec
    1A6F5ZpEaKAWs/+i9c73hrnakgHgUhZUyA7A0ZYUqKCSXzgexDBOyd9dAoUrAv7PxQQlSCCOkKuK
    9dHh5+rH66mgKUiVNUzUiMlV3L4KlTFPlnMCbi1n5I3CvgeTWIanC4wFMzhBYJbtFIwFlgp9Sa+j
    y0x01QIDAQAB
    -----END PUBLIC KEY-----
    
    
    私钥:(填充方式:PKCS1_PADDING,输出类型:base64,字符集:utf8编码)
    -----BEGIN RSA PRIVATE KEY-----
    MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDB03j/YfgnpHMu7uAecZCAw3ih
    1C7CbOivX+6mZoeVdCp7DYBzlkBoOzuEouDuw7b7CAwsraJlIHeEEx7YvwKZeZE8o8lT5YbpLNZm
    abqAYy7Xw7WuUworGX39ZMGTydvTQW3fRt0Du+XRoRQ52gTPuWzjlu79kVl4KmZ2TdDg0RgSY7g4
    Hi68h5zUDoXlmkRooBaz/6L1zveGudqSAeBSFlTIDsDRlhSooJJfOB7EME7J310ChSsC/s/FBCVI
    II6Qq4r10eHn6sfrqaApSJU1TNSIyVXcvgqVMU+WcwJuLWfkjcK+B5NYhqcLjAUzOEFglu0UjAWW
    Cn1Jr6PLTHTVAgMBAAECggEAbeairOu38YJlbS81FQ3/iYNMWzYcbVGjfg0/HEr2hd+gVrWJKAEB
    9Bfh34sbT0bZ8ezWOl8ZvY1zNwhAbVWg+7TajS+xcEis/nnV96vre/DBFsZ5taaQFXAW6B7BRDMo
    2dg3nGpp1zwrS3myJjtgjwfsnf9u84f+2wvvnRTyYwYvfnYOGOe6MdIoKSDAZPVtHJHXhx7jtt0a
    H832U3liulI4H7yjZpqMO/jNwnVWB/wQlpnsBSQiR3pa+BpnQJzaY53te4g1vX4CLBfd8mCbcaS0
    rkqoKaMytyA7Uc5ztThTjJqNKsC0dPlr6T1jLtJcbEIoWoRu6SOgEWLOZ0ijAQKBgQDnPNVnaCY9
    pE35leINmV9xjwg4Yh4l8/KnaRLWd53uwiDolteVuN8uqR5ErZ/gkc9vHp5NuwHoJSmVAkeZqMT1
    ZlbLQybum1QAznzd4h9CiVjwn+WEvMBm6FRPBeo3ej78Q6qcw121MBJby2zQqw5128/VgbePQNEo
    DexTM+g6jQKBgQDWlQkTeo7uG5y3nK+u/FS2ZdzzsnMh68Ewxx/WLx1pTCK1bIoXglM8CiqNN3Fi
    iOQ0P8Q164ctn4X1zB+yu2HOLkwIjBrEFUjnyBD3xvp96386QE3BOQtHA6VgVkSGXfDlLIBtwUAc
    gcCCWFzYoSV5TK6zSm79RC0zI5qIWfJ1aQKBgQCMszpSmk+ycDg15ppOlgU6LsLcs+8OPtpmPQwG
    TXBep+aoP6hb5MqANM1DErZWScKDJYlDWMe3Pm2HyoRQnh2CCExFj82vn/nEJ+BYjk9hB/uDJnfc
    hZE4zKMIFlxGd269xlqY2lM5fU+eZTAH5B1/X0md6zkKxHC/w4EJu2rRvQKBgHzw1BMKZlSPsUVb
    rxN1CqIVV3xxqAXVLmyHVKsyTa60zPTT2OftUyd/R91nrdZQnIcrpcQ7ej7/RlGi48X+wuj5Hf5Q
    DXmkZwnF/NM4gTt2NmMlc+CQpVdY8R7RvB58bjoSGklNn1W52uxKJO1hjt883e+45D0FSEghb+X6
    cWkZAoGBANW3nScSIqbDxP3oY7Pmp+MmsJYM05RuGxKS11chNEJyLICUeXHli192T9rnPhjg8Xzz
    tDN1e55wkRI3wCiVpXw5VSaJdiFBSZUr09GVN21P1uoqkXUvoEM5QiStMy9dwk44B2Pki3n8xF/M
    MahlFyj6URw7QMSk7RiDc3TSdapP
    -----END RSA PRIVATE KEY-----
    
    
    密文:(填充方式:PKCS1_PADDING,输出类型:hex,字符集:utf8编码)
    70f00ee3a479ed6a0ebea17da9459bcbb9d1cbbdd456e1a13339f9632d842a23e423a8046cea40bbc7c6f0e6543a901e35457b65ffa5e774928eb58e374c7b3f51c3ee14339f9220ead039442520772b0d302d3aad7e84466eea57381131c0bfc866f5eb453b0890956e15992c36f2c860366575c29b3517e9ec562468169aedd45529849f9b3176f81d8fe5cdec5128b757a1e3c2291a635af1150a4db2b874237e668ba3990ff9a2919576baa8b7f4c7443a5fa91483bfb3a79fdfa55e1bec1bc08dc1eb2547153e47b9e3ea1a0de02845c2d167942d6621f2d06534e309356b7092eaf84e289d1a76905f6ab84b08522c2ad91718251378f0dd0f2acb965a
    
    
    解密后明文: 
    123
    

    5.3 线上验证

    验证地址:http://tool.chacuo.net/cryptrsaprikey

    请添加图片描述

    验证完毕,完结撒花 ~





    参考:
    1.RSA加密算法:https://blog.csdn.net/lbwnbnbnbnbnbnbn/article/details/124173910
    2.简述RSA加密算法:https://baijiahao.baidu.com/s?id=1734867691033700155&wfr=spider&for=pc

  • 相关阅读:
    跨域原理及解决
    2023-2024-2 高级语言程序设计-二维数组
    jQuery 密码验证和深入理解JSONP【前端jQuery框架】
    监听div元素高度变化的方案
    服务架构(微服务体系架构-项目)二.创建阿里云日志存储桶(日志记录-节省服务器及镜像资源,记录日志)
    神经网络是线性分类器吗,神经网络是线性的吗
    研发说:安全是软件开发的生命线
    datadog ebpf模块 offset-guess.o 问题排查解决
    SwiftUI 4 新功能大全之 Toggle与 Mixed Toggle 多个绑定组件
    MATLAB programming interface for STK software stkInit()
  • 原文地址:https://blog.csdn.net/qq_33204709/article/details/126939400