Skip to content

Instantly share code, notes, and snippets.

@clementlecorre
Last active May 19, 2021 10:10
Show Gist options
  • Save clementlecorre/de57fad7c88ad2658a464f2f85dafa19 to your computer and use it in GitHub Desktop.
Save clementlecorre/de57fad7c88ad2658a464f2f85dafa19 to your computer and use it in GitHub Desktop.
Golang gpg encryption (crypto/openpgp)
package gpg
import (
"errors"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/packet"
"io"
"os"
"path/filepath"
)
const (
defaultFileKeyExtension = ".pubkey"
defaultPubKeysPath = "./.keys"
)
type EncryptOption struct {
PubKeysPath string
FileToEncrypt string
FileKeyExtension string
OutputFileName string
}
type Encrypt struct {
EncryptOption
PublicKeyPath []string
PublicKeysEntity []*openpgp.Entity
sourceDataFile *os.File
destDataFile *os.File
}
func NewGpgEncrypt(gpgEncryptOption EncryptOption) (*Encrypt, error) {
gpgEncrypt := Encrypt{EncryptOption: gpgEncryptOption}
if gpgEncrypt.FileToEncrypt == "" {
return nil, errors.New("you must reference a file to encrypt")
}
if gpgEncrypt.OutputFileName == "" {
return nil, errors.New("you must reference a output file name")
}
if gpgEncrypt.PubKeysPath == "" {
gpgEncrypt.PubKeysPath = defaultPubKeysPath
}
if gpgEncrypt.FileKeyExtension == "" {
gpgEncrypt.FileKeyExtension = defaultFileKeyExtension
}
err := gpgEncrypt.readPublicKeys()
if err != nil {
return nil, err
}
if !gpgEncrypt.fileExists(gpgEncrypt.FileToEncrypt) {
return nil, errors.New("file to encrypt no found")
}
return &gpgEncrypt, nil
}
func (g *Encrypt) SetPubKeysPath(pubKeysPath string) {
g.PubKeysPath = pubKeysPath
}
func (g *Encrypt) SetFileToEncrypt(fileToEncrypt string) {
g.FileToEncrypt = fileToEncrypt
}
func (g *Encrypt) SetFileKeyExtension(fileKeyExtension string) {
g.FileKeyExtension = fileKeyExtension
}
func (g *Encrypt) fileExists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}
func (g *Encrypt) loadFiles() (*os.File, *os.File, error) {
var r *os.File
var w *os.File
r, err := os.Open(g.FileToEncrypt)
if err != nil {
return nil, nil, err
}
w, err = os.Create(g.OutputFileName)
if err != nil {
return nil, nil, err
}
return r, w, nil
}
func (g *Encrypt) walkPublicKey() error {
err := filepath.Walk(g.PubKeysPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if matched, err := filepath.Match("*"+g.FileKeyExtension, filepath.Base(path)); err != nil {
return err
} else if matched {
g.PublicKeyPath = append(g.PublicKeyPath, path)
}
return nil
})
if err != nil {
return err
}
return nil
}
func (g *Encrypt) readPublicKeys() error {
err := g.walkPublicKey()
if err != nil {
return err
}
for _, name := range g.PublicKeyPath {
entity, err := g.readPublicKey(name)
if err != nil {
return err
}
g.PublicKeysEntity = append(g.PublicKeysEntity, entity)
}
return nil
}
func (g *Encrypt) readPublicKey(name string) (*openpgp.Entity, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
block, err := armor.Decode(f)
if err != nil {
return nil, err
}
return openpgp.ReadEntity(packet.NewReader(block.Body))
}
func (g *Encrypt) Encrypt() error {
r, w, err := g.loadFiles()
if err != nil {
return err
}
defer r.Close()
defer w.Close()
wc, err := openpgp.Encrypt(w, g.PublicKeysEntity, nil, &openpgp.FileHints{IsBinary: true}, nil)
if err != nil {
return err
}
if _, err := io.Copy(wc, r); err != nil {
return err
}
return wc.Close()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment