Skip to content

Commit

Permalink
fix: improves things and follows lessons learned troubleshooting tsa …
Browse files Browse the repository at this point in the history
…vers of this tool.
  • Loading branch information
ianhundere committed Nov 26, 2024
1 parent cfdf4ea commit 713e53c
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 120 deletions.
7 changes: 6 additions & 1 deletion cmd/certificate_maker/certificate_maker.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@ import (
"encoding/json"
"fmt"
"os"
"time"

"github.com/sigstore/timestamp-authority/pkg/certmaker"
"github.com/spf13/cobra"
"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 Expand Up @@ -96,6 +99,9 @@ func init() {
}

func runCreate(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

// Build KMS config from flags and environment
config := certmaker.KMSConfig{
Type: getConfigValue(kmsType, "KMS_TYPE"),
Expand All @@ -120,7 +126,6 @@ func runCreate(cmd *cobra.Command, args []string) error {
}
}

ctx := context.Background()
km, err := certmaker.InitKMS(ctx, config)
if err != nil {
return fmt.Errorf("failed to initialize KMS: %w", err)
Expand Down
42 changes: 20 additions & 22 deletions pkg/certmaker/certmaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package certmaker
import (
"context"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"os"
Expand All @@ -33,6 +32,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,6 +41,8 @@ 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)
Expand Down Expand Up @@ -102,13 +104,17 @@ func CreateCertificates(km apiv1.KeyManager, config KMSConfig, rootTemplatePath,
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)
}

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

// Create intermediate cert
intermediateTmpl, err := ParseTemplate(intermediateTemplatePath, rootCert)
if err != nil {
return fmt.Errorf("error parsing intermediate template: %w", err)
Expand All @@ -127,27 +133,24 @@ func CreateCertificates(km apiv1.KeyManager, config KMSConfig, rootTemplatePath,
return fmt.Errorf("error creating intermediate signer: %w", err)
}

// Create intermediate certificate
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
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.ExtKeyUsageTimeStamping},
}
if _, err := intermediateCert.Verify(opts); err != nil {
return fmt.Errorf("certificate chain verification failed: %w", err)
}

return nil
Expand All @@ -170,6 +173,11 @@ func WriteCertificateToFile(cert *x509.Certificate, filename string) error {
return fmt.Errorf("failed to write certificate to file %s: %w", filename, err)
}

certType := "root"
if cert.Subject.OrganizationalUnit != nil && cert.Subject.OrganizationalUnit[0] == "TSA Intermediate CA" {
certType = "intermediate"
}
fmt.Printf("Your %s certificate has been saved in %s.\n", certType, filename)
return nil
}

Expand Down Expand Up @@ -216,15 +224,5 @@ func ValidateTemplatePath(path string) error {
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)
}

return nil
}
99 changes: 24 additions & 75 deletions pkg/certmaker/certmaker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,112 +124,61 @@ func TestParseTemplate(t *testing.T) {

// TestCreateCertificates tests certificate chain creation
func TestCreateCertificates(t *testing.T) {
t.Run("Fulcio", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "cert-test-fulcio-*")
t.Run("TSA", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "cert-test-tsa-*")
require.NoError(t, err)
t.Cleanup(func() { os.RemoveAll(tmpDir) })

// Root template (same for both)
rootContent := `{
"subject": {
"commonName": "https://blah.com"
"country": ["US"],
"organization": ["Sigstore"],
"organizationalUnit": ["Timestamp Authority Root CA"],
"commonName": "https://tsa.com"
},
"issuer": {
"commonName": "https://blah.com"
},
"keyUsage": [
"certSign",
"crlSign"
],
"extKeyUsage": [
"CodeSigning"
],
"basicConstraints": {
"isCA": true,
"maxPathLen": 0
"commonName": "https://tsa.com"
},
"notBefore": "2024-01-01T00:00:00Z",
"notAfter": "2025-01-01T00:00:00Z"
}`

// Fulcio intermediate template
intermediateContent := `{
"subject": {
"commonName": "https://blah.com"
},
"issuer": {
"commonName": "https://blah.com"
},
"keyUsage": [
"certSign",
"crlSign"
],
"extKeyUsage": [
"CodeSigning"
],
"notAfter": "2034-01-01T00:00:00Z",
"basicConstraints": {
"isCA": true,
"maxPathLen": 0
},
"notBefore": "2024-01-01T00:00:00Z",
"notAfter": "2025-01-01T00:00:00Z"
}`

testCertificateCreation(t, tmpDir, rootContent, intermediateContent)
})

t.Run("TSA", func(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "cert-test-tsa-*")
require.NoError(t, err)
t.Cleanup(func() { os.RemoveAll(tmpDir) })

// Root template (same for both)
rootContent := `{
"subject": {
"commonName": "https://blah.com"
},
"issuer": {
"commonName": "https://blah.com"
"maxPathLen": 1
},
"keyUsage": [
"certSign",
"crlSign"
],
"extKeyUsage": [
"CodeSigning"
],
"basicConstraints": {
"isCA": true,
"maxPathLen": 0
},
"notBefore": "2024-01-01T00:00:00Z",
"notAfter": "2025-01-01T00:00:00Z"
]
}`

// TSA intermediate template
intermediateContent := `{
"subject": {
"commonName": "https://blah.com"
"country": ["US"],
"organization": ["Sigstore"],
"organizationalUnit": ["Timestamp Authority"],
"commonName": "https://tsa.com"
},
"issuer": {
"commonName": "https://blah.com"
"commonName": "https://tsa.com"
},
"keyUsage": [
"certSign",
"crlSign"
],
"notBefore": "2024-01-01T00:00:00Z",
"notAfter": "2034-01-01T00:00:00Z",
"basicConstraints": {
"isCA": false
"isCA": false,
"maxPathLen": 0
},
"keyUsage": [
"digitalSignature"
],
"extensions": [
{
"id": "2.5.29.37",
"critical": true,
"value": "asn1Seq (asn1Enc oid:1.3.6.1.5.5.7.3.8) | toJson"
"value": {{ asn1Seq (asn1Enc "oid:1.3.6.1.5.5.7.3.8") | toJson }}
}
],
"notBefore": "2024-01-01T00:00:00Z",
"notAfter": "2025-01-01T00:00:00Z"
]
}`

testCertificateCreation(t, tmpDir, rootContent, intermediateContent)
Expand Down
Loading

0 comments on commit 713e53c

Please sign in to comment.