Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods to generate and configure TLS #9

Merged
merged 6 commits into from
May 18, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
tests:
go test --race -v -covermode=atomic -coverprofile=coverage.out ./...

benchmarks:
go test -bench=. -benchmem ./...
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ func TestFunc(t *testing.T) {
// Create a client with the self-signed CA
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: ca.CertPool(),
},
TLSClientConfig: certs.ConfigureTLSConfig(ca.GenerateTLSConfig()),
},
}

Expand Down
24 changes: 21 additions & 3 deletions testcerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ For more complex tests, you can also use this package to create a Certificate Au
// Create a client with the self-signed CA
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: ca.CertPool(),
},
TLSClientConfig: certs.ConfigureTLSConfig(ca.GenerateTLSConfig()),
},
}

Expand All @@ -66,6 +64,7 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
Expand Down Expand Up @@ -232,6 +231,13 @@ func (ca *CertificateAuthority) ToTempFile(dir string) (cfh *os.File, kfh *os.Fi
return cfh, kfh, nil
}

// GenerateTLSConfig returns a tls.Config with the CertificateAuthority as the RootCA.
func (ca *CertificateAuthority) GenerateTLSConfig() *tls.Config {
return &tls.Config{
RootCAs: ca.CertPool(),
}
madflojo marked this conversation as resolved.
Show resolved Hide resolved
}

// PrivateKey returns the private key of the KeyPair.
func (kp *KeyPair) PrivateKey() []byte {
return pem.EncodeToMemory(kp.privateKey)
Expand Down Expand Up @@ -296,6 +302,18 @@ func (kp *KeyPair) ToTempFile(dir string) (cfh *os.File, kfh *os.File, err error
return cfh, kfh, nil
}

// ConfigureTLSConfig will configure the tls.Config with the KeyPair certificate and private key.
// The returned tls.Config can be used for a server or client.
func (kp *KeyPair) ConfigureTLSConfig(tlsConfig *tls.Config) *tls.Config {
tlsConfig.Certificates = []tls.Certificate{
{
Certificate: [][]byte{kp.PublicKey()},
PrivateKey: kp.PrivateKey(),
},
}
return tlsConfig
}

// GenerateCerts generates a x509 certificate and key.
// It returns the certificate and key as byte slices, and any error that occurred.
//
Expand Down
67 changes: 59 additions & 8 deletions testcerts_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package testcerts

import (
"crypto/tls"
"crypto/x509"
"fmt"
"net/http"
Expand Down Expand Up @@ -341,7 +340,7 @@
// testUsingCerts is called by the two tests below. Both test setting up certificates and subsequently
// configuring the transport of the http.Client to use the generated certificate.
// One uses the own public key as part of the pool (self signed cert) and one uses the cert from the CA.
func testUsingCerts(t *testing.T, rootCAs func(ca *CertificateAuthority, certs *KeyPair) *x509.CertPool) {

Check warning on line 343 in testcerts_test.go

View workflow job for this annotation

GitHub Actions / golangci

unused-parameter: parameter 'rootCAs' seems to be unused, consider removing or renaming it as _ (revive)
// Create a signed Certificate and Key for "localhost"
ca := NewCA()
certs, err := ca.NewKeyPair("localhost")
Expand Down Expand Up @@ -373,15 +372,9 @@
<-time.After(3 * time.Second)

// Setup HTTP Client with Cert Pool
certpool := rootCAs(ca, certs)
if certpool == nil {
t.Fatalf("Test configuration error: rootCAs arg function returned nil instead of a x509.CertPool")
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: certpool,
},
TLSClientConfig: certs.ConfigureTLSConfig(ca.GenerateTLSConfig()),
},
}

Expand All @@ -405,3 +398,61 @@
return ca.certPool
})
}

func ExampleNewCA() {
// Generate a new Certificate Authority
ca := NewCA()

// Create a new KeyPair with a list of domains
certs, err := ca.NewKeyPair("localhost")
if err != nil {
fmt.Printf("Error generating keypair - %s", err)
}

// Write the certificates to a file
cert, key, err := certs.ToTempFile("")
if err != nil {
fmt.Printf("Error writing certs to temp files - %s", err)
}

// Create an HTTP Server
server := &http.Server{
Addr: "0.0.0.0:8443",
}
defer server.Close()

go func() {
// Start HTTP Listener
err = server.ListenAndServeTLS(cert.Name(), key.Name())
if err != nil && err != http.ErrServerClosed {
fmt.Printf("Listener returned error - %s", err)
}
}()

// Add handler
server.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

Check warning on line 433 in testcerts_test.go

View workflow job for this annotation

GitHub Actions / golangci

unused-parameter: parameter 'r' seems to be unused, consider removing or renaming it as _ (revive)
w.Write([]byte("Hello, World!"))

Check failure on line 434 in testcerts_test.go

View workflow job for this annotation

GitHub Actions / golangci

Error return value of `w.Write` is not checked (errcheck)
madflojo marked this conversation as resolved.
Show resolved Hide resolved
})

// Wait for Listener to start
<-time.After(3 * time.Second)

// Setup HTTP Client with Cert Pool
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: certs.ConfigureTLSConfig(ca.GenerateTLSConfig()),
},
}

// Make an HTTPS request
rsp, err := client.Get("https://localhost:8443")
if err != nil {
fmt.Printf("Client returned error - %s", err)
}

// Print the response
fmt.Println(rsp.Status)

// Output:
// 200 OK
}
Loading