rsa/pkg/ca/crl.go

88 lines
2.2 KiB
Go

/*
Package ca implements a disk file-backed certificate authority with a built-in
CRL signer. The certificate authority can issue new certificates (including the
creation of intermediate CAs) and revoke existing certificates, resulting in a
fresh CRL.
*/
package ca
import (
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"path/filepath"
"time"
"src.lwithers.me.uk/go/rsa/pkg/pemfile"
)
const (
crlFilename = "crl.pem"
crlKeyFilename = "crl-signer-key.pem"
crlCertFilename = "crl-signer-cert.pem"
)
func (ca *CA) loadCRLState() error {
var err error
ca.crl, err = pemfile.ReadCRL(filepath.Join(ca.dir, crlFilename))
if err != nil {
return err
}
ca.crlKey, err = pemfile.ReadKey(filepath.Join(ca.dir, crlKeyFilename))
if err != nil {
return err
}
ca.crlCert, err = pemfile.ReadCert(filepath.Join(ca.dir, crlCertFilename))
return err
}
func (ca *CA) Revoke(serial *big.Int) (*x509.RevocationList, error) {
// if the certificate is already revoked, update its revocation time
var found bool
for i := range ca.crl.RevokedCertificates {
if ca.crl.RevokedCertificates[i].SerialNumber.Cmp(serial) == 0 {
ca.crl.RevokedCertificates[i].RevocationTime = time.Now()
found = true
break
}
}
// otherwise, add a new entry
if !found {
n := big.NewInt(0)
n.SetBytes(serial.Bytes())
ca.crl.RevokedCertificates = append(ca.crl.RevokedCertificates,
pkix.RevokedCertificate{
SerialNumber: n,
RevocationTime: time.Now(),
})
}
// increment issue number
ca.crl.Number.Add(ca.crl.Number, big.NewInt(1))
ca.crl.ThisUpdate = time.Now()
// create new DER-form certificate
der, err := x509.CreateRevocationList(rand.Reader, ca.crl, ca.crlCert, ca.crlKey)
if err != nil {
return nil, fmt.Errorf("failed to sign new CRL: %w", err)
}
// ensure we can parse it
newCRL, err := x509.ParseRevocationList(der)
if err != nil {
return nil, fmt.Errorf("failed to parse newly-signed CRL: %w", err)
}
// save to disk
if err := pemfile.Write(filepath.Join(ca.dir, crlFilename), pemfile.TypeX509CRL, der); err != nil {
return nil, fmt.Errorf("failed to save CRL: %w", err)
}
// update in-memory copy
ca.crl = newCRL
return ca.crl, nil
}