TLS in Go

July 18, 2017
GO TLS X509 PKCS RSA

TLS in Go

This series of posts are aimed at explaining what is TLS, what are the most widely used encryption algorithms and how HTTPS works in general. These posts will be supported by examples of TLS components written using Go and its standard libraries. I am planning to write three posts and keep them extremely simple and useful for everyone new to HTTPS.

This post will explain what is HTTPS, why it is needed and will as well explain main principles of RSA

HTTPS

HTTPS is simply HTTP with additional security layer. This security layer is above Transport layer and below Application layer in traditional OSI model. This security layer is often called TLS/SSL. Why we need both names ? Well, SSL is simply a predecessor of TLS however still used in the internet (SSL 3.0). TLS itself comprises two protocols: TLS record protocol and TLS handshake protocol. Both will be explained further.

Purpose

HTTP is simply not secure enough for people to guarantee safety of exchanged data in the open web. HTTPS, however, aims to solve three main problems which HTTP is vulnerable to:

  1. Data privacy - only sender and recepient are aware of the content being sent across the web
  2. Data integrity - both sender and recepient do know if the content has been altered while being transported between two parties
  3. Identification - both sender and recepient are able to prove that they are who they claim to be, hence both parties have trust relationship

Additionally correctly configured TLS prevents leaked encryptions keys to be used to decrypt past messages

RSA

To understand what is RSA, we first need to understand what is asymmetric key encryption.

Essentially asymmetric keys are public/private key pair. Public key is used to encrypt the message. Encrypted message can only be decrypted using private key. Therefore, having encrypted message and public key without private key is useless.

RSA defines an algorithm for such key pair generation. It uses elementary math(see link #4) for generating the keys, yet making it nearly impossible to reverse engineer the private key from the public key. The process of cracking RSA requires to factorize a large number n into two prime factors, therefore the larger value of n, more difficult it is to crack RSA (the largest cracked value of n consisted of ~700 bits)

It is worth noting that there exist various ciphertext attacks (see #5), therefore, plaintext generally should be randomized before encryption. This for instance can be achieved by adding some random content at the beginning and the end of the plain text before encryption. This is know as padding. OAEP is one of the most commonly used padding schemes. There is a PKCS#1 padding scheme as well, but OAEP is generally superior (see link #2)

The last important thing to remember is that the encrypted message should be smaller than the bit size of the key pair used. For OAEP this length in the example below should not exceed (2048 - 2 * 256) bits (message is padded by 256 bit hash values from both sides).

Let’s see how it works in Go:

package main

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"fmt"
)

func main() {
	message := []byte("Hello World!")

	privKey, _ := GenerateKeyPair()
    // use OAEP for padding
	encryptedMessage, _ := rsa.EncryptOAEP(sha256.New(), rand.Reader, &privKey.PublicKey, message, nil)
    // use same hashing function as for encryption 
	decryptedMessage, _ := privKey.Decrypt(rand.Reader, encryptedMessage, &rsa.OAEPOptions{
		Hash: crypto.SHA256,
	})
	fmt.Printf("decrypted message: %s", string(decryptedMessage))
}

// GenerateKeyPair returns a private key (but it encapsulates the public key), therefore rsa.PrivateKey can be used as an asymmetric key pair
func GenerateKeyPair() (*rsa.PrivateKey, error) {
	// 2048 - specifies the size of the above-mentioned variable `n`
	// rand.Reader - allows to select random prime numbers p, q to compute n = p * q
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, err
	}
	return privateKey, nil
}

Next

  1. We will take a look at how RSA is used in SSL, and analyze AES as an alternative to RSA.
  2. We will study how to create and verify secure digital signatures in Go
  3. We will use Go to generate secure and self-signed certificates to establish secure connections
  4. We will take a closer look at HTTPS protocol implementation with details on the actual payloads and rules - more advanced topics
  1. https://msdn.microsoft.com/en-us/library/windows/desktop/aa387460(v=vs.85).aspx - on assymetric keys
  2. https://en.wikipedia.org/wiki/Optimal_asymmetric_encryption_padding - OAEP is a standard padding scheme
  3. https://security.stackexchange.com/questions/32050/what-specific-padding-weakness-does-oaep-address-in-rsa - OAEP vs PKCS
  4. https://simple.wikipedia.org/wiki/RSA_(algorithm) - RSA principles
  5. https://simple.wikipedia.org/wiki/Chosen-ciphertext_attack - Chosen-ciphertext_attack