• RSA加密算法的数学原理


    RSA数学原理

    获取两个不相等的质数pq

    质数又称素数,在自然数中,除了1和自身外,不能被其他自然数整除。比如10以内的质数有:1,2,3,5,7。

    把p和q相乘,得到n

    比如:n=61*53=3233,用二进制表示为:110010100001。

    我们常说的RSA算法中的多少位,就是n用二进制表示后的位数,在我们例子就是12位。目前商用中一般都是2048位。

    计算出小于n的自然数中,有多少数与n互质(欧拉函数

    如果两个数的最大公约数为1,那么我们说这两个数互质,记:GCD(a,b)=1。其中GCD表示两个数的最大公约数。

    使用欧拉函数来判断是否互质:

    • 情况1:如果n=1,那么与n互质的自然数只有1
      φ ( n ) = 1 φ(n)=1 φ(n)=1

    • 情况2:如果n是质数,那么与n互质的自然数有n-1个,
      φ ( n ) = n − 1 φ(n)=n-1 φ(n)=n1
      例:φ(7)=6例:φ(7)=6

    • 情况3:如果n可以因式分解为两个互质数的乘积,则:
      φ ( n ) = φ ( p ) × φ ( q ) = ( p − 1 ) × ( q − 1 ) 例: φ ( 56 ) = φ ( 7 ) ∗ φ ( 8 ) = 6 ∗ 7 = 42 φ(n)=φ(p)\times φ(q)=(p-1)\times (q-1) \\ 例:φ(56)=φ(7)*φ(8) = 6 * 7 = 42 φ(n)=φ(p)×φ(q)=(p1)×(q1)例:φ(56)=φ(7)φ(8)=67=42

    • 情况4:如果n可以写成某个数的质数次幂(其中k为质数),则:
      φ ( n ) = φ ( p k ) = p k − p k − 1 = p k ( 1 − 1 p ) 例: φ ( 49 ) = φ ( 7 2 ) = 7 2 − 7 1 = 42 φ(n)=φ(p^k)=p^k-p^{k-1}=p^k(1-\frac 1p) \\ 例:φ(49)=φ(7^2)=7^2 - 7^1 = 42 φ(n)=φ(pk)=pkpk1=pk(1p1)例:φ(49)=φ(72)=7271=42

    • 情况5:根据以上规律,总结出一个通用的公式:

    n = p 1 k 1 × p 2 k 2 … p r k r 注:任意一个整数,都可以写成两个质数的乘积 \qquad n=p_1^{k^1} \times p_2^{k^2} \ldots p_r^{k^r} \quad 注:任意一个整数,都可以写成两个质数的乘积 n=p1k1×p2k2prkr注:任意一个整数,都可以写成两个质数的乘积
    ⇓ \qquad \Downarrow
    φ ( n ) = φ ( p 1 k 1 ) × φ ( p 2 k 2 ) … φ ( p r k r ) \qquad φ(n)=φ(p_1^{k^1}) \times φ(p_2^{k^2})\ldots φ(p_r^{k^r}) φ(n)=φ(p1k1)×φ(p2k2)φ(prkr)
    ⇓ \qquad \Downarrow
    φ ( n ) = p 1 k 1 ( 1 − 1 p 1 ) × p 2 k 2 ( 1 − 1 p 2 ) … p r k r ( 1 − 1 p r ) \qquad φ(n)= p_1^{k^1}(1- \frac 1p_1) \times p_2^{k^2}(1- \frac 1p_2) \ldots p_r^{k^r}(1- \frac 1p_r) φ(n)=p1k1(1p11)×p2k2(1p12)prkr(1p1r)
    ⇓ \qquad \Downarrow
    φ ( n ) = p 1 k 1 × p 2 k 2 … p r k r × ( 1 − 1 p 1 ) × ( 1 − 1 p 2 ) … ( 1 − 1 p n ) \qquad φ(n)=p_1^{k^1} \times p_2^{k^2}\ldots p_r^{k^r}\times (1- \frac 1p_1)\times(1- \frac 1p_2)\ldots(1- \frac 1p_n) φ(n)=p1k1×p2k2prkr×(1p11)×(1p12)(1p1n)
    ⇓ \qquad \Downarrow
    φ ( n ) = n × ( 1 − 1 p 1 ) × ( 1 − 1 p 2 ) … ( 1 − 1 p n ) \qquad φ(n)=n\times(1- \frac 1p_1)\times(1- \frac 1p_2)\ldots(1- \frac 1p_n) φ(n)=n×(1p11)×(1p12)(1p1n)

    • 总结:通过欧拉函数最后的通式,我们发现最后的结果只和n和p有关,和p的幂无关,这点很重要,在我们用程序实现时,能够大大的简化我们的逻辑代码。

    回到算法中,我们需要计算与n互质的个数,也就是求φ(n),根据欧拉函数,计算过程如下:
    φ ( n ) = φ ( 3233 ) = φ ( 61 ) × φ ( 53 ) = 60 × 52 = 3120 φ(n)=φ(3233)=φ(61)×φ(53)=60×52=3120 φ(n)=φ(3233)=φ(61)×φ(53)=60×52=3120

    在1和φ(n)之间,选取一个随机质数e

    即在1~3120中选取e=17。

    求e和φ(n)的模反元素d(裴蜀定理、扩展欧几里得算法)

    模反元素也称为模倒数,可以写成 a − 1 ≡ b   ( m o d   n ) a^{−1}≡b \ (mod \ n) a1b (mod n) a b ≡ 1   ( m o d   n ) ab≡1 \ (mod \ n) ab1 (mod n)。(ab和1关于n同模)

    如果两个正整数a和n互质,那么一定可以找到整数b,使得a*b与n相除,余数为1,记作: ( a × b ) % n = 1 ⇒ ( a × b ) − 1 n = 1 (a \times b) \% n = 1 \Rightarrow \frac {(a \times b) - 1} n = 1 (a×b)%n=1n(a×b)1=1

    例:求3和11的模反元素
    ( 3 × b ) − 1 11 = 1 = > b = 4 \frac {(3 \times b) - 1} {11} =1 => b = 4 11(3×b)1=1=>b=4

    • 通过上面的运算,我们可以算出一些简单数的模反元素,对于较大的数来说,我们需要引入新的计算工具:裴蜀定理,通过它,我们可以通过一个二元一次方程来得出模反元素。在求模反元素时,给予二个整数a、b,必存在整数x、y使得ax + by = gcd(a,b),而我们真正要获取的是x的值。
    • 定义:如果a与b互质,即GCD(a, b) = 1,二元一次方程 a x + b y = 1 ax + by = 1 ax+by=1有一对正整数解,其中x即为a、b的模反元素。同理,上面的例子我们可以化简成这样:3x + 11y = 1
    • 疑惑:对于二元一次方程,好像不可解(也可以说有无穷多个解),我们之前都是解方程组。即然定理已经解决,两个互素(质)数组成的二元一次方程有一对整数解,那肯定是能解,解法我们需要引入另一个数学工具:扩展欧几里得算法

    扩展欧几里得算法这个算法其实就是上面我们求最大公约数时,用到的辗转相除法+它的逆运算,我们看个例子就明白是什么意思了

    例1:求 47 x + 30 y = 1 47x + 30y = 1 47x+30y=1的解

    解:使用辗转相除法,我们可以得到:
    47 = 30 × 1 + 17   30 = 17 × 1 + 13   17 = 13 × 1 + 4     13 = 4 × 3 + 1       4 = 1 × 4 + 0        47 = 30 \times 1 + 17 \ \\ 30 = 17 \times 1 + 13 \ \\ 17 = 13 \times 1 + 4 \ \ \ \\ 13 = 4 \times 3 + 1 \ \ \ \ \ \\ 4 = 1 \times 4 + 0 \ \ \ \ \ \ 47=30×1+17 30=17×1+13 17=13×1+4   13=4×3+1     4=1×4+0      
    我们可以得到GCD(47, 30) = 1,对到处第二行,我们移项处理:
    1 = 13 − 4 × 3 4 = 17 − 13 × 1 13 = 30 − 17 × 1 17 = 47 − 30 × 1 1 = 13 - 4 \times 3 \\ 4 = 17 - 13 \times 1 \\ 13 = 30 - 17 \times 1 \\ 17 = 47 - 30 \times 1 \\ 1=134×34=1713×113=3017×117=4730×1
    我们把第二行代入第一行中:
    1 = 13 − ( 17 − 13 × 1 ) ⏞ 4 × 3 ⇓ 1 = 4 × 13 − 3 × 17 ⇓ 1 = 4 × ( 30 − 17 ) ⏞ 13 − 3 × s 17 ⇓ … 1 = ( − 7 ) × 47 + 11 × 30 解得: x = − 7 ( 即为我们要求的模反元素 d ) y = 11 1 = 13 - \overbrace{(17 - 13 \times 1)}^4 \times 3 \\ \Downarrow \\ 1 = 4 \times 13 - 3 \times 17 \\ \Downarrow \\ 1 = 4 \times \overbrace{(30 - 17)}^{13} - 3 \times s17 \\ \Downarrow \\ \ldots \\ 1 = (-7) \times 47 + 11 \times 30 \\ 解得:x=-7(即为我们要求的模反元素d) \quad y = 11 1=13(1713×1) 4×31=4×133×171=4×(3017) 133×s171=(7)×47+11×30解得:x=7(即为我们要求的模反元素d)y=11
    回到算法中,我们根据e=17和φ(n)=3120,得到一个二元一次方程: 17 x + 3120 y = 1 17x + 3120y = 1 17x+3120y=1,再根据扩展欧几里得算法,我们可以得到方程的解: x = 2753 即: d = 2753 x = 2753 \quad 即:d = 2753 x=2753即:d=2753

    把n和e封装成公钥,n和d封装成私钥
    • 公钥: ( 3233 , 17 ) (3233, 17) (3233,17)
    • 私钥: ( 3233 , 2753 ) (3233, 2753) (3233,2753)
    实例

    私钥和公钥获取:

    1. 取p=47, q=71;

    2. 则n = p * q = 3337, Φ(n)=(p-1)(q-1)=3220;

    3. 随机选择加密密钥e,e与Φ(n)互素,取e=79;

    4. 79 * d=1(mod n) =》 d = 10 1 9 101^9 1019

    5. 公钥为(3337,79),私钥为(3379,1019)

    假设要加密的明文是m=6882326879666683:

    • 首先,根据n的大小将m进行分组,这里我们把明文m分成六个组,即:m1=688, m2=232, m3=687, m4=966, m5=668, m6=003;
    • 接着分别对各个分组进行加密运算,第一个分组加密为c1= 68 8 79 688^{79} 68879(mod 3337) = 1570,类似的,对其余各个分组分别进行加密运算,得到如下密文:c=1570 2756 2091 2276 2423 158;
    • 解密时用私钥1019分别对明文进行解密运算,即:m1= 157 0 1019 1570^{1019} 15701019(mod 3337) = 688,对其余的密文用同样的计算方法就可以把密文恢复出来,即得到密文。
    RSA参数的选择

    p和q的选择:

    • p和q要足够大

    • p和q应为强素数。如p满足以下三个条件,即为强素数:

      (1)P-1有大素数因子r

      (2)P+1有大素数因子s

      (3)R-1有大素数因子t

    • p和q的差不能太小

    • p-1和q-1的最大公因数应很小

    公钥e的选择:e不能太小;最好选择e为modΦ(n)的阶数,意思就是要使i的值尽量大才能使得ei≡ (mod Φ(n))成立。i等于(p-1)(q-1)/2是最好的。一般建议取e= 2 16 2^{16} 216+1=65537

    代码

    (1)质数判断

    import Gcd from './Gcd'
    /**
     * 质数类
     */
    class Prime {
        private static pNumArr: Array = []
        /**
         * 判断一个数是否为质数
         * @param num 转入值
         */
        public static isPrimeNum(num: number): boolean {
            // 1和2都是质数
            if (num < 3) {
                return true
            }
            // i取开方,效率要比/2高出许多
            for (let i = 2; i <= Math.sqrt(num); i++) {
                if (num % i === 0) {
                    return false
                }
            }
            return true
        }
        /**
         * 1. 任意两个质数构成互质关系,比如13和61。
          2. 一个数是质数,另一个数只要不是前者的倍数,两者就构成互质关系,比如3和10。
          3. 如果两个数之中,较大的那个数是质数,则两者构成互质关系,比如97和57。
          4. 1和任意一个自然数是都是互质关系,比如1和99。
          5. p是大于1的整数,则p和p-1构成互质关系,比如57和56。
          6. p是大于1的奇数,则p和p-2构成互质关系,比如17和15。
         */
        public static coprime(n1: number, n2: number): boolean {
            // 保证n1比n2要大
            if (n2 > n1) {
                let tmp = n1
                n1 = n2
                n2 = tmp
            }
            // 两个数中, 有一个是1, 则两数互质
            if (n1 === 1 || n2 === 1) {
                return true
            }
            // 如果两数相邻, 则两数互质
            if (n1 - n2 === 1) {
                return true
            }
            // 如果大数为奇数, 则n1-n2 = 2, 则两数互质
            if (n1 % 2 === 1 && n1 - n2 === 2) {
                return true
            }
            // 如果较大的数为质数, 则两数互质
            if (this.isPrimeNum(n1)) {
                return true
            }
            // 有一个是质数, 别一个不为此数的倍数, 则两数互质
            if ((this.isPrimeNum(n1) || this.isPrimeNum(n2)) && n1 % n2 !== 0) {
                return true
            }
            // 如果两个数都为质数, 则两个数互质
            if (this.isPrimeNum(n1) && this.isPrimeNum(n2)) {
                return true
            }
            // 如果两个数的最大公约数为1, 则两个数互质
            if (Gcd.gcd(n1, n2) === 1) {
                return true
            }
            return false
        }
        /**
         * 获取该数下所有的质数
         * @param num 转入的数
         */
        public static getAllPrimeNum(num: number) {
            let arr = []
            for (let i = 1; i <= num; i++) {
                if (this.isPrimeNum(i)) {
                    arr.push(i)
                }
            }
            return arr
        }
    
        /**
         * 把一个正整数分解为两个质数的乘积
         * @param num
         */
        public static break(num: number) {
            this.getBreakArr(num)
            return this.pNumArr
        }
    
        /**
         * 把一个正整数分解为两个质数的乘积(中间步骤)
         * @param num
         */
        private static getBreakArr(num: number) {
            // 获取该数/2的所有质数
            let arr = this.getAllPrimeNum(Math.floor(num / 2))
            // 能被这个数整除的最大质数
            let maxNum = 1
            for (let i = 0; i < arr.length; i++) {
                if (num % arr[i] === 0) {
                    maxNum = arr[i]
                }
            }
            // 除数
            let divNum = num / maxNum
            if (maxNum !== 1) {
                this.pNumArr.push(maxNum)
                return this.break(divNum)
            } else {
                this.pNumArr.push(divNum)
                return maxNum
            }
        }
    }
    
    export default Prime
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118

    (2)欧拉函数计算

    import Prime from './Prime'
    import Util from './Util'
    /**
     * 欧拉函数
     */
    class Euler {
        /**
         * 根据欧拉函数取与此数互质的个数
         * @param num 参数
         */
        public static getEulerNum(num: number) {
            Prime.break(num)
            let arr = Util.uniqueArr(Prime.break(num))
            let result = num
            for (let i = 0; i < arr.length; i++) {
                result *= 1 - 1 / arr[i]
            }
            return Math.floor(result)
        }
    }
    
    export default Euler
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    (3)最大公约数计算-欧几里得算法-辗转相除法

    /**
     * 最大公约数
     */
    class Gcd {
        /**
         * 计算2个数的最大公约数
         * @param n1 数1
         * @param n2 数2
         */
        public static gcd(n1: number, n2: number): number {
            let s = Math.floor(n1 / n2)
            let y = n1 % n2
            // console.log(n1 + ' = ' + n2 + '*' + s + ' + ' + y)
            if (y === 0) {
                return n2
            } else {
                return this.gcd(n2, y)
            }
        }
        /**
         * 扩展欧几里德算法 - 求方程的解 (详细注释版)
         * 我们要求:47x + 30y = 1的解,下面为辗转相除法的步骤:
         * 47 = 30*1 + 17
         * 30 = 17*1 + 13
         * 17 = 13*1 + 4
         * 13 = 4*3 + 1
         *
         *
         * @param num1 数1
         * @param num2 数2
         */
        public static gcdEx(n1: number, n2: number): Array {
            // 两数相除的商
            let q = Math.floor(n1 / n2)
            // 两数相除的余数
            let r = n1 % n2
            // 当到 13 = 4*3 + 1 即4 % 1 = 0时 返回
            // 返回值为:1 | -4 即:x=1 y=-4
            if (n2 % r === 0) {
                // 转化为:1 = 13 + 4*(-3)
                // 所以返回:x=1 y=q*(-1)=-3
                return [1, q * -1]
            } else {
                let ret = this.gcdEx(n2, r)
                // x和y为上一步返回的值
                let x = ret[0]
                let y = ret[1]
                // x1和y1为当前步骤的值
                let x1 = 1
                let y1 = -q
                // console.log('----', x, y, x1, y1)
                // x2和y2为当前步骤计算得到的值
                // 1 = 13 + 4*(-3)
                // 4 = 17 + 13*(-1)
                // 代入得:1 = 13 + (17 + 13*(-1)) * (-3)
                // 计算得:1 = 17*(-3) + 13*4 ==> x=-3 | y=4
                let x2 = y * x1
                let y2 = y * y1 + x
                ret = [x2, y2]
                return ret
            }
        }
    }
    export default Gcd
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    (4)裴蜀定理的求解-扩展欧几里得算法

    /**
     * 最大公约数
     */
    class Gcd {
        /**
         * 计算2个数的最大公约数
         * @param n1 数1
         * @param n2 数2
         */
        public static gcd(n1: number, n2: number): number {
            let s = Math.floor(n1 / n2)
            let y = n1 % n2
            // console.log(n1 + ' = ' + n2 + '*' + s + ' + ' + y)
            if (y === 0) {
                return n2
            } else {
                return this.gcd(n2, y)
            }
        }
        /**
         * 扩展欧几里德算法 - 求方程的解 (详细注释版)
         * 我们要求:47x + 30y = 1的解,下面为辗转相除法的步骤:
         * 47 = 30*1 + 17
         * 30 = 17*1 + 13
         * 17 = 13*1 + 4
         * 13 = 4*3 + 1
         *
         *
         * @param num1 数1
         * @param num2 数2
         */
        public static gcdEx(n1: number, n2: number): Array {
            // 两数相除的商
            let q = Math.floor(n1 / n2)
            // 两数相除的余数
            let r = n1 % n2
            // 当到 13 = 4*3 + 1 即4 % 1 = 0时 返回
            // 返回值为:1 | -4 即:x=1 y=-4
            if (n2 % r === 0) {
                // 转化为:1 = 13 + 4*(-3)
                // 所以返回:x=1 y=q*(-1)=-3
                return [1, q * -1]
            } else {
                let ret = this.gcdEx(n2, r)
                // x和y为上一步返回的值
                let x = ret[0]
                let y = ret[1]
                // x1和y1为当前步骤的值
                let x1 = 1
                let y1 = -q
                // console.log('----', x, y, x1, y1)
                // x2和y2为当前步骤计算得到的值
                // 1 = 13 + 4*(-3)
                // 4 = 17 + 13*(-1)
                // 代入得:1 = 13 + (17 + 13*(-1)) * (-3)
                // 计算得:1 = 17*(-3) + 13*4 ==> x=-3 | y=4
                let x2 = y * x1
                let y2 = y * y1 + x
                ret = [x2, y2]
                return ret
            }
        }
    }
    export default Gcd
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
  • 相关阅读:
    使用Dockerfile安装redis镜像
    oracle查询前几条数据的方法
    c++ 左值,右值
    零代码编程:用ChatGPT批量下载podomatic播客RSS页面音频
    学妹学Java(一)
    安装mysql时候的一些坑
    ts中的元组是什么有什么用
    软件成本评估的6个步骤
    GPT-4最新详解:能力对比,语言,视觉输入,操纵性,聊天GPT Plus等
    APS成功实施的关键要点
  • 原文地址:https://blog.csdn.net/qq_44766883/article/details/126817837