package revoking import ( "crypto" "crypto/rand" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "fmt" "log" "math/big" "strings" "time" ) var OidCRLReason = asn1.ObjectIdentifier{2, 5, 29, 21} type CRLReason int // CRL reason codes as defined in RFC 5280 section 5.3.1 const ( CRLReasonUnspecified CRLReason = 0 CRLReasonKeyCompromise CRLReason = 1 CRLReasonCACompromise CRLReason = 2 CRLReasonAffiliationChanged CRLReason = 3 CRLReasonSuperseded CRLReason = 4 CRLReasonCessationOfOperation CRLReason = 5 CRLReasonCertificateHold CRLReason = 6 CRLReasonRemoveFromCRL CRLReason = 8 CRLReasonPrivilegeWithdrawn CRLReason = 9 CRLReasonAACompromise CRLReason = 10 ) var crlReasonNames = map[CRLReason]string{ CRLReasonUnspecified: "unspecified", CRLReasonKeyCompromise: "keyCompromise", CRLReasonCACompromise: "CACompromise", CRLReasonAffiliationChanged: "affiliationChanged", CRLReasonSuperseded: "superseded", CRLReasonCessationOfOperation: "cessationOfOperation", CRLReasonCertificateHold: "certificateHold", CRLReasonRemoveFromCRL: "removeFromCRL", CRLReasonPrivilegeWithdrawn: "privilegeWithdrawn", CRLReasonAACompromise: "AACompromise", } func (r CRLReason) String() string { if reason, ok := crlReasonNames[r]; ok { return reason } return crlReasonNames[CRLReasonUnspecified] } func (r CRLReason) BuildExtension() pkix.Extension { extBytes, err := asn1.Marshal(r) if err != nil { // we can panic here because all values of CRLReason must be ASN.1 marshal-able log.Panicf("could not marshal revocation reason: %v", err) } return pkix.Extension{Id: OidCRLReason, Value: extBytes} } // ParseReason takes a reason string and performs a case-insensitive match to a reason code func ParseReason(rs string) CRLReason { for key, name := range crlReasonNames { if strings.EqualFold(name, rs) { return key } } return CRLReasonUnspecified } type X509Revoking struct { repository Repository crlAlgorithm x509.SignatureAlgorithm crlIssuer *x509.Certificate signer crypto.Signer } type RevokeCertificate struct { serialNumber *big.Int reason CRLReason } type CertificateRevoked struct { serialNumber *big.Int revocationTime time.Time reason string } type CRLInformation struct { CRL []byte // DER encoded CRL } func (r *X509Revoking) Revoke(revokeCertificate *RevokeCertificate) (*pkix.RevokedCertificate, error) { revoked := &pkix.RevokedCertificate{ SerialNumber: revokeCertificate.serialNumber, RevocationTime: time.Now(), Extensions: []pkix.Extension{revokeCertificate.reason.BuildExtension()}, } if err := r.repository.StoreRevocation(revoked); err != nil { return nil, err } return revoked, nil } func (r *X509Revoking) CreateCRL() (*CRLInformation, error) { revoked, err := r.repository.RevokedCertificates() if err != nil { return nil, fmt.Errorf("could not get revocation information: %w", err) } list, err := x509.CreateRevocationList(rand.Reader, &x509.RevocationList{ SignatureAlgorithm: r.crlAlgorithm, RevokedCertificates: revoked, }, r.crlIssuer, r.signer) if err != nil { return nil, fmt.Errorf("could not sign revocation list: %w", err) } return &CRLInformation{CRL: list}, nil } func NewX509Revoking( repo Repository, crlAlgorithm x509.SignatureAlgorithm, issuer *x509.Certificate, signer crypto.Signer, ) *X509Revoking { return &X509Revoking{repository: repo, crlAlgorithm: crlAlgorithm, crlIssuer: issuer, signer: signer} }