diff --git a/pki/pki.go b/pki/pki.go index 50ee4e56..fa1954ae 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -1,6 +1,7 @@ package pki import ( + "context" "crypto" "crypto/sha256" "crypto/x509" @@ -20,6 +21,8 @@ import ( "github.com/smallstep/certificates/authority" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/ca" + "github.com/smallstep/certificates/cas" + "github.com/smallstep/certificates/cas/apiv1" "github.com/smallstep/certificates/db" "github.com/smallstep/cli/config" "github.com/smallstep/cli/errs" @@ -157,6 +160,7 @@ type PKI struct { dnsNames []string caURL string enableSSH bool + authorityOptions *apiv1.Options } // New creates a new PKI configuration. @@ -233,6 +237,12 @@ func (p *PKI) GetRootFingerprint() string { return p.rootFingerprint } +// SetAuthorityOptions sets the authority options object, these options are used +// to configure a registration authority. +func (p *PKI) SetAuthorityOptions(opts *apiv1.Options) { + p.authorityOptions = opts +} + // SetProvisioner sets the provisioner name of the OTT keys. func (p *PKI) SetProvisioner(s string) { p.provisioner = s @@ -307,9 +317,11 @@ func (p *PKI) WriteRootCertificate(rootCrt *x509.Certificate, rootKey interface{ return err } - _, err := pemutil.Serialize(rootKey, pemutil.WithPassword(pass), pemutil.ToFile(p.rootKey, 0600)) - if err != nil { - return err + if rootKey != nil { + _, err := pemutil.Serialize(rootKey, pemutil.WithPassword(pass), pemutil.ToFile(p.rootKey, 0600)) + if err != nil { + return err + } } sum := sha256.Sum256(rootCrt.Raw) @@ -318,6 +330,37 @@ func (p *PKI) WriteRootCertificate(rootCrt *x509.Certificate, rootKey interface{ return nil } +// GetCertificateAuthority attempts to load the certificate authority from the +// RA. +func (p *PKI) GetCertificateAuthority() error { + ca, err := cas.New(context.Background(), *p.authorityOptions) + if err != nil { + return err + } + + srv, ok := ca.(apiv1.CertificateAuthorityGetter) + if !ok { + return nil + } + + resp, err := srv.GetCertificateAuthority(&apiv1.GetCertificateAuthorityRequest{ + Name: p.authorityOptions.CertificateAuthority, + }) + if err != nil { + return err + } + + if err := p.WriteRootCertificate(resp.RootCertificate, nil, nil); err != nil { + return err + } + + // Issuer is in the RA + p.intermediate = "" + p.intermediateKey = "" + + return nil +} + // GenerateIntermediateCertificate generates an intermediate certificate with // the given name. func (p *PKI) GenerateIntermediateCertificate(name string, rootCrt *x509.Certificate, rootKey interface{}, pass []byte) error { @@ -414,11 +457,18 @@ func (p *PKI) TellPKI() { func (p *PKI) tellPKI() { ui.Println() - ui.PrintSelected("Root certificate", p.root) - ui.PrintSelected("Root private key", p.rootKey) - ui.PrintSelected("Root fingerprint", p.rootFingerprint) - ui.PrintSelected("Intermediate certificate", p.intermediate) - ui.PrintSelected("Intermediate private key", p.intermediateKey) + if p.authorityOptions == nil || p.authorityOptions.Is(apiv1.SoftCAS) { + ui.PrintSelected("Root certificate", p.root) + ui.PrintSelected("Root private key", p.rootKey) + ui.PrintSelected("Root fingerprint", p.rootFingerprint) + ui.PrintSelected("Intermediate certificate", p.intermediate) + ui.PrintSelected("Intermediate private key", p.intermediateKey) + } else if p.rootFingerprint != "" { + ui.PrintSelected("Root certificate", p.root) + ui.PrintSelected("Root fingerprint", p.rootFingerprint) + } else { + ui.Printf(`{{ "%s" | red }} {{ "Root certificate:" | bold }} failed to retrieve it from RA`+"\n", ui.IconBad) + } if p.enableSSH { ui.PrintSelected("SSH user root certificate", p.sshUserPubKey) ui.PrintSelected("SSH user root private key", p.sshUserKey) @@ -485,6 +535,7 @@ func (p *PKI) GenerateConfig(opt ...Option) (*authority.Config, error) { DataSource: GetDBPath(), }, AuthorityConfig: &authority.AuthConfig{ + Options: p.authorityOptions, DisableIssuedAtCheck: false, Provisioners: provisioner.List{prov}, }, @@ -591,7 +642,11 @@ func (p *PKI) Save(opt ...Option) error { ui.PrintSelected("Default configuration", p.defaults) ui.PrintSelected("Certificate Authority configuration", p.config) ui.Println() - ui.Println("Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.") + if p.authorityOptions == nil || p.authorityOptions.Is(apiv1.SoftCAS) { + ui.Println("Your PKI is ready to go. To generate certificates for individual services see 'step help ca'.") + } else { + ui.Println("Your registration authority is ready to go. To generate certificates for individual services see 'step help ca'.") + } p.askFeedback()