Not a long time ago in a city far, far away, it is a period of civil war. A brave alliance of freedom fighters has challenged the tyranny and oppression of the evil Empire. But it is a dark time for the Rebellion. It is long known that the Empire can intercept Rebellion communications, and as a result many in the resistance are captured and killed. You want to contribute to the fight by using your cybersecurity knowledge to develop a secure communication system for the Rebellion.
Description of the required system
Starting the programs.
The system consists of a client and a server program. The server runs from some secret location, and each user has a copy of the client program. They are started by running the commands
java Server port
java Client host port userid
specifying the hostname and port number of the server, and the userid of the client.
The server program is always running once started, and listens for incoming connections at the port specified. When a client is connected, the server handles the request, then waits for the next request (i.e., the server never terminates). For simplicity, you can assume that only one client will connect to the server at any one time.
Userids and keys.
Each user has a unique userid, which is a simple string like alice, bob etc. Each user is associated with a pair of RSA public and private keys, with filenames of the form .pub and .prv. Thus the key files are named alice.pub, bob.prv, etc. These keys are generated separately by a program RSAKeyGen.java, given on the module webpage. More details are in the comment of that program. It is assumed that all the users already have (and only have) the appropriate keys, via some offline method not described here, prior to the execution of the programs, and that the keys are in the current working directories of the client programs.
The same set of RSA keys are used for both RSA encryption and signatures.
Message objects. The system transmits and stores messages, which are objects of the follow- ing class, defined in Message.java.
public class Message implements Serializable {
public String recipientHash; // SHA-256 hash of recipient userid
public Date timestamp; // timestamp (java.util.Date)
public byte[] key; // AES key used, encrypted with RSA
public byte[] iv; // unencrypted IV
public byte[] encryptedMsg; // sender userid + message, encrypted with AES
public byte[] signature; // signature of all above
}
Encryption.
Each message is encrypted as follows. The client generates a fresh 256-bit AES key. It then generates 16 random bytes to be used as the IV. The sender userid is concate- nated with the user’s message, separated by the newline character, and is then encrypted using AES/CBC/PKCS5Padding with the above AES key and IV. The AES key is then en- crypted using RSA/ECB/PKCS1Padding with the public key of the recipient. Finally, the encrypted AES key, the (unencrypted) IV, and the encrypted message are stored in the key, iv and encrytedMsg members of a Message object.
Signatures.
The whole content of the message object (recipientHash, timestamp, key, iv, encryptedMsg) is signed with the sender’s RSA private key and the SHA1withRSA algo- rithm. The resulting signature is stored in the signature field of the Message object.
General client-server interaction.
When the client starts, it sends the SHA-256 hash of the client’s userid to the server. The server checks its collection of received messages to see how many of them are for this user. It sends this number (which can be zero) to the client, followed by each of the Message objects intended for this user. The server deletes those messages afterwards.
For each such Message object received, the client decrypts it to find out the sender, and verifies the signature of this Message object using the appropriate key. If the decryption fails (i.e., it results in a BadPaddingException), the client should display an appropriate message; otherwise, it displays the sender userid, the timestamp and the decrypted mes- sage. If the signature does not verify, the client should show some kind of warning (but still display the message). It then repeats the same process for the next Message object.
Once this is done, it repeatedly asks if the user wants to send a message (until the user replies no). It prompts the user for the recipient userid, and the message. It should then form a Message object, with the SHA-256 hash of the recipient userid (as a hex string) in the recipientHash field, the current time in the timestamp field, and the necessary encryption and signature as described above. It then sends this Message object to the server.
Note that the server merely serves as a repository of these (encrypted and signed) Message objects. It does not hold any userids or keys, does not en/decrypt anything, and does not verify any signatures. The sender userid is encrypted and the recipient userid is hashed, so if the server is compromised, no secret information or the identity of the user is revealed.
You can assume the server only holds those messages in memory (i.e. with no persistence, although if you so wish, you can choose to implement that). You can store the Message objects in any data structure you want.
A sample run of the client program might look something like this (you do not have to reproduce this exactly). Lines with > are typed in by the user.
> java Client localhost 4567 alice
You received 2 messages.
alice’s message:
The green stormtroopers are coming for you! Run!
Fri Jan 31 15:01:05 GMT 2020
bob’s message:
I’m very sorry, but you must pass on the code then destroy it before it’s too late
Fri Jan 31 15:06:57 GMT 2020
Do you want to send a message? Y
Who to?
> carol
Type your message:
> 2344697362616e64484b506f6c696365
Do you want to send a message? N