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(); } }
/// <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(); }
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); }
/// <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; }