feat: cert generation implementation
This commit is contained in:
		
							parent
							
								
									973fe838b6
								
							
						
					
					
						commit
						df92c82b3f
					
				
							
								
								
									
										191
									
								
								internal/cert/cert.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								internal/cert/cert.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,191 @@
 | 
			
		||||
package cert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"crypto/x509/pkix"
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"gitlab.com/urkob/go-cert-gen/pkg/ca"
 | 
			
		||||
 | 
			
		||||
	"gitlab.com/urkob/go-cert-gen/pkg/client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Creates a new 512bit private key.
 | 
			
		||||
func newPrivateKey() (*ecdsa.PrivateKey, error) {
 | 
			
		||||
	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("ecdsa: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return priv, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)")
 | 
			
		||||
	}
 | 
			
		||||
	cert, err := x509.ParseCertificate(block.Bytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("parseCertificate: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return cert, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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("marshal: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	pem.Encode(out, &pem.Block{
 | 
			
		||||
		Type:  "PRIVATE KEY",
 | 
			
		||||
		Bytes: privBytes,
 | 
			
		||||
	})
 | 
			
		||||
	return out.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create a self-signed certificate.
 | 
			
		||||
func newRootCA(config ca.CaConfig) ([]byte, []byte, error) {
 | 
			
		||||
	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{}
 | 
			
		||||
	pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: der})
 | 
			
		||||
	caPEM := out.Bytes()
 | 
			
		||||
	keyPEM, err := encodePrivateKey(priv)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, fmt.Errorf("encodePrivateKey: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return caPEM, keyPEM, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newClientCert(config *client.ClientCertConfig, rootCA *x509.Certificate, rootKeyPEM []byte) ([]byte, []byte, error) {
 | 
			
		||||
	template := &x509.Certificate{
 | 
			
		||||
		SerialNumber: config.Serial,
 | 
			
		||||
		Subject: pkix.Name{
 | 
			
		||||
			Organization:  []string{config.Subject.Organization},
 | 
			
		||||
			Country:       []string{config.Subject.Country},
 | 
			
		||||
			Province:      []string{config.Subject.Province},
 | 
			
		||||
			Locality:      []string{config.Subject.Locality},
 | 
			
		||||
			StreetAddress: []string{config.Subject.StreetAddress},
 | 
			
		||||
			PostalCode:    []string{config.Subject.PostalCode},
 | 
			
		||||
		},
 | 
			
		||||
		NotBefore:    time.Now(),
 | 
			
		||||
		NotAfter:     time.Now().Add(config.Duration),
 | 
			
		||||
		SubjectKeyId: config.SubjectKeyId,
 | 
			
		||||
		ExtKeyUsage:  config.ExtKeyUsage,
 | 
			
		||||
		KeyUsage:     config.KeyUsage,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	block, _ := pem.Decode(rootKeyPEM)
 | 
			
		||||
	caPrivKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("x509.ParsePKCS8PrivateKey: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	priv, err := newPrivateKey()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, fmt.Errorf("newPrivateKey: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	der, err := x509.CreateCertificate(rand.Reader, template, rootCA, &priv.PublicKey, caPrivKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, fmt.Errorf("x509.CreateCertificate: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out := &bytes.Buffer{}
 | 
			
		||||
	pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: der})
 | 
			
		||||
	certPEM := out.Bytes()
 | 
			
		||||
	keyPEM, err := encodePrivateKey(priv)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, fmt.Errorf("encodePrivateKey: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return certPEM, keyPEM, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rootCA struct {
 | 
			
		||||
	caPEM  []byte
 | 
			
		||||
	keyPEM []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type clientCert struct {
 | 
			
		||||
	certPEM []byte
 | 
			
		||||
	keyPEM  []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *clientCert) Key() []byte {
 | 
			
		||||
	return c.keyPEM
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *clientCert) PEM() []byte {
 | 
			
		||||
	return c.certPEM
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *rootCA) WithClientCert(config *client.ClientCertConfig) (client.ClientCertIface, error) {
 | 
			
		||||
	x509RootCA, err := parseCertificate(r.caPEM)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("parseCertificate: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clientCertPEM, clientKeyPEM, err := newClientCert(config, x509RootCA, r.keyPEM)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("newClientCert: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &clientCert{
 | 
			
		||||
		certPEM: clientCertPEM,
 | 
			
		||||
		keyPEM:  clientKeyPEM,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *rootCA) Key() []byte {
 | 
			
		||||
	return r.keyPEM
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *rootCA) PEM() []byte {
 | 
			
		||||
	return r.caPEM
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewRootCA(config ca.CaConfig) (ca.RootCACertificateIface, error) {
 | 
			
		||||
	caPEM, keyPEM, err := newRootCA(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("newRootCA: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &rootCA{
 | 
			
		||||
		caPEM:  caPEM,
 | 
			
		||||
		keyPEM: keyPEM,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user