RSA加密算法及一般攻击方式

RSA算法

RSA公钥加密算法是RSA公钥加密算法是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。1987年7月首次在美国公布,当时他们三人都在麻省理工学院工作实习。RSA就是他们三人姓氏开头字母拼在一起组成的。

RSA是目前最有影响力和最常用的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。同时RSA算法还能用于数字签名,因此,RSA是被应用最为广泛的公钥密码算法之一。

RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即RSA算法是基于因子分解难题原理形成的加密算法。

RSA加密算法属于现代密码中的公钥密码机制。

文章目录

  • RSA算法
    • 1、RSA加密过程
    • 2、RSA数字签名过程
  • RSA算法的一般攻击方式
    • 1、因子分解攻击
    • 2、选择密文攻击
    • 3、共用模数攻击
    • 4、广播攻击
  • 完整代码下载

1、RSA加密过程

RSA算法的关键是需要生成公钥和密钥;

  1. 选择两个大素数p和q,n = p * q
  2. 则n的简化剩余系的个数(n的欧拉函数)φ(n) = (p - 1) (q - 1)。(由费马小定理容易证明)。
  3. 设RSA算法的公钥为e,私钥为d,选择较小素数e,保证 1 < e < φ(n), gcd(e,φ(n)) = 1,由式子:ed = 1 mod φ(n),计算私钥d的值;
  4. 公开n,e的值,保留p,q,d的值。即完成RSA密钥生成的过程;
  5. 加密时:设明文为m,密文为c,则c = me mod n;
  6. 解密时:m = cd mod n,即可完成解密。

RSA的安全性依赖于大数分解,但是否等同于大数分解一直未能得到理论上的证明,因为没有证明破解RSA就一定需要作大数分解。假设存在一种无须分解大数的算法,那它肯定可以修改成为大数分解算法。 RSA 的一些变种算法已被证明等价于大数分解。不管怎样,分解n是最显然的攻击方法。人们已能分解多个十进制位的大素数。因此,模数n必须选大一些,从安全性考虑,n的值至少要为1024~2048比特。

2、RSA数字签名过程

RSA算法同样能够应用于数字签名领域。

  1. 与RSA加密过程中的密钥生成阶段一样,生成公钥e和私钥d,并公开e和n,保留d;
  2. 在数字签名时,签名者计算 c = md mod n;
  3. 他人验证时,计算 m = ce mod n,即完成验证。

RSA算法的一般攻击方式

1、因子分解攻击

从上面RSA算法的加密过程我们可以看出模数n的重要性,如果我们知道了n的因子p和q的值,我们就能很容易的计算出**φ(n)**的值,进而通过 ed = 1 mod φ(n),就能计算出私钥d的值,进行破解。

因此,对RSA算法最直接的攻击方式就是因子分解攻击,但此方法也是最困难的方法,对于大数的因子分解到目前为止一直都是一个难题。

大数因子分解算法:Pollard’s Rho算法。能够较快找到一个合数的较小因子。

	// 大数因子分解
	public BigInteger rho(BigInteger N) {
		BigInteger divisor;
		BigInteger c = new BigInteger(N.bitLength(), random);
		BigInteger x = new BigInteger(N.bitLength(), random);
		BigInteger xx = x;

		if (N.mod(TWO).compareTo(ZERO) == 0)
			return TWO;

		do {
			x = x.multiply(x).mod(N).add(c).mod(N);
			xx = xx.multiply(xx).mod(N).add(c).mod(N);
			xx = xx.multiply(xx).mod(N).add(c).mod(N);
			divisor = x.subtract(xx).gcd(N);
		} while ((divisor.compareTo(ONE)) == 0);
		return divisor;
	}

	public void factor(BigInteger M) {
		BigInteger N = M;
		if (N.compareTo(ONE) == 0)
			return;
		if (N.isProbablePrime(5)) {
			p = N;
			return;
		}
		BigInteger divisor = rho(N);
		factor(divisor);
		factor(N.divide(divisor));
	}

Pollard’s Rho算法代码参考https://introcs.cs.princeton.edu/java/99crypto/PollardRho.java.html

因子分解攻击核心部分就是Pollard’s Rho算法,只要能够解出因子,剩下的部分就很容易了。

乘法逆元方法,利用拓展欧几里得算法计算乘法逆元,计算求取私钥 d = e-1 mod φ(n);

//乘法逆元
public BigInteger inv(BigInteger A, BigInteger B){
    BigInteger a = A, b = B;
    BigInteger quotient, remainder, lastY;
    BigInteger x = ZERO, y = ONE;
    BigInteger X = ONE, Y = ONE;
 
    while (!a.equals(ZERO)){
        quotient = b.divide(a);
        remainder = b.mod(a);
        b = a;
        a = remainder;
        lastY = y;
        y = y.multiply(quotient);
         
        if (X.equals(Y)){
            if (x.compareTo(y)>=0){
                y = x.subtract(y);
            }
            else{
                y = y.subtract(x);
                Y = ZERO;
            }
        }
        else{
            y = x.add(y);
            X = ONE.subtract(X);
            Y = ONE.subtract(Y);
        }
        x = lastY;
    }
 
    if (X.equals(ZERO)){
        x = B.subtract(x);
    }
    return x;
}

求乘方取模方法 求明文 m = cd mod n;

 //乘方取模  (temp^k1)%m
public BigInteger chengfang(BigInteger temp,BigInteger k1,BigInteger m) {
	BigInteger result = ONE;
	while (k1.compareTo(ZERO) > 0){
		if (k1.mod(TWO).intValue() == 1) {
			result = (result.multiply(temp)).mod(m);
			}
		temp = (temp.multiply(temp)).mod(m);
		k1 = k1.divide(TWO);
	}
	return result;
}

2、选择密文攻击

在使用RSA加密算法时,如果密钥使用不当的话也会有一定的危险,由于RSA既能作加密算法也能作数字签名算法,因此攻击者可以通过选择密文攻击的方式进行破解。

基本过程:
RSA加密算法及一般攻击方式_第1张图片

  1. 攻击者C欲破解B发给A的密文c;
  2. C选择 r < n,计算 t = r-1 mod n
  3. x = re mod n, y = xc mod n
  4. C把y发送给A,要求数字签名;
  5. A对y进行数字签名 u = yd mod n
  6. 计算 tu = r-1yd = r-1(x * c)d = r-1 r m = m mod n,得到m的值。

求取乘法逆元方法和计算乘方取模方法和因子分解攻击里的一样,在这里不在粘贴具体说明。

求乘法取模方法,求解 y = xc mod n;

//乘法取模
public BigInteger mul(BigInteger a, BigInteger b, BigInteger p){
    a=a.mod(p);
    b=b.mod(p);
    BigInteger c = (a.multiply(b)).divide(p);
    BigInteger ans =(a.multiply(b)).subtract(c.multiply(p));
    if(ans.compareTo(ZERO)==-1) 
    	ans = ans.add(p);
    else if (ans.compareTo(p)>=0) 
    	ans = ans.subtract(p);
    return ans;
}

3、共用模数攻击

在使用RSA加密的时候,对于加密明文m,用于生成公钥e的模数n只能使用一次,即下一次加密m时若还要生成公钥e2,必须更换n的值,否则,如果用同一个n生成了两个公钥e1和e2,则攻击者可以通过共用模数攻击进行破解m的值。

基本过程:

  1. 已知n, e1 ,e2 ,c1 ,c1 的值,
    c1 = me1 mod n
    c2 = me2 mod n
  2. 若e1和e2互素,有 re1 + se2 = 1,则可以通过
    (c1)r * (c2)s = m(re1+se2) = m mod n得到明文m的值。

共用模数攻击用到的方法函数在上面两种攻击方法中都有用到,在这里不在具体粘贴说明。

4、广播攻击

RSA算法中的公钥e也被称为加密指数,如果选取的加密指数较低,并且使用了相同的加密指数给一个接受者的群发送相同的信息,那么可以进行广播攻击得到明文m。

基本过程:

  1. 设公钥e = 3,对相同的明文m进行了加密,
    c1 = me mod n1
    c2 = me mod n2
    c3 = me mod n3

  2. 对这三个式子运用中国剩余定理
    cx = m3 mod n1n2n3,对cx开三次方就可以求得明文m。

中国剩余定理

//中国剩余定理
public BigInteger CRT(BigInteger[] C,BigInteger[] N) {
	BigInteger M = (N[0].multiply(N[1])).multiply(N[2]);
	BigInteger w0 = M.divide(N[0]);
	BigInteger w1 = M.divide(N[1]);
	BigInteger w2 = M.divide(N[2]);
	BigInteger t0 = inv(w0, N[0]);
	BigInteger t1 = inv(w1, N[1]);
	BigInteger t2 = inv(w2, N[2]);
	BigInteger x0 = ((C[0].multiply(t0)).multiply(w0)).mod(M);
	BigInteger x1 = ((C[1].multiply(t1)).multiply(w1)).mod(M);
	BigInteger x2 = ((C[2].multiply(t2)).multiply(w2)).mod(M);
	
	BigInteger x = ((x0.add(x1)).add(x2)).mod(M);
	return x;
}

广播攻击的难点核心在于最后一步开方运算,大数运算,且Java BigInteger类中没有直接计算开方的方法,需要自己写;

//number开n次方根,精度为scale
@SuppressWarnings("deprecation")
public BigDecimal bigRoot(BigDecimal number, int n, int scale, int roundingMode) {
	boolean negate = false;
	if (n < 0)
		throw new ArithmeticException();
	if (number.compareTo(BigDecimal.ZERO) < 0) {
		if (n % 2 == 0)
			throw new ArithmeticException();
		else {
			number = number.negate();
			negate = true;
		}
	}
	
	BigDecimal root;
	
	if (n == 0)
		root = BigDecimal.ONE;
	else if (n == 1)
		root = number;
	else {		
		final BigInteger N = BigInteger.valueOf(n);
		final BigInteger N2 = BigInteger.TEN.pow(n);
		final BigInteger N3 = BigInteger.TEN.pow(n - 1);
		final BigInteger NINE = BigInteger.valueOf(9);
		
		BigInteger[] C = new BigInteger[n + 1];
		for (int i = 0; i <= n; i++) {
			C[i] = combination(n, i);
		}

		BigInteger integer = number.toBigInteger();
		String strInt = integer.toString();
		int lenInt = strInt.length();
		for (int i = lenInt % n; i < n && i > 0; i++)
			strInt = "0" + strInt;
		lenInt = (lenInt + n - 1) / n * n;
		BigDecimal fraction = number.subtract(number.setScale(0, BigDecimal.ROUND_DOWN));
		int lenFrac = (fraction.scale() + n - 1) / n * n;
		fraction = fraction.movePointRight(lenFrac);
		String strFrac = fraction.toPlainString();
		for (int i = strFrac.length(); i < lenFrac; i++)
			strFrac = "0" + strFrac;

		BigInteger res = BigInteger.ZERO;
		BigInteger rem = BigInteger.ZERO;
		for (int i = 0; i < lenInt / n; i++) {
			rem = rem.multiply(N2);

			BigInteger temp = new BigInteger(strInt.substring(i * n, i * n + n));
			rem = rem.add(temp);

			BigInteger j;
			if (res.compareTo(BigInteger.ZERO) != 0)
				j = rem.divide(res.pow(n - 1).multiply(N).multiply(N3));
			else
				j = NINE;
			BigInteger test = BigInteger.ZERO;
			temp = res.multiply(BigInteger.TEN);
			while (j.compareTo(BigInteger.ZERO) >= 0) {
				//test = res.multiply(BigInteger.TEN);
				//test = ((test.add(j)).pow(n)).subtract(test.pow(n));
				test = BigInteger.ZERO;
				if (j.compareTo(BigInteger.ZERO) > 0)
					for (int k = 1; k <= n; k++)
						test = test.add(j.pow(k).multiply(C[k]).multiply(temp.pow(n - k)));
				if (test.compareTo(rem) <= 0)
					break;
				j = j.subtract(BigInteger.ONE);
			}

			rem = rem.subtract(test);
			res = res.multiply(BigInteger.TEN);
			res = res.add(j);
		}
		for (int i = 0; i <= scale; i++) {
			rem = rem.multiply(N2);

			if (i < lenFrac / n) {
				BigInteger temp = new BigInteger(strFrac.substring(i * n, i * n	+ n));
				rem = rem.add(temp);
			}

			BigInteger j;
			if (res.compareTo(BigInteger.ZERO) != 0) {
				j = rem.divide(res.pow(n - 1).multiply(N).multiply(N3));
			}
			else
				j = NINE;
			BigInteger test = BigInteger.ZERO;
			BigInteger temp = res.multiply(BigInteger.TEN);
			while (j.compareTo(BigInteger.ZERO) >= 0) {
				test = BigInteger.ZERO;
				if (j.compareTo(BigInteger.ZERO) > 0)
					for (int k = 1; k <= n; k++)
						test = test.add(j.pow(k).multiply(C[k]).multiply(temp.pow(n - k)));
				if (test.compareTo(rem) <= 0)
					break;
				j = j.subtract(BigInteger.ONE);
			}

			rem = rem.subtract(test);
			res = res.multiply(BigInteger.TEN);
			res = res.add(j);
		}
		root = new BigDecimal(res).movePointLeft(scale + 1);
		if (negate)
			root = root.negate();
	}
	return root.setScale(scale, roundingMode);
}

public static BigInteger combination(int n, int k) {
	if (k > n || n < 0 || k < 0)
		return ZERO;
	if (k > n / 2)
		return combination(n, n - k);
	BigInteger N1 = ONE;
	BigInteger N2 = ONE;
	BigInteger N = BigInteger.valueOf(n);
	BigInteger K = BigInteger.valueOf(k);
	for (int i = 0; i < k; i++) {
		N1 = N1.multiply(N);
		N2 = N2.multiply(K);
		N = N.subtract(ONE);
		K = K.subtract(ONE);
	}
	return N1.divide(N2);
}

完整代码下载

https://download.csdn.net/download/weixin_42182525/11422382

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