change GenerateCertificateRevocationList to return DER, store DER in db instead of PEM, nicer PEM encoding of CRL, add Mock stubs

This commit is contained in:
Raal Goff 2021-11-02 13:26:07 +08:00
parent 56926b9012
commit 8545adea92
5 changed files with 40 additions and 21 deletions

View file

@ -46,7 +46,7 @@ type Authority interface {
GetRoots() (federation []*x509.Certificate, err error) GetRoots() (federation []*x509.Certificate, err error)
GetFederation() ([]*x509.Certificate, error) GetFederation() ([]*x509.Certificate, error)
Version() authority.Version Version() authority.Version
GenerateCertificateRevocationList(force bool) (string, error) GenerateCertificateRevocationList(force bool) ([]byte, error)
} }
// TimeDuration is an alias of provisioner.TimeDuration // TimeDuration is an alias of provisioner.TimeDuration

View file

@ -580,6 +580,10 @@ type mockAuthority struct {
version func() authority.Version version func() authority.Version
} }
func (m *mockAuthority) GenerateCertificateRevocationList(force bool) ([]byte, error) {
panic("implement me")
}
// TODO: remove once Authorize is deprecated. // TODO: remove once Authorize is deprecated.
func (m *mockAuthority) Authorize(ctx context.Context, ott string) ([]provisioner.SignOption, error) { func (m *mockAuthority) Authorize(ctx context.Context, ott string) ([]provisioner.SignOption, error) {
return m.AuthorizeSign(ott) return m.AuthorizeSign(ott)

View file

@ -1,16 +1,24 @@
package api package api
import "net/http" import (
"encoding/pem"
"net/http"
)
// CRL is an HTTP handler that returns the current CRL // CRL is an HTTP handler that returns the current CRL in PEM format
func (h *caHandler) CRL(w http.ResponseWriter, r *http.Request) { func (h *caHandler) CRL(w http.ResponseWriter, r *http.Request) {
crl, err := h.Authority.GenerateCertificateRevocationList(false) crlBytes, err := h.Authority.GenerateCertificateRevocationList(false)
if err != nil { if err != nil {
w.WriteHeader(500) w.WriteHeader(500)
return return
} }
pemBytes := pem.EncodeToMemory(&pem.Block{
Type: "X509 CRL",
Bytes: crlBytes,
})
w.WriteHeader(200) w.WriteHeader(200)
_, err = w.Write([]byte(crl)) _, err = w.Write(pemBytes)
} }

View file

@ -9,7 +9,6 @@ import (
"encoding/asn1" "encoding/asn1"
"encoding/base64" "encoding/base64"
"encoding/pem" "encoding/pem"
"fmt"
"math/big" "math/big"
"net/http" "net/http"
"time" "time"
@ -469,24 +468,24 @@ func (a *Authority) revokeSSH(crt *ssh.Certificate, rci *db.RevokedCertificateIn
// a new CRL on demand if it has expired (or a CRL does not already exist). // a new CRL on demand if it has expired (or a CRL does not already exist).
// //
// force set to true will force regeneration of the CRL regardless of whether it has actually expired // force set to true will force regeneration of the CRL regardless of whether it has actually expired
func (a *Authority) GenerateCertificateRevocationList(force bool) (string, error) { func (a *Authority) GenerateCertificateRevocationList(force bool) ([]byte, error) {
// check for an existing CRL in the database, and return that if its valid // check for an existing CRL in the database, and return that if its valid
crlInfo, err := a.db.GetCRL() crlInfo, err := a.db.GetCRL()
if err != nil { if err != nil {
return "", err return nil, err
} }
if !force && crlInfo != nil && crlInfo.ExpiresAt.After(time.Now().UTC()) { if !force && crlInfo != nil && crlInfo.ExpiresAt.After(time.Now().UTC()) {
return crlInfo.PEM, nil return crlInfo.DER, nil
} }
// some CAS may not implement the CRLGenerator interface, so check before we proceed // some CAS may not implement the CRLGenerator interface, so check before we proceed
caCRLGenerator, ok := a.x509CAService.(casapi.CertificateAuthorityCRLGenerator) caCRLGenerator, ok := a.x509CAService.(casapi.CertificateAuthorityCRLGenerator)
if !ok { if !ok {
return "", errors.Errorf("CRL Generator not implemented") return nil, errors.Errorf("CRL Generator not implemented")
} }
revokedList, err := a.db.GetRevokedCertificates() revokedList, err := a.db.GetRevokedCertificates()
@ -529,28 +528,24 @@ func (a *Authority) GenerateCertificateRevocationList(force bool) (string, error
certificateRevocationList, err := caCRLGenerator.CreateCertificateRevocationList(&revocationList) certificateRevocationList, err := caCRLGenerator.CreateCertificateRevocationList(&revocationList)
if err != nil { if err != nil {
return "", err return nil, err
} }
// Quick and dirty PEM encoding
// TODO: clean this up
pemCRL := fmt.Sprintf("-----BEGIN X509 CRL-----\n%s\n-----END X509 CRL-----\n", base64.StdEncoding.EncodeToString(certificateRevocationList))
// Create a new db.CertificateRevocationListInfo, which stores the new Number we just generated, the // Create a new db.CertificateRevocationListInfo, which stores the new Number we just generated, the
// expiry time, and the byte-encoded CRL - then store it in the DB // expiry time, and the byte-encoded CRL - then store it in the DB
newCRLInfo := db.CertificateRevocationListInfo{ newCRLInfo := db.CertificateRevocationListInfo{
Number: n, Number: n,
ExpiresAt: revocationList.NextUpdate, ExpiresAt: revocationList.NextUpdate,
PEM: pemCRL, DER: certificateRevocationList,
} }
err = a.db.StoreCRL(&newCRLInfo) err = a.db.StoreCRL(&newCRLInfo)
if err != nil { if err != nil {
return "", err return nil, err
} }
// Finally, return our CRL PEM // Finally, return our CRL PEM
return pemCRL, nil return certificateRevocationList, nil
} }
// GetTLSCertificate creates a new leaf certificate to be used by the CA HTTPS server. // GetTLSCertificate creates a new leaf certificate to be used by the CA HTTPS server.

View file

@ -113,12 +113,12 @@ type RevokedCertificateInfo struct {
MTLS bool MTLS bool
} }
// CertificateRevocationListInfo contains a CRL in PEM and associated metadata to allow a decision on whether // CertificateRevocationListInfo contains a CRL in DER format and associated
// to regenerate the CRL or not easier // metadata to allow a decision on whether to regenerate the CRL or not easier
type CertificateRevocationListInfo struct { type CertificateRevocationListInfo struct {
Number int64 Number int64
ExpiresAt time.Time ExpiresAt time.Time
PEM string DER []byte
} }
// IsRevoked returns whether or not a certificate with the given identifier // IsRevoked returns whether or not a certificate with the given identifier
@ -378,6 +378,18 @@ type MockAuthDB struct {
MShutdown func() error MShutdown func() error
} }
func (m *MockAuthDB) GetRevokedCertificates() (*[]RevokedCertificateInfo, error) {
panic("implement me")
}
func (m *MockAuthDB) GetCRL() (*CertificateRevocationListInfo, error) {
panic("implement me")
}
func (m *MockAuthDB) StoreCRL(info *CertificateRevocationListInfo) error {
panic("implement me")
}
// IsRevoked mock. // IsRevoked mock.
func (m *MockAuthDB) IsRevoked(sn string) (bool, error) { func (m *MockAuthDB) IsRevoked(sn string) (bool, error) {
if m.MIsRevoked != nil { if m.MIsRevoked != nil {