Skip to content

Instantly share code, notes, and snippets.

@korc
Last active November 26, 2019 21:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save korc/032812cb6500de2aba27622cf320675a to your computer and use it in GitHub Desktop.
Save korc/032812cb6500de2aba27622cf320675a to your computer and use it in GitHub Desktop.
Encrypt HTTP (ex PUT/POST) payload
package main
import (
"bytes"
"flag"
"io"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"strings"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/packet"
)
type EncryptedBody struct {
OrigBody io.ReadCloser
CryptedBody *bytes.Buffer
pWr io.WriteCloser
bodyClosed bool
}
func NewEncryptedBody(body io.ReadCloser, to []*openpgp.Entity, signer *openpgp.Entity) (*EncryptedBody, error) {
eb := &EncryptedBody{
OrigBody: body,
CryptedBody: bytes.NewBuffer([]byte{}),
}
var err error
eb.pWr, err = openpgp.Encrypt(eb.CryptedBody, to, signer, &openpgp.FileHints{IsBinary: true}, nil)
if err != nil {
return nil, err
}
return eb, err
}
func (eb *EncryptedBody) Close() error {
eb.bodyClosed = true
eb.pWr.Close()
return eb.OrigBody.Close()
}
func (eb *EncryptedBody) Read(p []byte) (n int, err error) {
if !eb.bodyClosed {
bodybuf := make([]byte, len(p))
n, err := eb.OrigBody.Read(bodybuf)
log.Printf("Read %d (out of %d) bytes from original body, err = %v", n, len(p), err)
if n > 0 {
if nWrote, err := eb.pWr.Write(bodybuf[:n]); err != nil {
log.Printf("Could not write (%d) to encryptor: %s", nWrote, err)
}
}
if err == io.EOF {
eb.bodyClosed = true
eb.pWr.Close()
}
}
return eb.CryptedBody.Read(p)
}
type arrayFlag []string
func (f *arrayFlag) String() string {
return strings.Join(*f, ", ")
}
func (f *arrayFlag) Set(value string) error {
*f = append(*f, value)
return nil
}
func main() {
var rcptFlag arrayFlag
listenAddrFlag := flag.String("listen", ":8080", "Listen address")
remoteAddr := flag.String("remote", "", "Remote address")
signFlag := flag.String("sign", "", "Sign with entity")
addExtFlag := flag.String("ext", "", "Add extension (ex: .pgp) to URL path")
methodFlag := flag.String("methods", "PUT,POST", "methods to encrypt")
flag.Var(&rcptFlag, "to", "Public key file of recipient to encrypt to(multiple args ok)")
flag.Parse()
if *remoteAddr == "" || len(rcptFlag) == 0 {
log.Fatal("Need -remote and -to flags")
}
var signer *openpgp.Entity
if *signFlag != "" {
if f, err := os.Open(*signFlag); err != nil {
log.Fatalf("Cannot open signing file %#v: %s", *signFlag, err)
} else {
var err error
if signer, err = openpgp.ReadEntity(packet.NewReader(f)); err != nil {
log.Fatalf("Could not read signing entity from %#v: %s", *signFlag, err)
}
if signer.PrivateKey == nil {
log.Fatalf("File %#v does not contain private key", *signFlag)
}
if signer.PrivateKey.Encrypted {
log.Fatalf("Cannot use encrypted private key in %#v", *signFlag)
}
}
}
rcpts := make([]*openpgp.Entity, 0)
for _, pubkeyFile := range rcptFlag {
if f, err := os.Open(pubkeyFile); err != nil {
log.Fatalf("Cannot open public key %#v: %s", pubkeyFile, err)
} else {
if rcpt, err := openpgp.ReadEntity(packet.NewReader(f)); err != nil {
log.Fatal("Cannot read entity: ", err)
} else {
rcpts = append(rcpts, rcpt)
}
}
}
remoteUrl, err := url.Parse(*remoteAddr)
if err != nil {
log.Fatalf("Cannot create URL from %#v: %s", *remoteAddr, err)
}
var methods map[string]bool
if *methodFlag != "" {
methods = make(map[string]bool)
for _, m := range strings.Split(*methodFlag, ",") {
methods[m] = true
}
}
log.Print("Listening and serving on: ", *listenAddrFlag)
if err := http.ListenAndServe(*listenAddrFlag, &httputil.ReverseProxy{
Director: func(request *http.Request) {
request.URL.Host = remoteUrl.Host
request.URL.Scheme = remoteUrl.Scheme
request.URL.Path = remoteUrl.Path + request.URL.Path
log.Printf("%s %s (%d bytes)", request.Method, request.URL, request.ContentLength)
if methods == nil || methods[request.Method] {
if *addExtFlag != "" {
request.URL.Path = request.URL.Path + *addExtFlag
}
if request.Body != nil {
request.Header.Del("Content-Length")
request.ContentLength = -1
request.Body, err = NewEncryptedBody(request.Body, rcpts, signer)
if err != nil {
log.Fatal("Cannot create encryptor: ", err)
}
} else {
log.Printf("No request body to encrypt")
}
}
},
}); err != nil {
log.Fatal("Could not listen: ", err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment