// Package certtest builds throwaway CAs and leaf certificates for certificate posture tests. package certtest import ( "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "math/big" "testing" "time" "github.com/stretchr/testify/require" ) type CA struct { Cert *x509.Certificate Key crypto.Signer PEM string } func NewCA(t *testing.T, name string) *CA { t.Helper() return newCA(t, name, nil) } // NewIntermediate creates a CA signed by parent. func NewIntermediate(t *testing.T, parent *CA, name string) *CA { t.Helper() return newCA(t, name, parent) } func newCA(t *testing.T, name string, parent *CA) *CA { t.Helper() key := ECDSAKey(t) tmpl := &x509.Certificate{ SerialNumber: big.NewInt(time.Now().UnixNano()), Subject: pkix.Name{CommonName: name}, NotBefore: time.Now().Add(-24 * time.Hour), NotAfter: time.Now().Add(48 * time.Hour), IsCA: true, BasicConstraintsValid: true, KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature, } issuer, issuerKey := tmpl, key if parent != nil { issuer, issuerKey = parent.Cert, parent.Key } der, err := x509.CreateCertificate(rand.Reader, tmpl, issuer, key.Public(), issuerKey) require.NoError(t, err) cert, err := x509.ParseCertificate(der) require.NoError(t, err) return &CA{Cert: cert, Key: key, PEM: CertPEM(cert)} } // Issue signs a leaf certificate for key with the CA. func (ca *CA) Issue(t *testing.T, key crypto.Signer, cn string) *x509.Certificate { t.Helper() tmpl := &x509.Certificate{ SerialNumber: big.NewInt(time.Now().UnixNano()), Subject: pkix.Name{CommonName: cn}, NotBefore: time.Now().Add(-24 * time.Hour), NotAfter: time.Now().Add(48 * time.Hour), KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, } der, err := x509.CreateCertificate(rand.Reader, tmpl, ca.Cert, key.Public(), ca.Key) require.NoError(t, err) cert, err := x509.ParseCertificate(der) require.NoError(t, err) return cert } func ECDSAKey(t *testing.T) crypto.Signer { t.Helper() key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) require.NoError(t, err) return key } func RSAKey(t *testing.T) crypto.Signer { t.Helper() key, err := rsa.GenerateKey(rand.Reader, 2048) require.NoError(t, err) return key } func Ed25519Key(t *testing.T) crypto.Signer { t.Helper() _, key, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) return key } func CertPEM(cert *x509.Certificate) string { return string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})) } func KeyPEM(t *testing.T, key crypto.Signer) string { t.Helper() der, err := x509.MarshalPKCS8PrivateKey(key) require.NoError(t, err) return string(pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: der})) }