/// <summary> /// Adds padding to the input data and returns the padded data. /// </summary> /// <param name="dataBytes">Data to be padded prior to encryption</param> /// <param name="params">RSA Parameters used for padding computation</param> /// <returns>Padded message</returns> public byte[] EncodeMessage(byte[] dataBytes, RSAParameters @params) { //Determine if we can add padding. if (dataBytes.Length > GetMaxMessageLength(@params)) { throw new CryptographicException("Data length is too long. Increase your key size or consider encrypting less data."); } int padLength = @params.N.Length - dataBytes.Length - 3; BigInteger biRnd = new BigInteger(); biRnd.genRandomBits(padLength * 8, new Random(DateTime.Now.Millisecond)); byte[] bytRandom = null; bytRandom = biRnd.getBytes(); int z1 = bytRandom.Length; //Make sure the bytes are all > 0. for (int i = 0; i <= bytRandom.Length - 1; i++) { if (bytRandom[i] == 0x00) { bytRandom[i] = 0x01; } } byte[] result = new byte[@params.N.Length]; //Add the starting 0x00 byte result[0] = 0x00; //Add the version code 0x02 byte result[1] = 0x02; for (int i = 0; i <= bytRandom.Length - 1; i++) { z1 = i + 2; result[z1] = bytRandom[i]; } //Add the trailing 0 byte after the padding. result[bytRandom.Length + 2] = 0x00; //This starting index for the unpadded data. int idx = bytRandom.Length + 3; //Copy the unpadded data to the padded byte array. dataBytes.CopyTo(result, idx); return result; }
/// <summary> /// Adds padding to the input data and returns the padded data. /// </summary> /// <param name="dataBytes">Data to be padded prior to encryption</param> /// <param name="params">RSA Parameters used for padding computation</param> /// <returns>Padded message</returns> public byte[] EncodeMessage(byte[] dataBytes, RSAParameters @params) { //Iterator int i = 0; //Get the size of the data to be encrypted m_mLen = dataBytes.Length; //Get the size of the public modulus (will serve as max length for cipher text) m_k = @params.N.Length; if (m_mLen > GetMaxMessageLength(@params)) { throw new CryptographicException("Bad Data."); } //Generate the random octet seed (same length as hash) BigInteger biSeed = new BigInteger(); biSeed.genRandomBits(m_hLen * 8, new Random()); byte[] bytSeed = biSeed.getBytesRaw(); //Make sure all of the bytes are greater than 0. for (i = 0; i <= bytSeed.Length - 1; i++) { if (bytSeed[i] == 0x00) { //Replacing with the prime byte 17, no real reason...just picked at random. bytSeed[i] = 0x17; } } //Mask the seed with MFG Function(SHA1 Hash) //This is the mask to be XOR'd with the DataBlock below. byte[] dbMask = Mathematics.OAEPMGF(bytSeed, m_k - m_hLen - 1, m_hLen, m_hashProvider); //Compute the length needed for PS (zero padding) and //fill a byte array to the computed length int psLen = GetMaxMessageLength(@params) - m_mLen; //Generate the SHA1 hash of an empty L (Label). Label is not used for this //application of padding in the RSA specification. byte[] lHash = m_hashProvider.ComputeHash(System.Text.Encoding.UTF8.GetBytes(string.Empty.ToCharArray())); //Create a dataBlock which will later be masked. The //data block includes the concatenated hash(L), PS, //a 0x01 byte, and the message. int dbLen = m_hLen + psLen + 1 + m_mLen; byte[] dataBlock = new byte[dbLen]; int cPos = 0; //Current position //Add the L Hash to the data blcok for (i = 0; i <= lHash.Length - 1; i++) { dataBlock[cPos] = lHash[i]; cPos += 1; } //Add the zero padding for (i = 0; i <= psLen - 1; i++) { dataBlock[cPos] = 0x00; cPos += 1; } //Add the 0x01 byte dataBlock[cPos] = 0x01; cPos += 1; //Add the message for (i = 0; i <= dataBytes.Length - 1; i++) { dataBlock[cPos] = dataBytes[i]; cPos += 1; } //Create the masked data block. byte[] maskedDB = Mathematics.BitwiseXOR(dbMask, dataBlock); //Create the seed mask byte[] seedMask = Mathematics.OAEPMGF(maskedDB, m_hLen, m_hLen, m_hashProvider); //Create the masked seed byte[] maskedSeed = Mathematics.BitwiseXOR(bytSeed, seedMask); //Create the resulting cipher - starting with a 0 byte. byte[] result = new byte[@params.N.Length]; result[0] = 0x00; //Add the masked seed maskedSeed.CopyTo(result, 1); //Add the masked data block maskedDB.CopyTo(result, maskedSeed.Length + 1); return result; }
//*********************************************************************** // Generates a random number with the specified number of bits such // that gcd(number, this) = 1 //*********************************************************************** internal BigInteger genCoPrime(int bits, Random rand) { bool done = false; BigInteger result = new BigInteger(); while (!done) { result.genRandomBits(bits, rand); // gcd test BigInteger g = result.gcd(this); if (g.dataLength == 1 && g.data[0] == 1) done = true; } return result; }
//*********************************************************************** // Generates a positive BigInteger that is probably prime. // Overloaded to use the isProbablePrime method with no confidence value //*********************************************************************** internal static BigInteger genPseudoPrime(int bits, Random rand) { BigInteger result = new BigInteger(); bool done = false; while (!done) { result.genRandomBits(bits, rand); result.data[0] |= 0x01; // make it odd // prime test done = result.isProbablePrime(); } return result; }
//*********************************************************************** // Probabilistic prime test based on Solovay-Strassen (Euler Criterion) // // 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. // //*********************************************************************** internal bool SolovayStrassenTest(int confidence) { BigInteger thisVal; if ((this.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(); BigInteger a = new BigInteger(); BigInteger p_sub1 = thisVal - 1; BigInteger p_sub1_shift = p_sub1 >> 1; Random 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); // if they are different then it is not prime if (expResult != jacob) return false; } return true; }
//*********************************************************************** // Probabilistic prime test based on Rabin-Miller's // // 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. // //*********************************************************************** internal bool RabinMillerTest(int confidence) { BigInteger thisVal; if ((this.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(); BigInteger a = new BigInteger(); Random 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); 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 // // 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. // //*********************************************************************** internal bool FermatLittleTest(int confidence) { BigInteger thisVal; if ((this.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(); BigInteger a = new BigInteger(); BigInteger p_sub1 = thisVal - (new BigInteger(1)); Random 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)) { return false; } } return true; }