RSA算法Java版(不要求支持大数)但实现了(dog

RSA算法(支持大数)

题目描述

C++中数据的类型与长度参考:

因此,C++最大能支持的十进制是19位的整数。如果要支持更大的整数,需要实现Big Number类。RSA目前比较安全的密钥长度是2048位二进制,即是617位的十进制。因此,C++自带的数据类型无法实现安全的RSA密钥加解密。

为了降低难度,该题不要求实现大数支持,因此只使用C++自带的long long 数据类型。

该实验主要包含三部分:

1. 公私钥的生成。

在公私钥生成中,有p、q、e三个参数是随机选择的,其中p、q要求是质数,因此需要实现一个函数检查一个整数是否是质数。由p、q的乘积可以得到n:n=pq,以及n的欧拉函数: φ(n) = (p-1)(q-1)。e是在(1, φ(n))之间随机选取的整数,需要满足gcd(e, φ(n)) = 1,因此,需要通过扩展欧几里得算法验证取得的e是与φ(n)互质的。d可以通过扩展欧几里得算法求得 。以满足
在这里插入图片描述
在这里插入图片描述

公钥为(n, e),私钥为(n,d)

检查一个整数是否为质数-Rabin-Miller算法,请参考:

https://blog.csdn.net/ECNU_LZJ/article/details/72675595

https://www.cnblogs.com/zwfymqz/p/8150969.html

https://www.cnblogs.com/zwfymqz/p/8150861.html

扩展欧几里得算法:请参考:

https://blog.csdn.net/u014117943/article/details/108428551

2.加密过程,使用加密算法c = m^e mod n,计算出密文c;

3.解密过程,使用私钥d和解密算法m = c^d mod n, ,计算m;

加密和解密过程需要做幂运算取余,如果直接先做幂运算再取余,则很容易出现溢出,因此,我们需要采用快速幂运算取余算法,请参考:https://jlice.top/p/7tbs7/

因此,该次实验主要难点在于以下三个算法的理解与实现:

  1. Rabin-Miller算法

  2. 扩展欧几里得算法

  3. 快速幂取余算法

根据前面的算法,我们知道明文和密文都不能大于n,假设n的长度为L,对于明文,我们需要按照L-1的长度对其分组然后再加密,每组的密文长度L。解密的时候使用L的长度对其进行分组然后解密,每组的明文长度为L-1。分组按照整数从低到高(即从右往左)

输入

第一行是p

第二行是q

第三行是e

第四行是待加密数据

第五行是待解密数据

输出

第一行输出p是否是质数

第二行输出q是否是质数

第三行打印n

第四行打印d

第五行显示输入第四行的加密结果

第六行显示输入第五行的解密结果

输入样例1

67
43
13
281
2154

输出样例1

Yes
Yes
2881
853
325
54

输入样例2

67
43
13
54281
3252154

输出样例2

Yes
Yes
2881
853
21540325
281054

输入样例3

11
17
7
88
11

输出样例3

Yes
Yes
187
23
11
88

前置条件

虽然题目没要求大数,但是现在不用以后也会遇到这种问题,就顺便学了一下Java.BigInteger类的常用API

求和:
BigInteger add(BigInteger val) 返回两个大整数的和
求差:
BigInteger subtract(BigInteger val) 返回两个大整数的差
求积:
BigInteger multiply(BigInteger val) 返回两个大整数的积
求商:
BigInteger divide(BigInteger val) 返回两个大整数的商
求模:
BigInteger mod(BigInteger val) 用当前大整数对val求模
求乘方:
BigInteger pow(int exponent) 返回当前大整数的exponent次方
求余:
BigInteger remainder(BigInteger val) 返回当前大整数除以val的余数
类型转换:
double doubleValue() 返回大整数的double类型的值
float floatValue() 返回大整数的float类型的值
long longValue() 返回大整数的long型值
int intValue() 返回大整数的整型值
其它:
BigInteger abs() 返回大整数的绝对值
BigInteger gcd(BigInteger val) 返回大整数的最大公约数
BigInteger max(BigInteger val) 返回两个大整数的最大者
BigInteger min(BigInteger val) 返回两个大整数的最小者
byte[] toByteArray(BigInteger val)将大整数转换成二进制反码保存在byte数组中

此外还有String和BigInteger的相互转化

public class test {
    public static void main(String[] args){
        String a = "1234567890123456789";
        //String转为BigInteger
        BigInteger b=new BigInteger(a);
        //BigInteger转为String
        String c = String.valueOf(b);
        System.out.println("b:"+b);
        System.out.println("c:"+c);
    }
}

判断素数还有比较大小…

.isProbablePrime(int certainty):如果该 BigInteger 可能是素数,则返回 true ;如果它很明确是一个合数,则返回 false 。 参数 certainty 是对调用者愿意忍受的不确定性的度量:如果该数是素数的概率超过了 1 - 1/2*certainty方法,则该方法返回 true 。执行时间正比于参数确定性的值。

AC代码

import java.math.BigInteger;
import java.util.Scanner;


/**
 * RSA算法(不要求支持大数)
 *
 * @author xzx
 * @date 2022/11/22
 */
public class Main {


    private static BigInteger x = BigInteger.ZERO;
    private static BigInteger y = BigInteger.ZERO;

    public static void main(String[] args) {
        // parse input arguments
        Scanner scanner = new Scanner(System.in);
        BigInteger p = scanner.nextBigInteger();
        BigInteger q = scanner.nextBigInteger();
        BigInteger e = scanner.nextBigInteger();
        BigInteger plainNumber = scanner.nextBigInteger();
        BigInteger encryptedNumber = scanner.nextBigInteger();
        System.out.println(isPrime(p) ? "Yes" : "No");
        System.out.println(isPrime(q) ? "Yes" : "No");

        BigInteger n = getN(p, q);

        // P 与 Q 均为质数 φ(n) = φ(P*Q)= (P-1)(Q-1)
        BigInteger mod = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
        System.out.println(n);
        if (exgcd(e, mod).equals(BigInteger.ONE)) {
            if (x.compareTo(BigInteger.ZERO) == -1) x = x.add(mod);
            System.out.println(x);
        }
        // 根据前面的算法,我们知道明文和密文都不能大于n,假设n的长度为L
        int L = n.toString().length();
        // 对于明文,我们需要按照【L-1的长度对其分组然后再加密,每组的密文长度L】。
        System.out.println(encrypt(plainNumber, n, e, L, L - 1));
        // 解密的时候使用【L的长度对其进行分组然后解密,每组的明文长度为L-1】
        System.out.println(decrypt(encryptedNumber, n, x, L - 1, L));

    }

    /**
     * 扩展欧几里得算法 ax+by=gcd(a,b)
     *
     * @param a
     * @param b
     * @return
     */
    private static BigInteger exgcd(BigInteger a, BigInteger b) {
        if (b.equals(BigInteger.ZERO)) {
            x = BigInteger.ONE;
            y = BigInteger.ZERO;
            return a;  //到达递归边界开始向上一层返回
        }
        BigInteger r = exgcd(b, a.mod(b));
        BigInteger temp = y;    //把x y变成上一层的
        y = x.subtract(a.divide(b).multiply(y));
        x = temp;
        return r;     //得到a b的最大公因数
    }

    /**
     * 解密过程,使用私钥d和解密算法m = c^d mod n
     *
     * @param c      密文
     * @param n
     * @param d
     * @param L      分组长度
     * @param length 明文长度
     * @return
     */
    private static BigInteger decrypt(BigInteger c, BigInteger n, BigInteger d, int L, int length) {
        return encrypt(c, n, d, L, length);
    }

    /**
     * 加密过程,使用加密算法c = m^e mod n,计算出密文c
     *
     * @param m      明文
     * @param n
     * @param e
     * @param L      算法前对数字分组的长度
     * @param length 算法后结果的长度(即存在补'0'操作)
     * @return
     */
    private static BigInteger encrypt(BigInteger m, BigInteger n, BigInteger e, int L, int length) {
        // m < n
        if (m.compareTo(n) != 1) {
            return powMod(m, e, n);
        }
        // 分组按照整数从低到高(即从右往左)
        String mStr = m.toString();
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = mStr.length(); i - length > -length; i -= length) {
            // 分组从左到右,长度为length
            String batchM = mStr.substring(Math.max(0, i - length), i);
            // 计算
            StringBuilder subStr = new StringBuilder(powMod(new BigInteger(batchM), e, n).toString());
            // 补足'0'
            while (subStr.length() < L) {
                subStr.insert(0, "0");
            }
            // 头插
            stringBuilder.insert(0, subStr);
        }
        return new BigInteger(stringBuilder.toString());
    }


    /**
     * 快速幂取余算法
     *
     * @param a
     * @param b
     * @param c
     * @return
     */
    private static BigInteger powMod(BigInteger a, BigInteger b, BigInteger c) {
        BigInteger result = BigInteger.ONE;
        while (!b.equals(BigInteger.ZERO)) {
            if (b.mod(BigInteger.valueOf(2)).equals(BigInteger.ONE)) {
                result = result.multiply(a).mod(c);
            }
            b = b.divide(BigInteger.valueOf(2));
            a = a.multiply(a).mod(c);
        }
        return result;
    }

    /**
     * p * q
     *
     * @param p
     * @param q
     * @return
     */
    private static BigInteger getN(BigInteger p, BigInteger q) {
        return p.multiply(q);
    }

    /**
     * 检查一个整数是否是质数
     *
     * @param number
     * @return
     */
    static boolean isPrime(BigInteger number) {
        return number.isProbablePrime(1);
    }
}

你可能感兴趣的:(密码学,算法,java,c++)