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<e<φ(n),且e与φ(n) 互质。(条件是1<e<φ(n),且e与φ(n) 互质。1到3120之间,随机选择了17。)

有一个整数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<n,则加密算法为:c=m^e mod n; c为密文,且0<=c<n。

3.4 RSA解密

对于密文0<=c<n,解密算法为:m=c^d mod n。

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 Maven 依赖

  • RSA加密 底层依赖的是 Bouncy Castle 的 bcprov-jdbc15on 库。
  • 这个库 包含了基本的密码学算法实现,如:对称加密算法(AES、DES等)、非对称加密算法(RSA、ECC等)、哈希函数(SHA-256、SHA-512等)、消息摘要算法等。
<!-- Bouncy Castle库 -->
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>

5.2 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.3 执行结果

公钥:(填充方式: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.4 线上验证

验证地址:

请添加图片描述

验证完毕,完结撒花 ~





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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不愿放下技术的小赵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值