/// <summary> /// Tests the correct implementation of the modulo exponential function /// using RSA encryption and decryption (using pre-computed encryption and /// decryption keys). /// </summary> /// <param name="rounds"></param> public static void RSATest(int rounds) { var rand = new Random(1); var val = new byte[64]; // private and public key var bi_e = new BigInteger( "a932b948feed4fb2b692609bd22164fc9edb59fae7880cc1eaff7b3c9626b7e5b241c27a974833b2622ebe09beb451917663d47232488f23a117fc97720f1e7", 16); var bi_d = new BigInteger( "4adf2f7a89da93248509347d2ae506d683dd3a16357e859a980c4f77a4e2f7a01fae289f13a851df6e9db5adaa60bfd2b162bbbe31f7c8f828261a6839311929d2cef4f864dde65e556ce43c89bbbf9f1ac5511315847ce9cc8dc92470a747b8792d6a83b0092d2e5ebaf852c85cacf34278efa99160f2f8aa7ee7214de07b7", 16); var bi_n = new BigInteger( "e8e77781f36a7b3188d711c2190b560f205a52391b3479cdb99fa010745cbeba5f2adc08e1de6bf38398a0487c4a73610d94ec36f17f3f46ad75e17bc1adfec99839589f45f95ccc94cb2a5c500b477eb3323d8cfab0c8458c96f0147a45d27e45a4d11d54d77684f65d48f15fafcc1ba208e71e921b9bd9017c16a5231af7f", 16); Console.WriteLine("e =\n" + bi_e.ToString(10)); Console.WriteLine("\nd =\n" + bi_d.ToString(10)); Console.WriteLine("\nn =\n" + bi_n.ToString(10) + "\n"); for (int count = 0; count < rounds; count++) { // generate data of random length int t1 = 0; while (t1 == 0) t1 = (int) (rand.NextDouble()*65); bool done = false; while (!done) { for (int i = 0; i < 64; i++) { if (i < t1) val[i] = (byte) (rand.NextDouble()*256); else val[i] = 0; if (val[i] != 0) done = true; } } while (val[0] == 0) val[0] = (byte) (rand.NextDouble()*256); Console.Write("Round = " + count); // encrypt and decrypt data var bi_data = new BigInteger(val, t1); BigInteger bi_encrypted = bi_data.ModPow(bi_e, bi_n); BigInteger bi_decrypted = bi_encrypted.ModPow(bi_d, bi_n); // compare if (bi_decrypted != bi_data) { Console.WriteLine("\nError at round " + count); Console.WriteLine(bi_data + "\n"); return; } Console.WriteLine(" <PASSED>."); } }
/// <summary> /// Tests the correct implementation of the modulo exponential and /// inverse modulo functions using RSA encryption and decryption. The two /// pseudoprimes p and q are fixed, but the two RSA keys are generated /// for each round of testing. /// </summary> /// <param name="rounds"></param> public static void RSATest2(int rounds) { var rand = new Random(); var val = new byte[64]; byte[] pseudoPrime1 = { 0x85, 0x84, 0x64, 0xFD, 0x70, 0x6A, 0x9F, 0xF0, 0x94, 0x0C, 0x3E, 0x2C, 0x74, 0x34, 0x05, 0xC9, 0x55, 0xB3, 0x85, 0x32, 0x98, 0x71, 0xF9, 0x41, 0x21, 0x5F, 0x02, 0x9E, 0xEA, 0x56, 0x8D, 0x8C, 0x44, 0xCC, 0xEE, 0xEE, 0x3D, 0x2C, 0x9D, 0x2C, 0x12, 0x41, 0x1E, 0xF1, 0xC5, 0x32, 0xC3, 0xAA, 0x31, 0x4A, 0x52, 0xD8, 0xE8, 0xAF, 0x42, 0xF4, 0x72, 0xA1, 0x2A, 0x0D, 0x97, 0xB1, 0x31, 0xB3, }; byte[] pseudoPrime2 = { 0x99, 0x98, 0xCA, 0xB8, 0x5E, 0xD7, 0xE5, 0xDC, 0x28, 0x5C, 0x6F, 0x0E, 0x15, 0x09, 0x59, 0x6E, 0x84, 0xF3, 0x81, 0xCD, 0xDE, 0x42, 0xDC, 0x93, 0xC2, 0x7A, 0x62, 0xAC, 0x6C, 0xAF, 0xDE, 0x74, 0xE3, 0xCB, 0x60, 0x20, 0x38, 0x9C, 0x21, 0xC3, 0xDC, 0xC8, 0xA2, 0x4D, 0xC6, 0x2A, 0x35, 0x7F, 0xF3, 0xA9, 0xE8, 0x1D, 0x7B, 0x2C, 0x78, 0xFA, 0xB8, 0x02, 0x55, 0x80, 0x9B, 0xC2, 0xA5, 0xCB, }; var bi_p = new BigInteger(pseudoPrime1); var bi_q = new BigInteger(pseudoPrime2); BigInteger bi_pq = (bi_p - 1)*(bi_q - 1); BigInteger bi_n = bi_p*bi_q; for (int count = 0; count < rounds; count++) { // generate private and public key BigInteger bi_e = bi_pq.genCoPrime(512, rand); BigInteger bi_d = bi_e.modInverse(bi_pq); Console.WriteLine("\ne =\n" + bi_e.ToString(10)); Console.WriteLine("\nd =\n" + bi_d.ToString(10)); Console.WriteLine("\nn =\n" + bi_n.ToString(10) + "\n"); // generate data of random length int t1 = 0; while (t1 == 0) t1 = (int) (rand.NextDouble()*65); bool done = false; while (!done) { for (int i = 0; i < 64; i++) { if (i < t1) val[i] = (byte) (rand.NextDouble()*256); else val[i] = 0; if (val[i] != 0) done = true; } } while (val[0] == 0) val[0] = (byte) (rand.NextDouble()*256); Console.Write("Round = " + count); // encrypt and decrypt data var bi_data = new BigInteger(val, t1); BigInteger bi_encrypted = bi_data.ModPow(bi_e, bi_n); BigInteger bi_decrypted = bi_encrypted.ModPow(bi_d, bi_n); // compare if (bi_decrypted != bi_data) { Console.WriteLine("\nError at round " + count); Console.WriteLine(bi_data + "\n"); return; } Console.WriteLine(" <PASSED>."); } }
/// <summary> /// Probabilistic prime test based on Solovay-Strassen (Euler Criterion) /// <remarks> /// <![CDATA[ /// p is probably prime if for any a < p (a is not multiple of p), /// a^((p-1)/2) mod p = J(a, p) /// /// where J is the Jacobi symbol. /// /// Otherwise, p is composite. /// /// Returns /// ------- /// True if "this" is a Euler pseudoprime to randomly chosen /// bases. The number of chosen bases is given by the "confidence" /// parameter. /// /// False if "this" is definitely NOT prime. /// ]]></remarks> /// </summary> /// <param name="confidence"></param> /// <returns></returns> public bool SolovayStrassenTest(int confidence) { BigInteger thisVal; if ((data[maxLength - 1] & 0x80000000) != 0) // negative thisVal = -this; else thisVal = this; if (thisVal.dataLength == 1) { // test small numbers if (thisVal.data[0] == 0 || thisVal.data[0] == 1) return false; else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) return true; } if ((thisVal.data[0] & 0x1) == 0) // even numbers return false; int bits = thisVal.bitCount(); var a = new BigInteger(); BigInteger p_sub1 = thisVal - 1; BigInteger p_sub1_shift = p_sub1 >> 1; var rand = new Random(); for (int round = 0; round < confidence; round++) { bool done = false; while (!done) // generate a < n { int testBits = 0; // make sure "a" has at least 2 bits while (testBits < 2) testBits = (int) (rand.NextDouble()*bits); a.genRandomBits(testBits, rand); int byteLen = a.dataLength; // make sure "a" is not 0 if (byteLen > 1 || (byteLen == 1 && a.data[0] != 1)) done = true; } // check whether a factor exists (fix for version 1.03) BigInteger gcdTest = a.gcd(thisVal); if (gcdTest.dataLength == 1 && gcdTest.data[0] != 1) return false; // calculate a^((p-1)/2) mod p BigInteger expResult = a.ModPow(p_sub1_shift, thisVal); if (expResult == p_sub1) expResult = -1; // calculate Jacobi symbol BigInteger jacob = Jacobi(a, thisVal); //Console.WriteLine("a = " + a.ToString(10) + " b = " + thisVal.ToString(10)); //Console.WriteLine("expResult = " + expResult.ToString(10) + " Jacob = " + jacob.ToString(10)); // if they are different then it is not prime if (expResult != jacob) return false; } return true; }
/// <summary> /// Probabilistic prime test based on Rabin-Miller's /// <![CDATA[ /// /// for any p > 0 with p - 1 = 2^s * t /// /// p is probably prime (strong pseudoprime) if for any a < p, /// 1) a^t mod p = 1 or /// 2) a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 /// /// Otherwise, p is composite. /// /// Returns /// ------- /// True if "this" is a strong pseudoprime to randomly chosen /// bases. The number of chosen bases is given by the "confidence" /// parameter. /// /// False if "this" is definitely NOT prime. /// ]]> /// </summary> /// <param name="confidence"></param> /// <returns></returns> public bool RabinMillerTest(int confidence) { BigInteger thisVal; if ((data[maxLength - 1] & 0x80000000) != 0) // negative thisVal = -this; else thisVal = this; if (thisVal.dataLength == 1) { // test small numbers if (thisVal.data[0] == 0 || thisVal.data[0] == 1) return false; else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) return true; } if ((thisVal.data[0] & 0x1) == 0) // even numbers return false; // calculate values of s and t BigInteger p_sub1 = thisVal - (new BigInteger(1)); int s = 0; for (int index = 0; index < p_sub1.dataLength; index++) { uint mask = 0x01; for (int i = 0; i < 32; i++) { if ((p_sub1.data[index] & mask) != 0) { index = p_sub1.dataLength; // to break the outer loop break; } mask <<= 1; s++; } } BigInteger t = p_sub1 >> s; int bits = thisVal.bitCount(); var a = new BigInteger(); var rand = new Random(); for (int round = 0; round < confidence; round++) { bool done = false; while (!done) // generate a < n { int testBits = 0; // make sure "a" has at least 2 bits while (testBits < 2) testBits = (int) (rand.NextDouble()*bits); a.genRandomBits(testBits, rand); int byteLen = a.dataLength; // make sure "a" is not 0 if (byteLen > 1 || (byteLen == 1 && a.data[0] != 1)) done = true; } // check whether a factor exists (fix for version 1.03) BigInteger gcdTest = a.gcd(thisVal); if (gcdTest.dataLength == 1 && gcdTest.data[0] != 1) return false; BigInteger b = a.ModPow(t, thisVal); /* Console.WriteLine("a = " + a.ToString(10)); Console.WriteLine("b = " + b.ToString(10)); Console.WriteLine("t = " + t.ToString(10)); Console.WriteLine("s = " + s); */ bool result = false; if (b.dataLength == 1 && b.data[0] == 1) // a^t mod p = 1 result = true; for (int j = 0; result == false && j < s; j++) { if (b == p_sub1) // a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 { result = true; break; } b = (b*b)%thisVal; } if (result == false) return false; } return true; }
//*********************************************************************** // Probabilistic prime test based on Fermat's little theorem // // //*********************************************************************** /// <summary> /// Probabilistic prime test based on Fermat's little theorem /// <example> /// <![CDATA[ /// /// for any a < p (p does not divide a) if /// a^(p-1) mod p != 1 then p is not prime. /// /// Otherwise, p is probably prime (pseudoprime to the chosen base). /// /// Returns /// ------- /// True if "this" is a pseudoprime to randomly chosen /// bases. The number of chosen bases is given by the "confidence" /// parameter. /// /// False if "this" is definitely NOT prime. /// /// Note - this method is fast but fails for Carmichael numbers except /// when the randomly chosen base is a factor of the number. /// /// ]]> /// </example> /// </summary> /// <param name="confidence"></param> /// <returns></returns> public bool FermatLittleTest(int confidence) { BigInteger thisVal; if ((data[maxLength - 1] & 0x80000000) != 0) // negative thisVal = -this; else thisVal = this; if (thisVal.dataLength == 1) { // test small numbers if (thisVal.data[0] == 0 || thisVal.data[0] == 1) return false; else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) return true; } if ((thisVal.data[0] & 0x1) == 0) // even numbers return false; int bits = thisVal.bitCount(); var a = new BigInteger(); BigInteger p_sub1 = thisVal - (new BigInteger(1)); var rand = new Random(); for (int round = 0; round < confidence; round++) { bool done = false; while (!done) // generate a < n { int testBits = 0; // make sure "a" has at least 2 bits while (testBits < 2) testBits = (int) (rand.NextDouble()*bits); a.genRandomBits(testBits, rand); int byteLen = a.dataLength; // make sure "a" is not 0 if (byteLen > 1 || (byteLen == 1 && a.data[0] != 1)) done = true; } // check whether a factor exists (fix for version 1.03) BigInteger gcdTest = a.gcd(thisVal); if (gcdTest.dataLength == 1 && gcdTest.data[0] != 1) return false; // calculate a^(p-1) mod p BigInteger expResult = a.ModPow(p_sub1, thisVal); int resultLen = expResult.dataLength; // is NOT prime is a^(p-1) mod p != 1 if (resultLen > 1 || (resultLen == 1 && expResult.data[0] != 1)) { //Console.WriteLine("a = " + a.ToString()); return false; } } return true; }
/// <summary> /// 用指定的密匙加密 /// </summary> /// <param name="source">明文</param> /// <param name="d">密钥大素数(可以是RSACryptoServiceProvider生成的D或是Exponent)</param> /// <param name="n">大整数N(可以是RSACryptoServiceProvider生成的Modulus)</param> /// <returns>返回密文</returns> private static byte[] Encrypt(byte[] source, BigInteger d, BigInteger n) { var len = source.Length; int len1; if ((len % 120) == 0) len1 = len / 120; else len1 = len / 120 + 1; var tempbytes = new List<byte>(); for (int i = 0; i < len1; i++) { var blockLen = len >= 120 ? 120 : len; var oText = new byte[blockLen]; Array.Copy(source, i * 120, oText, 0, blockLen); //string res = Encoding.UTF8.GetString(oText); var biText = new BigInteger(oText); var biEnText = biText.ModPow(d, n); //补位 //byte[] testbyte = null; var resultStr = biEnText.ToHexString(); if (resultStr.Length < 256) { while (resultStr.Length != 256) { resultStr = "0" + resultStr; } } var returnBytes = new byte[128]; for (var j = 0; j < returnBytes.Length; j++) returnBytes[j] = Convert.ToByte(resultStr.Substring(j * 2, 2), 16); tempbytes.AddRange(returnBytes); len -= blockLen; } return tempbytes.ToArray(); }
/// <summary> /// 用指定的密匙解密 /// </summary> /// <param name="ciphertext">密文</param> /// <param name="e">密钥大素数(可以是RSACryptoServiceProvider生成的Exponent或者D)</param> /// <param name="n">大整数N(可以是RSACryptoServiceProvider生成的Modulus)</param> /// <returns>返回明文</returns> private static byte[] Decrypt(byte[] ciphertext, BigInteger e, BigInteger n) { var len = ciphertext.Length; int len1; if (len % 128 == 0) len1 = len / 128; else len1 = len / 128 + 1; var result = new List<byte>(); for (int i = 0; i < len1; i++) { var blockLen = len >= 128 ? 128 : len; var oText = new byte[blockLen]; Array.Copy(ciphertext, i * 128, oText, 0, blockLen); var biText = new BigInteger(oText); var biEnText = biText.ModPow(e, n); result.AddRange(biEnText.GetBytes()); len -= blockLen; } return result.ToArray(); }