RSA公钥加密算法是RSA公钥加密算法是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。1987年7月首次在美国公布,当时他们三人都在麻省理工学院工作实习。RSA就是他们三人姓氏开头字母拼在一起组成的。
RSA是目前最有影响力和最常用的公钥加密算法,它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。同时RSA算法还能用于数字签名,因此,RSA是被应用最为广泛的公钥密码算法之一。
RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,即RSA算法是基于因子分解难题原理形成的加密算法。
RSA加密算法属于现代密码中的公钥密码机制。
RSA算法的关键是需要生成公钥和密钥;
RSA的安全性依赖于大数分解,但是否等同于大数分解一直未能得到理论上的证明,因为没有证明破解RSA就一定需要作大数分解。假设存在一种无须分解大数的算法,那它肯定可以修改成为大数分解算法。 RSA 的一些变种算法已被证明等价于大数分解。不管怎样,分解n是最显然的攻击方法。人们已能分解多个十进制位的大素数。因此,模数n必须选大一些,从安全性考虑,n的值至少要为1024~2048比特。
RSA算法同样能够应用于数字签名领域。
从上面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;
}
在使用RSA加密算法时,如果密钥使用不当的话也会有一定的危险,由于RSA既能作加密算法也能作数字签名算法,因此攻击者可以通过选择密文攻击的方式进行破解。
求取乘法逆元方法和计算乘方取模方法和因子分解攻击里的一样,在这里不在粘贴具体说明。
求乘法取模方法,求解 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;
}
在使用RSA加密的时候,对于加密明文m,用于生成公钥e的模数n只能使用一次,即下一次加密m时若还要生成公钥e2,必须更换n的值,否则,如果用同一个n生成了两个公钥e1和e2,则攻击者可以通过共用模数攻击进行破解m的值。
基本过程:
共用模数攻击用到的方法函数在上面两种攻击方法中都有用到,在这里不在具体粘贴说明。
RSA算法中的公钥e也被称为加密指数,如果选取的加密指数较低,并且使用了相同的加密指数给一个接受者的群发送相同的信息,那么可以进行广播攻击得到明文m。
基本过程:
设公钥e = 3,对相同的明文m进行了加密,
c1 = me mod n1
c2 = me mod n2
c3 = me mod n3
对这三个式子运用中国剩余定理
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