RSA算法原理详解

一些相关的数学概念

在理解RSA算法以前,咱们必须理解一些相关的数学概念。html

质数及互质

质数(Prime number)又称素数,指在大于1的天然数中,除了1和该数自身外,没法被其余天然数整除的数。大于1的天然数若不是素数,则称之为合数web

若是两个或两个以上的整数的最大公约数是 1,则称它们为互质(也叫互素)。两个整数 a 与 b 互质,记为 a ⊥ b。算法

互质的两个数,有以下性质swift

整数a和b互质当且仅当存在整数x,y使得xa+yb=1。安全

也就是说,若是a、b互质,那么xa+yb=1必然有解。若是xa+yb=1有解,则a、b必然互质;若是xa+yb=1无解,则a、b必然不是互质。bash

这个性质涉及扩展欧几里德算法,咱们后面会提到。网络

互质的判别方法主要有:函数

  • 两个不一样的质数必定互质。例如,2与七、13与19。
  • 较大的数是质数,则两数互质。如33与51
  • 一个素数,另外一个不为它的倍数,这两个数互质。例如,3与十、5与 26。
  • 相邻两个天然数互质。即,若是p是大于1整数,则p与p-一、p+1互质。如15与1六、14互质
  • 相邻两个奇数互质。如19与2一、17互质
扩展欧几里得算法

扩展欧几里得算法,能够用于咱们后面说的模反元素的计算,及一些性质的证实。性能

欧几里得算法

欧几里得算法,又叫展转相除法,是求最大公约数的一种算法。ui

假设

a = q*b + r
复制代码

其中a、b、q、r都是整数,gcd为计算公约数函数,则

gcd(a,b) = gcd(b,r)
复制代码

gcd(a,b) = gcd(b,a%b)
复制代码

这样,咱们就能够以log(n)的时间复杂度求解a、b的最大公约数。用swift实现为:

func gcd(a:Int,b:Int)->Int{
    return b == 0 ? a : gcd(a:b, b:a%b)
}
复制代码
扩展欧几里得算法

扩展欧几里德算法基本过程以下:

对于不彻底为 0 的非负整数 a,b,gcd(a,b)表示 a,b 的最大公约数,必然存在整数对 x,y ,使得

a*x + b*y = gcd(a,b)
复制代码

若是x0、y0是上述二元一次不定方程的解,那么该方程的通解为:

x = x0 + (b/gcd)*t
y = y0 – (a/gcd)*t
复制代码
扩展欧几里得算法的计算过程

若是已知任意整数a、b,求未知数x、y,使得:

a*x + b*y = gcd(a,b)
复制代码

用swift实现为:

static func gcdEx(a:Int,b:Int,x:inout Int,y:inout Int)->Int{
    if b == 0 { // 递归直到 b == 0
        x = 1
        y = 0
        return a
    }else{
        let r = gcdEx(a:b, b:a % b, x: &x, y: &y)
        let t = x
        x = y
        y = t - a/b * y
        return r
    }
}
复制代码

计算过程,大体以下:

  • 每次取a=bb=a%b,进行相同欧几里得扩展算法,而后压栈。此时不知道x、y,因此x、y仍是初始值
  • 当碰到一个特例b=0时,由于0与任何数的公约数是该数自己。即,当b=0时,a * 1 + 0 * 0 = a,也就是说x=1,y=0。
  • 获得x=1y=0以后,依次出栈。若是栈顶的解为x一、y1,栈下面的一个解(前一个解)为x0、y0,那么两组解有以下关系:
x0  = y1
y0  = x1 - (a0/b0) * y1
复制代码
  • 最后栈低出栈,获得原始的a、b的解:x、y

对与两组解的关系,简单证实以下:

对整数a0、b0,存在x0、y0使得:

a0 * x0 + b0 * y0 = gcd(a0,b0) (等式一)
复制代码

咱们令

a1 = b0   (等式二)
b1 = a0%b0  (等式三)
复制代码

对整数a一、b1,存在x一、y1使得:

a1 * x1 + b1 * y1 = gcd(a1,b1) (等式四)
复制代码

在计算机中,有

a0 % b0 = a0 - (a0/b0) * b0 (等式五)
复制代码

由欧几里得算法有:

gcd(a0,b0) = gcd(b0,a0%b0)   (等式六)
复制代码

联立上面六个等式得:

x0  = y1
y0  = x1 - (a0/b0) * y1
复制代码
欧拉函数
欧拉函数的定义

欧拉函数(Euler' totient function ):用φ(n)表示,是小于或等于n的正整数中与n互质的数的数目。

例如小于8的数中,与8互质的有一、三、五、7,一共4个,那么φ(8)=4。

欧拉函数的一些定理
  • 当n为1时,φ(1)=1

  • 当n为质数时,φ(n)=n-1。由于,两个数中较大一个数是质数,则两个数互质。那么质数n与全部小于本身的数互质。而小于n的正整数有n-1个

  • 若是整数m、n互质,则 φ(m*n)=φ(m)*φ(n);也就是说欧拉函数是积性函数

  • 当n为奇数时,φ(2n)=φ(n)

  • 若是n是质数p的k次幂,则φ(n)=φ(p^k)=p^k-p^(k-1)=(p-1)p^(k-1),由于除了p的倍数外,其余数都跟n互质。

欧拉函数的计算

将n表示为若干质数的乘积

n = p1^k1 * p2^k2 * p3^k3 ... * pn^kn
复制代码

其中p一、p二、p3...pn是都是质数

则欧拉函数通式为:

φ(n)=n * (1-1/p1) * (1-1/p2) * (1-1/p3) ... * (1-1/pn)
复制代码

由上式,咱们能够实现一个求欧拉函数的方法:

static func euler(n:Int)->Int{
    var n  = n
    var i  = 2
    var result  = Double(n)
    while i*i <=  n {
        if n % i == 0 { // 若是i是n的因子
            result = result * (1.0 - 1.0 / Double(i))
            while n % i == 0 { // 除去n全部i因子
                n /= i
            }
            print("i=\(i) n=\(n) reuslt=\(result)")
        }
        i += 1
    }
    // 处理最后一个质因子
    result = result * (1 - 1 / Double(n))
    return Int(result)
}
复制代码

该函数的时间复杂度为O(sqrt(n))

须要注意的是:

  • 计算欧拉函数的核心是找到n全部的质因子,也就是对n进行因数分解
  • 因为任何一个合数,它大于sqrt(n)的质因素最多只有一个。咱们在函数末尾已经处理了最后一个质因子。因此只需遍历到sqrt(n)便可。
模反元素
模运算

模运算,又称取模、模除,它用mod表示,也就是求余数运算。在程序里一般表示为%运算符。譬如:

17 mod 5 = 17 % 5 = 2
复制代码
单位元素

单位元素的定义是:任意一个元素和单位元素作了某种二元运算以后,获得的结果等于本来那个元素。

譬如加法的单位元素是0,任何数n与0进行加法运算后,获得的仍是n

依此,我么知道乘法的单位元素是1,矩阵乘法的单位元素是单位方阵

反元素

若是一个元素x和另外一个元素y作某种运算f,获得的结果是该运算f的单位元素,那么y就是x在这种状况下的反元素。

譬如,3的加分反元素是-3,由于 3+(-3)= 0

3的乘法反元素是1/3,由于 3 * 1/3 = 1

模反元素

模反元素也叫模逆元。

按照上面咱们说的反元素,它应该是在模运算下的反元素。而模运算的单位元素是1,即任意数n有n%1=n。那么若是整数a的模反元素是b,则有:

a % b = 1  <=>  a ≡ 1 (mod b)
复制代码

而实际上,模反元素除了模运算以后还有乘法运算,描述的是三个数以前的关系。

,或者能够说是在模运算下的乘法反元素

若是一个整数a对模数(同余数)n的模反元素是b,则有

a * b = 1 (mod n)
复制代码

也就是说,若是a、b的乘积,对n取模的余数是1,即

(a * b) % n = 1
复制代码

则,b是整数a对同余数n的模反元素。

譬如,在同余数为n=5时,整数a=3的模反元素是b=2,由于(3*2)%5=1

必然存在系数k,使得:

a * b - k*n + 1 
复制代码

这至关于一个二元一次方程:

a * x +  n * y = 1
复制代码
模反元素的性质

整数 a 对模数 n 之模逆元存在的充分必要条件是 a 和 n 互质

也就是说,若是a 和 n 互质,那么整数 a 对模数 n 的模反元素必然存在。反之,若是a、n最大公约数不是1,那么模反元素必然不存在。

简单证实一下:

若是a对模数n的模反元素为x,根据上一节咱们推出的二元一次方程,有

a * x + n * y = 1   (等式一)
复制代码

根据欧几里得扩展算法有:

a * x + n * y = gcd(a,n)  (等式二)
复制代码

若是a、n互质,则gcd(a,n)=1,那么咱们前面的到的二元一次方程必然是有解的,模反元素d存在。

若是gcd(a,n) != 1,等式一和二是矛盾的,等式一无解。

模反元素的计算

根据上面的推倒,咱们能够获得一个二元一次方程:

a * d + (-k) * n = 1
复制代码

对于这个二元一次方程,咱们能够用扩展欧几里得算法求解。

func gcdEx(a:Int,b:Int,x:inout Int,y:inout Int)->Int{
    if b == 0 {
        x = 1
        y = 0
        return a
    }else{
        // r = GCD(a,b) = GCD(b,a%b)
        // 递归直到a % b == 0
        let r = gcdEx(a:b, b:a % b, x: &x, y: &y)
        let t = x
        x = y
        y = t - a/b * y
        return r
    }
}
复制代码

则,求整数a对于模数n的模反元素d,即a*d + (-k)*n = 1的解,有

static func modularInverse(a:Int,n:Int,d:inout Int,k:inout Int){
    var x : Int = 0
    var y : Int = 0
    _  = gcdEx(a: Int(a), b:Int(n), x: &x, y: &y)
    d  = x
    k  = -y
}
复制代码

须要注意的是,这样计算的到的模反元素d有多是负数,而咱们指望的是在整数范围取值。也就是说,咱们指望d和k都是大于0的。即x=d=>0y=-k<=0

而二元一次不定方程的解,不止一个,咱们能够根据通解:

x = x0 + (b/gcd)*t
y = y0 – (a/gcd)*t
复制代码

找到知足咱们预期的最小的一组解,则求a对于同余数n的模反元素

static func modularInverse(a:UInt,n:UInt)->UInt{
    var x : Int = 0
    var y : Int = 0
    _ = gcdEx(a: Int(a), b:Int(n), x: &x, y: &y)
    if x < 0  || y > 0{
        let t0 = ceil((0 - Float(x)) / Float(n))
        let t1 = ceil((0 - Float(y)) / Float(a))
        let t  = Int(max(t0, t1))
        x = x + Int(n) * t
        y = y - Int(a) * t
    }
    let d  = UInt(x)
    let k  = UInt(-y)
    print("modular inverse a=\(a) n=\(n) d=\(d) k=\(k)")
    return d
}
复制代码
欧拉定理

欧拉定理,也称费马-欧拉定理,是一个关于同余的性质。

欧拉定理代表,若n、a为正整数,且n与a互质,则:

a ^ φ(n) = 1 (mod n)
复制代码

即:

a ^ φ(n) % n = 1
复制代码

也就是说a的φ(n)次方,对n取模获得余数是1.

由欧拉定理有:

a * a ^ (φ(n) - 1) = 1
复制代码

也就是说,若是整数a、n互质,那么必然存在a对于模数n的模反元素d

d = a ^ (φ(n) - 1)
复制代码
费马小定理

费马小定理是欧拉定理的一个特殊状况。也就是当模数n为质数的状况,此时:

φ(n) = n - 1
复制代码

a ^ (n-1) = 1 (mod n)
复制代码

密钥的生成

  • 一、随机选择两个不相等的大质数pq。假设咱们取
p=61,q=53
复制代码
  • 二、计算pq的乘积nn的二进制长度是密钥长度,通常是1024位,重要场合位2048位
n = p * q = 61 * 53 = 3233
复制代码
  • 三、计算n的欧拉函数φ(n)

欧拉函数φ(n) 是小于或等于n的正整数中与n互质的数的数目。

一个数的欧拉函数,等于其各个因子的欧拉函数之积,即

φ(n) = φ(p*q) = φ(p) * φ(q)
复制代码

由于质数与小于它的每个数,都构成互质关系。因此

φ(p) = p - 1
φ(q) = q - 1
复制代码

则:

φ(n) = φ(p*q) = φ(p) * φ(q) = (p-1)(q-1) = 60 * 52 = 3120
复制代码
  • 四、随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质。 假设咱们选择了17,即e=17。(实际应用中,经常选择65537。)

  • 五、计算e对于φ(n)的模反元素d。

ed ≡ 1 (mod φ(n))
复制代码

17 * d ≡ 1 (mod 3120)  <=> 17 * d - k * 3120 = 1
复制代码

用"扩展欧几里得算法"求解,用咱们上面的函数modularInverse(a:n:)计算,最后获得d=2753、k=15

  • 六、将(e,n)封装成公钥,(d,n)封装成私钥,即公钥(17, 3233),私钥(2753,3233)

加密算法

用公钥(e,n)对明文M进行加密,获得密文C

C = M^e mod n
复制代码

若是假设M=65,以前咱们获得的公钥是(17,3233),则:

C = 65^17 % 3233  = 2790
复制代码

须要注意的是:

  • 一、明文M必须是整数。而使用中M常常是字符串,咱们须要转化为相应的ascii值或unicode值,即转化为Data类型进行运算。
  • 二、M必须小于n。若是M大于n,咱们须要分块进行运算。即若是n是1024位,若是M大于1024位,须要切分红若干小于1024位的块。然后面咱们会说到Padding,每块还须要减去Padding的大小。

解密算法

用私钥解密密文C,获得明文M

M = C^d mod n
复制代码

即:

M = 2790 ^ 2753 % 3233 = 65
复制代码

签名算法

用私钥(d,n)对信息M进行签名,获得签名S

S = M^d mod n
复制代码

至关于用私钥对信息进行加密

验证签名算法

验证算法,先用公钥(e,n)对签名S进行下列运算获得M'

M' = S^d mod n 复制代码

至关于用公钥对签名进行解密

而后对比M和M',若是相等则验证成功

算法的简单证实

那么,如何保证用用私钥解密出来的数就是原来的明文呢。

根据加密规则

C ≡ M^e mod n (等式一)
复制代码

即:

M^e % n = C
复制代码

能够写成:

M^e - kn = C  (等式二)
复制代码

将上式带入解密算法:

M ≡ C^d mod n  (等式三)
复制代码

M ≡ (M1^e - kn)^d mod n
复制代码

则:

M^(ed) -k^dn^d - k2*n = M
复制代码

化简得

M^(ed) - (k^dn^(d-1) - k2)*n = M
复制代码

至关于

M^(ed) - k3*n = M   
复制代码

也就是求:

M^(ed) ≡ M (mod n)  (等式四)
复制代码

密钥生成时有:

ed ≡ 1 (mod φ(n))  (等式五)
复制代码

ed = hφ(n) + 1   (等式六)
复制代码

将上式带入等式四,有

M^(hφ(n) + 1) =  M (mod n) (等式七)

复制代码

化简得

M^hφ(n)  = 1 (mod n) (等式八)
复制代码

接下来,分红两种状况证实上面这个式子。

######(1)m与n互质。

由于m与n互质,根据欧拉定理有:

M^φ(n) ≡ 1 (mod n) (等式九)
复制代码

联立等式8、等式九,有

hφ(n) = φ(n)
复制代码

h=1时,等式成立,原式获得证实。

######(2)m与n不是互质关系。

由于M与n不是互质关系,因此M与n必然有大于一得公约数。而n的是质数p与q的乘积,因此n因数只有p、q。那么M与n的p、q之一必然是M与n的公约数。则 M=kpM=kq,必然有一个成立。

若是M同时是p、q的倍数,那么M=ln>=n,与RSA加密对明文的要求M<n互斥。因此M对于p、q,确定是一个的倍数,与另外一个互质。

假设M是p的倍数,与q互质,则有

M = kp  (等式十)
复制代码

由于M、q互质,由欧拉定理有

M^(φ(q) = 1 (mod q) (等式十一)
复制代码

则:

M^(φ(q) = 1 + l*q 
复制代码

两边同时取h次幂,有

(M^(φ(q))^h = (1 + l*q)^h (等式十二)
复制代码

由于对(1 + l*q)^h拆分以后,只有第一项1不含n,因此

(1 + l*q)^h % q = 1
复制代码

(1 + l*q)^h = 1 (mod q)  (等式十三)
复制代码

联立等式12、十三,有

(M^(φ(q))^h = 1 (mod q)  (等式十四)
复制代码

h=j(p-1),并将M=kp(φ(q)=q-1带入(等式十四),有

((kp)^(q-1))^(j(p-1)) ≡ 1 mod q (等式十五)
复制代码

φ(n)=(p-1)(q-1)带入上式,有

kp^jφ(n) ≡ 1 mod q => kp^(jφ(n) + 1)  ≡ kp (mod q)
复制代码

有上面的等式六,有jφ(n) + 1 = ed,带入上式,得

kp^ed = kp (mod q)
复制代码

kp^ed   = tq + kp  (等式十六)
复制代码

两边同时对p取模,

kp^ed % p  = tq % p + kp % p
复制代码

tq % p = 0
复制代码

由于p、q互质,因此t必然是p的倍数,即

t = rq
复制代码

带入等式十六

kp^ed   = rpq + kp
复制代码

由于M=kpn=pq,因此:

M^ed = rn + M => M^ed ≡ M (mod n)
复制代码

在生成密钥时

ed ≡ 1(modφ(n)) => ed=hφ(n)+1
复制代码

M^(hφ(n)+1) ≡ M (mod n) 
复制代码

M^hφ(n)  ≡  1 (mod n)
复制代码

最后证实等式八成立,原式得以证实。

算法的安全性

(n,e)做为公钥,是能够经过网络公开传播的。最主要是保证私钥(n,d)的安全性。而n是在公钥和私钥中都存在的,那么d就最为关键了。

算法的安全性取决于由(n,e)计算出d的难度。而e咱们已经知道,只要求出(n),就能获得d。

前面咱们给出了求φ(n)的一种算法,属于暴力破解,它的时间复杂度为O(sqr(n))。看似很简单,也很快,可是n每增长一位时间复杂度会指数增加,想象一下当n达到1024位时,须要的算力是多么恐怖。

而对极大数进行质因数分解,至今仍是世界级难题。

即使是超级计算机,也很难有效对两个质数相乘获得的合数进行质因数分解,因此这样的原理能够用于加密算法。

当合数全部的因子都很大时,采用强力方式获得具体的因子是很困难的,而这也正是 RSA体制理论的核心。

截止2000年,RSA模数分解的最大位数是768位。

截至目前,分解 1024 bit 以上的 RSA number 仍然是一项耗资巨大的工程难题,大整数分解问题仍然被认为是困难的。

因此,目前来讲普通应该场景采用1024位的是相对安全的,一些比较机密的场景须要采用2048位。

咱们能够在苹果的钥匙串中看到,大部分RSA整数基本都已是2018。而一般咱们应用时大多采用1024位就够了。位数越多加解密消耗的性能越高。

参考资料

相关文章
相关标签/搜索