Skip to content

Instantly share code, notes, and snippets.

@erikdubbelboer
Created January 24, 2021 13:34
Show Gist options
  • Select an option

  • Save erikdubbelboer/f14b38d258f376dd6d4d47491f32535a to your computer and use it in GitHub Desktop.

Select an option

Save erikdubbelboer/f14b38d258f376dd6d4d47491f32535a to your computer and use it in GitHub Desktop.

Revisions

  1. erikdubbelboer created this gist Jan 24, 2021.
    219 changes: 219 additions & 0 deletions mtlstest.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,219 @@
    package main

    import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/tls"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "math/big"
    "net"
    "net/http"
    "runtime"
    "sync/atomic"
    "time"

    "github.com/valyala/fasthttp"
    )

    func main() {
    caCert, key, err := GenerateCert("localhost")
    if err != nil {
    panic(err)
    }

    caCertPool := x509.NewCertPool()
    if !caCertPool.AppendCertsFromPEM(caCert) {
    panic("not ok")
    }

    caCertPool.AppendCertsFromPEM(caCert)

    cert, err := tls.X509KeyPair(caCert, key)
    if err != nil {
    panic(err)
    }

    go server(caCertPool, cert)

    time.Sleep(time.Second)

    // fasthttpClient(caCertPool, cert)
    httpClient(caCertPool, cert)
    }

    func server(caCertPool *x509.CertPool, cert tls.Certificate) {
    // Create the TLS Config with the CA pool and enable Client certificate validation
    cfg := &tls.Config{
    ClientCAs: caCertPool,
    ClientAuth: tls.RequireAndVerifyClientCert,
    Certificates: []tls.Certificate{cert},
    }
    cfg.BuildNameToCertificate()

    ln, err := net.Listen("tcp4", "localhost:8443")
    if err != nil {
    panic(err)
    }

    lnTls := tls.NewListener(ln, cfg)

    server := &fasthttp.Server{
    IdleTimeout: 30 * time.Second,
    TCPKeepalive: true,
    TCPKeepalivePeriod: 30 * time.Second,
    MaxConnsPerIP: 200,
    Handler: func(ctx *fasthttp.RequestCtx) {
    ctx.SetStatusCode(200)
    ctx.SetBody([]byte("hello"))
    },
    }

    if err := server.Serve(lnTls); err != nil {
    panic(err)
    }
    }

    func fasthttpClient(caCertPool *x509.CertPool, cert tls.Certificate) {
    client := &fasthttp.Client{
    TLSConfig: &tls.Config{
    RootCAs: caCertPool,
    Certificates: []tls.Certificate{cert},
    ClientSessionCache: tls.NewLRUClientSessionCache(64),
    },
    }

    var requests int64

    worker := func() {
    for {
    req := fasthttp.AcquireRequest()
    res := fasthttp.AcquireResponse()

    req.SetRequestURI("https://localhost:8443/")

    if err := client.Do(req, res); err != nil {
    println(err.Error())
    }

    atomic.AddInt64(&requests, 1)

    fasthttp.ReleaseRequest(req)
    fasthttp.ReleaseResponse(res)
    }
    }

    for i := 0; i < 4; i++ {
    go worker()
    }

    // Print the request counters every second.
    for {
    time.Sleep(time.Second)

    r := atomic.SwapInt64(&requests, 0)

    fmt.Println(r, runtime.NumGoroutine())
    }
    }

    func httpClient(caCertPool *x509.CertPool, cert tls.Certificate) {
    client := &http.Client{
    Transport: &http.Transport{
    TLSHandshakeTimeout: 5 * time.Second,
    MaxIdleConns: 2000,
    MaxIdleConnsPerHost: 200,
    MaxConnsPerHost: 2000,
    IdleConnTimeout: 20 * time.Second,
    WriteBufferSize: 1024,
    ReadBufferSize: 1024,
    TLSClientConfig: &tls.Config{
    RootCAs: caCertPool,
    Certificates: []tls.Certificate{cert},
    ClientSessionCache: tls.NewLRUClientSessionCache(64),
    },
    },
    }

    var requests int64

    worker := func() {
    for {
    if res, err := client.Get("https://localhost:8443/"); err != nil {
    println(err.Error())
    } else {
    _, err := ioutil.ReadAll(res.Body)
    if err != nil {
    panic(err)
    }
    }

    atomic.AddInt64(&requests, 1)
    }
    }

    for i := 0; i < 4; i++ {
    go worker()
    }

    // Print the request counters every second.
    for {
    time.Sleep(time.Second)

    r := atomic.SwapInt64(&requests, 0)

    fmt.Println(r, runtime.NumGoroutine())
    }
    }

    // GenerateCert generates certificate and private key based on the given host.
    func GenerateCert(host string) ([]byte, []byte, error) {
    priv, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
    return nil, nil, err
    }

    serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
    serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
    if err != nil {
    return nil, nil, err
    }

    cert := &x509.Certificate{
    SerialNumber: serialNumber,
    Subject: pkix.Name{
    Organization: []string{"I have your data"},
    },
    NotBefore: time.Now(),
    NotAfter: time.Now().Add(365 * 24 * time.Hour),
    KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature,
    ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
    SignatureAlgorithm: x509.SHA256WithRSA,
    DNSNames: []string{host},
    BasicConstraintsValid: true,
    IsCA: true,
    }

    certBytes, err := x509.CreateCertificate(
    rand.Reader, cert, cert, &priv.PublicKey, priv,
    )

    p := pem.EncodeToMemory(
    &pem.Block{
    Type: "PRIVATE KEY",
    Bytes: x509.MarshalPKCS1PrivateKey(priv),
    },
    )

    b := pem.EncodeToMemory(
    &pem.Block{
    Type: "CERTIFICATE",
    Bytes: certBytes,
    },
    )

    return b, p, err
    }