329 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			329 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
// Package crl provides a RFC 5280 X509v3 conformant CRL creation function.
 | 
						|
// This code is copied from an upstream golang stdlib CL
 | 
						|
// (https://go-review.googlesource.com/c/go/+/217298). Once this CL lands
 | 
						|
// and the functionality is included in a versioned golang release we can
 | 
						|
// delete this code and use the stdlib function.
 | 
						|
//
 | 
						|
// Since this code relies on a number of private crypto/x509 functions and
 | 
						|
// variables those have also been copied verbatim into this package.
 | 
						|
package crl
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto"
 | 
						|
	"crypto/ecdsa"
 | 
						|
	"crypto/ed25519"
 | 
						|
	"crypto/elliptic"
 | 
						|
	"crypto/rsa"
 | 
						|
	"crypto/x509"
 | 
						|
	"crypto/x509/pkix"
 | 
						|
	"encoding/asn1"
 | 
						|
	"errors"
 | 
						|
	"io"
 | 
						|
	"time"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	oidSignatureMD2WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
 | 
						|
	oidSignatureMD5WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
 | 
						|
	oidSignatureSHA1WithRSA     = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
 | 
						|
	oidSignatureSHA256WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
 | 
						|
	oidSignatureSHA384WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
 | 
						|
	oidSignatureSHA512WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
 | 
						|
	oidSignatureRSAPSS          = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
 | 
						|
	oidSignatureDSAWithSHA1     = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
 | 
						|
	oidSignatureDSAWithSHA256   = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
 | 
						|
	oidSignatureECDSAWithSHA1   = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
 | 
						|
	oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
 | 
						|
	oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
 | 
						|
	oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
 | 
						|
	oidSignatureEd25519         = asn1.ObjectIdentifier{1, 3, 101, 112}
 | 
						|
 | 
						|
	oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
 | 
						|
	oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
 | 
						|
	oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
 | 
						|
 | 
						|
	oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
 | 
						|
 | 
						|
	// oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA
 | 
						|
	// but it's specified by ISO. Microsoft's makecert.exe has been known
 | 
						|
	// to produce certificates with this OID.
 | 
						|
	oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29}
 | 
						|
)
 | 
						|
 | 
						|
var signatureAlgorithmDetails = []struct {
 | 
						|
	algo       x509.SignatureAlgorithm
 | 
						|
	name       string
 | 
						|
	oid        asn1.ObjectIdentifier
 | 
						|
	pubKeyAlgo x509.PublicKeyAlgorithm
 | 
						|
	hash       crypto.Hash
 | 
						|
}{
 | 
						|
	{x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
 | 
						|
	{x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
 | 
						|
	{x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
 | 
						|
	{x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
 | 
						|
	{x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
 | 
						|
	{x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
 | 
						|
	{x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
 | 
						|
	{x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256},
 | 
						|
	{x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384},
 | 
						|
	{x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512},
 | 
						|
	{x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
 | 
						|
	{x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
 | 
						|
	{x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
 | 
						|
	{x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
 | 
						|
	{x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
 | 
						|
	{x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
 | 
						|
	{x509.PureEd25519, "Ed25519", oidSignatureEd25519, x509.Ed25519, crypto.Hash(0) /* no pre-hashing */},
 | 
						|
}
 | 
						|
 | 
						|
// pssParameters reflects the parameters in an AlgorithmIdentifier that
 | 
						|
// specifies RSA PSS. See RFC 3447, Appendix A.2.3.
 | 
						|
type pssParameters struct {
 | 
						|
	// The following three fields are not marked as
 | 
						|
	// optional because the default values specify SHA-1,
 | 
						|
	// which is no longer suitable for use in signatures.
 | 
						|
	Hash         pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"`
 | 
						|
	MGF          pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"`
 | 
						|
	SaltLength   int                      `asn1:"explicit,tag:2"`
 | 
						|
	TrailerField int                      `asn1:"optional,explicit,tag:3,default:1"`
 | 
						|
}
 | 
						|
 | 
						|
// rsaPSSParameters returns an asn1.RawValue suitable for use as the Parameters
 | 
						|
// in an AlgorithmIdentifier that specifies RSA PSS.
 | 
						|
func rsaPSSParameters(hashFunc crypto.Hash) asn1.RawValue {
 | 
						|
	var hashOID asn1.ObjectIdentifier
 | 
						|
 | 
						|
	switch hashFunc {
 | 
						|
	case crypto.SHA256:
 | 
						|
		hashOID = oidSHA256
 | 
						|
	case crypto.SHA384:
 | 
						|
		hashOID = oidSHA384
 | 
						|
	case crypto.SHA512:
 | 
						|
		hashOID = oidSHA512
 | 
						|
	}
 | 
						|
 | 
						|
	params := pssParameters{
 | 
						|
		Hash: pkix.AlgorithmIdentifier{
 | 
						|
			Algorithm:  hashOID,
 | 
						|
			Parameters: asn1.NullRawValue,
 | 
						|
		},
 | 
						|
		MGF: pkix.AlgorithmIdentifier{
 | 
						|
			Algorithm: oidMGF1,
 | 
						|
		},
 | 
						|
		SaltLength:   hashFunc.Size(),
 | 
						|
		TrailerField: 1,
 | 
						|
	}
 | 
						|
 | 
						|
	mgf1Params := pkix.AlgorithmIdentifier{
 | 
						|
		Algorithm:  hashOID,
 | 
						|
		Parameters: asn1.NullRawValue,
 | 
						|
	}
 | 
						|
 | 
						|
	var err error
 | 
						|
	params.MGF.Parameters.FullBytes, err = asn1.Marshal(mgf1Params)
 | 
						|
	if err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
 | 
						|
	serialized, err := asn1.Marshal(params)
 | 
						|
	if err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
 | 
						|
	return asn1.RawValue{FullBytes: serialized}
 | 
						|
}
 | 
						|
 | 
						|
// signingParamsForPublicKey returns the parameters to use for signing with
 | 
						|
// priv. If requestedSigAlgo is not zero then it overrides the default
 | 
						|
// signature algorithm.
 | 
						|
func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error) {
 | 
						|
	var pubType x509.PublicKeyAlgorithm
 | 
						|
 | 
						|
	switch pub := pub.(type) {
 | 
						|
	case *rsa.PublicKey:
 | 
						|
		pubType = x509.RSA
 | 
						|
		hashFunc = crypto.SHA256
 | 
						|
		sigAlgo.Algorithm = oidSignatureSHA256WithRSA
 | 
						|
		sigAlgo.Parameters = asn1.NullRawValue
 | 
						|
 | 
						|
	case *ecdsa.PublicKey:
 | 
						|
		pubType = x509.ECDSA
 | 
						|
 | 
						|
		switch pub.Curve {
 | 
						|
		case elliptic.P224(), elliptic.P256():
 | 
						|
			hashFunc = crypto.SHA256
 | 
						|
			sigAlgo.Algorithm = oidSignatureECDSAWithSHA256
 | 
						|
		case elliptic.P384():
 | 
						|
			hashFunc = crypto.SHA384
 | 
						|
			sigAlgo.Algorithm = oidSignatureECDSAWithSHA384
 | 
						|
		case elliptic.P521():
 | 
						|
			hashFunc = crypto.SHA512
 | 
						|
			sigAlgo.Algorithm = oidSignatureECDSAWithSHA512
 | 
						|
		default:
 | 
						|
			err = errors.New("x509: unknown elliptic curve")
 | 
						|
		}
 | 
						|
 | 
						|
	case ed25519.PublicKey:
 | 
						|
		pubType = x509.Ed25519
 | 
						|
		sigAlgo.Algorithm = oidSignatureEd25519
 | 
						|
 | 
						|
	default:
 | 
						|
		err = errors.New("x509: only RSA, ECDSA and Ed25519 keys supported")
 | 
						|
	}
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	if requestedSigAlgo == 0 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	found := false
 | 
						|
	for _, details := range signatureAlgorithmDetails {
 | 
						|
		if details.algo == requestedSigAlgo {
 | 
						|
			if details.pubKeyAlgo != pubType {
 | 
						|
				err = errors.New("x509: requested SignatureAlgorithm does not match private key type")
 | 
						|
				return
 | 
						|
			}
 | 
						|
			sigAlgo.Algorithm, hashFunc = details.oid, details.hash
 | 
						|
			if hashFunc == 0 && pubType != x509.Ed25519 {
 | 
						|
				err = errors.New("x509: cannot sign with hash function requested")
 | 
						|
				return
 | 
						|
			}
 | 
						|
			if requestedSigAlgo == x509.SHA256WithRSAPSS || requestedSigAlgo == x509.SHA384WithRSAPSS || requestedSigAlgo == x509.SHA512WithRSAPSS {
 | 
						|
				sigAlgo.Parameters = rsaPSSParameters(hashFunc)
 | 
						|
			}
 | 
						|
			found = true
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if !found {
 | 
						|
		err = errors.New("x509: unknown SignatureAlgorithm")
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
// RFC 5280,  4.2.1.1
 | 
						|
type authKeyId struct {
 | 
						|
	Id []byte `asn1:"optional,tag:0"`
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	oidExtensionSubjectKeyId          = []int{2, 5, 29, 14}
 | 
						|
	oidExtensionKeyUsage              = []int{2, 5, 29, 15}
 | 
						|
	oidExtensionExtendedKeyUsage      = []int{2, 5, 29, 37}
 | 
						|
	oidExtensionAuthorityKeyId        = []int{2, 5, 29, 35}
 | 
						|
	oidExtensionBasicConstraints      = []int{2, 5, 29, 19}
 | 
						|
	oidExtensionSubjectAltName        = []int{2, 5, 29, 17}
 | 
						|
	oidExtensionCertificatePolicies   = []int{2, 5, 29, 32}
 | 
						|
	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}
 | 
						|
)
 | 
						|
 | 
						|
// CRLTemplate contains the fields used to create an X.509 v2 Certificate
 | 
						|
// Revocation list.
 | 
						|
type CRLTemplate struct {
 | 
						|
	RevokedCertificates []pkix.RevokedCertificate
 | 
						|
	Number              int
 | 
						|
	ThisUpdate          time.Time
 | 
						|
	NextUpdate          time.Time
 | 
						|
	Extensions          []pkix.Extension
 | 
						|
}
 | 
						|
 | 
						|
// CreateCRL creates a new x509 v2 Certificate Revocation List.
 | 
						|
//
 | 
						|
// The CRL is signed by priv.
 | 
						|
//
 | 
						|
// revokedCerts may be nil, in which case an empty CRL will be created.
 | 
						|
//
 | 
						|
// The issuer distinguished name CRL field and authority key identifier extension
 | 
						|
// are populated using the issuer certificate. issuer must have SubjectKeyId set.
 | 
						|
//
 | 
						|
// The CRL number extension is populated using the Number field of template.
 | 
						|
//
 | 
						|
// The template fields NextUpdate must be greater than ThisUpdate.
 | 
						|
//
 | 
						|
// Any extensions in the Extensions field of template will be copied directly into
 | 
						|
// the CRL.
 | 
						|
//
 | 
						|
// This method is differentiated from the Certificate.CreateCRL method as
 | 
						|
// it creates X509 v2 conformant CRLs as defined by the RFC 5280 CRL profile.
 | 
						|
// This method should be used if created CRLs need to be standards compliant.
 | 
						|
func CreateCRL(rand io.Reader, issuer *x509.Certificate, priv crypto.Signer, template CRLTemplate) ([]byte, error) {
 | 
						|
	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")
 | 
						|
	}
 | 
						|
 | 
						|
	hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), 0)
 | 
						|
	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,
 | 
						|
		Signature:           signatureAlgorithm,
 | 
						|
		Issuer:              issuer.Subject.ToRDNSequence(),
 | 
						|
		ThisUpdate:          template.ThisUpdate.UTC(),
 | 
						|
		NextUpdate:          template.NextUpdate.UTC(),
 | 
						|
		RevokedCertificates: revokedCertsUTC,
 | 
						|
		Extensions: []pkix.Extension{
 | 
						|
			{
 | 
						|
				Id:    oidExtensionAuthorityKeyId,
 | 
						|
				Value: aki,
 | 
						|
			},
 | 
						|
			{
 | 
						|
				Id:    oidExtensionCRLNumber,
 | 
						|
				Value: crlNum,
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	if len(template.Extensions) > 0 {
 | 
						|
		tbsCertList.Extensions = append(tbsCertList.Extensions, template.Extensions...)
 | 
						|
	}
 | 
						|
 | 
						|
	tbsCertListContents, err := asn1.Marshal(tbsCertList)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	h := hashFunc.New()
 | 
						|
	h.Write(tbsCertListContents)
 | 
						|
	digest := h.Sum(nil)
 | 
						|
 | 
						|
	signature, err := priv.Sign(rand, digest, hashFunc)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return asn1.Marshal(pkix.CertificateList{
 | 
						|
		TBSCertList:        tbsCertList,
 | 
						|
		SignatureAlgorithm: signatureAlgorithm,
 | 
						|
		SignatureValue:     asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
 | 
						|
	})
 | 
						|
}
 |