go-cert-gen/internal/cert/root_ca.go

146 lines
3.3 KiB
Go
Raw Normal View History

2023-02-15 11:10:23 +01:00
package cert
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"time"
"gitlab.com/urkob/go-cert-gen/pkg/ca"
"gitlab.com/urkob/go-cert-gen/pkg/client"
)
2023-03-03 22:31:10 +01:00
const (
CERTIFICATE = "CERTIFICATE"
PRIVATE_KEY = "PRIVATE KEY"
)
2023-02-15 11:10:23 +01:00
2023-03-03 22:31:10 +01:00
type rootCA struct {
caPEM []byte
keyPEM []byte
2023-02-15 11:10:23 +01:00
}
2023-03-03 22:31:10 +01:00
func (r *rootCA) Key() []byte {
return r.keyPEM
}
func (r *rootCA) PEM() []byte {
return r.caPEM
}
func (r *rootCA) WithClientCert(config *client.ClientCertConfig) (client.ClientCertIface, error) {
x509RootCA, err := parseCertificate(r.caPEM)
2023-02-15 11:10:23 +01:00
if err != nil {
2023-03-03 22:31:10 +01:00
return nil, fmt.Errorf("parseCertificate: %s", err)
2023-02-15 11:10:23 +01:00
}
2023-03-03 22:31:10 +01:00
clientCertPEM, clientKeyPEM, err := newClientCert(config, x509RootCA, r.keyPEM)
2023-02-15 11:10:23 +01:00
if err != nil {
2023-03-03 22:31:10 +01:00
return nil, fmt.Errorf("newClientCert: %s", err)
2023-02-15 11:10:23 +01:00
}
2023-03-03 22:31:10 +01:00
return &clientCert{
certPEM: clientCertPEM,
keyPEM: clientKeyPEM,
}, nil
2023-02-15 11:10:23 +01:00
}
// Create a self-signed certificate.
func newRootCA(config *ca.CaConfig) ([]byte, []byte, error) {
2023-02-15 11:10:23 +01:00
priv, err := newPrivateKey()
if err != nil {
return nil, nil, fmt.Errorf("newPrivateKey: %s", err)
}
template := x509.Certificate{
SerialNumber: config.SerialNumber,
Subject: pkix.Name{
Organization: []string{config.Subject.Organization},
CommonName: config.Subject.CommonName,
},
NotBefore: time.Now().Add(-time.Minute),
NotAfter: time.Now().Add(config.Duration),
IsCA: true,
KeyUsage: config.KeyUsage,
ExtKeyUsage: config.ExtKeyUsage,
BasicConstraintsValid: true,
}
der, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
return nil, nil, fmt.Errorf("x509.CreateCertificate: %s", err)
}
out := &bytes.Buffer{}
2023-03-03 22:31:10 +01:00
err = pem.Encode(out, &pem.Block{Type: CERTIFICATE, Bytes: der})
2023-02-15 19:47:52 +01:00
if err != nil {
return nil, nil, fmt.Errorf("pem.Encode: %s", err)
}
2023-02-15 11:10:23 +01:00
caPEM := out.Bytes()
keyPEM, err := encodePrivateKey(priv)
if err != nil {
return nil, nil, fmt.Errorf("encodePrivateKey: %s", err)
}
return caPEM, keyPEM, nil
}
2023-03-03 22:31:10 +01:00
func NewRootCA(config *ca.CaConfig) (ca.RootCACertificateIface, error) {
caPEM, keyPEM, err := newRootCA(config)
2023-02-15 11:10:23 +01:00
if err != nil {
2023-03-03 22:31:10 +01:00
return nil, fmt.Errorf("newRootCA: %s", err)
2023-02-15 11:10:23 +01:00
}
2023-03-03 22:31:10 +01:00
return &rootCA{
caPEM: caPEM,
keyPEM: keyPEM,
}, nil
}
2023-02-15 19:47:52 +01:00
2023-03-03 22:31:10 +01:00
// Creates a new 512bit private key.
func newPrivateKey() (*ecdsa.PrivateKey, error) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
2023-02-15 11:10:23 +01:00
if err != nil {
2023-03-03 22:31:10 +01:00
return nil, fmt.Errorf("ecdsa: %s", err)
2023-02-15 11:10:23 +01:00
}
2023-03-03 22:31:10 +01:00
return priv, nil
2023-02-15 11:10:23 +01:00
}
2023-03-03 22:31:10 +01:00
// Parse a text PEM file into a x509 cert.
func parseCertificate(data []byte) (*x509.Certificate, error) {
block, _ := pem.Decode(data)
if block == nil {
return nil, fmt.Errorf("parseCertificate (pem.Decode)")
2023-02-15 11:10:23 +01:00
}
2023-03-03 22:31:10 +01:00
cert, err := x509.ParseCertificate(block.Bytes)
2023-02-15 11:10:23 +01:00
if err != nil {
2023-03-03 22:31:10 +01:00
return nil, fmt.Errorf("parseCertificate: %w", err)
2023-02-15 11:10:23 +01:00
}
2023-03-03 22:31:10 +01:00
return cert, nil
2023-02-15 11:10:23 +01:00
}
2023-03-03 22:31:10 +01:00
// Encodes a private key into PEM.
func encodePrivateKey(priv *ecdsa.PrivateKey) ([]byte, error) {
out := &bytes.Buffer{}
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
return nil, fmt.Errorf("x509.MarshalPKCS8PrivateKey: %s", err)
}
2023-02-15 11:10:23 +01:00
2023-03-03 22:31:10 +01:00
err = pem.Encode(out, &pem.Block{
Type: PRIVATE_KEY,
Bytes: privBytes,
})
2023-02-15 11:10:23 +01:00
if err != nil {
2023-03-03 22:31:10 +01:00
return nil, err
2023-02-15 11:10:23 +01:00
}
2023-03-03 22:31:10 +01:00
return out.Bytes(), nil
2023-02-15 11:10:23 +01:00
}