github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/cloudflare/cfssl/csr/csr.go (about) 1 // Package csr implements certificate requests for CFSSL. 2 package csr 3 4 import ( 5 "crypto" 6 "crypto/elliptic" 7 "crypto/rand" 8 "crypto/rsa" 9 "encoding/asn1" 10 "encoding/pem" 11 "errors" 12 "github.com/hellobchain/newcryptosm/ecdsa" 13 "github.com/hellobchain/newcryptosm/sm2" 14 "github.com/hellobchain/newcryptosm/x509" 15 "github.com/hellobchain/newcryptosm/x509/pkix" 16 "net" 17 "net/mail" 18 "strings" 19 20 cferr "github.com/hellobchain/third_party/cloudflare/cfssl/errors" 21 "github.com/hellobchain/third_party/cloudflare/cfssl/helpers" 22 "github.com/hellobchain/third_party/cloudflare/cfssl/log" 23 ) 24 25 const ( 26 curveP256 = 256 27 curveP384 = 384 28 curveP521 = 521 29 ) 30 31 // A Name contains the SubjectInfo fields. 32 type Name struct { 33 C string // Country 34 ST string // State 35 L string // Locality 36 O string // OrganisationName 37 OU string // OrganisationalUnitName 38 SerialNumber string 39 } 40 41 // A KeyRequest is a generic request for a new key. 42 type KeyRequest interface { 43 Algo() string 44 Size() int 45 Generate() (crypto.PrivateKey, error) 46 SigAlgo() x509.SignatureAlgorithm 47 } 48 49 // A BasicKeyRequest contains the algorithm and key size for a new private key. 50 type BasicKeyRequest struct { 51 A string `json:"algo" yaml:"algo"` 52 S int `json:"size" yaml:"size"` 53 } 54 55 // NewBasicKeyRequest returns a default BasicKeyRequest. 56 func NewBasicKeyRequest(algo string) *BasicKeyRequest { 57 return &BasicKeyRequest{algo, curveP256} 58 } 59 60 // Algo returns the requested key algorithm represented as a string. 61 func (kr *BasicKeyRequest) Algo() string { 62 return kr.A 63 } 64 65 // Size returns the requested key size. 66 func (kr *BasicKeyRequest) Size() int { 67 return kr.S 68 } 69 70 // Generate generates a key as specified in the request. Currently, 71 // only ECDSA and RSA are supported. 72 func (kr *BasicKeyRequest) Generate() (crypto.PrivateKey, error) { 73 log.Debugf("generate key from request: algo=%s, size=%d", kr.Algo(), kr.Size()) 74 switch kr.Algo() { 75 case "rsa": 76 if kr.Size() < 2048 { 77 return nil, errors.New("RSA key is too weak") 78 } 79 if kr.Size() > 8192 { 80 return nil, errors.New("RSA key size too large") 81 } 82 return rsa.GenerateKey(rand.Reader, kr.Size()) 83 case "ecdsa": 84 var curve elliptic.Curve 85 switch kr.Size() { 86 case curveP256: 87 curve = elliptic.P256() 88 case curveP384: 89 curve = elliptic.P384() 90 case curveP521: 91 curve = elliptic.P521() 92 default: 93 return nil, errors.New("invalid curve") 94 } 95 return ecdsa.GenerateKey(curve, rand.Reader) 96 case "sm2": 97 var curve elliptic.Curve 98 switch kr.Size() { 99 case curveP256: 100 curve = sm2.SM2() 101 default: 102 return nil, errors.New("invalid curve") 103 } 104 return ecdsa.GenerateKey(curve, rand.Reader) 105 default: 106 return nil, errors.New("invalid algorithm") 107 } 108 } 109 110 // SigAlgo returns an appropriate X.509 signature algorithm given the 111 // key request's type and size. 112 func (kr *BasicKeyRequest) SigAlgo() x509.SignatureAlgorithm { 113 switch kr.Algo() { 114 case "rsa": 115 switch { 116 case kr.Size() >= 4096: 117 return x509.SHA512WithRSA 118 case kr.Size() >= 3072: 119 return x509.SHA384WithRSA 120 case kr.Size() >= 2048: 121 return x509.SHA256WithRSA 122 default: 123 return x509.SHA1WithRSA 124 } 125 case "ecdsa": 126 switch kr.Size() { 127 case curveP521: 128 return x509.ECDSAWithSHA512 129 case curveP384: 130 return x509.ECDSAWithSHA384 131 case curveP256: 132 return x509.ECDSAWithSHA256 133 default: 134 return x509.ECDSAWithSHA1 135 } 136 case "sm2": 137 switch kr.Size() { 138 case curveP256: 139 return x509.SM2WithSM3 140 default: 141 return x509.SM2WithSHA1 142 } 143 default: 144 return x509.UnknownSignatureAlgorithm 145 } 146 } 147 148 // CAConfig is a section used in the requests initialising a new CA. 149 type CAConfig struct { 150 PathLength int `json:"pathlen" yaml:"pathlen"` 151 PathLenZero bool `json:"pathlenzero" yaml:"pathlenzero"` 152 Expiry string `json:"expiry" yaml:"expiry"` 153 } 154 155 // A CertificateRequest encapsulates the API interface to the 156 // certificate request functionality. 157 type CertificateRequest struct { 158 CN string 159 Names []Name `json:"names" yaml:"names"` 160 Hosts []string `json:"hosts" yaml:"hosts"` 161 KeyRequest KeyRequest `json:"key,omitempty" yaml:"key,omitempty"` 162 CA *CAConfig `json:"ca,omitempty" yaml:"ca,omitempty"` 163 SerialNumber string `json:"serialnumber,omitempty" yaml:"serialnumber,omitempty"` 164 } 165 166 // New returns a new, empty CertificateRequest with a 167 // BasicKeyRequest. 168 func New(algo string) *CertificateRequest { 169 return &CertificateRequest{ 170 KeyRequest: NewBasicKeyRequest(algo), 171 } 172 } 173 174 // appendIf appends to a if s is not an empty string. 175 func appendIf(s string, a *[]string) { 176 if s != "" { 177 *a = append(*a, s) 178 } 179 } 180 181 // Name returns the PKIX name for the request. 182 func (cr *CertificateRequest) Name() pkix.Name { 183 var name pkix.Name 184 name.CommonName = cr.CN 185 186 for _, n := range cr.Names { 187 appendIf(n.C, &name.Country) 188 appendIf(n.ST, &name.Province) 189 appendIf(n.L, &name.Locality) 190 appendIf(n.O, &name.Organization) 191 appendIf(n.OU, &name.OrganizationalUnit) 192 } 193 name.SerialNumber = cr.SerialNumber 194 return name 195 } 196 197 // BasicConstraints CSR information RFC 5280, 4.2.1.9 198 type BasicConstraints struct { 199 IsCA bool `asn1:"optional"` 200 MaxPathLen int `asn1:"optional,default:-1"` 201 } 202 203 // ParseRequest takes a certificate request and generates a key and 204 // CSR from it. It does no validation -- caveat emptor. It will, 205 // however, fail if the key request is not valid (i.e., an unsupported 206 // curve or RSA key size). The lack of validation was specifically 207 // chosen to allow the end user to define a policy and validate the 208 // request appropriately before calling this function. 209 func ParseRequest(defaultAlgo string, req *CertificateRequest) (csr, key []byte, err error) { 210 log.Info("received CSR") 211 if req.KeyRequest == nil { 212 req.KeyRequest = NewBasicKeyRequest(defaultAlgo) 213 } 214 215 log.Infof("generating key: %s-%d", req.KeyRequest.Algo(), req.KeyRequest.Size()) 216 priv, err := req.KeyRequest.Generate() 217 if err != nil { 218 err = cferr.Wrap(cferr.PrivateKeyError, cferr.GenerationFailed, err) 219 return 220 } 221 222 switch priv := priv.(type) { 223 case *rsa.PrivateKey: 224 key = x509.MarshalPKCS1PrivateKey(priv) 225 block := pem.Block{ 226 Type: "RSA PRIVATE KEY", 227 Bytes: key, 228 } 229 key = pem.EncodeToMemory(&block) 230 case *ecdsa.PrivateKey: 231 key, err = x509.MarshalECPrivateKey(priv) 232 if err != nil { 233 err = cferr.Wrap(cferr.PrivateKeyError, cferr.Unknown, err) 234 return 235 } 236 block := pem.Block{ 237 Type: "EC PRIVATE KEY", 238 Bytes: key, 239 } 240 key = pem.EncodeToMemory(&block) 241 default: 242 panic("Generate should have failed to produce a valid key.") 243 } 244 245 csr, err = Generate(priv.(crypto.Signer), req) 246 if err != nil { 247 log.Errorf("failed to generate a CSR: %v", err) 248 err = cferr.Wrap(cferr.CSRError, cferr.BadRequest, err) 249 } 250 return 251 } 252 253 // ExtractCertificateRequest extracts a CertificateRequest from 254 // x509.Certificate. It is aimed to used for generating a new certificate 255 // from an existing certificate. For a root certificate, the CA expiry 256 // length is calculated as the duration between cert.NotAfter and cert.NotBefore. 257 func ExtractCertificateRequest(defaultAlgo string, cert *x509.Certificate) *CertificateRequest { 258 req := New(defaultAlgo) 259 req.CN = cert.Subject.CommonName 260 req.Names = getNames(cert.Subject) 261 req.Hosts = getHosts(cert) 262 req.SerialNumber = cert.Subject.SerialNumber 263 264 if cert.IsCA { 265 req.CA = new(CAConfig) 266 // CA expiry length is calculated based on the input cert 267 // issue date and expiry date. 268 req.CA.Expiry = cert.NotAfter.Sub(cert.NotBefore).String() 269 req.CA.PathLength = cert.MaxPathLen 270 req.CA.PathLenZero = cert.MaxPathLenZero 271 } 272 273 return req 274 } 275 276 func getHosts(cert *x509.Certificate) []string { 277 var hosts []string 278 for _, ip := range cert.IPAddresses { 279 hosts = append(hosts, ip.String()) 280 } 281 for _, dns := range cert.DNSNames { 282 hosts = append(hosts, dns) 283 } 284 for _, email := range cert.EmailAddresses { 285 hosts = append(hosts, email) 286 } 287 288 return hosts 289 } 290 291 // getNames returns an array of Names from the certificate 292 // It onnly cares about Country, Organization, OrganizationalUnit, Locality, Province 293 func getNames(sub pkix.Name) []Name { 294 // anonymous func for finding the max of a list of interger 295 max := func(v1 int, vn ...int) (max int) { 296 max = v1 297 for i := 0; i < len(vn); i++ { 298 if vn[i] > max { 299 max = vn[i] 300 } 301 } 302 return max 303 } 304 305 nc := len(sub.Country) 306 norg := len(sub.Organization) 307 nou := len(sub.OrganizationalUnit) 308 nl := len(sub.Locality) 309 np := len(sub.Province) 310 311 n := max(nc, norg, nou, nl, np) 312 313 names := make([]Name, n) 314 for i := range names { 315 if i < nc { 316 names[i].C = sub.Country[i] 317 } 318 if i < norg { 319 names[i].O = sub.Organization[i] 320 } 321 if i < nou { 322 names[i].OU = sub.OrganizationalUnit[i] 323 } 324 if i < nl { 325 names[i].L = sub.Locality[i] 326 } 327 if i < np { 328 names[i].ST = sub.Province[i] 329 } 330 } 331 return names 332 } 333 334 // A Generator is responsible for validating certificate requests. 335 type Generator struct { 336 Validator func(*CertificateRequest) error 337 } 338 339 // ProcessRequest validates and processes the incoming request. It is 340 // a wrapper around a validator and the ParseRequest function. 341 func (g *Generator) ProcessRequest(defaultAlgo string, req *CertificateRequest) (csr, key []byte, err error) { 342 343 log.Info("generate received request") 344 err = g.Validator(req) 345 if err != nil { 346 log.Warningf("invalid request: %v", err) 347 return nil, nil, err 348 } 349 350 csr, key, err = ParseRequest(defaultAlgo, req) 351 if err != nil { 352 return nil, nil, err 353 } 354 return 355 } 356 357 // IsNameEmpty returns true if the name has no identifying information in it. 358 func IsNameEmpty(n Name) bool { 359 empty := func(s string) bool { return strings.TrimSpace(s) == "" } 360 361 if empty(n.C) && empty(n.ST) && empty(n.L) && empty(n.O) && empty(n.OU) { 362 return true 363 } 364 return false 365 } 366 367 // Regenerate uses the provided CSR as a template for signing a new 368 // CSR using priv. 369 func Regenerate(priv crypto.Signer, csr []byte) ([]byte, error) { 370 req, extra, err := helpers.ParseCSR(csr) 371 if err != nil { 372 return nil, err 373 } else if len(extra) > 0 { 374 return nil, errors.New("csr: trailing data in certificate request") 375 } 376 377 return x509.CreateCertificateRequest(rand.Reader, req, priv) 378 } 379 380 // Generate creates a new CSR from a CertificateRequest structure and 381 // an existing key. The KeyRequest field is ignored. 382 func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err error) { 383 sigAlgo := helpers.SignerAlgo(priv) 384 if sigAlgo == x509.UnknownSignatureAlgorithm { 385 return nil, cferr.New(cferr.PrivateKeyError, cferr.Unavailable) 386 } 387 388 var tpl = x509.CertificateRequest{ 389 Subject: req.Name(), 390 SignatureAlgorithm: sigAlgo, 391 } 392 393 for i := range req.Hosts { 394 if ip := net.ParseIP(req.Hosts[i]); ip != nil { 395 tpl.IPAddresses = append(tpl.IPAddresses, ip) 396 } else if email, err := mail.ParseAddress(req.Hosts[i]); err == nil && email != nil { 397 tpl.EmailAddresses = append(tpl.EmailAddresses, email.Address) 398 } else { 399 tpl.DNSNames = append(tpl.DNSNames, req.Hosts[i]) 400 } 401 } 402 403 if req.CA != nil { 404 err = appendCAInfoToCSR(req.CA, &tpl) 405 if err != nil { 406 err = cferr.Wrap(cferr.CSRError, cferr.GenerationFailed, err) 407 return 408 } 409 } 410 411 csr, err = x509.CreateCertificateRequest(rand.Reader, &tpl, priv) 412 if err != nil { 413 log.Errorf("failed to generate a CSR: %v", err) 414 err = cferr.Wrap(cferr.CSRError, cferr.BadRequest, err) 415 return 416 } 417 block := pem.Block{ 418 Type: "CERTIFICATE REQUEST", 419 Bytes: csr, 420 } 421 422 log.Info("encoded CSR") 423 csr = pem.EncodeToMemory(&block) 424 return 425 } 426 427 // appendCAInfoToCSR appends CAConfig BasicConstraint extension to a CSR 428 func appendCAInfoToCSR(reqConf *CAConfig, csr *x509.CertificateRequest) error { 429 pathlen := reqConf.PathLength 430 if pathlen == 0 && !reqConf.PathLenZero { 431 pathlen = -1 432 } 433 val, err := asn1.Marshal(BasicConstraints{true, pathlen}) 434 435 if err != nil { 436 return err 437 } 438 439 csr.ExtraExtensions = []pkix.Extension{ 440 { 441 Id: asn1.ObjectIdentifier{2, 5, 29, 19}, 442 Value: val, 443 Critical: true, 444 }, 445 } 446 447 return nil 448 }