Skip to content

Instantly share code, notes, and snippets.

@kravemir
Last active June 16, 2021 18:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kravemir/b003a2926ec5da84a8c38fd1a8c2f4bb to your computer and use it in GitHub Desktop.
Save kravemir/b003a2926ec5da84a8c38fd1a8c2f4bb to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"encoding/base64"
"fmt"
"io/ioutil"
"log"
"os"
"golang.org/x/crypto/openpgp"
)
const textToEncrypt = "precious and secret!"
const secretKeyringLocation = "./.gnupg/secring.gpg"
const publicKeyringLocation = "./.gnupg/pubring.gpg"
const secretKeyringPassphrase = "123456"
func encryptAndSignString(textToEncrypt string) ([]byte, error) {
publicEntityList, err := getPublicKeys()
if err != nil {
return nil, fmt.Errorf("get public keys: %w", err)
}
privateEntityList, err := getPrivateKeys()
if err != nil {
return nil, fmt.Errorf("get private keys: %w", err)
}
var buf bytes.Buffer
w, err := openpgp.Encrypt(&buf, publicEntityList, privateEntityList[0], nil, nil)
if err != nil {
return nil, fmt.Errorf("begin encryption: %w", err)
}
_, err = w.Write([]byte(textToEncrypt))
if err != nil {
return nil, fmt.Errorf("write to encrypt writer: %w", err)
}
err = w.Close()
if err != nil {
return nil, fmt.Errorf("close encrypt write: %w", err)
}
return buf.Bytes(), nil
}
func decryptToStringAndVerifySignature(encryptedBytes []byte) (string, error) {
privateEntityList, err := getPrivateKeys()
if err != nil {
return "", fmt.Errorf("get private keys: %w", err)
}
md, err := openpgp.ReadMessage(bytes.NewBuffer(encryptedBytes), privateEntityList, nil, nil)
if err != nil {
return "", fmt.Errorf("read message: %w", err)
}
decryptedBytes, err := ioutil.ReadAll(md.UnverifiedBody)
if err != nil {
return "", fmt.Errorf("read body: %w", err)
}
err = verifySignature(md)
if err != nil {
return "", fmt.Errorf("verify signature: %w", err)
}
return string(decryptedBytes), nil
}
func verifySignature(md *openpgp.MessageDetails) error {
if !md.IsSigned {
return fmt.Errorf("not signed")
}
if md.SignedBy == nil {
return fmt.Errorf("missing signature key")
}
if md.SignatureError != nil {
return fmt.Errorf("signature error: %w", md.SignatureError)
}
return nil
}
func getPublicKeys() (openpgp.EntityList, error) {
return readKeyring(publicKeyringLocation)
}
func getPrivateKeys() (openpgp.EntityList, error) {
entityList, err := readKeyring(secretKeyringLocation)
if err != nil {
return nil, fmt.Errorf("read secret keyring: %w", err)
}
entity := entityList[0]
passphraseByte := []byte(secretKeyringPassphrase)
err = entity.PrivateKey.Decrypt(passphraseByte)
if err != nil {
return nil, fmt.Errorf("decrypt private key %X: %w", entity.PrivateKey.KeyId, err)
}
for _, subkey := range entity.Subkeys {
err = subkey.PrivateKey.Decrypt(passphraseByte)
if err != nil {
return nil, fmt.Errorf("decrypt subkey key %X: %w", subkey.PrivateKey.KeyId, err)
}
}
return entityList, nil
}
func readKeyring(location string) (openpgp.EntityList, error) {
keyringFileBuffer, err := os.Open(location)
if err != nil {
return nil, fmt.Errorf("open keyring: %w", err)
}
defer keyringFileBuffer.Close()
entityList, err := openpgp.ReadKeyRing(keyringFileBuffer)
if err != nil {
return nil, fmt.Errorf("read keyring: %w", err)
}
return entityList, nil
}
func main() {
encryptedBytes, err := encryptAndSignString(textToEncrypt)
if err != nil {
log.Fatal("encrypt error: ", err)
}
base64EncryptedBytes := base64.StdEncoding.EncodeToString(encryptedBytes)
log.Println("Encrypted content in base64:", base64EncryptedBytes)
decryptedString, err := decryptToStringAndVerifySignature(encryptedBytes)
if err != nil {
log.Fatal("decrypt error: ", err)
}
log.Println("Decrypted content:", decryptedString)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment