Skip to content

Commit

Permalink
fix: improves validation and corrects code signing extension.
Browse files Browse the repository at this point in the history
Signed-off-by: ianhundere <[email protected]>
  • Loading branch information
ianhundere committed Nov 26, 2024
1 parent 7d98f91 commit 9d68b02
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 30 deletions.
2 changes: 2 additions & 0 deletions cmd/certificate_maker/certificate_maker.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"go.uber.org/zap"
)

// CLI flags and env vars for config.
// Supports AWS KMS, Google Cloud KMS, and Azure Key Vault configurations.
var (
logger *zap.Logger
version string
Expand Down
44 changes: 20 additions & 24 deletions pkg/certmaker/certmaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"go.step.sm/crypto/x509util"
)

// KMSConfig holds config for KMS providers.
type KMSConfig struct {
Type string // KMS provider type: "awskms", "cloudkms", "azurekms"
Region string // AWS region or Cloud location
Expand All @@ -41,17 +42,18 @@ type KMSConfig struct {
Options map[string]string // Provider-specific options
}

// InitKMS initializes KMS provider based on the given config, KMSConfig.
// Supports AWS KMS, Google Cloud KMS, and Azure Key Vault.
func InitKMS(ctx context.Context, config KMSConfig) (apiv1.KeyManager, error) {
if err := ValidateKMSConfig(config); err != nil {
return nil, fmt.Errorf("invalid KMS configuration: %w", err)
}

opts := apiv1.Options{
Type: apiv1.Type(config.Type),
URI: "",
}

// Use RootKeyID as the primary key ID, fall back to IntermediateKeyID if root is not set
// Falls back to IntermediateKeyID if root is not set
keyID := config.RootKeyID
if keyID == "" {
keyID = config.IntermediateKeyID
Expand Down Expand Up @@ -83,71 +85,67 @@ func InitKMS(ctx context.Context, config KMSConfig) (apiv1.KeyManager, error) {
// 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 templates
// Parse root template
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,
})
if err != nil {
return fmt.Errorf("error creating root signer: %w", err)
}

// Create root certificate
// 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 intermediate template
// Parse / sign intermediate template
intermediateTmpl, err := ParseTemplate(intermediateTemplatePath, rootCert)
if err != nil {
return fmt.Errorf("error parsing intermediate template: %w", err)
}

intermediateKeyName := config.IntermediateKeyID
if config.Type == "azurekms" {
intermediateKeyName = fmt.Sprintf("azurekms:vault=%s;name=%s",
config.Options["vault-name"], config.IntermediateKeyID)
}

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

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

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

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

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

return nil
Expand All @@ -165,11 +163,12 @@ 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)
}

certType := "root"
fmt.Printf("Your %s certificate has been saved in %s.\n", certType, filename)
return nil
}

Expand Down Expand Up @@ -211,16 +210,13 @@ func ValidateTemplatePath(path string) error {
if _, err := os.Stat(path); err != nil {
return fmt.Errorf("template not found at %s: %w", path, err)
}

if !strings.HasSuffix(path, ".json") {
return fmt.Errorf("template file must have .json extension: %s", path)
}

content, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("error reading template file: %w", err)
}

var js json.RawMessage
if err := json.Unmarshal(content, &js); err != nil {
return fmt.Errorf("invalid JSON in template file: %w", err)
Expand Down
4 changes: 2 additions & 2 deletions pkg/certmaker/certmaker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func TestCreateCertificates(t *testing.T) {
require.NoError(t, err)
t.Cleanup(func() { os.RemoveAll(tmpDir) })

// Root template (same for both)
// root template (same for both)
rootContent := `{
"subject": {
"commonName": "https://blah.com"
Expand All @@ -154,7 +154,7 @@ func TestCreateCertificates(t *testing.T) {
"notAfter": "2025-01-01T00:00:00Z"
}`

// Fulcio intermediate template
// intermediate template
intermediateContent := `{
"subject": {
"commonName": "https://blah.com"
Expand Down
6 changes: 3 additions & 3 deletions pkg/certmaker/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ type CertificateTemplate struct {
NotBefore string `json:"notBefore"`
NotAfter string `json:"notAfter"`
KeyUsage []string `json:"keyUsage"`
ExtKeyUsage []string `json:"extKeyUsage,omitempty"`
BasicConstraints struct {
IsCA bool `json:"isCA"`
MaxPathLen int `json:"maxPathLen"`
Expand All @@ -39,6 +38,7 @@ type CertificateTemplate struct {
Critical bool `json:"critical"`
Value string `json:"value"`
} `json:"extensions,omitempty"`
ExtKeyUsage []string `json:"extKeyUsage,omitempty"`
}

// ParseTemplate creates an x509 certificate from JSON template
Expand Down Expand Up @@ -89,7 +89,7 @@ func ValidateTemplate(tmpl *CertificateTemplate, parent *x509.Certificate) error
// Fulcio-specific validation for code signing
hasCodeSigning := false
for _, usage := range tmpl.ExtKeyUsage {
if usage == "codeSign" {
if usage == "CodeSigning" {
hasCodeSigning = true
break
}
Expand Down Expand Up @@ -159,7 +159,7 @@ func SetKeyUsages(cert *x509.Certificate, usages []string) {
func SetExtKeyUsages(cert *x509.Certificate, usages []string) {
for _, usage := range usages {
switch usage {
case "codeSign":
case "CodeSigning":
cert.ExtKeyUsage = append(cert.ExtKeyUsage, x509.ExtKeyUsageCodeSigning)
}
}
Expand Down
1 change: 0 additions & 1 deletion pkg/certmaker/templates/intermediate-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
},
"notBefore": "2024-01-01T00:00:00Z",
"notAfter": "2034-01-01T00:00:00Z",
"serialNumber": 2,
"basicConstraints": {
"isCA": true,
"maxPathLen": 0
Expand Down

0 comments on commit 9d68b02

Please sign in to comment.