Asymmetric Cryptography with Golang
Tags: go, security, encryption, tutorial
Published: 2023-04-05 14:31:02

Asymmetric cryptography, also known as public-key cryptography, is a cryptographic system where the key used to encrypt data is different from the key used to decrypt it. This allows for secure communication between parties even without a shared secret key. In this article, we will explore how to implement asymmetric cryptography using the Go programming language.

## Generating Key Pairs
The first step in implementing asymmetric cryptography is to generate a key pair. The key pair consists of a public key and a private key. The public key can be freely shared with anyone, whereas the private key must be kept secret.

Go's standard library provides the `crypto/rand` and `crypto/rsa` packages for generating cryptographically secure random numbers and generating RSA key pairs, respectively. Here's an example of how to generate a 2048-bit RSA key pair:

```go 
package main

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "encoding/pem"
    "os"
)

func main() {
    privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        panic(err)
    }

    publicKey := &privateKey.PublicKey

    privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
    privateKeyPEM := pem.EncodeToMemory(&pem.Block{
        Type:  "RSA PRIVATE KEY",
        Bytes: privateKeyBytes,
    })
    err = os.WriteFile("private.pem", privateKeyPEM, 0644)
    if err != nil {
        panic(err)
    }

    publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
    if err != nil {
        panic(err)
    }
    publicKeyPEM := pem.EncodeToMemory(&pem.Block{
        Type:  "RSA PUBLIC KEY",
        Bytes: publicKeyBytes,
    })
    err = os.WriteFile("public.pem", publicKeyPEM, 0644)
    if err != nil {
        panic(err)
    }
}
```

In this example, we use the `rsa.GenerateKey` function to generate a 2048-bit RSA key pair. We then use the `x509.MarshalPKCS1PrivateKey` and `x509.MarshalPKIXPublicKey` functions to marshal the private and public keys into their respective PEM-encoded formats. Finally, we write the PEM-encoded keys to disk.

## Encrypting and Decrypting Data
With a key pair generated, we can now encrypt and decrypt data using the public and private keys, respectively. To encrypt data with the public key, we can use the `crypto/x509`package's `EncryptPKCS1v15` function:

```go 
package main

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

func main() {
    publicKeyPEM, err := ioutil.ReadFile("public.pem")
    if err != nil {
        panic(err)
    }
    publicKeyBlock, _ := pem.Decode(publicKeyPEM)
    publicKey, err := x509.ParsePKIXPublicKey(publicKeyBlock.Bytes)
    if err != nil {
        panic(err)
    }

    plaintext := []byte("hello, world!")
    ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey.(*rsa.PublicKey), plaintext)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Encrypted: %x\n", ciphertext)
}
```

In this example, we first read in the PEM-encoded public key from the `public.pem` file. We then decode the PEM block and parse the public key using the `x509.ParsePKIXPublicKey` function. Finally, we use the `rsa.EncryptPKCS1v15` function to encrypt the plaintext message using the public key.

To decrypt data with the private key, we can use the `crypto/rsa` package's `DecryptPKCS1v15` function:


```go
package main

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

func main() {
    privateKeyPEM, err := ioutil.ReadFile("private.pem")
    if err != nil {
        panic(err)
    }
    privateKeyBlock, _ := pem.Decode(privateKeyPEM)
    privateKey, err := x509.ParsePKCS1PrivateKey(privateKeyBlock.Bytes)
    if err != nil {
        panic(err)
    }

    ciphertext := []byte{0x88, 0xaa, 0x63, 0x24, 0x2d, 0x48, 0xfd, 0xb1, 0x63, 0x71, 0x33, 0x17, 0x2a, 0x01, 0xce, 0x15, 0x1b, 0x25, 0xac, 0xcd, 0x35, 0xc1, 0x7c, 0x2a, 0x48, 0x58, 0x79, 0xae, 0x73, 0xf3, 0x5e, 0xc9, 0x89, 0xa7, 0x8a, 0x92, 0xa4, 0x3f, 0x3d, 0xb3, 0x43, 0x1d, 0x01, 0x74, 0xee, 0xd1, 0x1e, 0x95, 0x2b, 0x4f, 0x42, 0x46, 0x0b}
    plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, ciphertext)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Decrypted: %s\n", plaintext)
}
```

In this example, we first read in the PEM-encoded private key from the `private.pem` file. We then decode the PEM block and parse the private key using the `x509.ParsePKCS1PrivateKey` function. Finally, we use the `rsa.DecryptPKCS1v15` function to decrypt the ciphertext message using the private key.

## Conclusion
In this article, we have explored how to implement asymmetric cryptography using the Go programming language. We have shown how to generate an RSA key pair, encrypt and decrypt data using the public and private keys, respectively. With this knowledge, you can now implement secure communication between parties even without a shared secret key.


- [Portfolio](elioenai-ferrari.vercel.app)
- [Colab](https://8bits-colab.vercel.app/)
- [Github](https://github.com/ElioenaiFerrari)
- [LinkedIn](https://www.linkedin.com/in/elioenai-ferrari)