Question: You will write a Java client-server system that allows users to upload and save their files to a server with end-to-end encryption and authentication. Client-server
You will write a Java client-server system that allows users to upload and save their files to a server with end-to-end encryption and authentication.
Client-server architecture, public and private keys
The system consists of a client and a server Java program, and they must be named Client.java and Server.java respectively. They are started by running the commands
java Server port java Client host port userid filename
specifying the hostname and port number of the server, the userid of the client, and the name of the file to be uploaded.
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.
Each user has a unique userid, which is a simple string like alice, bob etc. The server has userid "server". Each user is associated with a pair of RSA public and private keys, with filenames that have .pub or .prv after the userid, respectively. Thus the key files are named alice.pub, server.prv, etc. These keys are generated separately by a program RSAKeyGen.java. More details are in the comment of that program.
It is assumed that the server already has the public keys of all legitimate users, and each client program user already has their own private key as well as the public key of the server. They obtained these keys via some separate mechanism not described here, prior to the execution of the client and server programs, and is not part of these programs. The client and server programs never create any new keys or distribute them.
All the key files are in the folder where the respective client/server program runs from. They must not be read from other folders. Your programs must not require keys that they are not supposed to have.
The key agreement stage
When the client program starts, it connects to the server and sends it the userid of the client (unencrypted). The client and server then go through a key generation and authentication process.
First, the client program generates 16 fresh random bytes. It encrypts them with RSA/ECB/PKCS1Padding, with an appropriate RSA key so that only the server can decrypt it. It also generates a signature of these encrypted bytes using the SHA1withRSA signature algorithm, with the appropriate RSA key that proves the identity of this client. (RSA keys can also be used for signatures; in this system the same set of RSA keys are used for both encryption and signature purposes.) The encrypted bytes and the signature are sent to the server.
Upon accepting the connection of a client, the server receives the contents described in the previous two steps, verifies the signature, and decrypts the encrypted bytes to obtain the client's 16 bytes. If the signature does not verify, it should terminate the connection (and do not continue with the rest of this protocol).
Next we perform the same procedure but from server to client this time: the server generates 16 fresh random bytes, encrypts them with RSA/ECB/PKCS1Padding and with a key so that the result can only be decrypted by the client. It then generates a signature of the encrypted key using the SHA1withRSA algorithm and with a key that proves the identity of the server. The encrypted key and the signature are sent to the client.
Likewise, when the client received these contents, it should verify the signature and decrypt the encrypted bytes to obtain the server's 16 bytes. If the signature does not verify, it should terminate the connection (and do not continue with the rest of this protocol).
Both the client and server (independently) form 32 bytes by appending the server's 16 bytes to the client's 16 bytes, and use these 32 bytes to generate a 256-bit AES key. Note that these unencrypted bytes (and the resulting key) are never sent across the network.
The file transmission stage
The client program reads the contents of the file specified by the user, encrypts it with AES/CBC/PKCS5Padding with the AES key just agreed, and sends the encrypted content to the server. The CBC mode needs a 16-byte initialisation vector (IV); here we will re-use the 16 bytes that the client generated in the key agreement stage for the IV. The server decrypts the received content and saves it locally. (Yes, we assume the file is stored unencrypted while "at rest" in the server. This is because we assume the server uses some kind of full-disk encryption scheme (not to be implemented here) which is transparent to any program running on the server.)
In order to further protect the security and privacy of the users, the received content is not saved in the server with the same filename as the one in the client side. Instead, the original filename is prepended with the client's userid, and the special string "gfhk7346", separated by the ":" character, and then the MD5 hash of this string is computed and converted to hex (with lowercase a-f). This hex is the resulting filename. For example, if the userid is alice and the filename is secret.jpg, the filename stored in the server is
hex(MD5("alice:gfhk7346:secret.jpg")) which is
3c78bc645aaa4dd60bc126264cf0342f
The filename should not retain any file extension such as ".jpg". This hashed filename is sent to the server, and the server should store the file with this hashed filename. Note that the filename hashing should be done in the client side; the original, unhashed filename is never transmitted.
The server stores all files in its current working folder. All different users' files are stored in the same folder; it is assumed that files from different users will not hash into the same filename (if it happens, the old file is just quietly overwritten).
When the server is finished with this client, it should end this connection and wait for the next client. The server should not quit or terminate (even if the signature check fails).
Program outputs
The client program does not need to print any message on screen; it can just quietly finish its job. If you prefer, you can add any messages on screen, but they are not required.
The same would normally be true for the server program as well, but for marking purposes, it should print on the screen the userid of each client upon connection, the 16 client bytes, the 16 server bytes, (in hex or whatever printable formats you prefer), and the received (hashed) filename. There is no fixed format required for how these printouts should look like. (RSAKeyGen.java)
import java.io.*; import java.security.*;
public class RSAKeyGen {
public static void main(String [] args) throws Exception {
if (args.length != 1) { System.err.println("Usage: java RSAKeyGen userid"); System.exit(-1); }
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(2048); KeyPair kp = kpg.genKeyPair();
ObjectOutputStream objOut = new ObjectOutputStream(new FileOutputStream(args[0] + ".pub")); objOut.writeObject(kp.getPublic()); objOut.close();
objOut = new ObjectOutputStream(new FileOutputStream(args[0] + ".prv")); objOut.writeObject(kp.getPrivate());
}
}
Step by Step Solution
There are 3 Steps involved in it
Get step-by-step solutions from verified subject matter experts
