diff --git a/cmd/cosign/cli/options/trustedroot.go b/cmd/cosign/cli/options/trustedroot.go index f400c22870d..66497690ae7 100644 --- a/cmd/cosign/cli/options/trustedroot.go +++ b/cmd/cosign/cli/options/trustedroot.go @@ -20,51 +20,43 @@ import ( ) type TrustedRootCreateOptions struct { - CAIntermediates string - CARoots string - CertChain string - CtfeKeyPath string - RekorKeyPath string + CertChain []string + CtfeKeyPath []string + CtfeStartTime []string Out string - TSACertChainPath string + RekorKeyPath []string + RekorStartTime []string + TSACertChainPath []string } var _ Interface = (*TrustedRootCreateOptions)(nil) func (o *TrustedRootCreateOptions) AddFlags(cmd *cobra.Command) { - cmd.Flags().StringVar(&o.CAIntermediates, "ca-intermediates", "", - "path to a file of intermediate CA certificates in PEM format which will be needed "+ - "when building the certificate chains for the signing certificate. "+ - "The flag is optional and must be used together with --ca-roots, conflicts with "+ - "--certificate-chain.") - _ = cmd.Flags().SetAnnotation("ca-intermediates", cobra.BashCompFilenameExt, []string{"cert"}) - - cmd.Flags().StringVar(&o.CARoots, "ca-roots", "", - "path to a bundle file of CA certificates in PEM format which will be needed "+ - "when building the certificate chains for the signing certificate. Conflicts with --certificate-chain.") - _ = cmd.Flags().SetAnnotation("ca-roots", cobra.BashCompFilenameExt, []string{"cert"}) - - cmd.Flags().StringVar(&o.CertChain, "certificate-chain", "", + cmd.Flags().StringArrayVar(&o.CertChain, "certificate-chain", nil, "path to a list of CA certificates in PEM format which will be needed "+ "when building the certificate chain for the signing certificate. "+ "Must start with the parent intermediate CA certificate of the "+ "signing certificate and end with the root certificate. Conflicts with --ca-roots and --ca-intermediates.") _ = cmd.Flags().SetAnnotation("certificate-chain", cobra.BashCompFilenameExt, []string{"cert"}) - cmd.MarkFlagsMutuallyExclusive("ca-roots", "certificate-chain") - cmd.MarkFlagsMutuallyExclusive("ca-intermediates", "certificate-chain") - - cmd.Flags().StringVar(&o.CtfeKeyPath, "ctfe-key", "", + cmd.Flags().StringArrayVar(&o.CtfeKeyPath, "ctfe-key", nil, "path to a PEM-encoded public key used by certificate authority for "+ "certificate transparency log.") - cmd.Flags().StringVar(&o.RekorKeyPath, "rekor-key", "", + cmd.Flags().StringArrayVar(&o.CtfeStartTime, "ctfe-start-time", nil, + "RFC 3339 string describing validity start time for key use by "+ + "certificate transparency log.") + + cmd.Flags().StringVar(&o.Out, "out", "", "path to output trusted root") + + cmd.Flags().StringArrayVar(&o.RekorKeyPath, "rekor-key", nil, "path to a PEM-encoded public key used by transparency log like Rekor.") - cmd.Flags().StringVar(&o.Out, "out", "", - "path to output trusted root") + cmd.Flags().StringArrayVar(&o.RekorStartTime, "rekor-start-time", nil, + "RFC 3339 string describing validity start time for key use by "+ + "transparency log like Rekor.") - cmd.Flags().StringVar(&o.TSACertChainPath, "timestamp-certificate-chain", "", + cmd.Flags().StringArrayVar(&o.TSACertChainPath, "timestamp-certificate-chain", nil, "path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. "+ "Optionally may contain intermediate CA certificates") } diff --git a/cmd/cosign/cli/trustedroot.go b/cmd/cosign/cli/trustedroot.go index 3b3a2b326f1..5ea67fc33e1 100644 --- a/cmd/cosign/cli/trustedroot.go +++ b/cmd/cosign/cli/trustedroot.go @@ -45,12 +45,12 @@ func trustedRootCreate() *cobra.Command { Long: "Create a Sigstore protobuf trusted root by supplying verification material", RunE: func(cmd *cobra.Command, _ []string) error { trCreateCmd := &trustedroot.CreateCmd{ - CAIntermediates: o.CAIntermediates, - CARoots: o.CARoots, CertChain: o.CertChain, CtfeKeyPath: o.CtfeKeyPath, + CtfeStartTime: o.CtfeStartTime, Out: o.Out, RekorKeyPath: o.RekorKeyPath, + RekorStartTime: o.RekorStartTime, TSACertChainPath: o.TSACertChainPath, } diff --git a/cmd/cosign/cli/trustedroot/trustedroot.go b/cmd/cosign/cli/trustedroot/trustedroot.go index 78ef216a9e4..ee2faef41ac 100644 --- a/cmd/cosign/cli/trustedroot/trustedroot.go +++ b/cmd/cosign/cli/trustedroot/trustedroot.go @@ -23,6 +23,7 @@ import ( "encoding/pem" "fmt" "os" + "time" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore/pkg/cryptoutils" @@ -31,13 +32,13 @@ import ( ) type CreateCmd struct { - CAIntermediates string - CARoots string - CertChain string - CtfeKeyPath string + CertChain []string + CtfeKeyPath []string + CtfeStartTime []string Out string - RekorKeyPath string - TSACertChainPath string + RekorKeyPath []string + RekorStartTime []string + TSACertChainPath []string } func (c *CreateCmd) Exec(_ context.Context) error { @@ -46,71 +47,64 @@ func (c *CreateCmd) Exec(_ context.Context) error { var timestampAuthorities []root.CertificateAuthority rekorTransparencyLogs := make(map[string]*root.TransparencyLog) - if c.CertChain != "" { - fulcioAuthority, err := parsePEMFile(c.CertChain) + for i := 0; i < len(c.CertChain); i++ { + fulcioAuthority, err := parsePEMFile(c.CertChain[i]) if err != nil { return err } fulcioCertAuthorities = append(fulcioCertAuthorities, *fulcioAuthority) - } else if c.CARoots != "" { - roots, err := parseCerts(c.CARoots) + } + + for i := 0; i < len(c.CtfeKeyPath); i++ { + ctLogPubKey, id, idBytes, err := getPubKey(c.CtfeKeyPath[i]) if err != nil { return err } - var intermediates []*x509.Certificate - if c.CAIntermediates != "" { - intermediates, err = parseCerts(c.CAIntermediates) + startTime := time.Unix(0, 0) + + if i < len(c.CtfeStartTime) { + startTime, err = time.Parse(time.RFC3339, c.CtfeStartTime[i]) if err != nil { return err } } - // Here we're trying to "flatten" the x509.CertPool cosign was using - // into a trusted root with a clear mapping between roots and - // intermediates. Make a guess that if there are intermediates, there - // is one per root. - - for i, rootCert := range roots { - var fulcioAuthority root.CertificateAuthority - fulcioAuthority.Root = rootCert - if i < len(intermediates) { - fulcioAuthority.Intermediates = []*x509.Certificate{intermediates[i]} - } - fulcioCertAuthorities = append(fulcioCertAuthorities, fulcioAuthority) + ctLogs[id] = &root.TransparencyLog{ + HashFunc: crypto.SHA256, + ID: idBytes, + ValidityPeriodStart: startTime, + PublicKey: *ctLogPubKey, + SignatureHashFunc: crypto.SHA256, } } - if c.CtfeKeyPath != "" { - ctLogPubKey, id, idBytes, err := getPubKey(c.CtfeKeyPath) + for i := 0; i < len(c.RekorKeyPath); i++ { + tlogPubKey, id, idBytes, err := getPubKey(c.RekorKeyPath[i]) if err != nil { return err } - ctLogs[id] = &root.TransparencyLog{ - HashFunc: crypto.SHA256, - ID: idBytes, - PublicKey: *ctLogPubKey, - SignatureHashFunc: crypto.SHA256, - } - } + startTime := time.Unix(0, 0) - if c.RekorKeyPath != "" { - tlogPubKey, id, idBytes, err := getPubKey(c.RekorKeyPath) - if err != nil { - return err + if i < len(c.RekorStartTime) { + startTime, err = time.Parse(time.RFC3339, c.RekorStartTime[i]) + if err != nil { + return err + } } rekorTransparencyLogs[id] = &root.TransparencyLog{ - HashFunc: crypto.SHA256, - ID: idBytes, - PublicKey: *tlogPubKey, - SignatureHashFunc: crypto.SHA256, + HashFunc: crypto.SHA256, + ID: idBytes, + ValidityPeriodStart: startTime, + PublicKey: *tlogPubKey, + SignatureHashFunc: crypto.SHA256, } } - if c.TSACertChainPath != "" { - timestampAuthority, err := parsePEMFile(c.TSACertChainPath) + for i := 0; i < len(c.TSACertChainPath); i++ { + timestampAuthority, err := parsePEMFile(c.TSACertChainPath[i]) if err != nil { return err } @@ -152,6 +146,7 @@ func parsePEMFile(path string) (*root.CertificateAuthority, error) { var ca root.CertificateAuthority ca.Root = certs[len(certs)-1] + ca.ValidityPeriodStart = certs[len(certs)-1].NotBefore if len(certs) > 1 { ca.Intermediates = certs[:len(certs)-1] } diff --git a/cmd/cosign/cli/trustedroot/trustedroot_test.go b/cmd/cosign/cli/trustedroot/trustedroot_test.go index b5e56f9225e..746edff639c 100644 --- a/cmd/cosign/cli/trustedroot/trustedroot_test.go +++ b/cmd/cosign/cli/trustedroot/trustedroot_test.go @@ -44,9 +44,9 @@ func TestCreateCmd(t *testing.T) { outPath := filepath.Join(td, "trustedroot.json") trustedrootCreate := CreateCmd{ - CertChain: fulcioChainPath, + CertChain: []string{fulcioChainPath}, Out: outPath, - TSACertChainPath: tsaChainPath, + TSACertChainPath: []string{tsaChainPath}, } err := trustedrootCreate.Exec(ctx) diff --git a/doc/cosign_trusted-root_create.md b/doc/cosign_trusted-root_create.md index 5e7ea26d34b..0d62614d187 100644 --- a/doc/cosign_trusted-root_create.md +++ b/doc/cosign_trusted-root_create.md @@ -13,14 +13,14 @@ cosign trusted-root create [flags] ### Options ``` - --ca-intermediates string path to a file of intermediate CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. The flag is optional and must be used together with --ca-roots, conflicts with --certificate-chain. - --ca-roots string path to a bundle file of CA certificates in PEM format which will be needed when building the certificate chains for the signing certificate. Conflicts with --certificate-chain. - --certificate-chain string path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate. Conflicts with --ca-roots and --ca-intermediates. - --ctfe-key string path to a PEM-encoded public key used by certificate authority for certificate transparency log. - -h, --help help for create - --out string path to output trusted root - --rekor-key string path to a PEM-encoded public key used by transparency log like Rekor. - --timestamp-certificate-chain string path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. Optionally may contain intermediate CA certificates + --certificate-chain stringArray path to a list of CA certificates in PEM format which will be needed when building the certificate chain for the signing certificate. Must start with the parent intermediate CA certificate of the signing certificate and end with the root certificate. Conflicts with --ca-roots and --ca-intermediates. + --ctfe-key stringArray path to a PEM-encoded public key used by certificate authority for certificate transparency log. + --ctfe-start-time stringArray RFC 3339 string describing validity start time for key use by certificate transparency log. + -h, --help help for create + --out string path to output trusted root + --rekor-key stringArray path to a PEM-encoded public key used by transparency log like Rekor. + --rekor-start-time stringArray RFC 3339 string describing validity start time for key use by transparency log like Rekor. + --timestamp-certificate-chain stringArray path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. Optionally may contain intermediate CA certificates ``` ### Options inherited from parent commands