Web Security: Symmetric and Asymmetric Cryptography

In the previous post, you saw how hashing algorithms are applied in the security context. However, they are just one piece of the puzzle to secure a communication between two entities.

The post left an unanswered question:

How does the destination (Mary) know if a message and its hashing was modified after the origin (John) deliveries it?

Cryptography is one piece of the answer, helping to hide the message for non authorized parties.

TRY IT YOURSELF: You can find the source code of this post here.

Web Security Series

The Problem

In the previous post, you faced an attacker who was not authenticated and authorized, but, still, he changed the message content and its hashing you used to validate integrity (the message doesn’t change), as follows in John and Mary relationship example:

The ex-boyfriend improves the hack strategy, changing the message and the hashing value

He changes not only the message, but the hash too. He creates the hash of “Hate you” using the same algorithm John uses, and adds that hash to the message. Therefore, for Mary point of view, the message is totally valid and she will accept it.

Of course, again, modifying the message by a third party is unacceptable in a security context. What if the toxic ex-boyfriend not only cannot modify the message, but, he cannot read and understand it?

Let’s talk about cryptography.

What is Cryptography?

Cryptography is a strategy to hide information from third parties unauthorized to see it. In the case of Mary and John’s relationship, they are the trusted parties, and they want to send messages that no other third party can read, in this case, the toxic ex-boyfriend.

The following are important concepts to understand before moving forward with the cryptography details:

  • Plain text: This is any text you can read and make sense of it.
  • Nonsense text: This is any text you can read but you cannot make sense of it.
  • Encryption: This is the process of taking a plain text and transforming it to a nonsense text.
  • Decryption: This is the process of taking a nonsense text and transforming it to a plain text.
  • Key: This is a code used to encrypt a plain text to a nonsense text, or, decrypt a nonsense text to a plain text, or both. The key is like your key to go in your house, that key is “unique” to open that door and see what it is inside.

The following diagram shows the general approach to cryptography:

High level strategy for cryptography

There are two known strategies for cryptography, based on how the key is used, they are named symmetric key and asymmetric key. Let’s discuss both:

Symmetric Key Cryptography

Symmetric means uniform, equal, so, a symmetric key strategy means you will use the same key, to encrypt and decrypt the messages, as follows:

Symmetric cryptography

As you can see, the same key locks and unlocks the message. The same thing happens with your house door, you only have one key to open and close the door.

One interesting property of symmetric keys, is that the process is fast, so, you can encrypt and decrypt a plain text rapidly.

Now, let’s see an example using Mary and John’s relationship. Jhon decides to encrypt the messages he sends to Mary to avoid her ex-boyfriend reads them, so, he defines the following key, where he changes one character of the message, for a different one, as follows:

  • L encrypts to A, and A decrypts to L
  • O encrypts to K, and K decrypts to O
  • V encrypts to R, and R decrypts to V
  • E encrypts to T, and T decrypts to E
  • Y encrypts to Z, and Z decrypts to Y
  • U encrypts to P, and P decrypts to U
  • H encrypts to Q, and Q decrypts to H
  • A encrypts to B, and B decrypts to A
  • T encrypts to C, and C decrypts to T

After Jhon defined the key, he shares it with Mary, and send her a message, as follows:

John and Mary use symmetric cryptography to communicate

As you can see, John sends the plain text “Love you” through the encryption strategy, using the key, resulting in a non sense text “AKRTZKP“. Later, Mary uses the same key Jhon did, to decrypt the message from the non sense text, to “Love you“.

Now, the toxic ex-boyfriend tries to capture and modify the message as follows:

Toxic ex-boyfriend captures the message, but, he doesn’t understand it because it is encrypted

The toxic ex-boyfriend spies the communication between John and Mary, however, he cannot understand the message because it is encrypted.

Of course, in spite of he cannot understand the message, he tries to change it, as follows:

Toxic ex-boyfriend uses another key to create a new message to send to Mary

The ex-boyfriend uses his own key to encrypt the message “Hate you“, resulting in a non sense text “HSUEBAKSD” to try to fool Mary, however, when Mary tries to use John’s key to process the ex-boyfriend message, the key doesn’t work, so, she assumes the message was corrupted.

Seems right, John and Mary hid the message so the ex-boyfriend cannot read it and avoid receiving messages John didn’t send, but, there is a open question here:

How does John share the key with Mary in a safe manner?

Well, let’s send the key in another message:

John sends the symmetric key to Mary

Pretty straightforward approach, oh, hold on, the ex-boyfriend is not stupid, as follows:

The ex-boyfriend captures the key

The toxic ex-boyfriend captures the key and save it. Of course, he allows the key to flow to Mary, so, when John and Mary start to communicate, the ex-boyfriend can do the following:

The ex-boyfriend uses John’s key to send messages to Mary

Now, the ex-boyfriend can read and send messages to Mary, as he has John’s key. In this case, he sends Mary a “Hate you” message, and as that message is encrypted by John’s key, Mary accepts the message as valid. Of course, if Mary uses John’s key to send him messages, the toxic ex-boyfriend will be able to capture, read and send a different message to John, as symmetric cryptography uses the same key to encrypt and decrypt.

Well, what if John and Mary don’t use the same key, instead, they use two different ones? that might help to add another security layer. Let’s check the asymmetric key cryptography.

Asymmetric key cryptography

Asymmetric means different, distinct, so, an asymmetric key strategy means you have one key (public) to encrypt and another to decrypt (private) the messages, as follows:

Asymmetric cryptography

As you can see, the public key encrypts the message to a nonsense text, but unlike symmetric keys, the public key creates a green padlock, which means, only the private key will be able to decrypt the message.

NOTE: The effect happens in the other way around too, if you encrypt with your private key, only the right public key will be able to decrypt.

There are two interesting features regarding asymmetric keys strategy:

  • The public and private keys, are peers, they only make sense if they work together. Both keys are generated at the same time and cannot be used with other keys.
  • And, having two different keys to encrypt and decrypt has a cost in performance, so, the asymmetric keys strategy is slower than the symmetric one.

As an example, imagine that before leaving your home on morning, you lock the door with the public key, and right after, the padlock transforms to a numerical padlock, so, only if you have the right number (private key), you can go in. That means, your public key cannot unlock the door (yes, you are out your house now). Now, your wife comes at night, and she has the right number (private key) of the numerical padlock, so, she can open the door. Of course, right after, the padlock transforms again to a key padlock.

Now, let’s see another example using Mary and John’s relationship. They decide to encrypt the messages he sends to Mary to avoid her ex-boyfriend reads them, so, Mary generates two keys, public and private keys, and give the public key to John, as follows:

Mary generates a public and private key, and sends the public one to John

After Mary shares the public key with John, he starts sending messages to her, as follows:

John and Mary use asymmetric cryptography to communicate

As you can see, John sends the plain text “Love you” through the encryption strategy, using the public key, resulting in a non sense text, and a green padlock. Later, Mary uses the private key (green) to decrypt the message from the non sense text, to “Love you“.

Now, the toxic ex-boyfriend tries to capture and modify the message, as follows:

Toxic ex-boyfriend tries to read and create a new message to send to Mary

The toxic ex-boyfriend spies the communication between John and Mary, however, he cannot understand the message as it is encrypted, and he doesn’t have the private key. Besides, he cannot send a new message to Mary, as he doesn’t have the public key.

However, the ex-boyfriend is smart, and captures the public key when Mary sends it to John, as you can see:

The ex-boyfriend captures the public key

The toxic ex-boyfriend captures the key and save it. Of course, he allows the key to flow to John, so, when John and Mary starts to communicate, the ex-boyfriend tries to hack the communication:

The ex-boyfriend uses Mary’s public key to send messages to Mary

Now, the ex-boyfriend can send messages to Mary, as he has the public key, so, he captures John’s message and avoid that reaching Mary, and the ex-boyfriend send his own message to her.

Here, there is something important, yes, the ex-boyfriend can send message to Mary, but, he cannot read the message John sent in the first place, as the message has the green padlock and the ex-boyfriend has the blue key, so, he will need Mary’s private key to unlock John’s message. However, Mary never shared that private key, this means, John can send confidential messages to Mary (no one can read them, only Mary).

You might think: “Come on, we have the same problem here like the symmetric keys”, well, yes, sharing keys is difficult, but, asymmetric keys helps us to avoid a third party reading my messages in spite of the public key is captured by someone else.

Now, you see John using the Mary’s public key to send messages to her, but, what if Mary wants to send messages to John? She can use her private key to encrypt, and John can use the public key to decrypt, as follows:

However, as Mary shared the public key, if the ex-boyfriend captured the public key, he will be able to read Mary’s messages, but, he cannot send message to John, as the ex-boyfriend doesn’t have Mary’s private key.

As you have noticed, asymmetric key adds another layer of security, with some good use cases where the communication can work pretty well, however, you saw this doesn’t solve the whole problem again, it is just one piece of the puzzle. This open a new question:

How can Mary and John communicate securely in a two way fashion?

Did We solve the Open Questions?

Let’s check the open questions we have until now:

How does the destination (Mary) know if a message and its hashing was modified after the origin (John) deliveries it?

Well, we hide the message using cryptography, it is not a complete answer, but helps.

How does John share the key with Mary in a safe manner?

A new one, and tough one. We don’t know yet.

How can Mary and John communicate securely in a two way fashion?

Well, no idea yet.

Java Example: Symmetric and Asymmetric Keys

Java has an excellent security module for encryption and decryption. The following are examples of how to use symmetric and asymmetric keys in Java.

Symmetric Keys Example

Well, the following is a Java example to generate and use symmetric keys using AES algorithm:

public class SymmetricKeyExamples {
  private SecretKeySpec secretKey;

  @Before
  public void before()
          throws UnsupportedEncodingException,
          NoSuchAlgorithmException {
    String plainKey = "my secret";

    byte[] key = plainKey.getBytes("UTF-8");

    MessageDigest sha = MessageDigest
            .getInstance("SHA-1");
    key = sha.digest(key);
    key = Arrays.copyOf(key, 16);

    secretKey = new SecretKeySpec(key,
            "AES");
  }

  @Test
  public void encrypt()
          throws NoSuchAlgorithmException,
          UnsupportedEncodingException,
          NoSuchPaddingException, InvalidKeyException,
          BadPaddingException,
          IllegalBlockSizeException {

    String plainText = "love you";

    Cipher cipher = Cipher
            .getInstance("AES/ECB/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);

    String nonSenseText = Base64.getEncoder()
            .encodeToString(cipher.doFinal(
                    plainText.getBytes("UTF-8")));

    assertThat(nonSenseText)
            .isEqualTo("8wFONZ+k8VTBh7c0RFhBRg==");
  }

  @Test
  public void decrypt()
          throws NoSuchAlgorithmException,
          NoSuchPaddingException,
          UnsupportedEncodingException,
          InvalidKeyException, BadPaddingException,
          IllegalBlockSizeException {

    String nonSenseText = "8wFONZ+k8VTBh7c0RFhBRg==";

    Cipher cipher = Cipher
            .getInstance("AES/ECB/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secretKey);

    String plainText = new String(
            cipher.doFinal(Base64.getDecoder()
                    .decode(nonSenseText)));

    assertThat(plainText).isEqualTo("love you");
  }
}

First, before the tests, we create the symmetric key based on a plain text (my secret).

After, you find an encryption test. You see the Cipher class, which helps us to encrypt and decrypt, in this case, you see the setup for AES algorithm and the init method in ENCRYPT_MODE mode and we send the secret key. Finally, you see how the doFinal method from Cipher class generates a encrypted value for the plain text “love you“.

For the decrypt test, you see a pretty similar code, but, we setup the Cipher in DECRYPT_MODE mode. Note that we are using the same symmetric key to encrypt or decrypt.

Asymmetric Keys Example

Well, the following is a Java example to generate and use asymmetric keys using RSA algorithm:

public class AsymmetricKeyExamples {
  @Rule
  public ExpectedException expectedException = ExpectedException.none();
  private PrivateKey privateKey;
  private PublicKey publicKey;

  @Before
  public void before()
          throws NoSuchAlgorithmException {

      KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
      keyGen.initialize(1024);

      KeyPair pair = keyGen.generateKeyPair();

      privateKey = pair.getPrivate();
      publicKey = pair.getPublic();
  }

  @Test
  public void encryptWithPublicKey_decryptWithPrivateKey()
          throws NoSuchAlgorithmException,
          UnsupportedEncodingException,
          NoSuchPaddingException, InvalidKeyException,
          BadPaddingException,
          IllegalBlockSizeException {

    String plainText = "love you";

    Cipher cipherEncrypt = Cipher
            .getInstance("RSA/ECB/PKCS1Padding");
    cipherEncrypt.init(Cipher.ENCRYPT_MODE, publicKey);

    String nonSenseText = Base64.getEncoder()
            .encodeToString(cipherEncrypt.doFinal(
                    plainText.getBytes("UTF-8")));

    System.out.println(nonSenseText);

    Cipher cipherDecrypt = Cipher
            .getInstance("RSA/ECB/PKCS1Padding");
    cipherDecrypt.init(Cipher.DECRYPT_MODE, privateKey);

    String newPlainText = new String(
            cipherDecrypt.doFinal(Base64.getDecoder()
                    .decode(nonSenseText)));

    assertThat(newPlainText)
            .isEqualTo(plainText);
  }

  @Test
  public void encryptWithPrivateKey_decryptWithPublicKey()
          throws NoSuchAlgorithmException,
          UnsupportedEncodingException,
          NoSuchPaddingException, InvalidKeyException,
          BadPaddingException,
          IllegalBlockSizeException {

    String plainText = "love you";

    Cipher cipherEncrypt = Cipher
            .getInstance("RSA/ECB/PKCS1Padding");
    cipherEncrypt.init(Cipher.ENCRYPT_MODE, privateKey);

    String nonSenseText = Base64.getEncoder()
            .encodeToString(cipherEncrypt.doFinal(
                    plainText.getBytes("UTF-8")));

    System.out.println(nonSenseText);

    Cipher cipherDecrypt = Cipher
            .getInstance("RSA/ECB/PKCS1Padding");
    cipherDecrypt.init(Cipher.DECRYPT_MODE, publicKey);

    String newPlainText = new String(
            cipherDecrypt.doFinal(Base64.getDecoder()
                    .decode(nonSenseText)));

    assertThat(newPlainText)
            .isEqualTo(plainText);
  }

  @Test
  public void encryptWithPrivateKey_decryptWithPrivateKey()
          throws NoSuchAlgorithmException,
          UnsupportedEncodingException,
          NoSuchPaddingException, InvalidKeyException,
          BadPaddingException,
          IllegalBlockSizeException {

    expectedException.expect(BadPaddingException.class);
    expectedException.expectMessage("Decryption error");

    String plainText = "love you";

    Cipher cipherEncrypt = Cipher
            .getInstance("RSA/ECB/PKCS1Padding");
    cipherEncrypt.init(Cipher.ENCRYPT_MODE, privateKey);

    String nonSenseText = Base64.getEncoder()
            .encodeToString(cipherEncrypt.doFinal(
                    plainText.getBytes("UTF-8")));

    System.out.println(nonSenseText);

    Cipher cipherDecrypt = Cipher
            .getInstance("RSA/ECB/PKCS1Padding");
    cipherDecrypt.init(Cipher.DECRYPT_MODE, privateKey);

    cipherDecrypt.doFinal(Base64.getDecoder()
                    .decode(nonSenseText));
  }

  @Test
  public void encryptWithPublicKey_decryptWithPublicKey()
          throws NoSuchAlgorithmException,
          UnsupportedEncodingException,
          NoSuchPaddingException, InvalidKeyException,
          BadPaddingException,
          IllegalBlockSizeException {

    expectedException.expect(BadPaddingException.class);
    expectedException.expectMessage("Decryption error");

    String plainText = "love you";

    Cipher cipherEncrypt = Cipher
            .getInstance("RSA/ECB/PKCS1Padding");
    cipherEncrypt.init(Cipher.ENCRYPT_MODE, publicKey);

    String nonSenseText = Base64.getEncoder()
            .encodeToString(cipherEncrypt.doFinal(
                    plainText.getBytes("UTF-8")));

    System.out.println(nonSenseText);

    Cipher cipherDecrypt = Cipher
            .getInstance("RSA/ECB/PKCS1Padding");
    cipherDecrypt.init(Cipher.DECRYPT_MODE, publicKey);

    cipherDecrypt.doFinal(Base64.getDecoder()
            .decode(nonSenseText));
  }
}

First, before the tests, you see the keys generation using the RSA algorithm, and we got the public and private key.

Now, the first test show how to encrypt using the public key and decrypt using the private key. You see the same Cipher class we used in the symmetric key example.

The second test show how changing the private key to encrypt and the public key to decrypt, the test passes fine. This means, you can use both keys to encrypt or decrypt.

However, as we discuss before, you cannot use the same key to encrypt and decrypt, so, the third and fourth tests show how trying to use the public key to encrypt and decrypt, don’t work. The same happens if you use the private key to encrypt and decrypt.

Final Though

As you have noticed, doing safe communications is hard. In this post, you saw cryptography, and how it helps to hide messages. We discuss the two cryptography strategies, the symmetric and asymmetric ways, how they behave and their use cases.

Still, we have a lot of unanswered questions, but, we are moving in the right direction.

In the next post you will see the answer to the following question:

How does the destination (Mary) know if a message and its hashing was modified after the origin (John) deliveries it?

Digital signatures tackle this problem completely.

If you liked this post and are interested in hearing more about my journey as a Software Engineer, you can follow me on Twitter and travel together.

5 comments

Leave a comment