Asymmetric Encryption, or Public Key Encryption, is a fundamental part of security on modern systems. It assures the authentication and non-repudiation principles of cryptography.

In this tutorial, we are going to see how to implement it using Go.

Note: the version of Go used in this article is 1.15.3.

TL;DR: you can find the complete code here.

Introduction

RSA is a public-key algorithm. It is named after its creators (_Rivest-Shamir-Adleman). _It was made public in 1977 and it is one of the most used algorithms today.

The public-key cryptography, also known as asymmetric cryptography, uses two different keys, one to encrypt and another one to decrypt:

  • A public key is used in the process whenever there is no secret involved, such as encryption or verification.
  • A private key is used when there is a secret involved, such as decryption or signing.

Image for post

Image by author.

This means we can share the public key with anyone we want, so they can encrypt any information they want to send us. The only way to access this information is to use the private key to decrypt. This is why it must be kept secret.

The public and private keys are generated together (the public key is derived from the private) and form a key pair.

Image for post

Key pair generation process.

Note: if you want to know more about how the keys are generated or how the process of encryption works, you can check this video.

Let’s see how to implement it using Go.

Key Pair Generation

The first thing we have to do is generate the public and private keys. If you already a pair, you can skip this process and read it from the file.

The package crypto/rsagenerates the keys, with the packagecrypto/rand generating the random numbers.

package main

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

func generateKeyPair(bits int) (*rsa.PrivateKey, *rsa.PublicKey) {
	// This method requires a random number of bits.
	privateKey, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		fmt.Println("Error: ", err)
	}

	// The public key is part of the PrivateKey struct
	return privateKey, &privateKey.PublicKey
}

func main() {
	// Generate a 2048-bits key
	privateKey, publicKey := generateKeyPair(2048)

	fmt.Printf("Private key: %v\n", privateKey)
	fmt.Printf("Public Key: %v", publicKey)
}

In order to use the key with another program, or at another time, you can save the keys into a file. The usual way is to save as a PEM format, using the package encoding/pem.

The public key is part of the private key struct, so you don’t need to save them both. But remember to never share this file with anyone.

package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
)

// ommited function for simplicity

// Export public key as a string in PEM format
func exportPubKeyAsPEMStr(pubkey *rsa.PublicKey) string {
	pubKeyPem := string(pem.EncodeToMemory(
		&pem.Block{
			Type:  "RSA PUBLIC KEY",
			Bytes: x509.MarshalPKCS1PublicKey(pubkey),
		},
	))
	return pubKeyPem
}

// Export private key as a string in PEM format
func exportPrivKeyAsPEMStr(privkey *rsa.PrivateKey) string {
	privKeyPem := string(pem.EncodeToMemory(
		&pem.Block{
			Type:  "RSA PRIVATE KEY",
			Bytes: x509.MarshalPKCS1PrivateKey(privkey),
		},
	))
	return privKeyPem

}

// Save string to a file
func saveKeyToFile(keyPem, filename string) {
	pemBytes := []byte(keyPem)
	ioutil.WriteFile(filename, pemBytes, 0400)
}

func main() {
	// Generate a 2048-bits key
	privateKey, publicKey := generateKeyPair(2048)

	fmt.Printf("Private key: %v\n", privateKey)
	fmt.Printf("Public Key: %v", publicKey)

  // Create PEM string 
	privKeyStr := exportPrivKeyAsPEMStr(privateKey)
	pubKeyStr := exportPubKeyAsPEMStr(publicKey)

	fmt.Println(privKeyStr)
	fmt.Println(pubKeyStr)

	saveKeyToFile(privKeyStr, "privkey.pem")
	saveKeyToFile(pubKeyStr, "pubkey.pem")
}

Note: the error handling was omitted for simplicity. You should check them in real applications.

#go #security #progamming #developer

How to Implement RSA Encryption with Go
3.30 GEEK