From e311064c480c5e007d572c254b169a2bde242de9 Mon Sep 17 00:00:00 2001 From: Tobias Eidelpes Date: Tue, 29 Dec 2020 11:54:08 +0100 Subject: [PATCH] Implement startsecure() and login() on client --- src/main/java/dslab/client/MessageClient.java | 91 ++++++++++++++++--- .../FailedVerificationException.java | 7 ++ 2 files changed, 86 insertions(+), 12 deletions(-) create mode 100644 src/main/java/dslab/exception/FailedVerificationException.java diff --git a/src/main/java/dslab/client/MessageClient.java b/src/main/java/dslab/client/MessageClient.java index 102ca2e..7dad433 100644 --- a/src/main/java/dslab/client/MessageClient.java +++ b/src/main/java/dslab/client/MessageClient.java @@ -13,6 +13,7 @@ import java.util.Base64; import java.util.logging.Logger; import dslab.ComponentFactory; +import dslab.exception.FailedVerificationException; import dslab.util.Config; import javax.crypto.*; @@ -40,7 +41,8 @@ public class MessageClient implements IMessageClient, Runnable { private PrintWriter dmapOut; private BufferedReader dmapIn; - private Cipher aesCipher; + private Cipher aesEncryptCipher; + private Cipher aesDecryptCipher; private byte[] challenge; /** @@ -73,20 +75,36 @@ public class MessageClient implements IMessageClient, Runnable { this.dmapOut = new PrintWriter(this.dmapSocket.getOutputStream(), true); String input = null; + String message = null; + if (!(input = this.dmapIn.readLine()).startsWith("ok DMAP2.0")) shutdown(); + startSecure(); + loginUser(); + while (!Thread.currentThread().isInterrupted() && ((input = this.dmapIn.readLine()) != null)) { - // TODO initiate startsecure command - // TODO login user (mailboxUser, mailboxPassword) - // startSecure() + // TODO implement commands } } catch (IOException e) { logger.severe("Could not connect to MailboxHost " + this.mailboxHost + " on port " + this.mailboxPort); shutdown(); + } catch (IllegalBlockSizeException | BadPaddingException e) { + logger.severe(e.getMessage()); + shutdown(); } } + private void loginUser() throws BadPaddingException, IllegalBlockSizeException, IOException { + String message; + logger.info("Trying to log in using " + this.mailboxUser + " " + this.mailboxPassword); + message = "login " + this.mailboxUser + " " + this.mailboxPassword; + this.dmapOut.println(getAesCiphertext(message)); + String response = getAesPlaintext(this.dmapIn.readLine()); + if (!response.startsWith("ok DMAP2.0")) + shutdown(); + } + private void startSecure() throws IOException { String input; String componentId; @@ -108,18 +126,65 @@ public class MessageClient implements IMessageClient, Runnable { serverPublicKey = kf.generatePublic(spec); logger.info("Server's Public Key: " + serverPublicKey); String clientChallenge = generateChallengeMessage(serverPublicKey); - // TODO send clientChallenge to server - // TODO Receive AES encrypted message saying "ok " - // 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) { + // Send clientChallenge to server + this.dmapOut.println(clientChallenge); + // Receive AES encrypted message saying "ok " + String response = this.dmapIn.readLine(); + // Compare received client challenge with generated client challenge + verifyChallenge(response); + // Answer with AES encrypted "ok" if matching and use AES cipher for subsequent communication + this.dmapOut.println(getAesCiphertext("ok")); + } catch (NoSuchAlgorithmException | InvalidKeySpecException | IllegalBlockSizeException | BadPaddingException | FailedVerificationException e) { logger.severe(e.getMessage()); shutdown(); } + } else { + logger.severe("MailboxHost " + mailboxHost + " on port " + mailboxPort + " did not respond with 'ok' "); + shutdown(); } } + /** + * Takes a plaintext message, encrypts and encodes it and returns the encoded ciphertext ready for sending. + * @param message Plaintext message + * @return B64 encoded and AES encrypted ciphertext + */ + private String getAesCiphertext(String message) throws IllegalBlockSizeException, BadPaddingException { + byte[] cipherText = aesEncryptCipher.doFinal(message.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(cipherText); + } + + /** + * Takes an AES encrypted message, decrypts and decodes it and returns the plaintext. + * @param message B64 encoded and AES encrypted message + * @return B64 decoded and AES decrypted plaintext + */ + private String getAesPlaintext(String message) throws BadPaddingException, IllegalBlockSizeException { + byte[] cipherText = Base64.getDecoder().decode(message.getBytes(StandardCharsets.UTF_8)); + return new String(aesDecryptCipher.doFinal(cipherText)); + } + + private void verifyChallenge(String response) throws FailedVerificationException { + // Decrypt to base64 encoded byte array + String plainText; + try { + plainText = new String(aesDecryptCipher.doFinal(Base64.getDecoder().decode(response))); + logger.info("Decrypted AES challenge: " + plainText); + } catch (IllegalBlockSizeException | BadPaddingException e) { + logger.severe("Error during decryption of client challenge. Aborting..."); + shutdown(); + return; + } + // Decode + if (plainText.split("\\s+").length != 2) + shutdown(); + String clientChallenge = new String(Base64.getDecoder().decode(plainText.split("\\s+")[1])); + // Compare received challenge with sent + + if (!clientChallenge.equals(new String(this.challenge))) + throw new FailedVerificationException("Server's clientChallenge " + clientChallenge + " does not match sent clientChallenge " + new String(this.challenge)); + } + private SecretKeySpec generateSecretKey() { try { KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); @@ -142,7 +207,8 @@ public class MessageClient implements IMessageClient, Runnable { private void setAesCipher(SecretKeySpec secretKey, IvParameterSpec iv) { try { - this.aesCipher = Cipher.getInstance("AES/CTR/NoPadding"); + this.aesEncryptCipher = Cipher.getInstance("AES/CTR/NoPadding"); + this.aesDecryptCipher = Cipher.getInstance("AES/CTR/NoPadding"); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { logger.severe("This should not be happening!"); e.printStackTrace(); @@ -150,7 +216,8 @@ public class MessageClient implements IMessageClient, Runnable { } try { - this.aesCipher.init(Cipher.ENCRYPT_MODE, secretKey, iv); + this.aesEncryptCipher.init(Cipher.ENCRYPT_MODE, secretKey, iv); + this.aesDecryptCipher.init(Cipher.DECRYPT_MODE, secretKey, iv); } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { logger.severe("This should not be happening!"); e.printStackTrace(); diff --git a/src/main/java/dslab/exception/FailedVerificationException.java b/src/main/java/dslab/exception/FailedVerificationException.java new file mode 100644 index 0000000..ff5b77a --- /dev/null +++ b/src/main/java/dslab/exception/FailedVerificationException.java @@ -0,0 +1,7 @@ +package dslab.exception; + +public class FailedVerificationException extends Exception { + public FailedVerificationException(String errorMessage) { + super(errorMessage); + } +}