I have an app that uses gpg secret keys and prompts for a password to read it. Here's the way I do that (based on an example I found elsewhere:
func Decrypt(publicKeyring string, secretKeyring string, key string, password string) (string, error) {
var entity *openpgp.Entity
var entityList openpgp.EntityList
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]
passphraseByte := []byte(password)
entity.PrivateKey.Decrypt(passphraseByte)
for _, subkey := range entity.Subkeys {
subkey.PrivateKey.Decrypt(passphraseByte)
}
dec, err := base64.StdEncoding.DecodeString(key)
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
}
The assumption made here is that the user has a KeyRin which is passed, and the default value for this is the secring, like so:
viper.SetDefault("gpgsecretkeyring", home+"/.gnupg/secring.gpg")
However,
I was getting reports that some users on macs were struggling to get the app working, and the reason was they didn't know how to define the secring.
It seems newer versions of GnuPG have deprecated the secring.
https://www.gnupg.org/faq/whats-new-in-2.1.html#nosecring
I have no idea how to read the secret key using golang.org/x/crypto/openpgp
at this point. Are there any examples of the best way to do this?
I got sick of dealing with this, so I've decided it's easier to just shell out to gpg -dq
from the os.Exec
. Sample:
package gpg
import (
"bytes"
"encoding/base64"
"os/exec"
)
func Decrypt(key string) (string, error) {
var cmd exec.Cmd
var output bytes.Buffer
gpgCmd, err := exec.LookPath("gpg")
if err != nil {
return "", err
}
cmd.Path = gpgCmd
cmd.Args = []string{"--decrypt", "--quiet"}
dec, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return "", err
}
// return the reader interface for dec (byte array)
d := bytes.NewReader(dec)
// pipe d to gpg commands stdin
cmd.Stdin = d
cmd.Stdout = &output
if err := cmd.Run(); err != nil {
return "", err
}
// return the output from the gpg command
return output.String(), nil
}
GnuPG 2.1 introduced two changes:
secring.gpg
into the pubring.gpg
file, you should be able to read the secret keys from the pubring.gpg
file.If you want to use GnuPG's keyring, directly call GnuPG. If you want to use Go's library, don't mess with GnuPG's keyring files and store your own copy of the keys.