RSA Encryption in Go

Here's something that sounds like a trivial thing:

"I want to encrypt an arbitrary payload using the RSA encryption algorithm in Go." — Me

Let's look at how we can achieve this in Go by example.

Preface

Before we go further, I would like to stress that I'm not by any means a cryptography expert.

You should not copy and paste cryptographic functions you read on the internet before consulting a real expert.

Generating a key pair

To encrypt and decrypt data, we need a key pair.

func CreateKeyPair() (*rsa.PrivateKey, error) {

  size := 1024

  priv, err := rsa.GenerateKey(rand.Reader, size)

  if err != nil {
    log.Fatalf("Failed to generate %d-bit key", size)
    return nil, err
  }

  return priv, err
}

This function returns a private key, but also contains the public key (since the public key is derived from the private key). We'll work with this key pair for the remainder of the examples.

To encrypt a payload, we'll need the public key and of course the payload itself.

Encrypting

func Encrypt(in []byte, pub rsa.PublicKey) ([]byte, error) {

  sha1 := sha1.New()

  out, err := rsa.EncryptOAEP(sha1, rand.Reader, &pub, in, nil)

  if err != nil {
    log.Fatalf("Failed to encrypt message %v", err)
    return nil, err
  }

  return out, nil
}

We're using the OAEP padding scheme in this example. I have no idea what that means, but it seems to be preferred over PKCS1v1.5.

OAEP should be used in any new application, and PKCS#1 v1.5 padding should be replaced wherever possible.

Let's trust Wikipedia on this one.

Decrypting

To decrypt a ciphertext, we need the private key.

func Decrypt(ciphertext []byte, priv *rsa.PrivateKey) ([]byte, error) {

  sha1 := sha1.New()

  out, err := rsa.DecryptOAEP(sha1, rand.Reader, priv, ciphertext, nil)

  if err != nil {
    log.Fatalf("Failed to decrypt message %v", err)
    return nil, err
  }

  return out, nil
}

And it returns us the original payload.

So putting everything together we can create a key pair, and encrypt and decrypt messages using the same key pair.

Here's some pseudo-code on how you would combine these functions.

func main() {
  kp, err := crypto.CreateKeyPair();

  msg := []byte("Hello, NSA!")

  c, _ := crypto.Encrypt(msg, kp.PublicKey)
  fmt.Printf("encrypted: %v\n", c)

  m, _ := crypto.Decrypt(c, kp)
  fmt.Printf("decrypted: %s\n", m)
}

The output of the program would look something like this:

encrypted: [6 154 138 133 132 51 72 166 69 58 161 63 51 17 221 119 144 57 160 36 9 102 151 8 86 216 29 181 117 99 125 116 17 236 43 43 222 99 6 202 91 28 188 181 131 143 69 160 62 148 24 161 78 100 238 148 159 156 220 169 72 173 97 238 127 219 38 65 69 150 158 180 80 233 175 186 178 218 48 24 163 237 89 162 98 200 125 91 13 184 208 240 160 58 71 120 198 200 217 170 88 197 143 50 69 234 192 9 107 141 104 89 172 66 158 68 154 173 164 210 4 241 244 1 19 103 236 176]
decrypted: Hello, NSA!

Success!

You can find a full example on GitHub:
https://github.com/gillesdemey/go-rsa-example

Suggested reading material:
https://leanpub.com/gocrypto/read