DEV Community

ElioenaiFerrari
ElioenaiFerrari

Posted on

Asymmetric Cryptography with Golang

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:

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)
    }
}
Enter fullscreen mode Exit fullscreen mode

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/x509package's EncryptPKCS1v15 function:

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)
}
Enter fullscreen mode Exit fullscreen mode

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:

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)
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)