Implement skeleton for MessageClient

This commit is contained in:
Tobias Eidelpes 2020-12-23 18:10:57 +01:00
parent 970b62ed72
commit 93157eb9de

View File

@ -1,12 +1,44 @@
package dslab.client;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.logging.Logger;
import dslab.ComponentFactory;
import dslab.util.Config;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
public class MessageClient implements IMessageClient, Runnable {
private static final Logger logger = Logger.getLogger(MessageClient.class.getName());
private final String transferHost;
private final int transferPort;
private final String mailboxHost;
private final int mailboxPort;
private final String transferEmail;
private final String mailboxUser;
private final String mailboxPassword;
private Socket dmtpSocket;
private Socket dmapSocket;
private PrintWriter dmtpOut;
private BufferedReader dmtpIn;
private PrintWriter dmapOut;
private BufferedReader dmapIn;
/**
* Creates a new client instance.
@ -17,12 +49,95 @@ public class MessageClient implements IMessageClient, Runnable {
* @param out the output stream to write console output to
*/
public MessageClient(String componentId, Config config, InputStream in, PrintStream out) {
this.transferHost = config.getString("transfer.host");
this.transferPort = config.getInt("transfer.port");
this.mailboxHost = config.getString("mailbox.host");
this.mailboxPort = config.getInt("mailbox.port");
this.transferEmail = config.getString("transfer.email");
this.mailboxUser = config.getString("mailbox.user");
this.mailboxPassword = config.getString("mailbox.password");
logger.info(String.format("TransferHost: %s\nTransferPort: %d\nMailboxHost: %s\nMailboxPort: %d\nTransferEmail: %s\nMailboxUser: %s\nMailboxPassword: %s",
transferHost, transferPort, mailboxHost, mailboxPort, transferEmail, mailboxUser, mailboxPassword));
}
@Override
public void run() {
try {
logger.info("Starting connection to MailboxHost on " + this.mailboxHost + " on port " + this.mailboxPort);
this.dmapSocket = new Socket(this.mailboxHost, this.mailboxPort);
this.dmapIn = new BufferedReader(new InputStreamReader(this.dmapSocket.getInputStream()));
this.dmapOut = new PrintWriter(this.dmapSocket.getOutputStream(), true);
String input = null;
if (!(input = this.dmapIn.readLine()).startsWith("ok DMAP2.0"))
shutdown();
while (!Thread.currentThread().isInterrupted() && ((input = this.dmapIn.readLine()) != null)) {
// TODO initiate startsecure command
// TODO login user (mailboxUser, mailboxPassword)
// startSecure()
}
} catch (IOException e) {
logger.severe("Could not connect to MailboxHost " + this.mailboxHost + " on port " + this.mailboxPort);
shutdown();
}
}
private void startSecure() throws IOException {
String input;
String componentId;
PublicKey serverPublicKey;
this.dmapOut.println("startsecure");
input = this.dmapIn.readLine();
if (input.startsWith("ok") && (input.split("\\s+").length == 2)) {
// Get the component-id from the server
componentId = input.split("\\s+")[1];
try {
// Attempt to read server public key from file called <component-id>_pub.der
byte[] keyBytes = Files.readAllBytes(Paths.get(componentId + "_pub.der"));
logger.info("Read bytes from path " + Paths.get(componentId + ".der") + ": " + Arrays.toString(keyBytes));
// Create X509 spec object from key
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
// Create generator for RSA scheme
KeyFactory kf = KeyFactory.getInstance("RSA");
// Make generator generate public key from X509 spec
serverPublicKey = kf.generatePublic(spec);
logger.info("Server's Public Key: " + serverPublicKey);
String clientChallenge = generateChallenge(serverPublicKey);
// TODO send clientChallenge to server
// TODO Receive AES encrypted message saying "ok <client-challenge>"
// TODO Compare received client challenge with generated client challenge
// TODO Answer with AES encrypted "ok" if matching and use AES cipher for subsequent communication
// TODO immediately terminate upon encountering an error (pull try-catch out of submethods)
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
logger.severe(e.getMessage());
shutdown();
}
}
}
public String generateChallenge(PublicKey serverPublicKey) {
SecureRandom secureRandom = new SecureRandom();
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, serverPublicKey);
// Generate new random 32 byte challenge
byte[] clearTextChallenge = new byte[32];
secureRandom.nextBytes(clearTextChallenge);
// Generate secretKey and initialization vector
String secretKey = "";
String IV = "";
// Encrypt "ok <clearTextChallenge> <secretKey> <IV>"
cipher.update(("ok " + (new String(clearTextChallenge)) + " " + secretKey + " " + IV).getBytes(StandardCharsets.UTF_8));
byte[] cipherTextChallenge = cipher.doFinal();
return (new String(cipherTextChallenge, StandardCharsets.UTF_8));
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
logger.severe(e.getMessage());
shutdown();
return null;
}
}
@Override
@ -42,12 +157,42 @@ public class MessageClient implements IMessageClient, Runnable {
@Override
public void msg(String to, String subject, String data) {
try {
logger.info("Starting connection to TransferServer on host " + this.transferHost + " on port " + this.transferPort);
this.dmtpSocket = new Socket(this.transferHost, this.transferPort);
this.dmtpIn = new BufferedReader(new InputStreamReader(this.dmtpSocket.getInputStream()));
this.dmtpOut = new PrintWriter(this.dmtpSocket.getOutputStream(), true);
} catch (IOException e) {
logger.severe("Could not connect to TransferHost " + transferHost + " on port " + transferPort);
shutdown();
}
}
@Override
public void shutdown() {
if (dmtpSocket != null) {
try {
this.dmtpSocket.close();
this.dmtpOut.close();
this.dmtpIn.close();
} catch (IOException e) {
logger.severe("Could not close connection to TransferHost");
e.printStackTrace();
}
}
if (dmapSocket != null) {
try {
this.dmapSocket.close();
this.dmapOut.close();
this.dmapIn.close();
} catch (IOException e) {
logger.severe("Could not close connection to MailboxHost");
e.printStackTrace();
}
}
Thread.currentThread().interrupt();
}
public static void main(String[] args) throws Exception {