Submit to ct log the renew and CA server certificate
Related to smallstep/ca-component#142
This commit is contained in:
parent
7b175004cb
commit
7012500aac
2 changed files with 144 additions and 62 deletions
137
authority/tls.go
137
authority/tls.go
|
@ -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))
|
||||
|
|
69
ct/ct.go
69
ct/ct.go
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue