/* 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 }