github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/cloudflare/cfssl/signer/signer.go (about)

     1  // Package signer implements certificate signature functionality for CFSSL.
     2  package signer
     3  
     4  import (
     5  	"crypto"
     6  	"crypto/elliptic"
     7  	"crypto/rsa"
     8  	"crypto/sha1"
     9  	"encoding/asn1"
    10  	"errors"
    11  	"github.com/hellobchain/newcryptosm/ecdsa"
    12  	"github.com/hellobchain/newcryptosm/http"
    13  	"github.com/hellobchain/newcryptosm/sm2"
    14  	"github.com/hellobchain/newcryptosm/x509"
    15  	"github.com/hellobchain/newcryptosm/x509/pkix"
    16  	"math/big"
    17  	"strings"
    18  	"time"
    19  
    20  	"github.com/hellobchain/third_party/cloudflare/cfssl/certdb"
    21  	"github.com/hellobchain/third_party/cloudflare/cfssl/config"
    22  	"github.com/hellobchain/third_party/cloudflare/cfssl/csr"
    23  	cferr "github.com/hellobchain/third_party/cloudflare/cfssl/errors"
    24  	"github.com/hellobchain/third_party/cloudflare/cfssl/info"
    25  )
    26  
    27  // Subject contains the information that should be used to override the
    28  // subject information when signing a certificate.
    29  type Subject struct {
    30  	CN           string
    31  	Names        []csr.Name `json:"names"`
    32  	SerialNumber string
    33  }
    34  
    35  // Extension represents a raw extension to be included in the certificate.  The
    36  // "value" field must be hex encoded.
    37  type Extension struct {
    38  	ID       config.OID `json:"id"`
    39  	Critical bool       `json:"critical"`
    40  	Value    string     `json:"value"`
    41  }
    42  
    43  // SignRequest stores a signature request, which contains the hostname,
    44  // the CSR, optional subject information, and the signature profile.
    45  //
    46  // Extensions provided in the signRequest are copied into the certificate, as
    47  // long as they are in the ExtensionWhitelist for the signer's policy.
    48  // Extensions requested in the CSR are ignored, except for those processed by
    49  // ParseCertificateRequest (mainly subjectAltName).
    50  type SignRequest struct {
    51  	Hosts       []string    `json:"hosts"`
    52  	Request     string      `json:"certificate_request"`
    53  	Subject     *Subject    `json:"subject,omitempty"`
    54  	Profile     string      `json:"profile"`
    55  	CRLOverride string      `json:"crl_override"`
    56  	Label       string      `json:"label"`
    57  	Serial      *big.Int    `json:"serial,omitempty"`
    58  	Extensions  []Extension `json:"extensions,omitempty"`
    59  	// If provided, NotBefore will be used without modification (except
    60  	// for canonicalization) as the value of the notBefore field of the
    61  	// certificate. In particular no backdating adjustment will be made
    62  	// when NotBefore is provided.
    63  	NotBefore time.Time
    64  	// If provided, NotAfter will be used without modification (except
    65  	// for canonicalization) as the value of the notAfter field of the
    66  	// certificate.
    67  	NotAfter time.Time
    68  }
    69  
    70  // appendIf appends to a if s is not an empty string.
    71  func appendIf(s string, a *[]string) {
    72  	if s != "" {
    73  		*a = append(*a, s)
    74  	}
    75  }
    76  
    77  // Name returns the PKIX name for the subject.
    78  func (s *Subject) Name() pkix.Name {
    79  	var name pkix.Name
    80  	name.CommonName = s.CN
    81  
    82  	for _, n := range s.Names {
    83  		appendIf(n.C, &name.Country)
    84  		appendIf(n.ST, &name.Province)
    85  		appendIf(n.L, &name.Locality)
    86  		appendIf(n.O, &name.Organization)
    87  		appendIf(n.OU, &name.OrganizationalUnit)
    88  	}
    89  	name.SerialNumber = s.SerialNumber
    90  	return name
    91  }
    92  
    93  // SplitHosts takes a comma-spearated list of hosts and returns a slice
    94  // with the hosts split
    95  func SplitHosts(hostList string) []string {
    96  	if hostList == "" {
    97  		return nil
    98  	}
    99  
   100  	return strings.Split(hostList, ",")
   101  }
   102  
   103  // A Signer contains a CA's certificate and private key for signing
   104  // certificates, a Signing policy to refer to and a SignatureAlgorithm.
   105  type Signer interface {
   106  	Info(info.Req) (*info.Resp, error)
   107  	Policy() *config.Signing
   108  	SetDBAccessor(certdb.Accessor)
   109  	GetDBAccessor() certdb.Accessor
   110  	SetPolicy(*config.Signing)
   111  	SigAlgo() x509.SignatureAlgorithm
   112  	Sign(req SignRequest) (cert []byte, err error)
   113  	SetReqModifier(func(*http.Request, []byte))
   114  }
   115  
   116  // Profile gets the specific profile from the signer
   117  func Profile(s Signer, profile string) (*config.SigningProfile, error) {
   118  	var p *config.SigningProfile
   119  	policy := s.Policy()
   120  	if policy != nil && policy.Profiles != nil && profile != "" {
   121  		p = policy.Profiles[profile]
   122  	}
   123  
   124  	if p == nil && policy != nil {
   125  		p = policy.Default
   126  	}
   127  
   128  	if p == nil {
   129  		return nil, cferr.Wrap(cferr.APIClientError, cferr.ClientHTTPError, errors.New("profile must not be nil"))
   130  	}
   131  	return p, nil
   132  }
   133  
   134  // DefaultSigAlgo returns an appropriate X.509 signature algorithm given
   135  // the CA's private key.
   136  func DefaultSigAlgo(priv crypto.Signer) x509.SignatureAlgorithm {
   137  	pub := priv.Public()
   138  	switch pub := pub.(type) {
   139  	case *rsa.PublicKey:
   140  		keySize := pub.N.BitLen()
   141  		switch {
   142  		case keySize >= 4096:
   143  			return x509.SHA512WithRSA
   144  		case keySize >= 3072:
   145  			return x509.SHA384WithRSA
   146  		case keySize >= 2048:
   147  			return x509.SHA256WithRSA
   148  		default:
   149  			return x509.SHA1WithRSA
   150  		}
   151  	case *ecdsa.PublicKey:
   152  		switch pub.Curve {
   153  		case elliptic.P256():
   154  			return x509.ECDSAWithSHA256
   155  		case elliptic.P384():
   156  			return x509.ECDSAWithSHA384
   157  		case elliptic.P521():
   158  			return x509.ECDSAWithSHA512
   159  		case sm2.SM2():
   160  			return x509.SM2WithSM3
   161  		default:
   162  			return x509.ECDSAWithSHA1
   163  		}
   164  	default:
   165  		return x509.UnknownSignatureAlgorithm
   166  	}
   167  }
   168  
   169  // ParseCertificateRequest takes an incoming certificate request and
   170  // builds a certificate template from it.
   171  func ParseCertificateRequest(s Signer, csrBytes []byte) (template *x509.Certificate, err error) {
   172  	csrv, err := x509.ParseCertificateRequest(csrBytes)
   173  	if err != nil {
   174  		err = cferr.Wrap(cferr.CSRError, cferr.ParseFailed, err)
   175  		return
   176  	}
   177  
   178  	err = csrv.CheckSignature()
   179  	if err != nil {
   180  		err = cferr.Wrap(cferr.CSRError, cferr.KeyMismatch, err)
   181  		return
   182  	}
   183  
   184  	template = &x509.Certificate{
   185  		Subject:            csrv.Subject,
   186  		PublicKeyAlgorithm: csrv.PublicKeyAlgorithm,
   187  		PublicKey:          csrv.PublicKey,
   188  		SignatureAlgorithm: s.SigAlgo(),
   189  		DNSNames:           csrv.DNSNames,
   190  		IPAddresses:        csrv.IPAddresses,
   191  		EmailAddresses:     csrv.EmailAddresses,
   192  	}
   193  
   194  	for _, val := range csrv.Extensions {
   195  		// Check the CSR for the X.509 BasicConstraints (RFC 5280, 4.2.1.9)
   196  		// extension and append to template if necessary
   197  		if val.Id.Equal(asn1.ObjectIdentifier{2, 5, 29, 19}) {
   198  			var constraints csr.BasicConstraints
   199  			var rest []byte
   200  
   201  			if rest, err = asn1.Unmarshal(val.Value, &constraints); err != nil {
   202  				return nil, cferr.Wrap(cferr.CSRError, cferr.ParseFailed, err)
   203  			} else if len(rest) != 0 {
   204  				return nil, cferr.Wrap(cferr.CSRError, cferr.ParseFailed, errors.New("x509: trailing data after X.509 BasicConstraints"))
   205  			}
   206  
   207  			template.BasicConstraintsValid = true
   208  			template.IsCA = constraints.IsCA
   209  			template.MaxPathLen = constraints.MaxPathLen
   210  			template.MaxPathLenZero = template.MaxPathLen == 0
   211  		}
   212  	}
   213  
   214  	return
   215  }
   216  
   217  type subjectPublicKeyInfo struct {
   218  	Algorithm        pkix.AlgorithmIdentifier
   219  	SubjectPublicKey asn1.BitString
   220  }
   221  
   222  // ComputeSKI derives an SKI from the certificate's public key in a
   223  // standard manner. This is done by computing the SHA-1 digest of the
   224  // SubjectPublicKeyInfo component of the certificate.
   225  func ComputeSKI(template *x509.Certificate) ([]byte, error) {
   226  	pub := template.PublicKey
   227  	encodedPub, err := x509.MarshalPKIXPublicKey(pub)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	var subPKI subjectPublicKeyInfo
   233  	_, err = asn1.Unmarshal(encodedPub, &subPKI)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  
   238  	pubHash := sha1.Sum(subPKI.SubjectPublicKey.Bytes)
   239  	return pubHash[:], nil
   240  }
   241  
   242  // FillTemplate is a utility function that tries to load as much of
   243  // the certificate template as possible from the profiles and current
   244  // template. It fills in the key uses, expiration, revocation URLs
   245  // and SKI.
   246  func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.SigningProfile, notBefore time.Time, notAfter time.Time) error {
   247  	ski, err := ComputeSKI(template)
   248  	if err != nil {
   249  		return err
   250  	}
   251  
   252  	var (
   253  		eku             []x509.ExtKeyUsage
   254  		ku              x509.KeyUsage
   255  		backdate        time.Duration
   256  		expiry          time.Duration
   257  		crlURL, ocspURL string
   258  		issuerURL       = profile.IssuerURL
   259  	)
   260  
   261  	// The third value returned from Usages is a list of unknown key usages.
   262  	// This should be used when validating the profile at load, and isn't used
   263  	// here.
   264  	ku, eku, _ = profile.Usages()
   265  	if profile.IssuerURL == nil {
   266  		issuerURL = defaultProfile.IssuerURL
   267  	}
   268  
   269  	if ku == 0 && len(eku) == 0 {
   270  		return cferr.New(cferr.PolicyError, cferr.NoKeyUsages)
   271  	}
   272  
   273  	if expiry = profile.Expiry; expiry == 0 {
   274  		expiry = defaultProfile.Expiry
   275  	}
   276  
   277  	if crlURL = profile.CRL; crlURL == "" {
   278  		crlURL = defaultProfile.CRL
   279  	}
   280  	if ocspURL = profile.OCSP; ocspURL == "" {
   281  		ocspURL = defaultProfile.OCSP
   282  	}
   283  
   284  	if notBefore.IsZero() {
   285  		if !profile.NotBefore.IsZero() {
   286  			notBefore = profile.NotBefore
   287  		} else {
   288  			if backdate = profile.Backdate; backdate == 0 {
   289  				backdate = -5 * time.Minute
   290  			} else {
   291  				backdate = -1 * profile.Backdate
   292  			}
   293  			notBefore = time.Now().Round(time.Minute).Add(backdate)
   294  		}
   295  	}
   296  	notBefore = notBefore.UTC()
   297  
   298  	if notAfter.IsZero() {
   299  		if !profile.NotAfter.IsZero() {
   300  			notAfter = profile.NotAfter
   301  		} else {
   302  			notAfter = notBefore.Add(expiry)
   303  		}
   304  	}
   305  	notAfter = notAfter.UTC()
   306  
   307  	template.NotBefore = notBefore
   308  	template.NotAfter = notAfter
   309  	template.KeyUsage = ku
   310  	template.ExtKeyUsage = eku
   311  	template.BasicConstraintsValid = true
   312  	template.IsCA = profile.CAConstraint.IsCA
   313  	if template.IsCA {
   314  		template.MaxPathLen = profile.CAConstraint.MaxPathLen
   315  		if template.MaxPathLen == 0 {
   316  			template.MaxPathLenZero = profile.CAConstraint.MaxPathLenZero
   317  		}
   318  		template.DNSNames = nil
   319  		template.EmailAddresses = nil
   320  	}
   321  	template.SubjectKeyId = ski
   322  
   323  	if ocspURL != "" {
   324  		template.OCSPServer = []string{ocspURL}
   325  	}
   326  	if crlURL != "" {
   327  		template.CRLDistributionPoints = []string{crlURL}
   328  	}
   329  
   330  	if len(issuerURL) != 0 {
   331  		template.IssuingCertificateURL = issuerURL
   332  	}
   333  	if len(profile.Policies) != 0 {
   334  		err = addPolicies(template, profile.Policies)
   335  		if err != nil {
   336  			return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
   337  		}
   338  	}
   339  	if profile.OCSPNoCheck {
   340  		ocspNoCheckExtension := pkix.Extension{
   341  			Id:       asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 5},
   342  			Critical: false,
   343  			Value:    []byte{0x05, 0x00},
   344  		}
   345  		template.ExtraExtensions = append(template.ExtraExtensions, ocspNoCheckExtension)
   346  	}
   347  
   348  	return nil
   349  }
   350  
   351  type policyInformation struct {
   352  	PolicyIdentifier asn1.ObjectIdentifier
   353  	Qualifiers       []interface{} `asn1:"tag:optional,omitempty"`
   354  }
   355  
   356  type cpsPolicyQualifier struct {
   357  	PolicyQualifierID asn1.ObjectIdentifier
   358  	Qualifier         string `asn1:"tag:optional,ia5"`
   359  }
   360  
   361  type userNotice struct {
   362  	ExplicitText string `asn1:"tag:optional,utf8"`
   363  }
   364  type userNoticePolicyQualifier struct {
   365  	PolicyQualifierID asn1.ObjectIdentifier
   366  	Qualifier         userNotice
   367  }
   368  
   369  var (
   370  	// Per https://tools.ietf.org/html/rfc3280.html#page-106, this represents:
   371  	// iso(1) identified-organization(3) dod(6) internet(1) security(5)
   372  	//   mechanisms(5) pkix(7) id-qt(2) id-qt-cps(1)
   373  	iDQTCertificationPracticeStatement = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 2, 1}
   374  	// iso(1) identified-organization(3) dod(6) internet(1) security(5)
   375  	//   mechanisms(5) pkix(7) id-qt(2) id-qt-unotice(2)
   376  	iDQTUserNotice = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 2, 2}
   377  
   378  	// CTPoisonOID is the object ID of the critical poison extension for precertificates
   379  	// https://tools.ietf.org/html/rfc6962#page-9
   380  	CTPoisonOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
   381  
   382  	// SCTListOID is the object ID for the Signed Certificate Timestamp certificate extension
   383  	// https://tools.ietf.org/html/rfc6962#page-14
   384  	SCTListOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2}
   385  )
   386  
   387  // addPolicies adds Certificate Policies and optional Policy Qualifiers to a
   388  // certificate, based on the input config. Go's x509 library allows setting
   389  // Certificate Policies easily, but does not support nested Policy Qualifiers
   390  // under those policies. So we need to construct the ASN.1 structure ourselves.
   391  func addPolicies(template *x509.Certificate, policies []config.CertificatePolicy) error {
   392  	asn1PolicyList := []policyInformation{}
   393  
   394  	for _, policy := range policies {
   395  		pi := policyInformation{
   396  			// The PolicyIdentifier is an OID assigned to a given issuer.
   397  			PolicyIdentifier: asn1.ObjectIdentifier(policy.ID),
   398  		}
   399  		for _, qualifier := range policy.Qualifiers {
   400  			switch qualifier.Type {
   401  			case "id-qt-unotice":
   402  				pi.Qualifiers = append(pi.Qualifiers,
   403  					userNoticePolicyQualifier{
   404  						PolicyQualifierID: iDQTUserNotice,
   405  						Qualifier: userNotice{
   406  							ExplicitText: qualifier.Value,
   407  						},
   408  					})
   409  			case "id-qt-cps":
   410  				pi.Qualifiers = append(pi.Qualifiers,
   411  					cpsPolicyQualifier{
   412  						PolicyQualifierID: iDQTCertificationPracticeStatement,
   413  						Qualifier:         qualifier.Value,
   414  					})
   415  			default:
   416  				return errors.New("Invalid qualifier type in Policies " + qualifier.Type)
   417  			}
   418  		}
   419  		asn1PolicyList = append(asn1PolicyList, pi)
   420  	}
   421  
   422  	asn1Bytes, err := asn1.Marshal(asn1PolicyList)
   423  	if err != nil {
   424  		return err
   425  	}
   426  
   427  	template.ExtraExtensions = append(template.ExtraExtensions, pkix.Extension{
   428  		Id:       asn1.ObjectIdentifier{2, 5, 29, 32},
   429  		Critical: false,
   430  		Value:    asn1Bytes,
   431  	})
   432  	return nil
   433  }