Skip to content
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

Certificate transparency #127

Draft
wants to merge 18 commits into
base: master
Choose a base branch
from
Draft
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
1,221 changes: 1,211 additions & 10 deletions Gopkg.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
name = "gopkg.in/square/go-jose.v2"
version = "2.3.1"

[[constraint]]
branch = "master"
name = "github.com/google/certificate-transparency-go"

[prune]
go-tests = true
unused-packages = true
11 changes: 11 additions & 0 deletions authority/authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/pkg/errors"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/ct"
"github.com/smallstep/certificates/db"
"github.com/smallstep/cli/crypto/pemutil"
"github.com/smallstep/cli/crypto/x509util"
Expand All @@ -30,6 +31,7 @@ type Authority struct {
startTime time.Time
provisioners *provisioner.Collection
db db.AuthDB
ctClient ct.Client
// Do not re-initialize
initOnce bool
}
Expand All @@ -52,10 +54,19 @@ func New(config *Config, opts ...Option) (*Authority, error) {
return nil, err
}

var ctClient ct.Client
// only first one is supported at the moment.
if len(config.CTs) > 0 {
if ctClient, err = ct.New(config.CTs[0]); err != nil {
return nil, err
}
}

var a = &Authority{
config: config,
certificates: new(sync.Map),
provisioners: provisioner.NewCollection(config.getAudiences()),
ctClient: ctClient,
}
for _, opt := range opts {
opt(a)
Expand Down
10 changes: 10 additions & 0 deletions authority/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/pkg/errors"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/ct"
"github.com/smallstep/certificates/db"
"github.com/smallstep/cli/crypto/tlsutil"
"github.com/smallstep/cli/crypto/x509util"
Expand Down Expand Up @@ -59,6 +60,7 @@ type Config struct {
AuthorityConfig *AuthConfig `json:"authority,omitempty"`
TLS *tlsutil.TLSOptions `json:"tls,omitempty"`
Password string `json:"password,omitempty"`
CTs []ct.Config `json:"cts"`
}

// AuthConfig represents the configuration options for the authority.
Expand Down Expand Up @@ -181,6 +183,14 @@ func (c *Config) Validate() error {
c.TLS.Renegotiation = c.TLS.Renegotiation || DefaultTLSOptions.Renegotiation
}

if len(c.CTs) > 0 {
for _, ct := range c.CTs {
if err := ct.Validate(); err != nil {
return err
}
}
}

return c.AuthorityConfig.Validate(c.getAudiences())
}

Expand Down
116 changes: 111 additions & 5 deletions authority/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ func (a *Authority) GetTLSOptions() *tlsutil.TLSOptions {
return a.config.TLS
}

var oidAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35}
var (
oidAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35}
// Certificate transparency extensions OIDs
ctPoisonOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
ctSigendCertificateTimestampOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2}
)

func withDefaultASN1DN(def *x509util.ASN1DN) x509util.WithOption {
return func(p x509util.Profile) error {
Expand Down Expand Up @@ -79,6 +84,11 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti
}
}

// Add CT Poison extension
if a.ctClient != nil {
mods = append(mods, x509util.WithCTPoison())
}

if err := csr.CheckSignature(); err != nil {
return nil, nil, &apiError{errors.Wrap(err, "sign: invalid certificate request"),
http.StatusBadRequest, errContext}
Expand All @@ -101,6 +111,25 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti
http.StatusInternalServerError, errContext}
}

if a.ctClient != nil {
// Submit precertificate chain and get SCTs
scts, err := a.ctClient.GetSCTs(crtBytes, issIdentity.Crt.Raw)
if err != nil {
return nil, nil, &apiError{errors.Wrap(err, "sign: error getting SCTs for certificate"),
http.StatusBadGateway, errContext}
}

// Remove ct poison extension and add sct extension
leaf.RemoveExtension(ctPoisonOID)
leaf.AddExtension(scts.GetExtension())

// Recreate final certificate
if crtBytes, err = leaf.CreateCertificate(); err != nil {
return nil, nil, &apiError{errors.Wrap(err, "sign: error creating final leaf certificate"),
http.StatusInternalServerError, errContext}
}
}

serverCert, err := x509.ParseCertificate(crtBytes)
if err != nil {
return nil, nil, &apiError{errors.Wrap(err, "sign: error parsing new leaf certificate"),
Expand All @@ -120,6 +149,14 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti
}
}

if a.ctClient != nil {
// Submit final certificate chain
if _, err := a.ctClient.SubmitToLogs(serverCert.Raw, caCert.Raw); err != nil {
return nil, nil, &apiError{errors.Wrap(err, "sign: error submitting final certificate to ct logs"),
http.StatusBadGateway, errContext}
}
}

return serverCert, caCert, nil
}

Expand Down Expand Up @@ -178,17 +215,45 @@ func (a *Authority) Renew(oldCert *x509.Certificate) (*x509.Certificate, *x509.C
}
}

leaf, err := x509util.NewLeafProfileWithTemplate(newCert,
issIdentity.Crt, issIdentity.Key)
opts := []x509util.WithOption{}
// Add CT Poison extension
if a.ctClient != nil {
opts = append(opts, x509util.WithCTPoison())
}

leaf, err := x509util.NewLeafProfileWithTemplate(newCert, issIdentity.Crt, issIdentity.Key, opts...)
if err != nil {
return nil, nil, &apiError{err, http.StatusInternalServerError, apiCtx{}}
}

// Remove previous SCTs if any
leaf.RemoveExtension(ctSigendCertificateTimestampOID)

crtBytes, err := leaf.CreateCertificate()
if err != nil {
return nil, nil, &apiError{errors.Wrap(err, "error renewing certificate from existing server certificate"),
http.StatusInternalServerError, apiCtx{}}
}

if a.ctClient != nil {
// Submit precertificate chain and get SCTs
scts, err := a.ctClient.GetSCTs(crtBytes, issIdentity.Crt.Raw)
if err != nil {
return nil, nil, &apiError{errors.Wrap(err, "renew: error getting SCTs for certificate"),
http.StatusBadGateway, apiCtx{}}
}

// Remove ct poison extension and add sct extension
leaf.RemoveExtension(ctPoisonOID)
leaf.AddExtension(scts.GetExtension())

// Recreate final certificate
if crtBytes, err = leaf.CreateCertificate(); err != nil {
return nil, nil, &apiError{errors.Wrap(err, "renew: error creating final leaf certificate"),
http.StatusInternalServerError, apiCtx{}}
}
}

serverCert, err := x509.ParseCertificate(crtBytes)
if err != nil {
return nil, nil, &apiError{errors.Wrap(err, "error parsing new server certificate"),
Expand All @@ -200,6 +265,14 @@ func (a *Authority) Renew(oldCert *x509.Certificate) (*x509.Certificate, *x509.C
http.StatusInternalServerError, apiCtx{}}
}

if a.ctClient != nil {
// Submit final certificate chain
if _, err := a.ctClient.SubmitToLogs(serverCert.Raw, caCert.Raw); err != nil {
return nil, nil, &apiError{errors.Wrap(err, "renew: error submitting final certificate to ct logs"),
http.StatusBadGateway, apiCtx{}}
}
}

return serverCert, caCert, nil
}

Expand Down Expand Up @@ -278,9 +351,17 @@ func (a *Authority) Revoke(opts *RevokeOptions) error {

// GetTLSCertificate creates a new leaf certificate to be used by the CA HTTPS server.
func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) {
opts := []x509util.WithOption{
x509util.WithHosts(strings.Join(a.config.DNSNames, ",")),
}

// Add CT Poison extension
if a.ctClient != nil {
opts = append(opts, x509util.WithCTPoison())
}

profile, err := x509util.NewLeafProfile("Step Online CA",
a.intermediateIdentity.Crt, a.intermediateIdentity.Key,
x509util.WithHosts(strings.Join(a.config.DNSNames, ",")))
a.intermediateIdentity.Crt, a.intermediateIdentity.Key, opts...)
if err != nil {
return nil, err
}
Expand All @@ -290,6 +371,23 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) {
return nil, err
}

if a.ctClient != nil {
// Submit precertificate chain and get SCTs
scts, err := a.ctClient.GetSCTs(crtBytes, a.intermediateIdentity.Crt.Raw)
if err != nil {
return nil, errors.Wrap(err, "error getting SCTs for certificate")
}

// Remove ct poison extension and add sct extension
profile.RemoveExtension(ctPoisonOID)
profile.AddExtension(scts.GetExtension())

// Recreate final certificate
if crtBytes, err = profile.CreateCertificate(); err != nil {
return nil, errors.Wrap(err, "error creating final leaf certificate")
}
}

keyPEM, err := pemutil.Serialize(profile.SubjectPrivateKey())
if err != nil {
return nil, err
Expand All @@ -306,6 +404,14 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) {
if err != nil {
return nil, err
}

if a.ctClient != nil {
// Submit final certificate chain
if _, err := a.ctClient.SubmitToLogs(crtBytes, intermediatePEM.Bytes); err != nil {
return nil, errors.Wrap(err, "error submitting final certificate to ct logs")
}
}

tlsCrt, err := tls.X509KeyPair(append(crtPEM,
pem.EncodeToMemory(intermediatePEM)...),
pem.EncodeToMemory(keyPEM))
Expand Down
2 changes: 2 additions & 0 deletions ca/renew.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ func NewTLSRenewer(cert *tls.Certificate, fn RenewFunc, opts ...tlsRenewerOption
if r.renewJitter == 0 {
r.renewJitter = period / 20
}
// Initialize certNotAfter
r.certNotAfter = cert.Leaf.NotAfter.Add(-1 * time.Minute)

return r, nil
}
Expand Down
Loading