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 3bc2887 commit 2a3e1a2
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 94 deletions.
36 changes: 22 additions & 14 deletions cmd/certificate_maker/certificate_maker.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,29 @@ 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",
"encoding": "json",
"outputPaths": ["stdout"],
"errorOutputPaths": ["stderr"],
"initialFields": {"service": "sigstore-certificate-maker"},
"initialFields": {"service": "fulcio-certificate-maker"},
"encoderConfig": {
"messageKey": "message",
"levelKey": "level",
Expand Down Expand Up @@ -93,6 +97,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 @@ -134,7 +142,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
103 changes: 66 additions & 37 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/json"
"encoding/pem"
Expand All @@ -35,11 +36,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 @@ -81,54 +83,83 @@ 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 leaf certificates using the provided templates
// and KMS signing keys.
func CreateCertificates(km apiv1.KeyManager, config KMSConfig, rootTemplatePath, leafTemplatePath, rootCertPath, leafCertPath string) error {
// Parse root template
// CreateCertificates creates a certificate chain using the provided KMS and templates.
// It can create either a two-level (root -> leaf) or three-level (root -> intermediate -> leaf) chain.
func CreateCertificates(km apiv1.KeyManager, config KMSConfig,
rootTemplatePath, leafTemplatePath string,
rootCertPath, leafCertPath string,
withIntermediate bool,
intermediateKeyID, intermediateTemplate, 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)
}

var signingCert *x509.Certificate
var signingKey crypto.Signer

if withIntermediate {
// Only create intermediate if flag is set
intermediateTmpl, err := ParseTemplate(intermediateTemplate, 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
}

// Create leaf cert
leafTmpl, err := ParseTemplate(leafTemplatePath, rootCert)
leafTmpl, err := ParseTemplate(leafTemplatePath, signingCert)
if err != nil {
return fmt.Errorf("error parsing leaf template: %w", err)
}
leafKeyName := config.LeafKeyID
if config.Type == "azurekms" {
leafKeyName = fmt.Sprintf("azurekms:vault=%s;name=%s",
config.Options["vault-name"], config.LeafKeyID)
}

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

leafCert, err := x509util.CreateCertificate(leafTmpl, rootCert, leafSigner.Public(), rootSigner)
leafCert, err := x509util.CreateCertificate(leafTmpl, signingCert, leafSigner.Public(), signingKey)
if err != nil {
return fmt.Errorf("error creating leaf certificate: %w", err)
}
Expand All @@ -137,17 +168,6 @@ func CreateCertificates(km apiv1.KeyManager, config KMSConfig, rootTemplatePath,
return fmt.Errorf("error writing leaf certificate: %w", err)
}

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

return nil
}

Expand All @@ -163,11 +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.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 Expand Up @@ -205,7 +233,8 @@ func ValidateKMSConfig(config KMSConfig) error {
return nil
}

// ValidateTemplatePath validates that a template file exists and contains valid JSON
// ValidateTemplatePath checks if the template file exists, has a .json extension,
// and contains valid JSON content.
func ValidateTemplatePath(path string) error {
if _, err := os.Stat(path); err != nil {
return fmt.Errorf("template not found at %s: %w", path, err)
Expand Down
Loading

0 comments on commit 2a3e1a2

Please sign in to comment.