Skip to content

Instantly share code, notes, and snippets.

@stuart-warren
Last active October 11, 2023 01:21
Show Gist options
  • Star 79 You must be signed in to star a gist
  • Fork 14 You must be signed in to fork a gist
  • Save stuart-warren/93750a142d3de4e8fdd2 to your computer and use it in GitHub Desktop.
Save stuart-warren/93750a142d3de4e8fdd2 to your computer and use it in GitHub Desktop.
golang gpg/openpgp encryption/decryption example
package main
import (
"bytes"
"code.google.com/p/go.crypto/openpgp"
"encoding/base64"
"io/ioutil"
"log"
"os"
)
// create gpg keys with
// $ gpg --gen-key
// ensure you correct paths and passphrase
const mySecretString = "this is so very secret!"
const prefix, passphrase = "/Users/stuart-warren/", "1234"
const secretKeyring = prefix + ".gnupg/secring.gpg"
const publicKeyring = prefix + ".gnupg/pubring.gpg"
func encTest(secretString string) (string, error) {
log.Println("Secret to hide:", secretString)
log.Println("Public Keyring:", publicKeyring)
// Read in public key
keyringFileBuffer, _ := os.Open(publicKeyring)
defer keyringFileBuffer.Close()
entityList, err := openpgp.ReadKeyRing(keyringFileBuffer)
if err != nil {
return "", err
}
// encrypt string
buf := new(bytes.Buffer)
w, err := openpgp.Encrypt(buf, entityList, nil, nil, nil)
if err != nil {
return "", err
}
_, err = w.Write([]byte(mySecretString))
if err != nil {
return "", err
}
err = w.Close()
if err != nil {
return "", err
}
// Encode to base64
bytes, err := ioutil.ReadAll(buf)
if err != nil {
return "", err
}
encStr := base64.StdEncoding.EncodeToString(bytes)
// Output encrypted/encoded string
log.Println("Encrypted Secret:", encStr)
return encStr, nil
}
func decTest(encString string) (string, error) {
log.Println("Secret Keyring:", secretKeyring)
log.Println("Passphrase:", passphrase)
// init some vars
var entity *openpgp.Entity
var entityList openpgp.EntityList
// Open the private key file
keyringFileBuffer, err := os.Open(secretKeyring)
if err != nil {
return "", err
}
defer keyringFileBuffer.Close()
entityList, err = openpgp.ReadKeyRing(keyringFileBuffer)
if err != nil {
return "", err
}
entity = entityList[0]
// Get the passphrase and read the private key.
// Have not touched the encrypted string yet
passphraseByte := []byte(passphrase)
log.Println("Decrypting private key using passphrase")
entity.PrivateKey.Decrypt(passphraseByte)
for _, subkey := range entity.Subkeys {
subkey.PrivateKey.Decrypt(passphraseByte)
}
log.Println("Finished decrypting private key using passphrase")
// Decode the base64 string
dec, err := base64.StdEncoding.DecodeString(encString)
if err != nil {
return "", err
}
// Decrypt it with the contents of the private key
md, err := openpgp.ReadMessage(bytes.NewBuffer(dec), entityList, nil, nil)
if err != nil {
return "", err
}
bytes, err := ioutil.ReadAll(md.UnverifiedBody)
if err != nil {
return "", err
}
decStr := string(bytes)
return decStr, nil
}
func main() {
encStr, err := encTest(mySecretString)
if err != nil {
log.Fatal(err)
}
decStr, err := decTest(encStr)
if err != nil {
log.Fatal(err)
}
// should be done
log.Println("Decrypted Secret:", decStr)
}
@antonputra
Copy link

Keys should be in binary NOT in base64, otherwise you'll get tag byte does not have MSB set error.

@llakes
Copy link

llakes commented Oct 4, 2018

use openpgp.ReadArmoredKeyRing instead of openpgp.ReadKeyRing if you exported your gpg keys with --armor

@arnisoph
Copy link

arnisoph commented Jan 9, 2020

thanks mates, now it finally works for me too..

If you have created your keyring with gnupg >=v2 you need to export the secret keys e.g. with

$ gpg --no-default-keyring --keyring ./ring.gpg --export-secret-keys > secret-key.gpg
$ file secret-key.gpg
secret-key.gpg: PGP	Secret Key - 2048b created on Thu Jan  9 01:46:30 2020 - RSA (Encrypt or Sign) e=65537 hashed AES with 128-bit key Salted&Iterated S2K SHA-1

🤦‍♂️

@skunkworker
Copy link

I believe the url has changed for the openpgp package to golang.org/x/crypto/openpgp

@domhoward14
Copy link

Awesome code snippet! Only comment was for anyone referencing this code to decrypt something other than a string, such as a big encrypted file. It would be safer and might save you a ton of time debugging if you read in the decrypted string using _, err := ioutil.copy(decryptWriter, md.UnverifiedBody) instead of bytes, err := ioutil.ReadAll(md.UnverifiedBody)

@kravemir
Copy link

Thanks, this gist works! I had struggled a bit with generation of secring.gpg and pubring.gpg, though.

To generate throwaway keys for experiments, following commands can be used generate a new key in .gnupg as subdirectory of the current directory, and .gnupg/secring.gpg and .gnupg/pubring.gpg keyrings:

gpg --gen-key --homedir .gnupg
gpg --no-default-keyring --homedir ./.gnupg/ --export-secret-keys > ./.gnupg/secring.gpg
gpg --no-default-keyring --homedir ./.gnupg/ --export > ./.gnupg/pubring.gpg

@nhaancs
Copy link

nhaancs commented Feb 13, 2023

Thanks, how about if I want to support dual keys?

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