Skip to content

Commit

Permalink
feat: adds optional intermediate flag(s).
Browse files Browse the repository at this point in the history
Signed-off-by: ianhundere <[email protected]>
  • Loading branch information
ianhundere committed Nov 30, 2024
1 parent b5390f4 commit a4e31c2
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 124 deletions.
34 changes: 21 additions & 13 deletions cmd/certificate_maker/certificate_maker.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,22 @@ var (
RunE: runCreate,
}

kmsType string
kmsRegion string
kmsKeyID string
kmsVaultName string
kmsTenantID string
kmsCredsFile string
rootTemplatePath string
leafTemplatePath string
rootKeyID string
leafKeyID string
rootCertPath string
leafCertPath string
kmsType string
kmsRegion string
kmsKeyID string
kmsVaultName string
kmsTenantID string
kmsCredsFile string
rootTemplatePath string
leafTemplatePath string
rootKeyID string
leafKeyID string
rootCertPath string
leafCertPath string
withIntermediate bool
intermediateKeyID string
intermediateTemplate string
intermediateCert string

rawJSON = []byte(`{
"level": "debug",
Expand Down Expand Up @@ -96,6 +100,10 @@ func init() {
createCmd.Flags().StringVar(&leafKeyID, "leaf-key-id", "", "KMS key identifier for leaf certificate")
createCmd.Flags().StringVar(&rootCertPath, "root-cert", "root.pem", "Output path for root certificate")
createCmd.Flags().StringVar(&leafCertPath, "leaf-cert", "leaf.pem", "Output path for leaf certificate")
createCmd.Flags().BoolVar(&withIntermediate, "with-intermediate", false, "Create certificate chain with intermediate CA")
createCmd.Flags().StringVar(&intermediateKeyID, "intermediate-key-id", "", "KMS key identifier for intermediate certificate")
createCmd.Flags().StringVar(&intermediateTemplate, "intermediate-template", "pkg/certmaker/templates/intermediate-template.json", "Path to intermediate certificate template")
createCmd.Flags().StringVar(&intermediateCert, "intermediate-cert", "intermediate.pem", "Output path for intermediate certificate")
}

func runCreate(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -139,7 +147,7 @@ func runCreate(cmd *cobra.Command, args []string) error {
return fmt.Errorf("leaf template error: %w", err)
}

return certmaker.CreateCertificates(km, config, rootTemplatePath, leafTemplatePath, rootCertPath, leafCertPath)
return certmaker.CreateCertificates(km, config, rootTemplatePath, leafTemplatePath, rootCertPath, leafCertPath, withIntermediate, intermediateKeyID, intermediateTemplate, intermediateCert)
}

func main() {
Expand Down
107 changes: 66 additions & 41 deletions pkg/certmaker/certmaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package certmaker

import (
"context"
"crypto"
"crypto/x509"
"encoding/pem"
"fmt"
Expand All @@ -34,11 +35,12 @@ import (

// KMSConfig holds config for KMS providers.
type KMSConfig struct {
Type string // KMS provider type: "awskms", "cloudkms", "azurekms"
Region string // AWS region or Cloud location
RootKeyID string // Root CA key identifier
LeafKeyID string // Leaf CA key identifier
Options map[string]string // Provider-specific options
Type string
Region string
RootKeyID string
IntermediateKeyID string
LeafKeyID string
Options map[string]string
}

// InitKMS initializes KMS provider based on the given config, KMSConfig.
Expand Down Expand Up @@ -83,69 +85,87 @@ func InitKMS(ctx context.Context, config KMSConfig) (apiv1.KeyManager, error) {
// CreateCertificates generates a certificate chain using the configured KMS provider.
// It creates both root and intermediate certificates using the provided templates
// and KMS signing keys.
func CreateCertificates(km apiv1.KeyManager, config KMSConfig, rootTemplatePath, intermediateTemplatePath, rootCertPath, intermCertPath string) error {
// Parse root template
func CreateCertificates(km apiv1.KeyManager, config KMSConfig,
rootTemplatePath, leafTemplatePath string,
rootCertPath, leafCertPath string,
withIntermediate bool,
intermediateKeyID, intermediateTemplatePath, intermediateCertPath string) error {

// Create root cert
rootTmpl, err := ParseTemplate(rootTemplatePath, nil)
if err != nil {
return fmt.Errorf("error parsing root template: %w", err)
}
rootKeyName := config.RootKeyID
if config.Type == "azurekms" {
rootKeyName = fmt.Sprintf("azurekms:vault=%s;name=%s",
config.Options["vault-name"], config.RootKeyID)
}

rootSigner, err := km.CreateSigner(&apiv1.CreateSignerRequest{
SigningKey: rootKeyName,
SigningKey: config.RootKeyID,
})
if err != nil {
return fmt.Errorf("error creating root signer: %w", err)
}

// Create root cert
rootCert, err := x509util.CreateCertificate(rootTmpl, rootTmpl, rootSigner.Public(), rootSigner)
if err != nil {
return fmt.Errorf("error creating root certificate: %w", err)
}

if err := WriteCertificateToFile(rootCert, rootCertPath); err != nil {
return fmt.Errorf("error writing root certificate: %w", err)
}

// Parse / sign intermediate template
intermediateTmpl, err := ParseTemplate(intermediateTemplatePath, rootCert)
if err != nil {
return fmt.Errorf("error parsing intermediate template: %w", err)
var signingCert *x509.Certificate
var signingKey crypto.Signer

if withIntermediate {
// Only create intermediate if flag is set
intermediateTmpl, err := ParseTemplate(intermediateTemplatePath, rootCert)
if err != nil {
return fmt.Errorf("error parsing intermediate template: %w", err)
}

intermediateSigner, err := km.CreateSigner(&apiv1.CreateSignerRequest{
SigningKey: intermediateKeyID,
})
if err != nil {
return fmt.Errorf("error creating intermediate signer: %w", err)
}

intermediateCert, err := x509util.CreateCertificate(intermediateTmpl, rootCert, intermediateSigner.Public(), rootSigner)
if err != nil {
return fmt.Errorf("error creating intermediate certificate: %w", err)
}

if err := WriteCertificateToFile(intermediateCert, intermediateCertPath); err != nil {
return fmt.Errorf("error writing intermediate certificate: %w", err)
}

signingCert = intermediateCert
signingKey = intermediateSigner
} else {
signingCert = rootCert
signingKey = rootSigner
}
intermediateKeyName := config.LeafKeyID
if config.Type == "azurekms" {
intermediateKeyName = fmt.Sprintf("azurekms:vault=%s;name=%s",
config.Options["vault-name"], config.LeafKeyID)

// Create leaf cert
leafTmpl, err := ParseTemplate(leafTemplatePath, signingCert)
if err != nil {
return fmt.Errorf("error parsing leaf template: %w", err)
}
intermediateSigner, err := km.CreateSigner(&apiv1.CreateSignerRequest{
SigningKey: intermediateKeyName,

leafSigner, err := km.CreateSigner(&apiv1.CreateSignerRequest{
SigningKey: config.LeafKeyID,
})
if err != nil {
return fmt.Errorf("error creating intermediate signer: %w", err)
return fmt.Errorf("error creating leaf signer: %w", err)
}

// Create intermediate/leaf cert
intermediateCert, err := x509util.CreateCertificate(intermediateTmpl, rootCert, intermediateSigner.Public(), rootSigner)
leafCert, err := x509util.CreateCertificate(leafTmpl, signingCert, leafSigner.Public(), signingKey)
if err != nil {
return fmt.Errorf("error creating intermediate certificate: %w", err)
}
if err := WriteCertificateToFile(intermediateCert, intermCertPath); err != nil {
return fmt.Errorf("error writing intermediate certificate: %w", err)
return fmt.Errorf("error creating leaf certificate: %w", err)
}

// Verify certificate chain
pool := x509.NewCertPool()
pool.AddCert(rootCert)
opts := x509.VerifyOptions{
Roots: pool,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping},
}
if _, err := intermediateCert.Verify(opts); err != nil {
return fmt.Errorf("certificate chain verification failed: %w", err)
if err := WriteCertificateToFile(leafCert, leafCertPath); err != nil {
return fmt.Errorf("error writing leaf certificate: %w", err)
}

return nil
Expand All @@ -163,14 +183,19 @@ func WriteCertificateToFile(cert *x509.Certificate, filename string) error {
return fmt.Errorf("failed to create file %s: %w", filename, err)
}
defer file.Close()

if err := pem.Encode(file, certPEM); err != nil {
return fmt.Errorf("failed to write certificate to file %s: %w", filename, err)
}

// Determine cert type
certType := "root"
if cert.Subject.OrganizationalUnit != nil && cert.Subject.OrganizationalUnit[0] == "TSA Intermediate CA" {
if !cert.IsCA {
certType = "leaf"
} else if cert.MaxPathLen == 0 {
certType = "intermediate"
}

fmt.Printf("Your %s certificate has been saved in %s.\n", certType, filename)
return nil
}
Expand Down
Loading

0 comments on commit a4e31c2

Please sign in to comment.