Skip to content

add intermediate-file flag to separate leaf and intermediate certs #1438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions command/ca/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ multiple SANs. The '--san' flag and the '--token' flag are mutually exclusive.`,
Usage: "The directory where TPM keys and certificates will be stored",
Value: filepath.Join(step.Path(), "tpm"),
},
cli.StringFlag{
Name: "intermediate-file",
Usage: "Write intermediate certificates to a separate file",
},
flags.TemplateSet,
flags.TemplateSetFile,
flags.CaConfig,
Expand Down Expand Up @@ -279,8 +283,42 @@ func certificateAction(ctx *cli.Context) error {
return errors.New("token is not supported")
}

if err := flow.Sign(ctx, tok, req.CsrPEM, crtFile); err != nil {
return err
if intermediateFile := ctx.String("intermediate-file"); intermediateFile != "" {
certs, err := flow.SignReturnsCerts(ctx, tok, req.CsrPEM)
if err != nil {
return err
}

if len(certs) < 2 {
return errors.New("certificate chain must contain at least 2 certificates (leaf + intermediate)")
}

// Leaf cert should NOT be a CA
if certs[0].IsCA {
return errors.New("first certificate (leaf) in chain should not be a CA certificate")
}

// Intermediates should be CAs
for i := 1; i < len(certs); i++ {
if !certs[i].IsCA {
return errors.New("intermediate certificate is not a CA certificate")
}
}

// Write leaf cert
if err := cautils.WriteCerts(crtFile, certs[:1]); err != nil {
return errors.Wrap(err, "error writing leaf certificate")
}
// Write intermediates
if err := cautils.WriteCerts(intermediateFile, certs[1:]); err != nil {
return errors.Wrap(err, "error writing intermediate certificates")
}
ui.PrintSelected("Intermediate Chain", intermediateFile)

} else {
if err := flow.Sign(ctx, tok, req.CsrPEM, crtFile); err != nil {
return err
}
}

_, err = pemutil.Serialize(pk, pemutil.ToFile(keyFile, 0600))
Expand Down
44 changes: 32 additions & 12 deletions utils/cautils/certificate_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,21 +247,30 @@ func (f *CertificateFlow) GenerateIdentityToken(ctx *cli.Context) (string, error

// Sign signs the CSR using the online or the offline certificate authority.
func (f *CertificateFlow) Sign(ctx *cli.Context, tok string, csr api.CertificateRequest, crtFile string) error {
client, err := f.GetClient(ctx, tok)
certs, err := f.SignReturnsCerts(ctx, tok, csr)
if err != nil {
return err
}
return WriteCerts(crtFile, certs)
}

// SignReturnsCerts signs the CSR and returns the certificates
func (f *CertificateFlow) SignReturnsCerts(ctx *cli.Context, tok string, csr api.CertificateRequest) ([]*x509.Certificate, error) {
client, err := f.GetClient(ctx, tok)
if err != nil {
return nil, err
}

// parse times or durations
notBefore, notAfter, err := flags.ParseTimeDuration(ctx)
if err != nil {
return err
return nil, err
}

// parse template data
templateData, err := flags.ParseTemplateData(ctx)
if err != nil {
return err
return nil, err
}

req := &api.SignRequest{
Expand All @@ -274,21 +283,18 @@ func (f *CertificateFlow) Sign(ctx *cli.Context, tok string, csr api.Certificate

resp, err := client.Sign(req)
if err != nil {
return err
return nil, err
}

if len(resp.CertChainPEM) == 0 {
resp.CertChainPEM = []api.Certificate{resp.ServerPEM, resp.CaPEM}
}
var data []byte
for _, certPEM := range resp.CertChainPEM {
pemblk, err := pemutil.Serialize(certPEM.Certificate)
if err != nil {
return errors.Wrap(err, "error serializing from step-ca API response")
}
data = append(data, pem.EncodeToMemory(pemblk)...)
certs := make([]*x509.Certificate, len(resp.CertChainPEM))

for i, certPEM := range resp.CertChainPEM {
certs[i] = certPEM.Certificate
}
return utils.WriteFile(crtFile, data, 0600)
return certs, nil
}

// CreateSignRequest is a helper function that given an x509 OTT returns a
Expand Down Expand Up @@ -418,3 +424,17 @@ func splitSANs(args ...[]string) (dnsNames []string, ipAddresses []net.IP, email
}
return x509util.SplitSANs(unique)
}

// writeCerts writes one or more certificates to a file using the standard overwrite protection
func WriteCerts(filename string, certs []*x509.Certificate) error {
var data []byte
for _, certPEM := range certs {
pemblk, err := pemutil.Serialize(certPEM)
if err != nil {
return errors.Wrap(err, "error serializing from step-ca API response")
}
data = append(data, pem.EncodeToMemory(pemblk)...)
}

return utils.WriteFile(filename, data, 0600)
}