You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
3.5 KiB
Go

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