Skip to content

Instantly share code, notes, and snippets.

@fuzzyami
Last active February 2, 2024 17:58
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save fuzzyami/f3a7231037166117a6fef9607960aee7 to your computer and use it in GitHub Desktop.
Save fuzzyami/f3a7231037166117a6fef9607960aee7 to your computer and use it in GitHub Desktop.
golang encyrpt, decrypt key with kms
/*
The code below shows how to encrypt and then decrypt some plaintext into a cyphertext using
KMS's Encrypt/Decrypt functions and secretbox (https://godoc.org/golang.org/x/crypto/nacl/secretbox).
The plaintext message is sealed into a secretbox using a key that is generated by kmsClient.GenerateDataKey().
Note that this procedure reuquires that a master key would *already exist in KMS* and that its arn/alias is specified.
The aws library assumes that the proper credentials can be found in the shared file (~/.aws/credentials)
and opts for the 'default' role.
Once sealed, the cyphertext is then unboxed, again by first getting the key from kms (kmsClient.Decrypt),
and then using that to decypher the text (secretbox.Open(..))
Note that that the kms keyname isn't specified on Decrypt. I guess that the ciphertext contains some meta regarding
which key should be used to decrypt.
*/
package main
import (
"bytes"
"crypto/rand"
"encoding/gob"
"fmt"
"io/ioutil"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/kms"
"golang.org/x/crypto/nacl/secretbox"
"strings"
)
const (
kmsKeyName = "tempKey" //must already exist in kms
)
func main() {
r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.")
plaintext, err := ioutil.ReadAll(r)
if err != nil {
panic(err)
}
fmt.Println("Plaintext: ", plaintext)
sess := session.Must(session.NewSession())
kmsClient := kms.New(sess, aws.NewConfig().WithRegion("us-east-1"))
// Encrypt plaintext
encrypted, err := Encrypt(kmsClient, plaintext) // <-- to be implemented
if err != nil {
panic(err)
}
fmt.Println("Encrypted: ", encrypted)
// Decrypt ciphertext
kmsClient = kms.New(sess, aws.NewConfig().WithRegion("us-east-1"))
decrypted, err := Decrypt(kmsClient, encrypted) // <-- to be implemented
if err != nil {
panic(err)
}
fmt.Println("Decrypted: ", decrypted)
}
const (
keyLength = 32
nonceLength = 24
)
type payload struct {
Key []byte
Nonce *[nonceLength]byte
Message []byte
}
func Encrypt(kmsClient *kms.KMS, plaintext []byte) ([]byte, error) {
// Generate data key
//provide either the key's arn OR its alias, as shown below:
//keyId := "arn:aws:kms:us-east-1:779993255822:key/bb1a147c-8600-4558-910d-8b841c8f7493"
keyId := "alias/" + kmsKeyName
keySpec := "AES_128"
dataKeyInput := kms.GenerateDataKeyInput{KeyId: &keyId, KeySpec: &keySpec}
dataKeyOutput, err := kmsClient.GenerateDataKey(&dataKeyInput)
if err == nil { // dataKeyOutput is now filled
fmt.Println(dataKeyOutput)
} else {
fmt.Println("error: ", err)
}
// Initialize payload
p := &payload{
Key: dataKeyOutput.CiphertextBlob,
Nonce: &[nonceLength]byte{},
}
// Set nonce
if _, err = rand.Read(p.Nonce[:]); err != nil {
return nil, err
}
// Create key
key := &[keyLength]byte{}
copy(key[:], dataKeyOutput.Plaintext)
// Encrypt message
p.Message = secretbox.Seal(p.Message, plaintext, p.Nonce, key)
buf := &bytes.Buffer{}
if err := gob.NewEncoder(buf).Encode(p); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func Decrypt(kmsClient *kms.KMS, ciphertext []byte) ([]byte, error) {
// Decode ciphertext with gob
var p payload
gob.NewDecoder(bytes.NewReader(ciphertext)).Decode(&p)
//Decrypt a ciphertext that was previously encrypted.
//Note that we dont actually specify the key name.
//I guess the ciphertext already encodes it?
dataKeyOutput, err := kmsClient.Decrypt(&kms.DecryptInput{
CiphertextBlob: p.Key,
})
if err == nil { // dataKeyOutput is now filled
fmt.Println(dataKeyOutput)
} else {
fmt.Println("error: ", err)
}
key := &[keyLength]byte{}
copy(key[:], dataKeyOutput.Plaintext)
// Decrypt message
var plaintext []byte
plaintext, ok := secretbox.Open(plaintext, p.Message, p.Nonce, key)
if !ok {
return nil, fmt.Errorf("Failed to open secretbox")
}
return plaintext, nil
}
@truescotian
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment