Created
January 4, 2016 05:01
-
-
Save eliquious/9e96017f47d9bd43cdf9 to your computer and use it in GitHub Desktop.
Revisions
-
eliquious created this gist
Jan 4, 2016 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,41 @@ ## Building ``` go build -o goencrypt main.go ``` ## Generating Keys ```sh # Produces keyname.pubkey and keyname.privkey ./goencrypt keygen keyname ``` ## Encrypting ```sh # Encrypts STDIN and outputs encrypted data to STDOUT (ascii armored) cat file.txt | ./goencrypt --public=keyname.pubkey --private=keyname.privkey encrypt > file.txt.asc ``` ## Signing ``` # Signs STDIN and writes signature to STDOUT (ascii armored) cat file.txt | ./goencrypt --public=keyname.pubkey --private=keyname.privkey sign > file.txt.sig ``` ## Verifying ```sh # Verifies STDIN against signature and public key cat file.txt | ./goencrypt --public=keyname.pubkey --sig=file.txt.sig verify ``` ## Decrypting ```sh # Decrypts STDIN and writes decrypted file to STDOUT cat file.txt.asc | ./goencrypt --public=keyname.pubkey --private=keyname.privkey decrypt > file.txt.bak ``` This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,310 @@ package main import ( "errors" "io" "os" "path/filepath" "time" "crypto" "crypto/rand" "crypto/rsa" _ "crypto/sha256" _ "golang.org/x/crypto/ripemd160" "compress/gzip" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/armor" "golang.org/x/crypto/openpgp/packet" kingpin "gopkg.in/alecthomas/kingpin.v2" ) var ( // Goencrypt app app = kingpin.New("goencrypt", "A command line tool for encrypting files") bits = app.Flag("bits", "Bits for keys").Default("4096").Int() privateKey = app.Flag("private", "Private key").String() publicKey = app.Flag("public", "Public key").String() signatureFile = app.Flag("sig", "Signature File").String() // Generates new public and private keys keyGenCmd = app.Command("keygen", "Generates a new public/private key pair") keyOutputPrefix = keyGenCmd.Arg("prefix", "Prefix of key files").Required().String() keyOutputDir = keyGenCmd.Flag("d", "Output directory of key files").Default(".").String() // Encrypts a file with a public key encryptionCmd = app.Command("encrypt", "Encrypt from stdin") // Signs a file with a private key signCmd = app.Command("sign", "Sign stdin") // Verifies a file was signed with the public key verifyCmd = app.Command("verify", "Verify a signature of stdin") // Decrypts a file with a private key decryptionCmd = app.Command("decrypt", "Decrypt from stdin") ) func main() { switch kingpin.MustParse(app.Parse(os.Args[1:])) { // generate keys case keyGenCmd.FullCommand(): generateKeys() // case createEntityCmd.FullCommand(): // newEntity() case encryptionCmd.FullCommand(): encryptFile() case signCmd.FullCommand(): signFile() case verifyCmd.FullCommand(): verifyFile() case decryptionCmd.FullCommand(): decryptFile() default: kingpin.FatalUsage("Unknown command") } } func encodePrivateKey(out io.Writer, key *rsa.PrivateKey) { w, err := armor.Encode(out, openpgp.PrivateKeyType, make(map[string]string)) kingpin.FatalIfError(err, "Error creating OpenPGP Armor: %s", err) pgpKey := packet.NewRSAPrivateKey(time.Now(), key) kingpin.FatalIfError(pgpKey.Serialize(w), "Error serializing private key: %s", err) kingpin.FatalIfError(w.Close(), "Error serializing private key: %s", err) } func decodePrivateKey(filename string) *packet.PrivateKey { // open ascii armored private key in, err := os.Open(filename) kingpin.FatalIfError(err, "Error opening private key: %s", err) defer in.Close() block, err := armor.Decode(in) kingpin.FatalIfError(err, "Error decoding OpenPGP Armor: %s", err) if block.Type != openpgp.PrivateKeyType { kingpin.FatalIfError(errors.New("Invalid private key file"), "Error decoding private key") } reader := packet.NewReader(block.Body) pkt, err := reader.Next() kingpin.FatalIfError(err, "Error reading private key") key, ok := pkt.(*packet.PrivateKey) if !ok { kingpin.FatalIfError(errors.New("Invalid private key"), "Error parsing private key") } return key } func encodePublicKey(out io.Writer, key *rsa.PrivateKey) { w, err := armor.Encode(out, openpgp.PublicKeyType, make(map[string]string)) kingpin.FatalIfError(err, "Error creating OpenPGP Armor: %s", err) pgpKey := packet.NewRSAPublicKey(time.Now(), &key.PublicKey) kingpin.FatalIfError(pgpKey.Serialize(w), "Error serializing public key: %s", err) kingpin.FatalIfError(w.Close(), "Error serializing public key: %s", err) } func decodePublicKey(filename string) *packet.PublicKey { // open ascii armored public key in, err := os.Open(filename) kingpin.FatalIfError(err, "Error opening public key: %s", err) defer in.Close() block, err := armor.Decode(in) kingpin.FatalIfError(err, "Error decoding OpenPGP Armor: %s", err) if block.Type != openpgp.PublicKeyType { kingpin.FatalIfError(errors.New("Invalid private key file"), "Error decoding private key") } reader := packet.NewReader(block.Body) pkt, err := reader.Next() kingpin.FatalIfError(err, "Error reading private key") key, ok := pkt.(*packet.PublicKey) if !ok { kingpin.FatalIfError(errors.New("Invalid public key"), "Error parsing public key") } return key } func decodeSignature(filename string) *packet.Signature { // open ascii armored public key in, err := os.Open(filename) kingpin.FatalIfError(err, "Error opening public key: %s", err) defer in.Close() block, err := armor.Decode(in) kingpin.FatalIfError(err, "Error decoding OpenPGP Armor: %s", err) if block.Type != openpgp.SignatureType { kingpin.FatalIfError(errors.New("Invalid signature file"), "Error decoding signature") } reader := packet.NewReader(block.Body) pkt, err := reader.Next() kingpin.FatalIfError(err, "Error reading signature") sig, ok := pkt.(*packet.Signature) if !ok { kingpin.FatalIfError(errors.New("Invalid signature"), "Error parsing signature") } return sig } func encryptFile() { pubKey := decodePublicKey(*publicKey) privKey := decodePrivateKey(*privateKey) to := createEntityFromKeys(pubKey, privKey) w, err := armor.Encode(os.Stdout, "Message", make(map[string]string)) kingpin.FatalIfError(err, "Error creating OpenPGP Armor: %s", err) defer w.Close() plain, err := openpgp.Encrypt(w, []*openpgp.Entity{to}, nil, nil, nil) kingpin.FatalIfError(err, "Error creating entity for encryption") defer plain.Close() compressed, err := gzip.NewWriterLevel(plain, gzip.BestCompression) kingpin.FatalIfError(err, "Invalid compression level") n, err := io.Copy(compressed, os.Stdin) kingpin.FatalIfError(err, "Error writing encrypted file") kingpin.Errorf("Encrypted %d bytes", n) compressed.Close() } func decryptFile() { pubKey := decodePublicKey(*publicKey) privKey := decodePrivateKey(*privateKey) entity := createEntityFromKeys(pubKey, privKey) block, err := armor.Decode(os.Stdin) kingpin.FatalIfError(err, "Error reading OpenPGP Armor: %s", err) if block.Type != "Message" { kingpin.FatalIfError(err, "Invalid message type") } var entityList openpgp.EntityList entityList = append(entityList, entity) md, err := openpgp.ReadMessage(block.Body, entityList, nil, nil) kingpin.FatalIfError(err, "Error reading message") compressed, err := gzip.NewReader(md.UnverifiedBody) kingpin.FatalIfError(err, "Invalid compression level") defer compressed.Close() n, err := io.Copy(os.Stdout, compressed) kingpin.FatalIfError(err, "Error reading encrypted file") kingpin.Errorf("Decrypted %d bytes", n) } func signFile() { pubKey := decodePublicKey(*publicKey) privKey := decodePrivateKey(*privateKey) signer := createEntityFromKeys(pubKey, privKey) err := openpgp.ArmoredDetachSign(os.Stdout, signer, os.Stdin, nil) kingpin.FatalIfError(err, "Error signing input") } func verifyFile() { pubKey := decodePublicKey(*publicKey) sig := decodeSignature(*signatureFile) hash := sig.Hash.New() io.Copy(hash, os.Stdin) err := pubKey.VerifySignature(hash, sig) kingpin.FatalIfError(err, "Error signing input") kingpin.Errorf("Verified signature") } func createEntityFromKeys(pubKey *packet.PublicKey, privKey *packet.PrivateKey) *openpgp.Entity { config := packet.Config{ DefaultHash: crypto.SHA256, DefaultCipher: packet.CipherAES256, DefaultCompressionAlgo: packet.CompressionZLIB, CompressionConfig: &packet.CompressionConfig{ Level: 9, }, RSABits: *bits, } currentTime := config.Now() uid := packet.NewUserId("", "", "") e := openpgp.Entity{ PrimaryKey: pubKey, PrivateKey: privKey, Identities: make(map[string]*openpgp.Identity), } isPrimaryId := false e.Identities[uid.Id] = &openpgp.Identity{ Name: uid.Name, UserId: uid, SelfSignature: &packet.Signature{ CreationTime: currentTime, SigType: packet.SigTypePositiveCert, PubKeyAlgo: packet.PubKeyAlgoRSA, Hash: config.Hash(), IsPrimaryId: &isPrimaryId, FlagsValid: true, FlagSign: true, FlagCertify: true, IssuerKeyId: &e.PrimaryKey.KeyId, }, } keyLifetimeSecs := uint32(86400 * 365) e.Subkeys = make([]openpgp.Subkey, 1) e.Subkeys[0] = openpgp.Subkey{ PublicKey: pubKey, PrivateKey: privKey, Sig: &packet.Signature{ CreationTime: currentTime, SigType: packet.SigTypeSubkeyBinding, PubKeyAlgo: packet.PubKeyAlgoRSA, Hash: config.Hash(), PreferredHash: []uint8{8}, // SHA-256 FlagsValid: true, FlagEncryptStorage: true, FlagEncryptCommunications: true, IssuerKeyId: &e.PrimaryKey.KeyId, KeyLifetimeSecs: &keyLifetimeSecs, }, } return &e } func generateKeys() { key, err := rsa.GenerateKey(rand.Reader, *bits) kingpin.FatalIfError(err, "Error generating RSA key: %s", err) priv, err := os.Create(filepath.Join(*keyOutputDir, *keyOutputPrefix+".privkey")) kingpin.FatalIfError(err, "Error writing private key to file: %s", err) defer priv.Close() pub, err := os.Create(filepath.Join(*keyOutputDir, *keyOutputPrefix+".pubkey")) kingpin.FatalIfError(err, "Error writing public key to file: %s", err) defer pub.Close() encodePrivateKey(priv, key) encodePublicKey(pub, key) }