tlscert

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 12, 2024 License: MIT Imports: 13 Imported by: 0

README

tlscert

This is a simple tool to generate self-signed certificates for testing purposes.

Motivation

This package is intended to be used in tests that require a self-signed certificate. It is not intended to be used in production code.

I many times found myself needing to generate a self-signed certificate for testing purposes, and I always had to look up how to do it. This package is an attempt to make this process easier, providing a simple API to generate self-signed certificates and save them to disk, if needed.

Features

The package exposes two functions and two types: SelfSigned and SelfSignedFromRequest, and Request and Certificate.

  • The Request type is used to specify the parameters for the certificate generation.
  • The Certificate type is used to store the generated certificate and key, including the paths to the files on disk.
  • The SelfSigned function generates a self-signed certificate and returns it as a Certificate value. This function only receives the host name for the certificate.
  • The SelfSignedFromRequest function generates a self-signed certificate based on the parameters in a Request value.

Therefore, it's possible to issue a self-signed certificate with a custom host name, and save it to disk, if needed, or to issue a certificate based on a parent certificate, which is useful for generating client certificates.

The Request struct also provides a ParentDir option that can be used to save the generated certificate to disk as a PEM file.

The Certificate struct provides a Transport method, which returns a pointer to a http.Transport that can be used to perform HTTP requests using the generated certificate; and a TLSConfig method, which returns a pointer to a tls.Config. The Transport method internally uses the TLSConfig method.

Example

You can find a simple example in the example_test.go file:

package tlscert_test

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"

	"github.com/mdelapenya/tlscert"
)

func ExampleSelfSigned() {
	tmp := os.TempDir()
	certsDir := tmp + "/certs"
	defer os.RemoveAll(certsDir)

	if err := os.MkdirAll(certsDir, 0o755); err != nil {
		log.Fatal(err) // nolint: gocritic
	}

	// Generate a certificate for localhost and save it to disk.
	caCert := tlscert.SelfSignedFromRequest(tlscert.Request{
		Host:      "localhost",
		Name:      "ca-cert",
		ParentDir: certsDir,
	})
	if caCert == nil {
		log.Fatal("Failed to generate CA certificate")
	}

	cert := tlscert.SelfSignedFromRequest(tlscert.Request{
		Host:      "localhost",
		Name:      "client-cert",
		Parent:    caCert,
		ParentDir: certsDir,
	})
	if cert == nil {
		log.Fatal("Failed to generate certificate")
	}

	// create an http server that uses the generated certificate
	// and private key to serve requests over HTTPS

	server := &http.Server{
		Addr: ":8443",
	}

	server.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Header().Set("Content-Type", "text/plain")
		_, err := w.Write([]byte("TLS works!\n"))
		if err != nil {
			log.Printf("Failed to write response: %v", err)
		}
	})

	go func() {
		_ = server.ListenAndServeTLS(cert.CertPath, cert.KeyPath)
	}()
	defer server.Close()

	// perform an HTTP request to the server, using the generated certificate

	const url = "https://localhost:8443/hello"

	client := &http.Client{Transport: cert.Transport()}
	resp, err := client.Get(url)
	if err != nil {
		log.Fatalf("Failed to get response: %v", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("Failed to read response body: %v", err)
	}

	fmt.Println(string(body))

	// Output:
	// TLS works!
}

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Certificate

type Certificate struct {
	Cert     *x509.Certificate
	Bytes    []byte
	Key      *rsa.PrivateKey
	KeyBytes []byte
	CertPath string
	KeyPath  string
	// contains filtered or unexported fields
}

Certificate represents a certificate and private key pair. It's a wrapper around the x509.Certificate and rsa.PrivateKey types, and includes the raw bytes of the certificate and private key.

func SelfSigned

func SelfSigned(host string) *Certificate

SelfSigned Generate a self-signed X.509 certificate for a TLS server. Returns a struct containing the certificate and private key, as well as the raw bytes for both of them. The raw bytes will be PEM-encoded. Considerations for the generated certificate are as follows:

  • will be valid for the duration set in the ValidFor option, starting from 1 minute ago. Else, it will be valid for 1 year.
  • will be signed by the parent certificate if the WithParent option is set. Else, it will be self-signed.
  • will be saved to the directory set in the WithSaveToFile option. Else, it will not be saved to disk.
  • will be its own Certificate Authority if the AsCA option is set. Else, it will not be a CA.
Example
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"

	"github.com/mdelapenya/tlscert"
)

func main() {
	tmp := os.TempDir()
	certsDir := tmp + "/certs"
	defer os.RemoveAll(certsDir)

	if err := os.MkdirAll(certsDir, 0o755); err != nil {
		log.Fatal(err) // nolint: gocritic
	}

	// Generate a certificate for localhost and save it to disk.
	caCert := tlscert.SelfSignedFromRequest(tlscert.Request{
		Host:      "localhost",
		Name:      "ca-cert",
		ParentDir: certsDir,
	})
	if caCert == nil {
		log.Fatal("Failed to generate CA certificate")
	}

	cert := tlscert.SelfSignedFromRequest(tlscert.Request{
		Host:      "localhost",
		Name:      "client-cert",
		Parent:    caCert,
		ParentDir: certsDir,
	})
	if cert == nil {
		log.Fatal("Failed to generate certificate")
	}

	// create an http server that uses the generated certificate
	// and private key to serve requests over HTTPS

	server := &http.Server{
		Addr: ":8443",
	}

	server.Handler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Header().Set("Content-Type", "text/plain")
		_, err := w.Write([]byte("TLS works!\n"))
		if err != nil {
			log.Printf("Failed to write response: %v", err)
		}
	})

	go func() {
		_ = server.ListenAndServeTLS(cert.CertPath, cert.KeyPath)
	}()
	defer server.Close()

	// perform an HTTP request to the server, using the generated certificate

	const url = "https://localhost:8443/hello"

	client := &http.Client{Transport: cert.Transport()}
	resp, err := client.Get(url)
	if err != nil {
		log.Fatalf("Failed to get response: %v", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("Failed to read response body: %v", err)
	}

	fmt.Println(string(body))

}
Output:

TLS works!

func SelfSignedCA

func SelfSignedCA(host string) *Certificate

SelfSignedCA Generate a self-signed X.509 certificate for a Certificate Authority. This function is a wrapper around SelfSigned, with the IsCA option set to true.

func SelfSignedFromRequest

func SelfSignedFromRequest(req Request) *Certificate

SelfSignedFromRequest Generate a self-signed X.509 certificate for a TLS server, using the provided CertRequest.

func (*Certificate) TLSConfig

func (c *Certificate) TLSConfig() *tls.Config

TLSConfig returns a tls.Config that uses the certificate as the root CA, and the certificate for the server's certificate. This method will cache the tls.Config for future calls.

func (*Certificate) Transport

func (c *Certificate) Transport() *http.Transport

Transport returns an http.Transport that uses the certificate as the root CA.

type Request

type Request struct {
	// Name of the certificate. Will be used to save the certificate and private key to disk (e.g. cert-<Name>.pem, key-<Name>.pem)
	Name string

	// CommonName is the subject name of the certificate
	SubjectCommonName string

	// Host sets the hostnames and IPs to generate a certificate for.
	// In the case the passed string contains comma-separated values,
	// it will be split into multiple hostnames and IPs. Each hostname and IP
	// will be trimmed of whitespace, and if the value is an IP, it will be
	// added to the IPAddresses field of the certificate, after the ones
	// passed with the WithIPAddresses option. Otherwise, it will be added
	// to the DNSNames field.
	Host string

	// Duration that certificate is valid for
	ValidFor time.Duration

	// IsCA sets the certificate as a Certificate Authority.
	// When passed, the KeyUsage field of the certificate
	// will append the x509.KeyUsageCertSign usage.
	IsCA bool

	// IPAddresses IP addresses to include in the Subject Alternative Name
	IPAddresses []net.IP

	// Parent the parent certificate and private key of the certificate.
	// It's used to sign the certificate with the parent certificate.
	// At the moment the parent is set, the issuer of the certificate will be
	// set to the common name of the parent certificate.
	Parent *Certificate

	// ParentDir sets the directory to save the certificate and private key.
	ParentDir string
}

Request represents a request to generate a self-signed X.509 certificate.

func NewRequest

func NewRequest(host string) Request

NewRequest returns a new CertRequest with default values to avoid nil pointers. The name of the certificate will be set to the host, replacing all commas with underscores. The certificate will be valid for 1 year.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL