Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto/x509: add RevocationList and CreateRevocationList #36945

Closed
142 changes: 142 additions & 0 deletions src/crypto/x509/x509.go
Original file line number Diff line number Diff line change
Expand Up @@ -1644,6 +1644,7 @@ var (
oidExtensionNameConstraints = []int{2, 5, 29, 30}
oidExtensionCRLDistributionPoints = []int{2, 5, 29, 31}
oidExtensionAuthorityInfoAccess = []int{1, 3, 6, 1, 5, 5, 7, 1, 1}
oidExtensionCRLNumber = []int{2, 5, 29, 20}
)

var (
Expand Down Expand Up @@ -2213,6 +2214,9 @@ func ParseDERCRL(derBytes []byte) (*pkix.CertificateList, error) {

// CreateCRL returns a DER encoded CRL, signed by this Certificate, that
// contains the given list of revoked certificates.
//
// Note: this method does not generate an RFC 5280 conformant X.509 v2 CRL.
// To generate a standards compliant CRL, use CreateRevocationList instead.
func (c *Certificate) CreateCRL(rand io.Reader, priv interface{}, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) {
key, ok := priv.(crypto.Signer)
if !ok {
Expand Down Expand Up @@ -2660,3 +2664,141 @@ func parseCertificateRequest(in *certificateRequest) (*CertificateRequest, error
func (c *CertificateRequest) CheckSignature() error {
return checkSignature(c.SignatureAlgorithm, c.RawTBSCertificateRequest, c.Signature, c.PublicKey)
}

// RevocationList contains the fields used to create an X.509 v2 Certificate
// Revocation list with CreateRevocationList.
type RevocationList struct {
// SignatureAlgorithm is used to determine the signature algorithm to be
// used when signing the CRL. If 0 the default algorithm for the signing
// key will be used.
SignatureAlgorithm SignatureAlgorithm

// RevokedCertificates is used to populate the revokedCertificates
// sequence in the CRL, it may be empty. RevokedCertificates may be nil,
// in which case an empty CRL will be created.
RevokedCertificates []pkix.RevokedCertificate

// Number is used to populate the X.509 v2 cRLNumber extension in the CRL,
// which should be a monotonically increasing sequence number for a given
// CRL scope and CRL issuer.
Number *big.Int
// ThisUpdate is used to populate the thisUpdate field in the CRL, which
// indicates the issuance date of the CRL.
ThisUpdate time.Time
// NextUpdate is used to populate the nextUpdate field in the CRL, which
// indicates the date by which the next CRL will be issued. NextUpdate
// must be greater than ThisUpdate.
NextUpdate time.Time
// ExtraExtensions contains any additional extensions to add directly to
// the CRL.
ExtraExtensions []pkix.Extension
}

// CreateRevocationList creates a new X.509 v2 Certificate Revocation List,
// according to RFC 5280, based on template.
//
// The CRL is signed by priv which should be the private key associated with
// the public key in the issuer certificate.
//
// The issuer may not be nil, and the crlSign bit must be set in KeyUsage in
// order to use it as a CRL issuer.
//
// The issuer distinguished name CRL field and authority key identifier
// extension are populated using the issuer certificate. issuer must have
// SubjectKeyId set.
func CreateRevocationList(rand io.Reader, template *RevocationList, issuer *Certificate, priv crypto.Signer) ([]byte, error) {
if template == nil {
return nil, errors.New("x509: template can not be nil")
}
if issuer == nil {
return nil, errors.New("x509: issuer can not be nil")
}
if (issuer.KeyUsage & KeyUsageCRLSign) == 0 {
return nil, errors.New("x509: issuer must have the crlSign key usage bit set")
}
if len(issuer.SubjectKeyId) == 0 {
return nil, errors.New("x509: issuer certificate doesn't contain a subject key identifier")
}
if template.NextUpdate.Before(template.ThisUpdate) {
return nil, errors.New("x509: template.ThisUpdate is after template.NextUpdate")
}
if template.Number == nil {
return nil, errors.New("x509: template contains nil Number field")
}

hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), template.SignatureAlgorithm)
if err != nil {
return nil, err
}

// Force revocation times to UTC per RFC 5280.
revokedCertsUTC := make([]pkix.RevokedCertificate, len(template.RevokedCertificates))
for i, rc := range template.RevokedCertificates {
rc.RevocationTime = rc.RevocationTime.UTC()
revokedCertsUTC[i] = rc
}

aki, err := asn1.Marshal(authKeyId{Id: issuer.SubjectKeyId})
if err != nil {
return nil, err
}
crlNum, err := asn1.Marshal(template.Number)
if err != nil {
return nil, err
}

tbsCertList := pkix.TBSCertificateList{
Version: 1, // v2
Signature: signatureAlgorithm,
Issuer: issuer.Subject.ToRDNSequence(),
ThisUpdate: template.ThisUpdate.UTC(),
NextUpdate: template.NextUpdate.UTC(),
Extensions: []pkix.Extension{
{
Id: oidExtensionAuthorityKeyId,
Value: aki,
},
{
Id: oidExtensionCRLNumber,
Value: crlNum,
},
},
}
if len(revokedCertsUTC) > 0 {
tbsCertList.RevokedCertificates = revokedCertsUTC
}

if len(template.ExtraExtensions) > 0 {
tbsCertList.Extensions = append(tbsCertList.Extensions, template.ExtraExtensions...)
}

tbsCertListContents, err := asn1.Marshal(tbsCertList)
if err != nil {
return nil, err
}

input := tbsCertListContents
if hashFunc != 0 {
h := hashFunc.New()
h.Write(tbsCertListContents)
input = h.Sum(nil)
}
var signerOpts crypto.SignerOpts = hashFunc
if template.SignatureAlgorithm.isRSAPSS() {
signerOpts = &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
Hash: hashFunc,
}
}

signature, err := priv.Sign(rand, input, signerOpts)
if err != nil {
return nil, err
}

return asn1.Marshal(pkix.CertificateList{
TBSCertList: tbsCertList,
SignatureAlgorithm: signatureAlgorithm,
SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
})
}
Loading