/// <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; }
//*********************************************************************** // 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; }
private byte[] decryptData(byte[] dataBytes, byte[] bytExponent, byte[] bytModulus) { BigInteger oEncData = new BigInteger(dataBytes, dataBytes.Length); return oEncData.modPow(new BigInteger(bytExponent), new BigInteger(bytModulus)).getBytesRaw(); }
private void OnPrimeGenerated(Object sender, RunWorkerCompletedEventArgs e) { m_primeProgress += 50; if (m_primeProgress == 100) { //Verify that P and Q are not equal...if they are, we need to regenerate Q //Handle the case where Q and P might end up being equal. This will run //the worker again using the same settings as before. BigInteger biP = new BigInteger(m_RSAParams.P); BigInteger biQ = new BigInteger(m_RSAParams.Q); if (biP == biQ) { m_primeProgress = 50; m_worker2.DoWork += Generate_Q; m_worker2.RunWorkerAsync(); return; } if (biP < biQ) { BigInteger biTmp = new BigInteger(biP); biP = biQ; biQ = biTmp; m_RSAParams.P = biP.getBytesRaw(); m_RSAParams.Q = biQ.getBytesRaw(); } BuildKeys(); } }
private void Generate_Q(Object sender, DoWorkEventArgs e) { DateTime dt = DateTime.Now; int iSeed = (dt.Year + dt.Second + dt.Minute + dt.Millisecond) / 3; byte[] tmp = new byte[m_bitLength + 1]; tmp = new BigInteger(BigInteger.genPseudoPrime(m_bitLength, new Random(iSeed))).getBytesRaw(); m_RSAParams.Q = tmp; }
//*********************************************************************** // Overloading of the NOT operator (1's complement) //*********************************************************************** public static BigInteger operator ~(BigInteger bi1) { BigInteger result = new BigInteger(bi1); for (int i = 0; i < maxLength; i++) result.data[i] = (uint)(~(bi1.data[i])); result.dataLength = maxLength; while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) result.dataLength--; return result; }
//*********************************************************************** // Private function that supports the division of two numbers with // a divisor that has more than 1 digit. // // Algorithm taken from [1] //*********************************************************************** private static void multiByteDivide(BigInteger bi1, BigInteger bi2, BigInteger outQuotient, BigInteger outRemainder) { uint[] result = new uint[maxLength]; int remainderLen = bi1.dataLength + 1; uint[] remainder = new uint[remainderLen]; uint mask = 0x80000000; uint val = bi2.data[bi2.dataLength - 1]; int shift = 0, resultPos = 0; while (mask != 0 && (val & mask) == 0) { shift++; mask >>= 1; } for (int i = 0; i < bi1.dataLength; i++) remainder[i] = bi1.data[i]; shiftLeft(remainder, shift); bi2 = bi2 << shift; int j = remainderLen - bi2.dataLength; int pos = remainderLen - 1; ulong firstDivisorByte = bi2.data[bi2.dataLength - 1]; ulong secondDivisorByte = bi2.data[bi2.dataLength - 2]; int divisorLen = bi2.dataLength + 1; uint[] dividendPart = new uint[divisorLen]; while (j > 0) { ulong dividend = ((ulong)remainder[pos] << 32) + (ulong)remainder[pos - 1]; ulong q_hat = dividend / firstDivisorByte; ulong r_hat = dividend % firstDivisorByte; bool done = false; while (!done) { done = true; if (q_hat == 0x100000000 || (q_hat * secondDivisorByte) > ((r_hat << 32) + remainder[pos - 2])) { q_hat--; r_hat += firstDivisorByte; if (r_hat < 0x100000000) done = false; } } for (int h = 0; h < divisorLen; h++) dividendPart[h] = remainder[pos - h]; BigInteger kk = new BigInteger(dividendPart); BigInteger ss = bi2 * (long)q_hat; while (ss > kk) { q_hat--; ss -= bi2; } BigInteger yy = kk - ss; for (int h = 0; h < divisorLen; h++) remainder[pos - h] = yy.data[bi2.dataLength - h]; result[resultPos++] = (uint)q_hat; pos--; j--; } outQuotient.dataLength = resultPos; int y = 0; for (int x = outQuotient.dataLength - 1; x >= 0; x--, y++) outQuotient.data[y] = result[x]; for (; y < maxLength; y++) outQuotient.data[y] = 0; while (outQuotient.dataLength > 1 && outQuotient.data[outQuotient.dataLength - 1] == 0) outQuotient.dataLength--; if (outQuotient.dataLength == 0) outQuotient.dataLength = 1; outRemainder.dataLength = shiftRight(remainder, shift); for (y = 0; y < outRemainder.dataLength; y++) outRemainder.data[y] = remainder[y]; for (; y < maxLength; y++) outRemainder.data[y] = 0; }
//*********************************************************************** // Returns the modulo inverse of this. Throws ArithmeticException if // the inverse does not exist. (i.e. gcd(this, modulus) != 1) //*********************************************************************** internal BigInteger modInverse(BigInteger modulus) { BigInteger[] p = { 0, 1 }; BigInteger[] q = new BigInteger[2]; // quotients BigInteger[] r = { 0, 0 }; // remainders int step = 0; BigInteger a = modulus; BigInteger b = this; while (b.dataLength > 1 || (b.dataLength == 1 && b.data[0] != 0)) { BigInteger quotient = new BigInteger(); BigInteger remainder = new BigInteger(); if (step > 1) { BigInteger pval = (p[0] - (p[1] * q[0])) % modulus; p[0] = p[1]; p[1] = pval; } if (b.dataLength == 1) singleByteDivide(a, b, quotient, remainder); else multiByteDivide(a, b, quotient, remainder); q[0] = q[1]; r[0] = r[1]; q[1] = quotient; r[1] = remainder; a = b; b = remainder; step++; } if (r[0].dataLength > 1 || (r[0].dataLength == 1 && r[0].data[0] != 1)) throw (new ArithmeticException("No inverse!")); BigInteger result = ((p[0] - (p[1] * q[0])) % modulus); if ((result.data[maxLength - 1] & 0x80000000) != 0) result += modulus; // get the least positive modulus return result; }
//*********************************************************************** // Returns a value that is equivalent to the integer square root // of the BigInteger. // // The integer square root of "this" is defined as the largest integer n // such that (n * n) <= this // //*********************************************************************** internal BigInteger sqrt() { uint numBits = (uint)this.bitCount(); if ((numBits & 0x1) != 0) // odd number of bits numBits = (numBits >> 1) + 1; else numBits = (numBits >> 1); uint bytePos = numBits >> 5; byte bitPos = (byte)(numBits & 0x1F); uint mask; BigInteger result = new BigInteger(); if (bitPos == 0) mask = 0x80000000; else { mask = (uint)1 << bitPos; bytePos++; } result.dataLength = (int)bytePos; for (int i = (int)bytePos - 1; i >= 0; i--) { while (mask != 0) { // guess result.data[i] ^= mask; // undo the guess if its square is larger than this if ((result * result) > this) result.data[i] ^= mask; mask >>= 1; } mask = 0x80000000; } 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; }
//*********************************************************************** // 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; }
//*********************************************************************** // Computes the Jacobi Symbol for a and b. // Algorithm adapted from [3] and [4] with some optimizations //*********************************************************************** internal static int Jacobi(BigInteger a, BigInteger b) { // Jacobi defined only for odd integers if ((b.data[0] & 0x1) == 0) throw (new ArgumentException("Jacobi defined only for odd integers.")); if (a >= b) a %= b; if (a.dataLength == 1 && a.data[0] == 0) return 0; // a == 0 if (a.dataLength == 1 && a.data[0] == 1) return 1; // a == 1 if (a < 0) { if ((((b - 1).data[0]) & 0x2) == 0) //if( (((b-1) >> 1).data[0] & 0x1) == 0) return Jacobi(-a, b); else return -Jacobi(-a, b); } int e = 0; for (int index = 0; index < a.dataLength; index++) { uint mask = 0x01; for (int i = 0; i < 32; i++) { if ((a.data[index] & mask) != 0) { index = a.dataLength; // to break the outer loop break; } mask <<= 1; e++; } } BigInteger a1 = a >> e; int s = 1; if ((e & 0x1) != 0 && ((b.data[0] & 0x7) == 3 || (b.data[0] & 0x7) == 5)) s = -1; if ((b.data[0] & 0x3) == 3 && (a1.data[0] & 0x3) == 3) s = -s; if (a1.dataLength == 1 && a1.data[0] == 1) return s; else return (s * Jacobi(b % a1, a1)); }
internal bool LucasStrongTestHelper(BigInteger thisVal) { // Do the test (selects D based on Selfridge) // Let D be the first element of the sequence // 5, -7, 9, -11, 13, ... for which J(D,n) = -1 // Let P = 1, Q = (1-D) / 4 long D = 5, sign = -1, dCount = 0; bool done = false; while (!done) { int Jresult = BigInteger.Jacobi(D, thisVal); if (Jresult == -1) done = true; // J(D, this) = 1 else { if (Jresult == 0 && Math.Abs(D) < thisVal) // divisor found return false; if (dCount == 20) { // check for square BigInteger root = thisVal.sqrt(); if (root * root == thisVal) return false; } //Console.WriteLine(D); D = (Math.Abs(D) + 2) * sign; sign = -sign; } dCount++; } long Q = (1 - D) >> 2; BigInteger p_add1 = thisVal + 1; int s = 0; for (int index = 0; index < p_add1.dataLength; index++) { uint mask = 0x01; for (int i = 0; i < 32; i++) { if ((p_add1.data[index] & mask) != 0) { index = p_add1.dataLength; // to break the outer loop break; } mask <<= 1; s++; } } BigInteger t = p_add1 >> s; // calculate constant = b^(2k) / m // for Barrett Reduction BigInteger constant = new BigInteger(); int nLen = thisVal.dataLength << 1; constant.data[nLen] = 0x00000001; constant.dataLength = nLen + 1; constant = constant / thisVal; BigInteger[] lucas = LucasSequenceHelper(1, Q, t, thisVal, constant, 0); bool isPrime = false; if ((lucas[0].dataLength == 1 && lucas[0].data[0] == 0) || (lucas[1].dataLength == 1 && lucas[1].data[0] == 0)) { // u(t) = 0 or V(t) = 0 isPrime = true; } for (int i = 1; i < s; i++) { if (!isPrime) { // doubling of index lucas[1] = thisVal.BarrettReduction(lucas[1] * lucas[1], thisVal, constant); lucas[1] = (lucas[1] - (lucas[2] << 1)) % thisVal; if ((lucas[1].dataLength == 1 && lucas[1].data[0] == 0)) isPrime = true; } lucas[2] = thisVal.BarrettReduction(lucas[2] * lucas[2], thisVal, constant); //Q^k } if (isPrime) // additional checks for composite numbers { // If n is prime and gcd(n, Q) == 1, then // Q^((n+1)/2) = Q * Q^((n-1)/2) is congruent to (Q * J(Q, n)) mod n BigInteger g = thisVal.gcd(Q); if (g.dataLength == 1 && g.data[0] == 1) // gcd(this, Q) == 1 { if ((lucas[2].data[maxLength - 1] & 0x80000000) != 0) lucas[2] += thisVal; BigInteger temp = (Q * BigInteger.Jacobi(Q, thisVal)) % thisVal; if ((temp.data[maxLength - 1] & 0x80000000) != 0) temp += thisVal; if (lucas[2] != temp) isPrime = false; } } return isPrime; }
//*********************************************************************** // Constructor (Default value provided by a string of digits of the // specified base) // // Example (base 10) // ----------------- // To initialize "a" with the default value of 1234 in base 10 // BigInteger a = new BigInteger("1234", 10) // // To initialize "a" with the default value of -1234 // BigInteger a = new BigInteger("-1234", 10) // // Example (base 16) // ----------------- // To initialize "a" with the default value of 0x1D4F in base 16 // BigInteger a = new BigInteger("1D4F", 16) // // To initialize "a" with the default value of -0x1D4F // BigInteger a = new BigInteger("-1D4F", 16) // // Note that string values are specified in the <sign><magnitude> // format. // //*********************************************************************** public BigInteger(string value, int radix) { BigInteger multiplier = new BigInteger(1); BigInteger result = new BigInteger(); value = (value.ToUpper()).Trim(); int limit = 0; if (value[0] == '-') limit = 1; for (int i = value.Length - 1; i >= limit; i--) { int posVal = (int)value[i]; if (posVal >= '0' && posVal <= '9') posVal -= '0'; else if (posVal >= 'A' && posVal <= 'Z') posVal = (posVal - 'A') + 10; else posVal = 9999999; // arbitrary large if (posVal >= radix) throw (new ArithmeticException("Invalid string in constructor.")); else { if (value[0] == '-') posVal = -posVal; result = result + (multiplier * posVal); if ((i - 1) >= limit) multiplier = multiplier * radix; } } if (value[0] == '-') // negative values { if ((result.data[maxLength - 1] & 0x80000000) == 0) throw (new ArithmeticException("Negative underflow in constructor.")); } else // positive values { if ((result.data[maxLength - 1] & 0x80000000) != 0) throw (new ArithmeticException("Positive overflow in constructor.")); } data = new uint[maxLength]; for (int i = 0; i < result.dataLength; i++) data[i] = result.data[i]; dataLength = result.dataLength; }
//*********************************************************************** // Overloading of unary << operators //*********************************************************************** public static BigInteger operator <<(BigInteger bi1, int shiftVal) { BigInteger result = new BigInteger(bi1); result.dataLength = shiftLeft(result.data, shiftVal); return result; }
//*********************************************************************** // Returns the k_th number in the Lucas Sequence reduced modulo n. // // Uses index doubling to speed up the process. For example, to calculate V(k), // we maintain two numbers in the sequence V(n) and V(n+1). // // To obtain V(2n), we use the identity // V(2n) = (V(n) * V(n)) - (2 * Q^n) // To obtain V(2n+1), we first write it as // V(2n+1) = V((n+1) + n) // and use the identity // V(m+n) = V(m) * V(n) - Q * V(m-n) // Hence, // V((n+1) + n) = V(n+1) * V(n) - Q^n * V((n+1) - n) // = V(n+1) * V(n) - Q^n * V(1) // = V(n+1) * V(n) - Q^n * P // // We use k in its binary expansion and perform index doubling for each // bit position. For each bit position that is set, we perform an // index doubling followed by an index addition. This means that for V(n), // we need to update it to V(2n+1). For V(n+1), we need to update it to // V((2n+1)+1) = V(2*(n+1)) // // This function returns // [0] = U(k) // [1] = V(k) // [2] = Q^n // // Where U(0) = 0 % n, U(1) = 1 % n // V(0) = 2 % n, V(1) = P % n //*********************************************************************** internal static BigInteger[] LucasSequence(BigInteger P, BigInteger Q, BigInteger k, BigInteger n) { if (k.dataLength == 1 && k.data[0] == 0) { BigInteger[] result = new BigInteger[3]; result[0] = 0; result[1] = 2 % n; result[2] = 1 % n; return result; } // calculate constant = b^(2k) / m // for Barrett Reduction BigInteger constant = new BigInteger(); int nLen = n.dataLength << 1; constant.data[nLen] = 0x00000001; constant.dataLength = nLen + 1; constant = constant / n; // calculate values of s and t int s = 0; for (int index = 0; index < k.dataLength; index++) { uint mask = 0x01; for (int i = 0; i < 32; i++) { if ((k.data[index] & mask) != 0) { index = k.dataLength; // to break the outer loop break; } mask <<= 1; s++; } } BigInteger t = k >> s; //Console.WriteLine("s = " + s + " t = " + t); return LucasSequenceHelper(P, Q, t, n, constant, s); }
//*********************************************************************** // Overloading of unary >> operators //*********************************************************************** public static BigInteger operator >>(BigInteger bi1, int shiftVal) { BigInteger result = new BigInteger(bi1); result.dataLength = shiftRight(result.data, shiftVal); if ((bi1.data[maxLength - 1] & 0x80000000) != 0) // negative { for (int i = maxLength - 1; i >= result.dataLength; i--) result.data[i] = 0xFFFFFFFF; uint mask = 0x80000000; for (int i = 0; i < 32; i++) { if ((result.data[result.dataLength - 1] & mask) != 0) break; result.data[result.dataLength - 1] |= mask; mask >>= 1; } result.dataLength = maxLength; } return result; }
//*********************************************************************** // Performs the calculation of the kth term in the Lucas Sequence. // For details of the algorithm, see reference [9]. // // k must be odd. i.e LSB == 1 //*********************************************************************** private static BigInteger[] LucasSequenceHelper(BigInteger P, BigInteger Q, BigInteger k, BigInteger n, BigInteger constant, int s) { BigInteger[] result = new BigInteger[3]; if ((k.data[0] & 0x00000001) == 0) throw (new ArgumentException("Argument k must be odd.")); int numbits = k.bitCount(); uint mask = (uint)0x1 << ((numbits & 0x1F) - 1); // v = v0, v1 = v1, u1 = u1, Q_k = Q^0 BigInteger v = 2 % n, Q_k = 1 % n, v1 = P % n, u1 = Q_k; bool flag = true; for (int i = k.dataLength - 1; i >= 0; i--) // iterate on the binary expansion of k { //Console.WriteLine("round"); while (mask != 0) { if (i == 0 && mask == 0x00000001) // last bit break; if ((k.data[i] & mask) != 0) // bit is set { // index doubling with addition u1 = (u1 * v1) % n; v = ((v * v1) - (P * Q_k)) % n; v1 = n.BarrettReduction(v1 * v1, n, constant); v1 = (v1 - ((Q_k * Q) << 1)) % n; if (flag) flag = false; else Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); Q_k = (Q_k * Q) % n; } else { // index doubling u1 = ((u1 * v) - Q_k) % n; v1 = ((v * v1) - (P * Q_k)) % n; v = n.BarrettReduction(v * v, n, constant); v = (v - (Q_k << 1)) % n; if (flag) { Q_k = Q % n; flag = false; } else Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); } mask >>= 1; } mask = 0x80000000; } // at this point u1 = u(n+1) and v = v(n) // since the last bit always 1, we need to transform u1 to u(2n+1) and v to v(2n+1) u1 = ((u1 * v) - Q_k) % n; v = ((v * v1) - (P * Q_k)) % n; if (flag) flag = false; else Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); Q_k = (Q_k * Q) % n; for (int i = 0; i < s; i++) { // index doubling u1 = (u1 * v) % n; v = ((v * v) - (Q_k << 1)) % n; if (flag) { Q_k = Q % n; flag = false; } else Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); } result[0] = u1; result[1] = v; result[2] = Q_k; return result; }
//*********************************************************************** // Overloading of the NEGATE operator (2's complement) //*********************************************************************** public static BigInteger operator -(BigInteger bi1) { // handle neg of zero separately since it'll cause an overflow // if we proceed. if (bi1.dataLength == 1 && bi1.data[0] == 0) return (new BigInteger()); BigInteger result = new BigInteger(bi1); // 1's complement for (int i = 0; i < maxLength; i++) result.data[i] = (uint)(~(bi1.data[i])); // add one to result of 1's complement long val, carry = 1; int index = 0; while (carry != 0 && index < maxLength) { val = (long)(result.data[index]); val++; result.data[index] = (uint)(val & 0xFFFFFFFF); carry = val >> 32; index++; } if ((bi1.data[maxLength - 1] & 0x80000000) == (result.data[maxLength - 1] & 0x80000000)) throw (new ArithmeticException("Overflow in negation.\n")); result.dataLength = maxLength; while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) result.dataLength--; return result; }
//*********************************************************************** // Overloading of addition operator //*********************************************************************** public static BigInteger operator +(BigInteger bi1, BigInteger bi2) { BigInteger result = new BigInteger(); result.dataLength = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; long carry = 0; for (int i = 0; i < result.dataLength; i++) { long sum = (long)bi1.data[i] + (long)bi2.data[i] + carry; carry = sum >> 32; result.data[i] = (uint)(sum & 0xFFFFFFFF); } if (carry != 0 && result.dataLength < maxLength) { result.data[result.dataLength] = (uint)(carry); result.dataLength++; } while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) result.dataLength--; // overflow check int lastPos = maxLength - 1; if ((bi1.data[lastPos] & 0x80000000) == (bi2.data[lastPos] & 0x80000000) && (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) { throw (new ArithmeticException()); } 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; }
//*********************************************************************** // Overloading of the unary ++ operator //*********************************************************************** public static BigInteger operator ++(BigInteger bi1) { BigInteger result = new BigInteger(bi1); long val, carry = 1; int index = 0; while (carry != 0 && index < maxLength) { val = (long)(result.data[index]); val++; result.data[index] = (uint)(val & 0xFFFFFFFF); carry = val >> 32; index++; } if (index > result.dataLength) result.dataLength = index; else { while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) result.dataLength--; } // overflow check int lastPos = maxLength - 1; // overflow if initial value was +ve but ++ caused a sign // change to negative. if ((bi1.data[lastPos] & 0x80000000) == 0 && (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) { throw (new ArithmeticException("Overflow in ++.")); } return result; }
/// <summary> /// Generate the RSA Key Pair using a supplied cipher strength value and exponent value. /// A prime number value between 3 and 65537 is recommended for the exponent. Larger /// exponents can increase security but also increase encryption time. Your supplied /// exponent may be automatically adjusted to ensure compatibility with the RSA algorithm /// security requirements. If a cipherStrength was specified in the constructor, /// the supplied <paramref name="cipherStrength"/> value will override it. /// </summary> /// <param name="cipherStrength">The strength of the cipher in bits. Must be a multiple of 8 /// and between 256 and 4096</param> /// <param name="exponent">Custom exponent value to be used for RSA Calculation</param> public void GenerateKeys(int cipherStrength, int exponent) { if ((cipherStrength > 4096) || (cipherStrength < 256) || (cipherStrength % 8 != 0)) throw new ArgumentException("cipherStrength must be a value between 256 and 4096 and must be a multiple of 8."); if (m_isBusy == true) throw new CryptographicException("Operation cannot be performed while a current key generation operation is in progress."); m_KeyLoaded = false; m_isBusy = true; //bitLength is used to calculate P and Q, so it needs //to be half of the cipherStrength. bitLength 512 = 1024-bit encryption. m_bitLength = cipherStrength / 2; //Make sure this is a positive number BigInteger iExp = new BigInteger(Math.Abs((long)exponent)); //Make sure this is an odd number if (iExp % 2 == 0) { iExp += 1; } m_RSAParams.E = iExp.getBytesRaw(); m_primeProgress = 0; m_worker1 = new BackgroundWorker(); m_worker2 = new BackgroundWorker(); m_worker1.RunWorkerCompleted += OnPrimeGenerated; m_worker2.RunWorkerCompleted += OnPrimeGenerated; Generate_Primes(); }
//*********************************************************************** // Overloading of subtraction operator //*********************************************************************** public static BigInteger operator -(BigInteger bi1, BigInteger bi2) { BigInteger result = new BigInteger(); result.dataLength = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; long carryIn = 0; for (int i = 0; i < result.dataLength; i++) { long diff; diff = (long)bi1.data[i] - (long)bi2.data[i] - carryIn; result.data[i] = (uint)(diff & 0xFFFFFFFF); if (diff < 0) carryIn = 1; else carryIn = 0; } // roll over to negative if (carryIn != 0) { for (int i = result.dataLength; i < maxLength; i++) result.data[i] = 0xFFFFFFFF; result.dataLength = maxLength; } // fixed in v1.03 to give correct datalength for a - (-b) while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) result.dataLength--; // overflow check int lastPos = maxLength - 1; if ((bi1.data[lastPos] & 0x80000000) != (bi2.data[lastPos] & 0x80000000) && (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) { throw (new ArithmeticException()); } return result; }
private void BuildKeys() { //Make a call to Generate_Primes. BigInteger P = new BigInteger(m_RSAParams.P); BigInteger Q = new BigInteger(m_RSAParams.Q); //Exponent. This needs to be a number such that the //GCD of the Exponent and Phi is 1. The larger the exp. //the more secure, but it increases encryption time. BigInteger E = new BigInteger(m_RSAParams.E); BigInteger N = new BigInteger(0); //Public and Private Key Part (Modulus) BigInteger D = new BigInteger(0); //Private Key Part BigInteger DP = new BigInteger(0); BigInteger DQ = new BigInteger(0); BigInteger IQ = new BigInteger(0); BigInteger Phi = new BigInteger(0); //Phi //Make sure P is greater than Q, swap if less. if (P < Q) { BigInteger biTmp = P; P = Q; Q = biTmp; biTmp = null; m_RSAParams.P = P.getBytesRaw(); m_RSAParams.Q = Q.getBytesRaw(); } //Calculate the modulus N = P * Q; m_RSAParams.N = N.getBytesRaw(); //Calculate Phi Phi = (P - 1) * (Q - 1); m_RSAParams.Phi = Phi.getBytesRaw(); //Make sure our Exponent will work, or choose a larger one. while (Phi.gcd(E) > 1) { //If the GCD is greater than 1 iterate the Exponent E = E + 2; //Also make sure the Exponent is prime. while (!E.isProbablePrime()) { E = E + 2; } } //Make sure the params contain the updated E value m_RSAParams.E = E.getBytesRaw(); //Calculate the private exponent D. D = E.modInverse(Phi); m_RSAParams.D = D.getBytesRaw(); //Calculate DP DP = E.modInverse(P - 1); m_RSAParams.DP = DP.getBytesRaw(); //Calculate DQ DQ = E.modInverse(Q - 1); m_RSAParams.DQ = DQ.getBytesRaw(); //Calculate InverseQ IQ = Q.modInverse(P); m_RSAParams.IQ = IQ.getBytesRaw(); m_KeyLoaded = true; m_isBusy = false; OnKeysGenerated(this); }
//*********************************************************************** // Overloading of the unary -- operator //*********************************************************************** public static BigInteger operator --(BigInteger bi1) { BigInteger result = new BigInteger(bi1); long val; bool carryIn = true; int index = 0; while (carryIn && index < maxLength) { val = (long)(result.data[index]); val--; result.data[index] = (uint)(val & 0xFFFFFFFF); if (val >= 0) carryIn = false; index++; } if (index > result.dataLength) result.dataLength = index; while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) result.dataLength--; // overflow check int lastPos = maxLength - 1; // overflow if initial value was -ve but -- caused a sign // change to positive. if ((bi1.data[lastPos] & 0x80000000) != 0 && (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) { throw (new ArithmeticException("Underflow in --.")); } return result; }
private byte[] encryptData(byte[] dataBytes, byte[] bytExponent, byte[] bytModulus) { //Make sure the data to be encrypted is not bigger than the modulus if (dataBytes.Length > bytModulus.Length) { throw new CryptographicException("Data length cannot be larger than the modulus. Specify a larger cipher strength " + "in the constructor and generate a new key pair, or consider encrypting a smaller " + "amount of data."); } BigInteger oRawData = new BigInteger(dataBytes, dataBytes.Length); BigInteger result = oRawData.modPow(new BigInteger(bytExponent), new BigInteger(bytModulus)); return result.getBytesRaw(); }
//*********************************************************************** // Overloading of multiplication operator //*********************************************************************** public static BigInteger operator *(BigInteger bi1, BigInteger bi2) { int lastPos = maxLength - 1; bool bi1Neg = false, bi2Neg = false; // take the absolute value of the inputs try { if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative { bi1Neg = true; bi1 = -bi1; } if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative { bi2Neg = true; bi2 = -bi2; } } catch (Exception) { } BigInteger result = new BigInteger(); // multiply the absolute values try { for (int i = 0; i < bi1.dataLength; i++) { if (bi1.data[i] == 0) continue; ulong mcarry = 0; for (int j = 0, k = i; j < bi2.dataLength; j++, k++) { // k = i + j ulong val = ((ulong)bi1.data[i] * (ulong)bi2.data[j]) + (ulong)result.data[k] + mcarry; result.data[k] = (uint)(val & 0xFFFFFFFF); mcarry = (val >> 32); } if (mcarry != 0) result.data[i + bi2.dataLength] = (uint)mcarry; } } catch (Exception) { throw (new ArithmeticException("Multiplication overflow.")); } result.dataLength = bi1.dataLength + bi2.dataLength; if (result.dataLength > maxLength) result.dataLength = maxLength; while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) result.dataLength--; // overflow check (result is -ve) if ((result.data[lastPos] & 0x80000000) != 0) { if (bi1Neg != bi2Neg && result.data[lastPos] == 0x80000000) // different sign { // handle the special case where multiplication produces // a max negative number in 2's complement. if (result.dataLength == 1) return result; else { bool isMaxNeg = true; for (int i = 0; i < result.dataLength - 1 && isMaxNeg; i++) { if (result.data[i] != 0) isMaxNeg = false; } if (isMaxNeg) return result; } } throw (new ArithmeticException("Multiplication overflow.")); } // if input has different signs, then result is -ve if (bi1Neg != bi2Neg) return -result; return result; }
private bool Validate_Key_Data(RSAParameters @params) { bool result = true; //Make sure the public bits have been set if (!(@params.N.Length > 0)) { throw new CryptographicException("Value for Modulus (N) is missing or invalid."); } if (!(@params.E.Length > 0)) { throw new CryptographicException("Value for Public Exponent (E) is missing or invalid."); } //If any of the private key data (D, P or Q) were supplied, validating private //key info. if (@params.D.Length > 0 || @params.P.Length > 0 || @params.Q.Length > 0) { if (!(@params.P.Length > 0)) { throw new CryptographicException("Value for P is missing or invalid."); } if (!(@params.Q.Length > 0)) { throw new CryptographicException("Value for Q is missing or invalid."); } if (!(@params.D.Length > 0)) { throw new CryptographicException("Value for Private Exponent (D) is missing or invalid."); } //Validate the key if (@params.P.Length != @params.N.Length / 2 || @params.Q.Length != @params.N.Length / 2) { throw new CryptographicException("Invalid Key."); } BigInteger biN = new BigInteger(@params.N); BigInteger biP = new BigInteger(@params.P); BigInteger biQ = new BigInteger(@params.Q); BigInteger tmpMod = new BigInteger(biP * biQ); if (!(tmpMod == biN)) { throw new CryptographicException("Invalid Key."); } tmpMod = null; } return result; }
//*********************************************************************** // 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; }