前言:本周继续进行了密码学的理论学习。对于密码学来说,对数学的理解是非常深刻的,理论是非常重要的环节,对于密码学的题目很多都是有工具可以进行解密操作的,但是数学基础依旧是少不了的,一方面可以提高自己的思维另一方面可以提高自己的算法能力。
这周详细看了同余方程,理解了同余方程的解法,孙子定理,二次剩余、Legendre与Jacobi符号。
结合了RSA算法,这才知道我前面学的理论部分有多么重要,例如计算模幂运算、模的性质、素数分解等等,在ctf题目中也有关于RSA的题目,但是一般都可以使用工具进行解密这里也不再赘述了。
理论部分就放图了使用Latex打公式实在是累人。
RSA是著名和可靠的非对称密钥加密算法。已经成为公钥密码的国际标准,其安全性依赖于大整数因子分解的困难性,
其加密与解密的过程如下:
注意N,E为公开
1.选择两个大素数P,Q,(两个数都接近于1024bit是安全的)
2.计算N=P*Q;
3.选择一个公钥(即加密公钥)E,使其不是(P-1)与(Q-1)的因子(欧拉函数,不大于n且与n互质的整数个数f(n)=(P-1)(Q-1)),意思就是gcd(E,f(n))=1。
4.选择私钥(即解密密钥)D,满足:
d * E mod(P-1) * (Q-1)=1 (欧几里得扩展算法计算私钥d)
5.加密时从明文PT计算密文CT如下:
CT=PT^E mod N
6.将密文CT发送给接收方
7.解密时,从CT计算PT如下:
PT=CT ^ D mod N
选取p,q应该满足下列要求:
1.p,q差值不应太小,gcd(p-1,q-1)尽可能小,p,q为强素数
实例
1.选择P=7,Q=17;
2.计算N=7*17=119
3.求(7-1)(17-1)=96,因此E的因子不能有2,3,假设选择5
4.选择私钥D,分别将E,P,Q代入可以得到d=77
5.假设加密明文10,则
CT=PT^E MOD N=10^5 mod 119=40
6.将密文CT 40发送给接收方
7.解密时,从CT计算PT如下:
PT=CT^D MOD N=40^77 mod 119=10
这里出现了40^77 mod 119的计算,所以有必要对模幂运算做一个解释与说明,见下图,并且写出关于模幂过程:
typedef _int64 BigInt
BigInt modexp(BigInt b,BigInt e,BigInt m)
{
b%=m;
BigInt result=1;
while(e>0)
{
if( e&1 == 1 )
result = (result*b)%m;
// multiply in this bit's contribution while using modulus to keep result small
// move to the next bit of the exponent, square (and mod) the base accordingly
e >>= 1;
b = (b*b)%m;
}
return result;
}
可以看出RSA比较简单,关键是选择正确的密钥。在实际过程中,P,Q都是很大的数字,因此从N求出P,Q不容易是相当复杂的,由于很难得出P,Q,也就无法求出D,(D取决于P,Q,E)数学分析证明,N为100位数时,要70多年才能计算出P,Q。
到目前为止还没有关于成功攻击RSA的报道,但仍有攻击的可能。
1.明文攻击
利用较短消息的攻击;周期性攻击;公开消息的攻击
2.因数分解攻击
攻击者把N分解为P,Q,可以得到私钥(非常难)
出现比较早的方法就是试除法,完全尝试小于n的所有素数,尝试次数为2sqrt(n)/log(sqrt(n))
二次筛选法:费马因子分解:找出x^2, y^2,使得 x^2 - y^2=nk,令k=1,直接计算n+1 ,n+2 ^2 ,n+3^2。如找n=295927,可以发现295927+9=295926=544544,故分解以后为547541.
3.对加密密钥的攻击,推荐使用E=2^16+1=65537
4.对解密密钥的攻击
// Java Program to Implement the RSA Algorithm
import java.math.*;
import java.util.*;
class RSA {
public static void main(String args[])
{
int p, q, n, z, d = 0, e, i;
// The number to be encrypted and decrypted
int msg = 12;
double c;
BigInteger msgback;
// 1st prime number p
p = 3;
// 2nd prime number q
q = 11;
n = p * q;
z = (p - 1) * (q - 1);
System.out.println("the value of z = " + z);
for (e = 2; e < z; e++) {
// e is for public key exponent
if (gcd(e, z) == 1) {
break;
}
}
System.out.println("the value of e = " + e);
for (i = 0; i <= 9; i++) {
int x = 1 + (i * z);
// d is for private key exponent
if (x % e == 0) {
d = x / e;
break;
}
}
System.out.println("the value of d = " + d);
c = (Math.pow(msg, e)) % n;
System.out.println("Encrypted message is : " + c);
// converting int value of n to BigInteger
BigInteger N = BigInteger.valueOf(n);
// converting float value of c to BigInteger
BigInteger C = BigDecimal.valueOf(c).toBigInteger();
msgback = (C.pow(d)).mod(N);
System.out.println("Decrypted message is : "
+ msgback);
}
static int gcd(int e, int z)
{
if (e == 0)
return z;
else
return gcd(z % e, e);
}
}