Skip to content

Instantly share code, notes, and snippets.

@rickcrawford
Last active October 17, 2017 23:46
Show Gist options
  • Save rickcrawford/a73a9ed33b8ef02d16cef5a526980fea to your computer and use it in GitHub Desktop.
Save rickcrawford/a73a9ed33b8ef02d16cef5a526980fea to your computer and use it in GitHub Desktop.
AES encryption example
package main
import (
"crypto/tls"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"time"
)
// Loads certificate automagically
type keypairReloader struct {
certMu sync.RWMutex
cert *tls.Certificate
certPath string
keyPath string
}
func NewKeypairReloader(certPath, keyPath string) (*keypairReloader, error) {
result := &keypairReloader{
certPath: certPath,
keyPath: keyPath,
}
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, err
}
result.cert = &cert
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP)
for range c {
log.Printf("Received SIGHUP, reloading TLS certificate and key from %q and %q", certPath, keyPath)
if err := result.maybeReload(); err != nil {
log.Printf("Keeping old TLS certificate because the new one could not be loaded: %v", err)
}
}
}()
return result, nil
}
func (kpr *keypairReloader) maybeReload() error {
newCert, err := tls.LoadX509KeyPair(kpr.certPath, kpr.keyPath)
if err != nil {
return err
}
kpr.certMu.Lock()
defer kpr.certMu.Unlock()
kpr.cert = &newCert
return nil
}
func (kpr *keypairReloader) GetCertificateFunc() func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
return func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
kpr.certMu.RLock()
defer kpr.certMu.RUnlock()
return kpr.cert, nil
}
}
type Certificates struct {
CertFile string
KeyFile string
}
func ListenAndServeTLSSNI(srv *http.Server, certs []Certificates) error {
addr := srv.Addr
if addr == "" {
addr = ":443"
}
config := &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
// curl uses below
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
},
}
if config.NextProtos == nil {
config.NextProtos = []string{"h2", "http/1.1"}
}
var err error
config.InsecureSkipVerify = false
config.Certificates = make([]tls.Certificate, len(certs))
for i, v := range certs {
config.Certificates[i], err = tls.LoadX509KeyPair(v.CertFile, v.KeyFile)
if err != nil {
return err
}
}
// kpr, err := NewKeypairReloader(*tlsCertPath, *tlsKeyPath)
// if err != nil {
// log.Fatal(err)
// }
// srv.TLSConfig.GetCertificate = kpr.GetCertificateFunc()
config.BuildNameToCertificate()
conn, err := net.Listen("tcp", addr)
if err != nil {
return err
}
tlsListener := tls.NewListener(conn, config)
return srv.Serve(tlsListener)
}
func main() {
var run = func() *http.Server {
srv := &http.Server{
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
var certs []Certificates
certs = append(certs, Certificates{
CertFile: "etc/site1.pem",
KeyFile: "etc/site1.key",
})
certs = append(certs, Certificates{
CertFile: "etc/site2.pem",
KeyFile: "etc/site2.key",
})
fmt.Println("Listening on port 443...")
go func() {
err := ListenAndServeTLSSNI(srv, certs)
if err != nil {
fmt.Printf("-->err: %s\n", err)
}
}()
return srv
}
s := run()
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGHUP)
for _ = range signalChan {
fmt.Println("Received an interrupt, stopping services...")
s.Close()
s = run()
}
// for _ = range c {
// w.WriteString("Received sighup\n")
// w.Flush()
// run()
// }
}
// https://play.golang.org/p/2XZp3CRbGu
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
)
const ck = "AES256Key-32Characters1234567890"
func ExampleNewGCMEncrypter(s string) string {
// The key argument should be the AES key, either 16 or 32 bytes
// to select AES-128 or AES-256.
key := []byte(ck)
plaintext := []byte(s)
block, err := aes.NewCipher(key)
if err != nil {
panic(err.Error())
}
// Never use more than 2^32 random nonces with a given key because of the risk of a repeat.
nonce := make([]byte, 12)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
panic(err.Error())
}
// fmt.Printf("%x\n", nonce)
aesgcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}
ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil)
str := base64.StdEncoding.EncodeToString(append(nonce, ciphertext...))
fmt.Printf("%s\n", str)
return str
}
func ExampleNewGCMDecrypter(s string) {
// The key argument should be the AES key, either 16 or 32 bytes
// to select AES-128 or AES-256.
key := []byte(ck)
data, _ := base64.StdEncoding.DecodeString(s)
nonce := data[:12]
ciphertext := data[12:]
block, err := aes.NewCipher(key)
if err != nil {
panic(err.Error())
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
panic(err.Error())
}
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
panic(err.Error())
}
fmt.Printf("%s\n", plaintext)
// Output: exampleplaintext
}
func main() {
s := ExampleNewGCMEncrypter("asdf 1234 asdf 1234 asdf 1234 asdf 1234 asdf 1234\nasdf 1234 asdf 1234 asdf 1234 asdf 1234 asdf 1234\nasdf 1234 asdf 1234 !!")
ExampleNewGCMDecrypter(s)
}
@rickcrawford
Copy link
Author

Couple examples - One does AES encryption/decryption, then a library that allows me to do SNI based certificates with the cert values encrypted. I wrote a proxy that supports multiple domains, encrypted certs that runs like a CDN.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment