Submit to ct log the renew and CA server certificate

Related to smallstep/ca-component#142
This commit is contained in:
Mariano Cano 2019-02-11 18:57:14 -08:00
parent 7b175004cb
commit 7012500aac
2 changed files with 144 additions and 62 deletions

View file

@ -30,8 +30,12 @@ type SignOptions struct {
}
var (
// Step extensions OIDs
stepOIDRoot = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 37476, 9000, 64}
stepOIDProvisioner = append(asn1.ObjectIdentifier(nil), append(stepOIDRoot, 1)...)
// 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}
)
type stepProvisionerASN1 struct {
@ -151,6 +155,25 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts SignOptions, ext
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"),
@ -163,42 +186,9 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts SignOptions, ext
http.StatusInternalServerError, errContext}
}
// Certificate transparency
if a.ctClient != nil {
scts, err := a.ctClient.GetSCTs(serverCert, caCert)
if err != nil {
return nil, nil, &apiError{errors.Wrap(err, "sign: error getting SCTs for certificate"),
http.StatusBadGateway, errContext}
}
crt := leaf.Subject()
crt.ExtraExtensions = append(crt.ExtraExtensions, scts.GetExtension())
for i, ext := range crt.ExtraExtensions {
if ext.Id.Equal(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}) {
crt.ExtraExtensions = append(crt.ExtraExtensions[:i], crt.ExtraExtensions[i+1:]...)
break
}
}
for i, ext := range crt.Extensions {
if ext.Id.Equal(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}) {
crt.Extensions = append(crt.Extensions[:i], crt.Extensions[i+1:]...)
break
}
}
crtBytes, err = leaf.CreateCertificate()
if err != nil {
return nil, nil, &apiError{errors.Wrap(err, "sign: error creating new 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"),
http.StatusInternalServerError, errContext}
}
if err := a.ctClient.SubmitToLogs(serverCert, caCert); err != 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}
}
@ -264,17 +254,45 @@ func (a *Authority) Renew(ocx *x509.Certificate) (*x509.Certificate, *x509.Certi
PolicyIdentifiers: oldCert.PolicyIdentifiers,
}
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, context{}}
}
// 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, context{}}
}
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, context{}}
}
// 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, context{}}
}
}
serverCert, err := x509.ParseCertificate(crtBytes)
if err != nil {
return nil, nil, &apiError{errors.Wrap(err, "error parsing new server certificate"),
@ -286,14 +304,30 @@ func (a *Authority) Renew(ocx *x509.Certificate) (*x509.Certificate, *x509.Certi
http.StatusInternalServerError, context{}}
}
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, context{}}
}
}
return serverCert, caCert, nil
}
// 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
}
@ -303,6 +337,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
@ -319,6 +370,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))

View file

@ -6,7 +6,6 @@ import (
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"fmt"
"io/ioutil"
"log"
"net/http"
@ -45,8 +44,8 @@ func (c *Config) Validate() error {
// Client is the interfaced used to communicate with the certificate transparency logs.
type Client interface {
GetSCTs(chain ...*x509.Certificate) (*SCT, error)
SubmitToLogs(chain ...*x509.Certificate) error
GetSCTs(asn1Data ...[]byte) (*SCT, error)
SubmitToLogs(asn1Data ...[]byte) error
}
type logClient interface {
@ -139,14 +138,15 @@ func New(c Config) (*ClientImpl, error) {
// GetSCTs submit the precertificate to the logs and returns the list of SCTs to
// embed into the certificate.
func (c *ClientImpl) GetSCTs(chain ...*x509.Certificate) (*SCT, error) {
ctChain := chainFromCerts(chain)
func (c *ClientImpl) GetSCTs(asn1Data ...[]byte) (*SCT, error) {
chain := chainFromDERs(asn1Data)
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
defer cancel()
sct, err := c.logClient.AddPreChain(ctx, ctChain)
sct, err := c.logClient.AddPreChain(ctx, chain)
if err != nil {
return nil, errors.Wrapf(err, "failed to get SCT from %s", c.config.URI)
}
logLeafHash(asn1Data, sct, true)
return &SCT{
LogURL: c.config.URI,
SCT: sct,
@ -154,32 +154,55 @@ func (c *ClientImpl) GetSCTs(chain ...*x509.Certificate) (*SCT, error) {
}
// SubmitToLogs submits the certificate to the certificate transparency logs.
func (c *ClientImpl) SubmitToLogs(chain ...*x509.Certificate) error {
ctChain := chainFromCerts(chain)
func (c *ClientImpl) SubmitToLogs(asn1Data ...[]byte) error {
chain := chainFromDERs(asn1Data)
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
defer cancel()
sct, err := c.logClient.AddChain(ctx, ctChain)
sct, err := c.logClient.AddChain(ctx, chain)
if err != nil {
return errors.Wrapf(err, "failed submit certificate to %s", c.config.URI)
}
// Calculate the leaf hash
leafEntry := ct.CreateX509MerkleTreeLeaf(ctChain[0], sct.Timestamp)
leafHash, err := ct.LeafHashForLeaf(leafEntry)
if err != nil {
log.Println(err)
}
// Display the SCT
fmt.Printf("LogID: %x\n", sct.LogID.KeyID[:])
fmt.Printf("LeafHash: %x\n", leafHash)
logLeafHash(asn1Data, sct, false)
return nil
}
func chainFromCerts(certs []*x509.Certificate) []ct.ASN1Cert {
func chainFromDERs(asn1Data [][]byte) []ct.ASN1Cert {
var chain []ct.ASN1Cert
for _, cert := range certs {
chain = append(chain, ct.ASN1Cert{Data: cert.Raw})
for _, der := range asn1Data {
chain = append(chain, ct.ASN1Cert{Data: der})
}
return chain
}
func logLeafHash(asn1Data [][]byte, sct *ct.SignedCertificateTimestamp, isPrecert bool) {
var etype ct.LogEntryType
if isPrecert {
etype = ct.PrecertLogEntryType
} else {
etype = ct.X509LogEntryType
}
chain := make([]*ctx509.Certificate, len(asn1Data))
for i := range asn1Data {
cert, err := ctx509.ParseCertificate(asn1Data[i])
if err != nil {
log.Println(err)
return
}
chain[i] = cert
}
leafEntry, err := ct.MerkleTreeLeafFromChain(chain, etype, sct.Timestamp)
if err != nil {
log.Println(err)
return
}
leafHash, err := ct.LeafHashForLeaf(leafEntry)
if err != nil {
log.Println(err)
return
}
log.Printf("LogID: %x, LeafHash: %x, Timestamp: %d\n", sct.LogID.KeyID[:], leafHash, sct.Timestamp)
}