I had an assignment to implement RSA algorithm, according to our textbook, the steps are like below
I have made my form so that user can enter the values of the prime numbers, p, q, and the public key, e, or the user can press on a buttons to generate these numbers randomly, I have made it very simple, with no validations, and encrypting only numbers below the length n of the RSA algorithm.
I have called the text fields p,q,e,d,m,c with IDs txtP, txtQ, txtE, txtD, txtM, txtC
To generate random p, and q, I am using a function RandomIntegerBelow (which I created with the help of some posts from stackoverflow) to generate a random BigInteger below a specific number then test if this number is prime or not using Rabin Millar test(I posted it in previous post) , and keep looping until I get it
private void btnGenerateP_Click(object sender, EventArgs e)
{
BigInteger p = RandomIntegerBelow(BigInteger.Parse(txtMax.Text));
while (!IsProbabilyPrime(p, 20))
{
p = RandomIntegerBelow(BigInteger.Parse(txtMax.Text));
}
txtP.Text = p.ToString();
}
To get random e, I generated a random number below totient and tested if gcd(totient, e) = 1, and looped until I get it.
private void btnGenerateE_Click(object sender, EventArgs e)
{
log("generating e randomely such that gcd(e,totient) = 1");
BigInteger temp = 0;
while (GCD_Euclidean(temp, BigInteger.Parse(txtTOT.Text)) != 1)
{
temp = RandomIntegerBelow(BigInteger.Parse(txtTOT.Text));
log("new E = " + temp);
}
txtE.Text = temp.ToString();
}
I used the function Extended_GCD (I have posted in in previous post) to get the modulo multiplicative inverse for the e value to get d, this function returns array of the values gcd, x, y respectively that satisfies gcd(m,n) = m*x + n*y, and I am reading always y as my answer since I am sending the totient first then the value of e.
private void btnCalculateD_Click(object sender, EventArgs e)
{
BigInteger[] result = new BigInteger[3];
result = Extended_GCD(BigInteger.Parse(txtTOT.Text), BigInteger.Parse(txtE.Text));
if (result[2] < 0)
result[2] = result[2] + BigInteger.Parse(txtTOT.Text);
txtD.Text = result[2].ToString();
}
Now I have e,d,n (public key, private key, length) which are the important parameters for RSA algorithm
Lastly, to encrypt and decrypt
private void btnEncrypt_Click(object sender, EventArgs e)
{
log("Encrypting, C = M^e mod n");
txtC.Text = BigInteger.ModPow(BigInteger.Parse(txtM.Text), BigInteger.Parse(txtE.Text), BigInteger.Parse(txtN.Text)).ToString();
}
private void btnDecrypt_Click(object sender, EventArgs e)
{
log("Encrypting, M' = C^d mod n");
txtMR.Text = BigInteger.ModPow(BigInteger.Parse(txtC.Text), BigInteger.Parse(txtD.Text), BigInteger.Parse(txtN.Text)).ToString();
}
Below is the full code including the helper functions
private void btnGenerateP_Click(object sender, EventArgs e)
{
BigInteger p = RandomIntegerBelow(BigInteger.Parse(txtMax.Text));
while (!IsProbabilyPrime(p, 20))
{
p = RandomIntegerBelow(BigInteger.Parse(txtMax.Text));
}
txtP.Text = p.ToString();
}
private void btnGenerateQ_Click(object sender, EventArgs e)
{
BigInteger p = RandomIntegerBelow(BigInteger.Parse(txtMax.Text));
while (!IsProbabilyPrime(p, 20))
{
p = RandomIntegerBelow(BigInteger.Parse(txtMax.Text));
}
txtQ.Text = p.ToString();
}
private void btnCalculateD_Click(object sender, EventArgs e)
{
BigInteger[] result = new BigInteger[3];
result = Extended_GCD(BigInteger.Parse(txtTOT.Text), BigInteger.Parse(txtE.Text));
if (result[2] < 0)
result[2] = result[2] + BigInteger.Parse(txtTOT.Text);
txtD.Text = result[2].ToString();
}
private void btnCalculateN_Click(object sender, EventArgs e)
{
log("N = P*Q = " + txtP.Text + " x " + txtQ.Text);
txtN.Text = (BigInteger.Multiply(BigInteger.Parse(txtP.Text), BigInteger.Parse(txtQ.Text))).ToString();
}
private void btnCalculateTOT_Click(object sender, EventArgs e)
{
log("totient = (P-1)*(Q-1) = " + (BigInteger.Parse(txtP.Text) - 1) + " x " + (BigInteger.Parse(txtQ.Text) - 1));
txtTOT.Text = (BigInteger.Multiply(BigInteger.Parse(txtP.Text) - 1, BigInteger.Parse(txtQ.Text) - 1)).ToString();
}
private void btnGenerateE_Click(object sender, EventArgs e)
{
log("generating e randomely such that gcd(e,totient) = 1");
BigInteger temp = 0;
while (GCD_Euclidean(temp, BigInteger.Parse(txtTOT.Text)) != 1)
{
temp = RandomIntegerBelow(BigInteger.Parse(txtTOT.Text));
log("new E = " + temp);
}
txtE.Text = temp.ToString();
}
private void btnEncrypt_Click(object sender, EventArgs e)
{
log("Encrypting, C = M^e mod n");
txtC.Text = BigInteger.ModPow(BigInteger.Parse(txtM.Text), BigInteger.Parse(txtE.Text), BigInteger.Parse(txtN.Text)).ToString();
}
private void btnDecrypt_Click(object sender, EventArgs e)
{
log("Encrypting, M' = C^d mod n");
txtMR.Text = BigInteger.ModPow(BigInteger.Parse(txtC.Text), BigInteger.Parse(txtD.Text), BigInteger.Parse(txtN.Text)).ToString();
}
#region helpers
public void log(string s)
{
txtLog.Text += s + "\r\n";
}
public static BigInteger GCD_Loop(BigInteger A, BigInteger B)
{
BigInteger R = BigInteger.One;
while (B != 0)
{
R = A % B;
A = B;
B = R;
}
return A;
}
public BigInteger GCD_Euclidean(BigInteger A, BigInteger B)
{
log("gcd(" + A + "," + B + ")");
if (B == 0)
return A;
if (A == 0)
return B;
if (A > B)
return GCD_Euclidean(B, A % B);
else
return GCD_Euclidean(B % A, A);
}
public bool IsProbabilyPrime(BigInteger n, int k)
{
bool result = false;
if (n < 2)
return false;
if (n == 2)
return true;
// return false if n is even -> divisbla by 2
if (n % 2 == 0)
return false;
//writing n-1 as 2^s.d
BigInteger d = n - 1;
BigInteger s = 0;
while (d % 2 == 0)
{
d >>= 1;
s = s + 1;
}
for (int i = 0; i < k; i++)
{
BigInteger a;
do
{
a = RandomIntegerBelow(n - 2);
}
while (a < 2 || a >= n - 2);
if (BigInteger.ModPow(a, d, n) == 1) return true;
for (int j = 0; j < s - 1; j++)
{
if (BigInteger.ModPow(a, 2 * j * d, n) == n - 1)
return true;
}
result = false;
}
return result;
}
public BigInteger RandomIntegerBelow(int n)
{
var rng = new RNGCryptoServiceProvider();
byte[] bytes = new byte[n / 8];
rng.GetBytes(bytes);
var msb = bytes[n / 8 - 1];
var mask = 0;
while (mask < msb)
mask = (mask << 1) + 1;
bytes[n - 1] &= Convert.ToByte(mask);
BigInteger p = new BigInteger(bytes);
return p;
}
public BigInteger RandomIntegerBelow(BigInteger bound)
{
var rng = new RNGCryptoServiceProvider();
//Get a byte buffer capable of holding any value below the bound
var buffer = (bound << 16).ToByteArray(); // << 16 adds two bytes, which decrease the chance of a retry later on
//Compute where the last partial fragment starts, in order to retry if we end up in it
var generatedValueBound = BigInteger.One << (buffer.Length * 8 - 1); //-1 accounts for the sign bit
var validityBound = generatedValueBound - generatedValueBound % bound;
while (true)
{
//generate a uniformly random value in [0, 2^(buffer.Length * 8 - 1))
rng.GetBytes(buffer);
buffer[buffer.Length - 1] &= 0x7F; //force sign bit to positive
var r = new BigInteger(buffer);
//return unless in the partial fragment
if (r >= validityBound) continue;
return r % bound;
}
}
public BigInteger[] Extended_GCD(BigInteger A, BigInteger B)
{
BigInteger[] result = new BigInteger[3];
bool reverse = false;
if (A < B) //if A less than B, switch them
{
BigInteger temp = A;
A = B;
B = temp;
reverse = true;
}
log("Extended GCD");
BigInteger r = B;
BigInteger q = 0;
BigInteger x0 = 1;
BigInteger y0 = 0;
BigInteger x1 = 0;
BigInteger y1 = 1;
BigInteger x = 0, y = 0;
log(A + "\t" + " " + "\t" + x0 + "\t" + y0);
log(B + "\t" + " " + "\t" + x1 + "\t" + y1);
while (A % B!=0)
{
r = A % B;
q = A / B;
x = x0 - q * x1;
y = y0 - q * y1;
x0 = x1;
y0 = y1;
x1 = x;
y1 = y;
A = B;
B = r;
log(B + "\t" + r + "\t" + x + "\t" + y);
}
result[0] = r;
if (reverse)
{
result[1] = y;
result[2] = x;
}
else
{
result[1] = x;
result[2] = y;
}
return result;
}
public BigInteger Extended_GCD2(BigInteger n, BigInteger m)
{
BigInteger[] Quot = new BigInteger[50];
bool reverse = false;
if (n < m)
{
BigInteger z;
z = n;
n = m;
m = z;
reverse = true;
}
BigInteger originaln = n;
BigInteger originalm = m;
int xstep = 1;
BigInteger r = 1;
while (r != 0)
{
BigInteger q = n / m;
r = n - m * q;
log(" " + n + " = " + m + "*" + q + " + " + r);
n = m;
m = r;
Quot[xstep] = q;
++xstep;
}
//setgcd(n)
BigInteger gcd = n;
BigInteger a = 1;
BigInteger b = 0;
for (int i = xstep; i > 0; i--)
{
BigInteger z = b - Quot[i] * a;
b = a;
a = z;
}
return a;
}
#endregion