CS 1501
Algorithm Implementation
Programming Project 4

Purpose

The purpose of this programming assignment is to implement a primitive digital signature system using the RSA cryptosystem.  You will be working with Java, utilizing both some predefined Java tools and some that you must code yourselves.

Instructions

1.      For this assignment you will need to use the BigInteger class for Java so you should thoroughly familiarize yourself with the class and its methods.

2.      Play around with the RSA demo at http://www.cs.pitt.edu/~kirk/cs1501/notes/rsademo/index.html to familiarize yourself with and make sure you understand the RSA cryptosystem. We will adopt the notation used in this demo for the rest of these instructions (note: the notation used in the text is slightly different).

3.      Write a program to implement the key generation part of the RSA system.

·        Pick P and Q to be random primes of some specified length using the appropriate BigInteger constructor for Java.

·        Pick E to be a random prime between 1 and PHI=(P-1)(Q-1), exclusive, such that GCD(E, PHI)=1 (E must not divide PHI evenly).   E should be similar in (bit) length to P and Q, but does not have to be the same length.

·        Solve the following equation

(E)(D) mod PHI = 1

for D such that 1 < D < PHI.

We discussed in lecture how to do this using the XGCD function.  However, the BigInteger class in Java does not have the XGCD method exactly as we discussed; rather it has a method to calculate the "inverse mod" that can also give us the correct answer.  To put it into mathematical terms, since

(E)(D) mod PHI = 1

we can solve for D and say that

D = E-1 mod PHI

This only works if E and PHI are relatively prime, which we know them to be.

In BigInteger the method used for this purpose is

public BigInteger modInverse(BigInteger m)

and it would be called from object E, passing PHI as the argument, with the return value assigned to D.

When you execute this program, it should generate new public and private keys for your RSA cryptosystem, where P, Q and E as defined above are all 512-bit integers. Also necessary for both encryption and decryption is the value (P)(Q) = N (which should be ~1024 bits). Your generation program should output all three values E, D and N to the console as it generates them.  The values E and N should also be saved to a file called "pubkey.rsa" and the values D and N should be saved to a file "privkey.rsa".  To allow for nice access of these files, you MUST output and input these keys to and from the files using a Java ObjectOutputStream and ObjectInputStream.

4. Write a second program that uses your RSA algorithm for a primitive "digital signature." As discussed in lecture, a digital signature is a way for the receiver to make sure a message has not been forged or tampered with (note: prevention of forging also requires third party authentication, but we will not be concerned with that here).  Recall that it is (usually) too time-consuming for the sender to "decrypt" the entire message and for the receiver to then "encrypt" it, otherwise that technique would work well.  Instead the sender will merely append a "signature" on the end of the message, such that any tampering with the message will be evident. 

The basic idea is that the sender processes the message using some message digest system such as MD5 to form the "signature".  The sender then encrypts the signature using his/her private key and appends it to the end of the message.  Thus, the message itself is unencrypted plaintext (however, the sender could encrypt the entire signed message with the receiver's public key if he/she wanted it to be private).  The receiver then decrypts the signature using the sender's public key, processes the message in the same way the sender did, and verifies that the signatures "match".  The basis for the success of this technique is the ability to process the message in a way that is fast yet that can detect (almost) any alteration to the message.  MD5 has these desired properties.

Details:

Your goal is for the receiver of a message to be able to discover any tampering with the message sent by the sender, and, assuming that the sender's private key has not been compromised, to verify that the sender is indeed the one who sent the message.

Sender:

The sender of the message will create the message itself in plaintext.  This can be any text message stored in any file on the sender's computer.  Before "sending" the message, the sender will do the following:

1)      Process the plaintext file in the following way:

a)      Read in the file byte by byte, adding each to an MD5 instance of a MessageDigest.  Once you have added the complete file to the MessageDigest, obtain the 128 bit (16 byte) signature array value from it.  For more details on using a MessageDigest, see the documentation in the Java API and see handout MD.java.

b)      You now have in your signature array the digital signature bytes ready to be "decrypted".  Using the sign/magnitude constructor for BigInteger in Java, convert this array into a single BigInteger.  Next "decrypt" the BigInteger using the private key that you generated in Part 1 of the assignment using the RSA algorithm.  To make verification easier, prepend this value to your plaintext file (i.e. put it in the front of the file).   You can do this using an ObjectOutputStream – first write your BigInteger signature value, then write the rest of your file byte by byte.  Your message is now ready to "send".  However, for the purposes of this assignment you will simply leave it in the file.  If your original file was <filename>.<ext>, call this file <filename>.<ext>.signed

Receiver

1)      Upon "receiving" the message the receiver first reads the BigInteger value (using an ObjectInputStream), then "encrypts" it using the public key generated in Part 1 of the assignment using the RSA algorithm.  The resulting BigInteger is then converted back to an array of bytes using the toByteArray() method, with the result being the signature array that you will now use to check to see if the message has been tampered with[1].

a)      Read the remaining bytes of the file, adding them to a new MessageDigest object.  Once you have added all of the bytes, obtain the message digest for the received file.  If this exactly matches the transmitted message digest, the message has not been tampered with – otherwise it has been tampered with.  The receiver should output a message indicating whether or not the received file was valid.

Main Program for Second Part

Have a menu-driven loop that allows the user to "send" or "receive" files.  "Send" will simply prompt for an input filename and process the file as specified above.  "Receive" will also prompt for a filename (should be a .signed file which had previously been output from a "Send") and process it as above, outputting whether or not the file has been tampered with.

Execution and Testing

To allow for easier grading by the TA, "capture" an interactive session in which you test your program.  If you are using Windows, this may involve cutting and pasting text from the console to a file.  If you are using Unixs, you can run a script.

1)      In this file you should first show your key generation by running your first program. 

2)      Then print out a short test file (but at least 5-10 lines of text) – call it "test.txt"

3)      "Send" it as described above, yielding the signed file "test.txt.signed".

4)      Print out the signed file (note that since your signature is written as a BigInteger object, the beginning of this file may not show correctly – this is ok). 

5)      Next "receive" "test.txt.signed" to show that it is ok

6)      Next, change one byte in "test.txt.signed" to show "tampering".  In order to do this so as not to (necessarily) cause an IOException with a Java ObjectInputStream, you need to write a simple program to change a byte.  This program should do the following:

1.      Prompt the user for the input file name

2.      Open the file as a binary file

3.      Prompt the user for which byte to change (i.e. you are indexing the file byte by byte)

4.      Change the byte to a random value and close the file

7)      Finally, "receive" the corrupted file and through the receiver indicate that it has been corrupted.  Note that if the byte you changed was within the BigInteger object portion of the file, the result may be an IOException when you try to read in the BigInteger. This should also be an indicator of tampering.

8)      Call your interactive session file "demo.txt" – make sure to submit this file with your other submission materials.

Notes:

·        Be sure "demo.txt" is submitted as specified above along with all other pertinent source and data files.  If not, you may lose credit!

·        Note that there are 3 programs associated with the assignment:

1.      The program to generate the RSA Keys

2.      The program to allow "sending" or "receiving" of files

3.      The program to change a byte within the signed file.

·        As with previous assignments, make sure you submit the appropriate .jar files or all .class files so that the TA can execute your programs without compiling them.

·        Don't forget to complete and submit your Assignment Information Sheet.

·        W Section Students:  Write a short (~4 page) paper giving some history and background on public key encryption and RSA.  Be sure to use multiple sources and cite all of them in your paper.



[1] There are some quirks related to converting a BigInteger to an array of bytes.  Specifically, the byte array obtained using toBytes() is not always identical to the byte array used to construct the BigInteger.  This clearly could be a problem when testing for the match of a digital signature.  However, the issues can be handled if considered correctly.  For more information on this issue, see the handout ByteBigByte.java.