C#筆記:RSA加解密實現
- 2019 年 11 月 22 日
- 筆記
上回研究產生大素數,產生大素數,肯定是和加密有關的。現在我們就來實現RSA演算法。哈哈。
第一步,隨機選擇兩個不相等的質數p和q。
第二步,計算p和q的乘積n。
第三步,計算n的歐拉函數φ(n)。
根據公式:φ(n) = (p-1)(q-1)
第四步,隨機選擇一個整數e,條件是1< e < φ(n),且e與φ(n) 互質。一般取e1=65537 第五步,計算e對於φ(n)的模反元素d。 滿足e*d ≡ 1 (mod φ(n))即可。 原理講完了。現在貼程式碼:
BigInteger p = GetPrimeNum.CreatePrimeNum(128); Console.WriteLine("p:" + p); Console.WriteLine(); BigInteger q = GetPrimeNum.CreatePrimeNum(128); Console.WriteLine("q:" + q); Console.WriteLine(); BigInteger on = (p - 1) * (q - 1); Console.WriteLine("o(n):" + on); Console.WriteLine(); BigInteger n = p * q; Console.WriteLine("n:" + n); Console.WriteLine(); BigInteger e1 = BigInteger.Parse("65537"); Console.WriteLine("e1:" + e1); Console.WriteLine(); BigInteger d = RSAProvider.CreateD(e1, on); Console.WriteLine("d:" + d); Console.WriteLine(); Console.WriteLine("加密123"); BigInteger c = RSAProvider.RsaEncrypt(123, e1, n); Console.WriteLine("c:" + c); Console.WriteLine("解密C"); BigInteger m = RSAProvider.RsaEncrypt(c, d, n); Console.WriteLine("m:" + m); public static class RSAProvider { /* int r = MaxDivisorex(1769, 551, out a, out b); MessageBox .Show(string.Format ("{0} * 1769 + {1} *551 = {2}",a,b,(a*1769+b*551))); * 擴展歐幾里德演算法 * */ private static BigInteger ext_euclid(BigInteger a, BigInteger b, out BigInteger x, out BigInteger y) { BigInteger t, d; if (b == 0) { x = 1; y = 0; return a; } d = ext_euclid(b, a % b, out x, out y); t = x; x = y; y = t - a / b * y; return d; } public static BigInteger CreateD(BigInteger e1, BigInteger on) { BigInteger d; BigInteger y; RSAProvider.ext_euclid(e1, on, out d, out y); if (d.Sign == -1) { int i = 1; while (true) { if (((1 + i * on) % e1) == 0) { d = (1 + i * on) / e1; y = i * (-1); break; } ++i; } } return d; } public static BigInteger RsaEncrypt(BigInteger m, BigInteger e1, BigInteger n) { BigInteger c = BigInteger.ModPow(m, e1, n); return c; } public static BigInteger RsaDecrypt(BigInteger c, BigInteger d, BigInteger n) { return BigInteger.ModPow(c, d, n); } } public static class GetPrimeNum { static RandomNumberGenerator rng = RandomNumberGenerator.Create(); public static BigInteger CreateRandomNum(int length) { byte[] bytes = new byte[length]; rng.GetBytes(bytes); BigInteger a = new BigInteger(bytes); if (a.IsEven)//如果是偶數就加一 { a += 1; } if (a.Sign == -1)//如果是負數就乘-1 { a *= -1; } return a; } static ManualResetEvent mre = new ManualResetEvent(false);//執行緒阻塞器設置為阻塞狀態 static BigInteger result = BigInteger.Zero;//result的初始化值設為0 //候選數的工作隊列,執行緒從此隊列取數驗證直到找到素數 static BlockQueue<BigInteger> quePrime = new BlockQueue<BigInteger>(2000); //這個標籤用來在運算完畢後結束多餘的執行緒 private static volatile bool isOver = false; public static BigInteger CreatePrimeNum(int length) { //將執行緒阻塞器設置為阻塞狀態。此狀態下,主執行緒會等待工作執行緒找到數再返回result。 mre.Reset(); isOver = false; //初始化7個任務用來對隊列中的數進行取數驗證 //使用的是系統默認的執行緒池,當執行緒池中有空餘執行緒時,會對這7個任務進行計算。 for (int i = 0; i < 7; i++) { ThreadPool.QueueUserWorkItem(ThreadMethod, mre); } //每次工作時將隊列清除 quePrime.Clear(); //重新初始化種子值,一個1024bit的數。 BigInteger bigNum = CreateRandomNum(128); //將種子值也入隊 quePrime.EnQueue(bigNum); //這個執行緒不斷的把種子值+2入隊,以免隊列空虛使得死鎖。 Thread thInsertNum = new Thread(new ThreadStart(delegate { while (true) { bigNum += 2; quePrime.EnQueue(bigNum); } })); thInsertNum.Start(); //等待後台執行緒釋放主執行緒的阻塞 mre.WaitOne(); /*後台執行緒找到了素數,並對result賦值,關閉將數字不斷入隊的執行緒。 * 至於執行緒池中的任務不必操心。後台自然會自動處理。因為標籤已經置為isover了。 */ thInsertNum.Abort(); //返回這個值,它是素數 return result; } public static BigInteger CreatePrimeNum2(int length) { //這是以單執行緒形式進行運算的例子,通過測試,顯然,它比較慢。 BigInteger primeNum = CreateRandomNum(length); int i = 0; do { i++; primeNum += 2; } while (!primeNum.IsProbablePrime()); return primeNum; } private static void ThreadMethod(object obj) { //尋找素數並賦值給result的執行緒中運行的方法 while (!isOver) { //將隊列中的數出隊,使用隊列是為了避免同一個數被重複運算。防止執行緒衝突。 BigInteger tempNum = quePrime.DeQueue(); if (tempNum.IsProbablePrime()) { //找到了素數,對全局變數賦值 result = tempNum; //釋放進程鎖,此後主進程將會把這個result返回 ManualResetEvent mre = (ManualResetEvent)obj; mre.Set(); isOver = true; break; } } } //此類來自維基百科 public static bool IsProbablePrime(this BigInteger source) { if (PreCheckPrime(source) != 0) { return false; } int certainty = 4;//確認4次差不多了 //if (source == 2 || source == 3) // return true; //if (source < 2 || source % 2 == 0) // return false; //就本類的隨機數發生器,以上情況不可能出現 BigInteger d = source - 1; int s = 0; while (d % 2 == 0) { d /= 2; s += 1; } byte[] bytes = new byte[source.ToByteArray().LongLength]; BigInteger a; for (int i = 0; i < certainty; i++) { do { rng.GetBytes(bytes); a = new BigInteger(bytes); } while (a < 2 || a >= source - 2); BigInteger x = BigInteger.ModPow(a, d, source); if (x == 1 || x == source - 1) continue; for (int r = 1; r < s; r++) { x = BigInteger.ModPow(x, 2, source); if (x == 1) return false; if (x == source - 1) break; } if (x != source - 1) return false; } return true; } //在使用RabinMiller演算法前先進行素數的篩選 public static int PreCheckPrime(BigInteger bigNum) { //檢驗被3,5,7,17特殊數字整除 string strBigNum = bigNum.ToString(); int everyDigit = 0;//這個數字除個位的其它位之和 for (int i = 0; i < strBigNum.Length - 1; i++) { everyDigit += int.Parse(strBigNum[i].ToString()); } int lastDigit = int.Parse(strBigNum[strBigNum.Length - 1].ToString()); //每一位上數字之和能被3整除,那麼這個數就能被3整除。 if ((everyDigit + lastDigit) % 3 == 0) { return 3; } //個位上是0或5的數都能被5整除。 if (lastDigit.Equals(5)) { return 5; } int[] shortPrimes = { 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, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999 }; for (int i = 0; i < shortPrimes.Length; i++) { if (bigNum % shortPrimes[i] == 0) { return shortPrimes[i]; } } return 0; } } //這是一個阻塞隊列 public class BlockQueue<T> { public readonly int SizeLimit = 0; private Queue<T> _inner_queue = null; public int Count { get { return _inner_queue.Count; } } private ManualResetEvent _enqueue_wait = null; private ManualResetEvent _dequeue_wait = null; public BlockQueue(int sizeLimit) { this.SizeLimit = sizeLimit; this._inner_queue = new Queue<T>(this.SizeLimit); this._enqueue_wait = new ManualResetEvent(false); this._dequeue_wait = new ManualResetEvent(false); } public void EnQueue(T item) { if (this._IsShutdown == true) throw new InvalidCastException("Queue was shutdown. Enqueue was not allowed."); while (true) { lock (this._inner_queue) { if (this._inner_queue.Count < this.SizeLimit) { this._inner_queue.Enqueue(item); this._enqueue_wait.Reset(); this._dequeue_wait.Set(); break; } } this._enqueue_wait.WaitOne(); } } public T DeQueue() { while (true) { if (this._IsShutdown == true) { lock (this._inner_queue) return this._inner_queue.Dequeue(); } lock (this._inner_queue) { if (this._inner_queue.Count > 0) { T item = this._inner_queue.Dequeue(); this._dequeue_wait.Reset(); this._enqueue_wait.Set(); return item; } } this._dequeue_wait.WaitOne(); } } public void Clear() { this._inner_queue.Clear(); this._dequeue_wait.Reset(); this._enqueue_wait.Set(); } private bool _IsShutdown = false; public void Shutdown() { this._IsShutdown = true; this._dequeue_wait.Set(); } }