Golang pgp without secring

1.1k views Asked by At

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?

2

There are 2 answers

0
jaxxstorm On

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

}
0
Jens Erat On

GnuPG 2.1 introduced two changes:

  • Merging the secring.gpg into the pubring.gpg file, you should be able to read the secret keys from the pubring.gpg file.
  • For new installations, the new keybox format is used, which is not supported by the go library (as of today, at least). Old installations (thus, with keyrings in the old format), are not automatically merged.

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.