Implement startsecure() and login() on client

This commit is contained in:
Tobias Eidelpes 2020-12-29 11:54:08 +01:00
parent d1bf65698d
commit e311064c48
2 changed files with 86 additions and 12 deletions

View File

@ -13,6 +13,7 @@ import java.util.Base64;
import java.util.logging.Logger; import java.util.logging.Logger;
import dslab.ComponentFactory; import dslab.ComponentFactory;
import dslab.exception.FailedVerificationException;
import dslab.util.Config; import dslab.util.Config;
import javax.crypto.*; import javax.crypto.*;
@ -40,7 +41,8 @@ public class MessageClient implements IMessageClient, Runnable {
private PrintWriter dmapOut; private PrintWriter dmapOut;
private BufferedReader dmapIn; private BufferedReader dmapIn;
private Cipher aesCipher; private Cipher aesEncryptCipher;
private Cipher aesDecryptCipher;
private byte[] challenge; private byte[] challenge;
/** /**
@ -73,20 +75,36 @@ public class MessageClient implements IMessageClient, Runnable {
this.dmapOut = new PrintWriter(this.dmapSocket.getOutputStream(), true); this.dmapOut = new PrintWriter(this.dmapSocket.getOutputStream(), true);
String input = null; String input = null;
String message = null;
if (!(input = this.dmapIn.readLine()).startsWith("ok DMAP2.0")) if (!(input = this.dmapIn.readLine()).startsWith("ok DMAP2.0"))
shutdown(); shutdown();
startSecure();
loginUser();
while (!Thread.currentThread().isInterrupted() && ((input = this.dmapIn.readLine()) != null)) { while (!Thread.currentThread().isInterrupted() && ((input = this.dmapIn.readLine()) != null)) {
// TODO initiate startsecure command // TODO implement commands
// TODO login user (mailboxUser, mailboxPassword)
// startSecure()
} }
} catch (IOException e) { } catch (IOException e) {
logger.severe("Could not connect to MailboxHost " + this.mailboxHost + " on port " + this.mailboxPort); logger.severe("Could not connect to MailboxHost " + this.mailboxHost + " on port " + this.mailboxPort);
shutdown(); 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 { private void startSecure() throws IOException {
String input; String input;
String componentId; String componentId;
@ -108,18 +126,65 @@ public class MessageClient implements IMessageClient, Runnable {
serverPublicKey = kf.generatePublic(spec); serverPublicKey = kf.generatePublic(spec);
logger.info("Server's Public Key: " + serverPublicKey); logger.info("Server's Public Key: " + serverPublicKey);
String clientChallenge = generateChallengeMessage(serverPublicKey); String clientChallenge = generateChallengeMessage(serverPublicKey);
// TODO send clientChallenge to server // Send clientChallenge to server
// TODO Receive AES encrypted message saying "ok <client-challenge>" this.dmapOut.println(clientChallenge);
// TODO Compare received client challenge with generated client challenge // Receive AES encrypted message saying "ok <client-challenge>"
// TODO Answer with AES encrypted "ok" if matching and use AES cipher for subsequent communication String response = this.dmapIn.readLine();
// TODO immediately terminate upon encountering an error (pull try-catch out of submethods) // Compare received client challenge with generated client challenge
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) { 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()); logger.severe(e.getMessage());
shutdown(); shutdown();
} }
} else {
logger.severe("MailboxHost " + mailboxHost + " on port " + mailboxPort + " did not respond with 'ok' <component-id>");
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 <client-challenge>
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() { private SecretKeySpec generateSecretKey() {
try { try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
@ -142,7 +207,8 @@ public class MessageClient implements IMessageClient, Runnable {
private void setAesCipher(SecretKeySpec secretKey, IvParameterSpec iv) { private void setAesCipher(SecretKeySpec secretKey, IvParameterSpec iv) {
try { 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) { } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
logger.severe("This should not be happening!"); logger.severe("This should not be happening!");
e.printStackTrace(); e.printStackTrace();
@ -150,7 +216,8 @@ public class MessageClient implements IMessageClient, Runnable {
} }
try { 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) { } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
logger.severe("This should not be happening!"); logger.severe("This should not be happening!");
e.printStackTrace(); e.printStackTrace();

View File

@ -0,0 +1,7 @@
package dslab.exception;
public class FailedVerificationException extends Exception {
public FailedVerificationException(String errorMessage) {
super(errorMessage);
}
}