Skip to content

Commit

Permalink
fix: HTTP TLS test
Browse files Browse the repository at this point in the history
  • Loading branch information
adityathebe committed Jul 27, 2024
1 parent a95bea5 commit f5cc78c
Showing 1 changed file with 98 additions and 47 deletions.
145 changes: 98 additions & 47 deletions http/http_tls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package http_test

import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
Expand All @@ -12,60 +13,82 @@ import (
"math"
"math/big"
"net/http"
"sync"
"testing"
"time"

chttp "github.com/flanksource/commons/http"
"github.com/flanksource/commons/logger"
"github.com/samber/lo"
)

func TestTLSConfig(t *testing.T) {
// Generate a self-signed certificate
certPemData, cert, err := generateSelfSignedCert()
caX509, caCrt, caPEM, _, err := createCert(nil, nil, "Flanksource")
if err != nil {
t.Fatal(err)
}

_, serverCrt, _, _, err := createCert(caX509, caCrt.PrivateKey, "localhost")
if err != nil {
t.Fatal(err)
}

_, _, clientPEM, clientKeyPem, err := createCert(caX509, caCrt.PrivateKey, "client")
if err != nil {
t.Fatal(err)
}

port := "18080"
server, err := tlsServer(cert, port)
server, err := tlsServer(*serverCrt, port)
if err != nil {
t.Fatal(err)
}
logger.Infof("Listening on port %s", port)

var wg sync.WaitGroup
wg.Add(1)
serverReady := make(chan struct{})
go func() {
close(serverReady)
err := server.ListenAndServeTLS("", "")
logger.Infof("server error: %v", err)
wg.Done()
}()

serverTerminate := make(chan struct{})
go func() {
time.Sleep(time.Second)
<-serverTerminate
_ = server.Shutdown(context.Background())
}()

client, err := chttp.NewClient().TLSConfig(chttp.TLSConfig{CA: string(certPemData)})
if err != nil {
t.Fatal(err)
}

response, err := client.BaseURL(fmt.Sprintf("https://localhost:%s", port)).R(context.Background()).Get("/")
if err != nil {
t.Fatal(err)
<-serverReady
testData := []struct {
name string
tlsConfig chttp.TLSConfig
}{
{"withca", chttp.TLSConfig{CA: string(caPEM)}},
{"with client certs and CA", chttp.TLSConfig{Cert: string(clientPEM), Key: string(clientKeyPem), CA: string(caPEM)}}, // FIXME: Setup an HTTPs server that requires client auth
}

r, err := response.AsString()
if err != nil {
t.Fatal(err)
}
if r != "Hello, World!" {
t.Fatal(r)
for _, td := range testData {
t.Run(td.name, func(t *testing.T) {
client, err := chttp.NewClient().TLSConfig(td.tlsConfig)
if err != nil {
t.Fatal(err)
}

response, err := client.BaseURL(fmt.Sprintf("https://localhost:%s", port)).R(context.Background()).Get("/")
if err != nil {
t.Fatal(err)
}

r, err := response.AsString()
if err != nil {
t.Fatal(err)
}
if r != "Hello, World!" {
t.Fatal(r)
}
})
}

wg.Wait()
serverTerminate <- struct{}{}
}

func tlsServer(cert tls.Certificate, port string) (*http.Server, error) {
Expand All @@ -81,44 +104,72 @@ func tlsServer(cert tls.Certificate, port string) (*http.Server, error) {
return server, nil
}

func generateSelfSignedCert() ([]byte, tls.Certificate, error) {
subject := pkix.Name{
Organization: []string{"Example Company"},
func createCert(parent *x509.Certificate, signerKey any, cn string) (*x509.Certificate, *tls.Certificate, []byte, []byte, error) {
isCa := parent == nil
template := &x509.Certificate{
Subject: pkix.Name{
CommonName: cn,
Organization: []string{"Example Company"},
Country: []string{"US"},
Province: []string{"CA"},
Locality: []string{"San Francisco"},
StreetAddress: []string{"1600 Amphitheatre Pkwy"},
PostalCode: []string{"94043"},
},
DNSNames: []string{cn},
NotBefore: time.Now(),
NotAfter: time.Now().Add(365 * 24 * time.Hour), // Valid for 1 year
IsCA: isCa,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}

privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, tls.Certificate{}, err
if isCa {
template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign
} else {
template.KeyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
template.BasicConstraintsValid = false
}

serialNumber, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
if err != nil {
return nil, tls.Certificate{}, err
return nil, nil, nil, nil, err
}
template.SerialNumber = serialNumber

certTemplate := x509.Certificate{
SerialNumber: serialNumber,
Subject: subject,
NotBefore: time.Now(),
DNSNames: []string{"localhost"},
NotAfter: time.Now().Add(365 * 24 * time.Hour), // Valid for 1 year
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
IsCA: true,
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, nil, nil, nil, err
}

derBytes, err := x509.CreateCertificate(rand.Reader, &certTemplate, &certTemplate, &privateKey.PublicKey, privateKey)
certBytes, err := x509.CreateCertificate(rand.Reader, template, lo.CoalesceOrEmpty(parent, template), &privateKey.PublicKey, lo.CoalesceOrEmpty[any](signerKey, privateKey))
if err != nil {
return nil, tls.Certificate{}, err
return nil, nil, nil, nil, err
}

certPEMData := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
keyPEMData := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)})
pemBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}
pemBytes := pem.EncodeToMemory(pemBlock)

// Create tls.Certificate
keyBytes, err := x509.MarshalECPrivateKey(privateKey)
if err != nil {
return nil, nil, nil, nil, err
}

// Create a new private key PEM
pemBlock = &pem.Block{
Type: "EC PRIVATE KEY",
Bytes: keyBytes,
}
privateKeyBytes := pem.EncodeToMemory(pemBlock)

cert, err := tls.X509KeyPair(certPEMData, keyPEMData)
certificate, err := tls.X509KeyPair(pemBytes, pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}))
if err != nil {
return nil, tls.Certificate{}, err
return nil, nil, nil, nil, err
}

return certPEMData, cert, nil
return template, &certificate, pemBytes, privateKeyBytes, nil
}

0 comments on commit f5cc78c

Please sign in to comment.