Last active
August 30, 2024 05:58
-
-
Save stuart-warren/93750a142d3de4e8fdd2 to your computer and use it in GitHub Desktop.
golang gpg/openpgp encryption/decryption example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
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
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
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 ofbytes, err := ioutil.ReadAll(md.UnverifiedBody)