RSA.mws

Using RSA

© Mike May, S. J., 2002, maymk@slu.edu

>    restart:

Setting up RSA to encode numbers

The first thing to do to use RSA is to find two large primes.  For this worksheet we will be looking for primes that are about 80 digits long.  (I chose 80 digits bacause that is what easily fits on one line.)  We do this with the rand and nextprime functions in Maple.

>    M1 := rand(10^80)();
M2 := rand(10^80)();
P1 := nextprime(M1);
P2 := nextprime(M2);

M1 := 19669081321110693270343633073697474256143563558458718976746753830538032062222085

M2 := 74121768604305613921745580037409259811952655310075487163797179490457039169594160

P1 := 19669081321110693270343633073697474256143563558458718976746753830538032062222257

P2 := 74121768604305613921745580037409259811952655310075487163797179490457039169594213

The use of a random number function in the selection of the primes is intended to make them hard to guess.  The output has been converted to Maple input so that we can work with repeatable computations.  (To convert to Maple input I had to copy the output to a simpletext document than copy fromthere back into Maple.)

>    M1 := 19669081321110693270343633073697474256143563558458718976746753\
830538032062222085;
M2 := 74121768604305613921745580037409259811952655310075487163797179\
490457039169594160;
P1 := 19669081321110693270343633073697474256143563558458718976746753\
830538032062222257;
P2 := 74121768604305613921745580037409259811952655310075487163797179\
490457039169594213;

M1 := 19669081321110693270343633073697474256143563558458718976746753830538032062222085

M2 := 74121768604305613921745580037409259811952655310075487163797179490457039169594160

P1 := 19669081321110693270343633073697474256143563558458718976746753830538032062222257

P2 := 74121768604305613921745580037409259811952655310075487163797179490457039169594213

I need to compute the product of the two primes and the product of one less than each of the primes.

>    n := P1*P2;
n2 := (P1-1)*(P2-1);

n := 1457907094342636571934108159685862980326515914911824861643397522980497550736230615496046802186876835611836753440525199587698019954839165932427842278373706998741
n := 1457907094342636571934108159685862980326515914911824861643397522980497550736230615496046802186876835611836753440525199587698019954839165932427842278373706998741

n2 := 1457907094342636571934108159685862980326515914911824861643397522980497550736230521705196876770569643522623642333791131491479151420633025388494521283302475182272
n2 := 1457907094342636571934108159685862980326515914911824861643397522980497550736230521705196876770569643522623642333791131491479151420633025388494521283302475182272

I now need to decide on a value for e, the encrypting number.  SInce e will be public, I don't need to worry about random generation.  I can consider numbers that will be easy to work with mathematically.  It seems that the best criterion for mathematical ease is that e represented base 2 should be almost all zeroes.

>    e:= 2^16+1;  isprime(e); gcd(e,n2);

e := 65537

true

1

Recall that we had to make sure that e is relatively prime to n2.  That is easier because we have chosen e to be prime.  Now we need to compute d, the multiplicative inverse of e mod n2.  We could do that directly with the extended Euclidean algorithm, but we will just use the modular arithmetic of Maple.

>    d := eval(1/e mod n2);

d := 341802989220968474720655078407209434254191022363248073594317758527173121550607778293183240178522095499109087453784896094825475099226794560236481979918863102913
d := 341802989220968474720655078407209434254191022363248073594317758527173121550607778293183240178522095499109087453784896094825475099226794560236481979918863102913

We are ready to define encoding and decoding proceedures.

>    encoder := (a,e,n) -> Power(a,e) mod n:
decoder := (a,d,n) -> Power(a,d) mod n:

We obviously want to check that the procedures reverse each other, at least for a small sample of messages.

>    m1 := encoder(5,e,n);p1 :=decoder(m1,d,n);

m1 := 446699826528570457727849702847882456011063958845436575406364845773934883185785904969215738362722324430978629195687422700096164664107349103915395164169538874261
m1 := 446699826528570457727849702847882456011063958845436575406364845773934883185785904969215738362722324430978629195687422700096164664107349103915395164169538874261

p1 := 5

Exercises

1)  The chosen primes let us encode and decode messages that are numbers approximately in the range of 0 to 10^159.  Randomly pick 5 numbers in that range and verify that encoding and decoding act as inverse operations on these numbers.

>   

2)  Of the numbers found so far, explain which numbers I want to publish, which ones I want to save but keep secret, and which ones I want to destroy and forget if I am using RSA as a public key system.

>   

3)  Randomly pick two primes of your own in the 1-10^80 range and prepare your own keys.  Test that your keys encrypt and decrypt on two numbers of your choice.  (You probably don't want to reuse the names n, e, or d, since we will continue to use these numbers in later exercises.

>   

4)  Post your public key and modulus to the bulletin board so that others in the class can send you messages.  (Do not post your secret key.)

>   

>   

Encoding messages rather than numbers

Now we want to turn our attention to encoding messages.  Following the pattern from earlier work, if we want to turn our message into into a single number, the trick is to use convert to change a message from ASCII to a string of decimal equivalents, then convert each of the decimal numbers into a two digit hex number, concatenate (string together) the hex numbers into one big number, then convert it back to a decimal number.  We can then encode our message number, convert it to a hex number and store it in some reasonable form.

Recall that we had a problem with hex conversion before.  If the decimal equivalent of the ASCII charachter is less than 16, the hex number is only one digit long, and we want to assume characters contribute 2 hex digits to keep placement straight.  (In particular the "return" character is equivalent to 13.)  The procedure twodigithex converts with a two digit output.

>    twodigithex := a -> substring(convert(a+256,hex),2..3):

We are ready to start with a small message.

>    mess1 := "Good morning Mr. Phelps,
This morning we look at RSA.
3rd line";

mess1 :=
mess1 :=
mess1 :=

We want a procedure to convert this to a number.

>    shortconverter := proc(messagestring)
  local stringofhex, lengthofmess, hexstring, i:
    #First we convert the ASCII string to a list of decimal equivalents
    #Then we convert thedecimal numbers to hex equivalents
  stringofhex := map(twodigithex, convert(messagestring,bytes)):
print(stringofhex);
  lengthofmess := nops(stringofhex):
print(lengthofmess);
    #Now we concatenate the hex numbers
  hexstring := cat(seq(stringofhex[i],i=1..lengthofmess)):
    #Finally we convert the big number back to decimal
  convert(hexstring,decimal,hex):
end:

>    messnum1 := shortconverter(mess1);

[`47`, `6F`, `6F`, `64`, `20`, `6D`, `6F`, `72`, `6E`, `69`, `6E`, `67`, `20`, `4D`, `72`, `2E`, `20`, `50`, `68`, `65`, `6C`, `70`, `73`, `2C`, `0A`, `54`, `68`, `69`, `73`, `20`, `6D`, `6F`, `72`, `6...
[`47`, `6F`, `6F`, `64`, `20`, `6D`, `6F`, `72`, `6E`, `69`, `6E`, `67`, `20`, `4D`, `72`, `2E`, `20`, `50`, `68`, `65`, `6C`, `70`, `73`, `2C`, `0A`, `54`, `68`, `69`, `73`, `20`, `6D`, `6F`, `72`, `6...

62

messnum1 := 57088774079733106725947856129207076812372928870871822207567432190628558150157207970485530589836894150684348539393766640624526013363224875431503687269
messnum1 := 57088774079733106725947856129207076812372928870871822207567432190628558150157207970485530589836894150684348539393766640624526013363224875431503687269

Next we want to encode our number and convert it to hex for easy handling.

>    sechex1 := convert(encoder(messnum1,e,n),hex);

sechex1 := `622ED113512082D85F0D6C7504B962D337173151BAF504F469586953300258AFFE7ED152E360ECF742152D71E8F60BB1CB5AC17F8022798960A3FB8DCE6796C1E2C1`
sechex1 := `622ED113512082D85F0D6C7504B962D337173151BAF504F469586953300258AFFE7ED152E360ECF742152D71E8F60BB1CB5AC17F8022798960A3FB8DCE6796C1E2C1`

This does not look very easy to copy back and forth.  (I am sure I would make a mistake if I tried to type a copy of a string that long.)  To give a nice form to the output, I want to pad the message out with leading zeroes, then break it into blocks of a nice size.  For today, I think 10 characters forms a nice block size.

>    length(sechex1);

132

>    sechex2 := cat(seq("0",counter = 1..(140-length(sechex1))),sechex1);

sechex2 :=
sechex2 :=

>    secmess := [seq(substring(sechex2,10*i-9..10*i),i=1..14)];

secmess := [
secmess := [

Now we want to verify that we can put the message back together and decode it.  We define a procedure for converting the vector of hexblock into a single number.

>    hexvectodec := (hexvec,size) ->
         convert(cat(seq(hexvec[i],i=1..size)),decimal,hex):

Now we can convert our message back to a decoded number.  We should get an answer equal to messnum1 above.

>    newnum1 := decoder(hexvectodec(secmess,14),d,n);

newnum1 := 57088774079733106725947856129207076812372928870871822207567432190628558150157207970485530589836894150684348539393766640624526013363224875431503687269
newnum1 := 57088774079733106725947856129207076812372928870871822207567432190628558150157207970485530589836894150684348539393766640624526013363224875431503687269

>    convert(newnum1,hex);

`476F6F64206D6F726E696E67204D722E205068656C70732C0A54686973206D6F726E696E67207765206C6F6F6B206174205253412E0A337264206C696E65`
`476F6F64206D6F726E696E67204D722E205068656C70732C0A54686973206D6F726E696E67207765206C6F6F6B206174205253412E0A337264206C696E65`

We also want a procedure for turning the decoded number back into a message.

>    numtostring := proc(bignum)
local hexstr1, listlength, declist, i:
#convert to a hex string
hexstr1 := convert(bignum,hex):
#make sure the hex string has even length
if (length(hexstr1) mod 2) = 1 then hexstr1 := cat(`0`,hexstr1) fi;
#compute the number of characters
listlength := length(hexstr1)/2:
#convert to a vector of decimal numbers
declist := linalg[vector](listlength);
for i from 1 to listlength do
declist[i] := convert(substring(hexstr1,2*i-1..2*i),decimal,hex);
od;
#convert the vector to a list, then to an ASCII string
convert(convert(convert(declist,list),bytes),name);
end:

>    numtostring(newnum1);

`Good morning Mr. Phelps,\nThis morning we look at RSA.\n3rd line`
`Good morning Mr. Phelps,\nThis morning we look at RSA.\n3rd line`
`Good morning Mr. Phelps,\nThis morning we look at RSA.\n3rd line`

There is one more issue to deal with.  For the procedures above to work, we have to restrict ourselves to messages that encode as numbers less than n.  A message of length t encodes as a numebr less than 256^t.  

Exercises

5)  What is the maximum length of a message that we can encode with n chosen as above?

>   

6)  Enter a message of about 50 characters and encode it Using the n and e defined above.

>   

7)  Verify that you can decrypt the message with the secret key.

>   

8)  For the rest of this worksheet, Fr. May's modulus is
145790709434263657193410815968586298032651591491182486164339\
752298049755073623061549604680218687683561183675344052519958\

7698019954839165932427842278373706998741

and his encrypting key is

65537.

Encrypt your short message and send it to him through the bulletin board.

>   

9)  Obtain the public keys of two othe people in the class and send them a short encrypted message via the bulletin board.

>   

10)  Respond to the messages you get with encrypted messages.

>   

11)  Make a shorter RSA worksheet that you can use as an application for encrypting and decrypting.

>   

>