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 }