Exchanging Encrypted Mails

2016 released Snowden is a biographical movie fictionalised life story of Former NSA employee Edward Snowden. The movie reveals illegal surveillance techniques of the government organization. Also, harversting email and search history data is revealed by Snowden, too. This paranoia might convince Zuckerberg. He covered his webcam and mic with tape.

snowden
Snowden


🙋‍♂️ You may consider to enroll my top-rated cryptography course on Udemy

Public Key Cryptography From Scratch

Beyond the paranoia, doubt often forces more rigorous scientific analysis and leads discoveries. In other words, thoroughly conscious ignorance. So, we can protect mails even if they are harvested by third parties. In this post, we will mention an implementation of exchanging encrypted mails.

We will build an exchanging encrypted mail implementation, and run it via gmail infrastructure. In order to work on gmail, you need to allow less secure applications to access your gmail account. You should skip this step if you work on an alternative mail server. Also, we would develop this implementation by referencing Java Mail API.

Mail Delivery

Suppose that Bob would like to send Alice lyrics of Careless Whisper. However, Bob would not like mail content to be seen by anybody else. That’s why, Bob will encrypt the lyrics of the song. The following code will encrypt body of a mail and deliver it.

public class MailSender {
 public static void main(String[] args) throws Exception{
  //crypto variables
  byte[] key = {75, 110, -45, -61, 101, -103, -26, -25, 55, -70, 19, 51, 66, -91, -35, 19}; //128 bit key
  String algorithm = "AES";

  //mail variables
  String subject = "You've got an encrypted mail!";
  String content = " Time can never mend\n The careless whispers of a good friend\n To the heart and mind\n Ignorance is kind\n There's no comfort in the truth\n Pain is all you'll find";
  String recipient = "recipientmail@gmail.com"; 

  //encryption
  String encryptedContent = encryptContent(key, algorithm, content);	

  //mail sending
  sendMail(recipient, subject, encryptedContent, encryptedByteKey);
 }

 public static String encryptContent(byte[] key, String algorithm, String content) throws Exception{
  byte[] plainContent = content.getBytes();
  Cipher encryption = Cipher.getInstance(algorithm);
  encryption.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, 0, key.length, algorithm));
  byte[] encryptedContent = encryption.doFinal(plainContent);
  return new String(Base64.getEncoder().encode(enryptedContent));
 }

 public static void sendMail(String recipient, String subject, String content, String encryptedKey) throws Exception{

  Properties props = new Properties();
  props.put("mail.smtp.host", "smtp.gmail.com");
  props.put("mail.smtp.socketFactory.port", "465");
  props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
  props.put("mail.smtp.auth", "true");
  props.put("mail.smtp.port", "465");

  Session session = Session.getDefaultInstance(props,
   new javax.mail.Authenticator() {
    protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
     return new javax.mail.PasswordAuthentication(USERNAME, PASSWORD);
    }
   });

  MimeMessage message = new MimeMessage(session);
  message.setFrom(new InternetAddress(USERNAME));
  message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipient));
  message.setSubject(subject);

  Multipart multipart = new MimeMultipart();
  MimeBodyPart messageBodyPart = new MimeBodyPart();
  messageBodyPart.setText(content);
  multipart.addBodyPart(messageBodyPart);
  message.setContent(multipart);

  Transport.send(message);

  System.out.println("mail delivered successfully...");
 }
}

So indeed, the mail is delivered cryptically as noticed below. Bob’s 128 bit AES key must be cracked to be figured out message even if Alice’s gmail password is broken.

gmail-inbox-illustration
Delivery of an encrypted mail

Reading Encrypted Mail

Suppose that Alice knows Bob’s encryption key. Then, Alice could decrypt delivered mail with the following code.

public class MailReader {
 public static void main(String[] args) throws Exception {
  //crypto variables
  byte[] key = {75, 110, -45, -61, 101, -103, -26, -25, 55, -70, 19, 51, 66, -91, -35, 19}; //128 bit key
  String algorithm = "AES";

  Properties properties = new Properties();
  properties.put("mail.store.protocol", "imaps");
  Session emailSession = Session.getDefaultInstance(properties);
  Store store = emailSession.getStore("imaps");
  store.connect("imap.gmail.com", USERNAME, PASSWORD);
  Folder emailFolder = store.getFolder("INBOX");
  emailFolder.open(Folder.READ_ONLY);
  Message[] messages = emailFolder.getMessages();

  for (int i = 0; i < messages.length; i++) {
   Message message = messages[i];

   System.out.println("Index " + (i + 1));
   System.out.println("From: " + message.getFrom()[0]);
   System.out.println("Subject: " + message.getSubject());

   Multipart multipart = (Multipart) message.getContent();
   BodyPart bodyPart = multipart.getBodyPart(0);
   String encryptedContent = bodyPart.getContent().toString();

   byte encryptedByteContent[] = Base64.getDecoder().decode(encryptedContent.getBytes());
   System.out.println("Content:\n"+decryptContent(algorithm, key, encryptedByteContent));
  }

  emailFolder.close(false);
  store.close();
 }
 public static String decryptContent(String algorithm, byte[] key, byte[] encryptedByteContent) throws Exception{
  Cipher decryption = Cipher.getInstance(algorithm);
  decryption.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, 0, key.length, algorithm));
  byte[] decryptedContent = decryption.doFinal(encryptedByteContent);
  return new String(decryptedContent);
 }
}

Hereby, Alice would be aware of the plain content of Bob’s mail.





decrypted-mail
Decryption of delivered encrypted mail

Maximum allowed key length of AES algorithm is 128 bit in Java Cryptography Extension. That’s why, emails are encrypted with 128 bit AES. This means 1.02×1018 years required to crack the ciphertext. In other words, it is almost billion years. Additionally, modification in policy files increases the key length uplimits. Thus, 256 bit AES requires 3.31×018 years to crack.

Finished? Not Yet!

Key Exchange

Although, implemenation is built and run successfully, it poses a big problem in practice. The both party must be aware of secret key. The point is that how they exchange key. Even if this difficulty is troubleshooted manually, either parties use same key every time or troubleshootig method should be applied repeatly. The both solutions are vulnerable. Alternatively, we would apply key exchange procedure for every mail delivery. This approach is inspired from PGP work scheme.

Firstly, Alice must generate both private and public key pair because she is the recipient one. Then, she publishes her public key. Thus, Bob can use this public key to encrypt his one time secret key. The following code would generate private and public key pair for RSA algorithm.

KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
PrivateKey secretKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
System.out.println("Secret key: "+Arrays.toString(secretKey.getEncoded()));
System.out.println("Public key: "+Arrays.toString(publicKey.getEncoded()));

Bob should generate different secret keys for every mail sending. The following code generates random secret keys. Then, Bob would encrypt mail content with randomly generated secret key.

KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(128);
SecretKey secretKey = generator.generateKey();
byte[] key = secretKey.getEncoded();

Moreover, Bob should encrypt this randomly generated secret key with Alice’s public key. Thus, Alice would decrypt it with her private key, and could be aware of encryption’s secret key.

Cipher publicKeyEncryption = Cipher.getInstance("RSA");
publicKeyEncryption.init(Cipher.ENCRYPT_MODE, KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(AlicePublicKey)));
byte[] encryptedKey = publicKeyEncryption.doFinal(key);
String encryptedByteKey = new String(Base64.getEncoder().encode(encryptedKey));

Bob would attach encrypted secret key into the mail with following commands.

MimeBodyPart attachment = new MimeBodyPart();
attachment.setText(encryptedKey);
attachment.setFileName("encrypted_key.txt");
multipart.addBodyPart(attachment);

Man in the middle

gmail-inbox-illustration-new
Encrypted key is attached into the mail
gmail-inbox-attachment
Mail attachment includes encrypted secret key

And finally, Alice would decrypt attached encrypted secret key with her private key. Thus, she obtains plain secret key. This means she can decrypt mail content from now on.

String encryptedKey = ((Multipart) message.getContent()).getBodyPart(1).getContent().toString();
byte[] encryptedByteKey = Base64.getDecoder().decode(encryptedKey);

Cipher publicKeyEncryption = Cipher.getInstance("RSA");
publicKeyEncryption.init(Cipher.DECRYPT_MODE, KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(mySecretKey)));
byte[] key = publicKeyEncryption.doFinal(encryptedByteKey);

So, we’ve built an implementation for exchanging secure mails. In this way, we would add an additional security layer for mail exchanging. This approach could be adapted into voice or audio based transmissions. Final form of the implementation including key exchange capability is shared on my GitHub profile.





Hope to see you more secure tomorrow.


Support this blog if you do like!

Buy me a coffee      Buy me a coffee