RSA计算过程及RSA密钥生成代码

Posted by ZSJ on January 15, 2019

RSA计算过程

1. 取两个质数p, q

p = 23, q = 29

2. 计算n = p*q = 23 * 29 = 667

667用二进制表示为1010011011 共十位,这个位数就是RSA中的位数

3. 计算n的欧拉函数φ(n) = (p-1)*(q-1) = 22 * 28 = 616

4. 随机选择整数e,1 < e <φ(n),并且e与φ(n)互质,选择13,实际应用中常选择65537

5. 计算e对于φ(n)的模反元素d,(模反元素:如果两个正整数a和n互质,那么一定可以找到整数b,使得 ab-1 被n整除,或者说ab被n除的余数是1,这时,b就叫做a的模反元素)

即e * d 除以φ(n)余数为1, ed Mod (φ(n)) = 1,13 * d Mod 616 = 1,可以转换为13d - 1 = 616 * k ,13x + 616y = 1这个可以使用扩展欧几里德算法求解,可以求得多个解 得到d = 237

6. 计算完成,取(n, e)为公钥,公钥为(667, 13), (n, d)为私钥,私钥为(667, 237)

7. 加密过程,对m进行加密,要求m < n,加密时计算m的e次方除以n,得到的余数即为密文c,取m = 320

me Mod n = c
32013 Mod 667 = 88
c=88

8. 解密过程,解密时计算c的d次方除以n,得到的余数即为明文m

cd Mod n = m 88237 Mod 667 = 320

使用C#生成RSA密钥

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Numerics;
using System.Security.Cryptography;
using System.IO;
using System.Security.Cryptography.X509Certificates;

namespace RSATest
{
    public class RSABuildMgr
    {
        private static readonly int[] smallPrimeArray =
        new int[] { 2,3,5,7,11,13,17,19,23,29,
            31,37,41,43,47,53,59,61,67,71,
            73,79,83,89,97,101,103,107,109,113,
            127,131,137,139,149,151,157,163,167,173,
            179,181,191,193,197,199,211,223,227,229,
            233,239,241 };

        private static readonly int exponent = 65537;

        public void BuildRSAPwd()
        {
            int pwdLen = 2048; //要求2048位密钥长度
            //第一步,生成两个二进制长度为2048/8/2位的素数 
            //由于拉宾米勒测试有极其微小的概率把合数当作素数,所以再计算一遍最大公约数
            BigInteger p, q, n, gcd;
            bool flag = false;

            do
            {
                do
                {
                    p = GetPrime(pwdLen / 16);
                    q = GetPrime(pwdLen / 16);
                    gcd = Gcd(p, q);
                } while (gcd != 1);
                n = p * q;
                byte[] pwdBytes = n.ToByteArray();
                //最后一位是00的话,不计算长度, 00是用来补到字节数组中标识数据是正数的
                if (pwdBytes[pwdBytes.Length - 1] == 0x0)
                {
                    flag = pwdBytes.Length - 1 == pwdLen / 8;
                }
                else
                {
                    flag = pwdBytes.Length == pwdLen / 8;
                }
            } while (!flag);

            BigInteger faiN = (p - 1) * (q - 1);
            BigInteger x, y;

            //求解以下方程:
            //exponent *x + faiN * y = 1;
            BigInteger r = exGcd(exponent, faiN, out x, out y);

            Console.WriteLine("x is " + x.ToString());
            Console.WriteLine("y is " + y.ToString());

            //扩展欧几里德有可能会算出x为负值,此时可以将x设置为x + faiN
            //同样此时也会有对应的y值,但y值并不需要。
            if (x < 0)
            {
                x += faiN;
                y = (1 - x * exponent) / faiN;
                Console.WriteLine("x is " + x.ToString());
                Console.WriteLine("y is " + y.ToString());
            }

            File.AppendAllText("D:\\temp\\m.txt", n.ToString());
            File.AppendAllText("D:\\temp\\e.txt", exponent.ToString());
            File.AppendAllText("D:\\temp\\d.txt", x.ToString());
        }

        //最大公约数, BigInteger本身提供了BigInteger.GreatestCommonDivisor来计算最大公约数
        public BigInteger Gcd(BigInteger a, BigInteger b)
        {
            if (a == 0) return b;
            if (b == 0) return a;
            if (a % 2 == 0 && b % 2 == 0) return 2 * Gcd(a >> 1, b >> 1);
            else if (a % 2 == 0) return Gcd(a >> 1, b);
            else if (b % 2 == 0) return Gcd(a, b >> 1);
            else return Gcd(BigInteger.Abs(a - b), BigInteger.Min(a, b));
        }

        //扩展欧几里德算法
        private BigInteger exGcd(
        BigInteger a, BigInteger b, out BigInteger x, out BigInteger y)
        {
            if (b == 0)
            {
                x = 1;
                y = 0;
                return a;
            }
            BigInteger r = exGcd(b, a % b, out x, out y);
            BigInteger t = x;
            x = y;
            y = t - a / b * y;

            return r;
        }

        public bool TestForPrime(BigInteger p)
        {
            bool result = false;

            if (p < int.MaxValue / 10)
            {
                result = PrimeCheckedByDevid((long)p);
            }
            else
            {
                if (PrimeFilter(p))
                {
                    result = PrimeCheckedByMillerRabin(p);
                }
                else
                {
                    result = false;
                }
            }
            return result;
        }

        private BigInteger GetPrime(int len)
        {
            BigInteger p;
            bool flag = true;
            do
            {
                p = GetBiginteger(1024 / 8);
                if (PrimeFilter(p))
                {
                    if (PrimeCheckedByMillerRabin(p))
                    {
                        flag = false;
                    }
                }
            } while (flag);
            return p;
        }

        private BigInteger GetBiginteger(int len)
        {
            using (RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider())
            {
                byte[] randomBytes = new byte[len];
                csp.GetBytes(randomBytes);
                BigInteger d = new BigInteger(randomBytes.Concat(new byte[] { 0 }).ToArray());
                return d;
            }
        }

        //试除法,对于小于int.MaxValue/10均可用
        private bool PrimeCheckedByDevid(long n)
        {
            bool result = true;
            if (n == 1)
            {
                result = false;
            }
            else if (n <= 241)
            {
                var b = smallPrimeArray.FirstOrDefault(c => c == n);
                if (b == 0)
                {
                    result = false;
                }
                else
                {
                    result = true;
                }
            }
            else
            {

                long m = (long)(Math.Sqrt(n));
                for (long i = 2; i <= m; i++)
                {
                    if (n % i == 0)
                    {
                        result = false;
                        break;
                    }
                }
            }
            return result;
        }

        //欧拉筛法统计素数个数,max不能太大,太大会out of memory,可以取到一亿
        private int PrimeCountEuler(int max)
        {
            int [] prime = new int[max /2 ];
            bool[] isPrime = new bool[max + 1];
            int totalPrimes = 1;

            isPrime[2] = true;
            for (int i = 3; i <= max; i+=2)
            {
                isPrime[i] = true;
            }

            prime[0] = 2;
            for (int i = 3; i <= max; i += 2)
            {
                if (isPrime[i])
                {
                    prime[totalPrimes++] = i;
                }
                for (int j = 1; i * prime[j] <= max && j < totalPrimes; j++)
                {
                    isPrime[i * prime[j]] = false;
                    if (i % prime[j] == 0) //保证每个数只被筛一次
                    {
                        break;
                    }
                }
            }
            return totalPrimes;
        }

        
        //素数筛选,用带筛选数除以53个素数,均不能除尽,认为通过筛选
        private bool PrimeFilter(BigInteger b)
        {
            bool result = true;
            for (int i = 0; i < smallPrimeArray.Length; i++)
            {
                if (b % smallPrimeArray[i] == 0)
                {
                    result = false;
                    break;
                }
            }
            return result;
        }

        //素数测试,拉宾米勒测试
        //将数字表示为 n - 1 = 2的s次方乘以d
        //然后测试
        /* 
        •该算法用于判断一个大于 3 的奇数 n 是否素数。参数 k 用于决定 n 是素数的概率。
        •该算法能够肯定地判断 n 是合数,但是只能说 n 可能是素数。
        •第 01 行,将 n – 1 分解为 2s·d 的形式,这里 d 是奇数。
        •第 02 行,将以下步骤(第 03 到 10 行)循环 k 次。
        •第 03 行,◇在 [2, n - 2] 的范围中独立和随机地选择一个正整数 a 。
        •第 04 行,◇计算该序列的第一个值:x ← a的d次方 mod n 。
        •第 05 行,◇如果该序列的第一个数是 1 或者 n - 1,符合上述条件,n 可能是素数,转到第 03 行进行一下次循环。
        •第 06 行,◇循环执行第 07 到 09 行,顺序遍历该序列剩下的 s – 1 个值。
        •第 07 行,◇◇计算该序列的下一个值:x ← x的2次方 mod n 。
        •第 08 行,◇◇如果这个值是 1 ,但是前边的值不是 n - 1,不符合上述条件,因此 n 肯定是合数,算法结束。
        •第 09 行,◇◇如果这个值是 n - 1,因此下一个值一定是 1,符合上述条件,n 可能是素数,转到第 03 行进行下一次循环。
        •第 10 行,◇发现该序列不是以 1 结束,不符合上述条件,因此 n 肯定是合数,算法结束。
        •第 11 行,已经对 k 个独立和随机地选择的 a 值进行了检验,因此判断 n 非常有可能是素数,算法结束。
        在一次检验中,该算法出错的可能顶多是四分之一,检验25次,则出错概率为10的15次方分之一
        参见《计算机程序设计艺术》 第二卷 P358
        */

        private bool PrimeCheckedByMillerRabin(BigInteger n)
        {
            bool result = true;

            //随机获取25个大于1并且小于b - 1的数进行测试
            int testCnt = 25;
            int len = n.ToByteArray().Length;
            BigInteger[] a = new BigInteger[testCnt];
            int index = 0;
            Random r = new Random();
            do
            {
                int l = r.Next(len + 1);
                BigInteger tempA = GetBiginteger(l);
                if (tempA > 1 && tempA < n)
                {
                    a[index++] = tempA;
                }
            } while (index < testCnt);

            int s;
            BigInteger d;
            GetMRNum(n, out s, out d);
            for (int i = 0; i < testCnt; i++)
            {
                BigInteger x = BigInteger.ModPow(a[i], d, n);
                if (x == 1 || x == n - 1)
                {
                    continue; //通过检测
                }
                else
                {
                    int j = 1;
                    for (; j < s; j++)
                    {
                        BigInteger x1 = BigInteger.ModPow(x, 2, n);
                        if (x1 == 1)
                        {
                            result = false;
                            break;
                        }
                        else if (x1 == n - 1)
                        {
                            break;
                        }
                        else
                        {
                            x = x1;
                        }
                    }
                    if (j == s && x != 1)
                    {
                        result = false;
                        break;
                    }
                    if (!result)
                    {
                        break;
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 将奇数n表示为n - 1 = 2的s次方乘以d (在是奇数)
        /// </summary>
        /// <param name="n"></param>
        /// <param name="s"></param>
        /// <param name="d"></param>
        private void GetMRNum(BigInteger n, out int s, out BigInteger d)
        {
            d = n - 1;
            s = 0;
            do
            {
                s++;
                d = d >> 1;
            } while (d % 2 == 0);
        }
    }
}