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.
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.