dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/credentials/certgenerate/generate_cert.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* 19 * 20 * Copyright Istio Authors 21 * 22 */ 23 24 package certgenerate 25 26 import ( 27 "crypto" 28 "crypto/ecdsa" 29 "crypto/ed25519" 30 "crypto/elliptic" 31 "crypto/rand" 32 "crypto/rsa" 33 "crypto/x509" 34 "crypto/x509/pkix" 35 "encoding/pem" 36 "errors" 37 "fmt" 38 "math/big" 39 "os" 40 "strings" 41 "time" 42 43 "github.com/dubbogo/gost/log/logger" 44 ) 45 46 // SupportedECSignatureAlgorithms are the types of EC Signature Algorithms 47 // to be used in key generation (e.g. ECDSA or ED2551) 48 type SupportedECSignatureAlgorithms string 49 50 const ( 51 // only ECDSA using P256 is currently supported 52 EcdsaSigAlg SupportedECSignatureAlgorithms = "ECDSA" 53 ) 54 55 // CertOptions contains options for generating a new certificate. 56 type CertOptions struct { 57 // Comma-separated hostnames and IPs to generate a certificate for. 58 // This can also be set to the identity running the workload, 59 // like kubernetes service account. 60 Host string 61 62 // The NotBefore field of the issued certificate. 63 NotBefore time.Time 64 65 // TTL of the certificate. NotAfter - NotBefore. 66 TTL time.Duration 67 68 // Signer certificate. 69 SignerCert *x509.Certificate 70 71 // Signer private key. 72 SignerPriv crypto.PrivateKey 73 74 // Signer private key (PEM encoded). 75 SignerPrivPem []byte 76 77 // Organization for this certificate. 78 Org string 79 80 // The size of RSA private key to be generated. 81 RSAKeySize int 82 83 // Whether this certificate is used as signing cert for CA. 84 IsCA bool 85 86 // Whether this certificate is self-signed. 87 IsSelfSigned bool 88 89 // Whether this certificate is for a client. 90 IsClient bool 91 92 // Whether this certificate is for a server. 93 IsServer bool 94 95 // Whether this certificate is for dual-use clients (SAN+CN). 96 IsDualUse bool 97 98 // If true, the private key is encoded with PKCS#8. 99 PKCS8Key bool 100 101 // The type of Elliptical Signature algorithm to use 102 // when generating private keys. Currently only ECDSA is supported. 103 // If empty, RSA is used, otherwise ECC is used. 104 ECSigAlg SupportedECSignatureAlgorithms 105 106 // Subjective Alternative Name values. 107 DNSNames string 108 } 109 110 // GenCertKeyFromOptions generates a X.509 certificate and a private key with the given options. 111 func GenCertKeyFromOptions(options CertOptions) (pemCert []byte, pemKey []byte, err error) { 112 // Generate the appropriate private&public key pair based on options. 113 // The public key will be bound to the certificate generated below. The 114 // private key will be used to sign this certificate in the self-signed 115 // case, otherwise the certificate is signed by the signer private key 116 // as specified in the CertOptions. 117 if options.ECSigAlg != "" { 118 var ecPriv *ecdsa.PrivateKey 119 120 switch options.ECSigAlg { 121 case EcdsaSigAlg: 122 ecPriv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 123 if err != nil { 124 return nil, nil, fmt.Errorf("cert generation fails at EC key generation (%v)", err) 125 } 126 default: 127 return nil, nil, errors.New("cert generation fails due to unsupported EC signature algorithm") 128 } 129 return genCert(options, ecPriv, &ecPriv.PublicKey) 130 } 131 132 if options.RSAKeySize < minimumRsaKeySize { 133 return nil, nil, fmt.Errorf("requested key size does not meet the minimum requied size of %d (requested: %d)", minimumRsaKeySize, options.RSAKeySize) 134 } 135 rsaPriv, err := rsa.GenerateKey(rand.Reader, options.RSAKeySize) 136 if err != nil { 137 return nil, nil, fmt.Errorf("cert generation fails at RSA key generation (%v)", err) 138 } 139 return genCert(options, rsaPriv, &rsaPriv.PublicKey) 140 } 141 142 func genCert(options CertOptions, priv interface{}, key interface{}) ([]byte, []byte, error) { 143 template, err := genCertTemplateFromOptions(options) 144 if err != nil { 145 return nil, nil, fmt.Errorf("cert generation fails at cert template creation (%v)", err) 146 } 147 signerCert, signerKey := template, crypto.PrivateKey(priv) 148 if !options.IsSelfSigned { 149 signerCert, signerKey = options.SignerCert, options.SignerPriv 150 } 151 certBytes, err := x509.CreateCertificate(rand.Reader, template, signerCert, key, signerKey) 152 if err != nil { 153 return nil, nil, fmt.Errorf("cert generation fails at X509 cert creation (%v)", err) 154 } 155 156 pemCert, pemKey, err := encodePem(false, certBytes, priv, options.PKCS8Key) 157 return pemCert, pemKey, err 158 } 159 160 func publicKey(priv interface{}) interface{} { 161 switch k := priv.(type) { 162 case *rsa.PrivateKey: 163 return &k.PublicKey 164 case *ecdsa.PrivateKey: 165 return &k.PublicKey 166 case ed25519.PrivateKey: 167 return k.Public().(ed25519.PublicKey) 168 default: 169 return nil 170 } 171 } 172 173 // GenRootCertFromExistingKey generates a X.509 certificate using existing 174 // CA private key. Only called by a self-signed Citadel. 175 func GenRootCertFromExistingKey(options CertOptions) (pemCert []byte, pemKey []byte, err error) { 176 if !options.IsSelfSigned || len(options.SignerPrivPem) == 0 { 177 return nil, nil, fmt.Errorf("skip cert " + 178 "generation. Citadel is not in self-signed mode or CA private key is not " + 179 "available") 180 } 181 182 template, err := genCertTemplateFromOptions(options) 183 if err != nil { 184 return nil, nil, fmt.Errorf("cert generation fails at cert template creation (%v)", err) 185 } 186 caPrivateKey, err := ParsePemEncodedKey(options.SignerPrivPem) 187 if err != nil { 188 return nil, nil, fmt.Errorf("unrecogniazed CA "+ 189 "private key, skip root cert rotation: %s", err.Error()) 190 } 191 certBytes, err := x509.CreateCertificate(rand.Reader, template, template, publicKey(caPrivateKey), caPrivateKey) 192 if err != nil { 193 return nil, nil, fmt.Errorf("cert generation fails at X509 cert creation (%v)", err) 194 } 195 196 pemCert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes}) 197 return pemCert, options.SignerPrivPem, nil 198 } 199 200 // GetCertOptionsFromExistingCert parses cert and generates a CertOptions 201 // that contains information about the cert. This is the reverse operation of 202 // genCertTemplateFromOptions(), and only called by a self-signed Citadel. 203 func GetCertOptionsFromExistingCert(certBytes []byte) (opts CertOptions, err error) { 204 cert, certErr := ParsePemEncodedCertificate(certBytes) 205 if certErr != nil { 206 return opts, certErr 207 } 208 209 orgs := cert.Subject.Organization 210 if len(orgs) > 0 { 211 opts.Org = orgs[0] 212 } 213 // TODO(JimmyCYJ): parse other fields from certificate, e.g. CommonName. 214 return opts, nil 215 } 216 217 // MergeCertOptions merges deltaOpts into defaultOpts and returns the merged 218 // CertOptions. Only called by a self-signed Citadel. 219 func MergeCertOptions(defaultOpts, deltaOpts CertOptions) CertOptions { 220 if len(deltaOpts.Org) > 0 { 221 defaultOpts.Org = deltaOpts.Org 222 } 223 // TODO(JimmyCYJ): merge other fields, e.g. Host, IsDualUse, etc. 224 return defaultOpts 225 } 226 227 // GenCertFromCSR generates a X.509 certificate with the given CSR. 228 func GenCertFromCSR(csr *x509.CertificateRequest, signingCert *x509.Certificate, publicKey interface{}, 229 signingKey crypto.PrivateKey, subjectIDs []string, ttl time.Duration, isCA bool, 230 ) (cert []byte, err error) { 231 tmpl, err := genCertTemplateFromCSR(csr, subjectIDs, ttl, isCA) 232 if err != nil { 233 return nil, err 234 } 235 return x509.CreateCertificate(rand.Reader, tmpl, signingCert, publicKey, signingKey) 236 } 237 238 // LoadSignerCredsFromFiles loads the signer cert&key from the given files. 239 // 240 // signerCertFile: cert file name 241 // signerPrivFile: private key file name 242 func LoadSignerCredsFromFiles(signerCertFile string, signerPrivFile string) (*x509.Certificate, crypto.PrivateKey, error) { 243 signerCertBytes, err := os.ReadFile(signerCertFile) 244 if err != nil { 245 return nil, nil, fmt.Errorf("certificate file reading failure (%v)", err) 246 } 247 248 signerPrivBytes, err := os.ReadFile(signerPrivFile) 249 if err != nil { 250 return nil, nil, fmt.Errorf("private key file reading failure (%v)", err) 251 } 252 253 cert, err := ParsePemEncodedCertificate(signerCertBytes) 254 if err != nil { 255 return nil, nil, fmt.Errorf("pem encoded cert parsing failure (%v)", err) 256 } 257 258 key, err := ParsePemEncodedKey(signerPrivBytes) 259 if err != nil { 260 return nil, nil, fmt.Errorf("pem encoded key parsing failure (%v)", err) 261 } 262 263 return cert, key, nil 264 } 265 266 // ClockSkewGracePeriod defines the period of time a certificate will be valid before its creation. 267 // This is meant to handle cases where we have clock skew between the CA and workloads. 268 const ClockSkewGracePeriod = time.Minute * 2 269 270 // genCertTemplateFromCSR generates a certificate template with the given CSR. 271 // The NotBefore value of the cert is set to current time. 272 func genCertTemplateFromCSR(csr *x509.CertificateRequest, subjectIDs []string, ttl time.Duration, isCA bool) ( 273 *x509.Certificate, error, 274 ) { 275 subjectIDsInString := strings.Join(subjectIDs, ",") 276 var keyUsage x509.KeyUsage 277 extKeyUsages := []x509.ExtKeyUsage{} 278 if isCA { 279 // If the cert is a CA cert, the private key is allowed to sign other certificates. 280 keyUsage = x509.KeyUsageCertSign 281 } else { 282 // Otherwise the private key is allowed for digital signature and key encipherment. 283 keyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment 284 // For now, we do not differentiate non-CA certs to be used on client auth or server auth. 285 extKeyUsages = append(extKeyUsages, x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth) 286 } 287 288 // Build cert extensions with the subjectIDs. 289 ext, err := BuildSubjectAltNameExtension(subjectIDsInString) 290 if err != nil { 291 return nil, err 292 } 293 exts := []pkix.Extension{*ext} 294 295 subject := pkix.Name{} 296 // Dual use mode if common name in CSR is not empty. 297 // In this case, set CN as determined by DualUseCommonName(subjectIDsInString). 298 if len(csr.Subject.CommonName) != 0 { 299 var cn string 300 if cn, err = DualUseCommonName(subjectIDsInString); err != nil { 301 // logger and continue 302 logger.Errorf("dual-use failed for cert template - omitting CN (%v)", err) 303 } else { 304 subject.CommonName = cn 305 } 306 } 307 308 now := time.Now() 309 310 serialNum, err := genSerialNum() 311 if err != nil { 312 return nil, err 313 } 314 // SignatureAlgorithm will use the default algorithm. 315 // See https://golang.org/src/crypto/x509/x509.go?s=5131:5158#L1965 . 316 return &x509.Certificate{ 317 SerialNumber: serialNum, 318 Subject: subject, 319 NotBefore: now.Add(-ClockSkewGracePeriod), 320 NotAfter: now.Add(ttl), 321 KeyUsage: keyUsage, 322 ExtKeyUsage: extKeyUsages, 323 IsCA: isCA, 324 BasicConstraintsValid: true, 325 ExtraExtensions: exts, 326 }, nil 327 } 328 329 // genCertTemplateFromoptions generates a certificate template with the given options. 330 func genCertTemplateFromOptions(options CertOptions) (*x509.Certificate, error) { 331 var keyUsage x509.KeyUsage 332 if options.IsCA { 333 // If the cert is a CA cert, the private key is allowed to sign other certificates. 334 keyUsage = x509.KeyUsageCertSign 335 } else { 336 // Otherwise the private key is allowed for digital signature and key encipherment. 337 keyUsage = x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment 338 } 339 340 extKeyUsages := []x509.ExtKeyUsage{} 341 if options.IsServer { 342 extKeyUsages = append(extKeyUsages, x509.ExtKeyUsageServerAuth) 343 } 344 if options.IsClient { 345 extKeyUsages = append(extKeyUsages, x509.ExtKeyUsageClientAuth) 346 } 347 348 notBefore := time.Now() 349 if !options.NotBefore.IsZero() { 350 notBefore = options.NotBefore 351 } 352 353 serialNum, err := genSerialNum() 354 if err != nil { 355 return nil, err 356 } 357 358 subject := pkix.Name{ 359 Organization: []string{options.Org}, 360 } 361 362 exts := []pkix.Extension{} 363 if h := options.Host; len(h) > 0 { 364 s, err := BuildSubjectAltNameExtension(h) 365 if err != nil { 366 return nil, err 367 } 368 if options.IsDualUse { 369 cn, err := DualUseCommonName(h) 370 if err != nil { 371 // logger and continue 372 logger.Errorf("dual-use failed for cert template - omitting CN (%v)", err) 373 } else { 374 subject.CommonName = cn 375 } 376 } 377 exts = []pkix.Extension{*s} 378 } 379 380 dnsNames := strings.Split(options.DNSNames, ",") 381 if len(dnsNames[0]) == 0 { 382 dnsNames = nil 383 } 384 385 return &x509.Certificate{ 386 SerialNumber: serialNum, 387 Subject: subject, 388 NotBefore: notBefore, 389 NotAfter: notBefore.Add(options.TTL), 390 KeyUsage: keyUsage, 391 ExtKeyUsage: extKeyUsages, 392 IsCA: options.IsCA, 393 BasicConstraintsValid: true, 394 ExtraExtensions: exts, 395 DNSNames: dnsNames, 396 }, nil 397 } 398 399 func genSerialNum() (*big.Int, error) { 400 serialNumLimit := new(big.Int).Lsh(big.NewInt(1), 128) 401 serialNum, err := rand.Int(rand.Reader, serialNumLimit) 402 if err != nil { 403 return nil, fmt.Errorf("serial number generation failure (%v)", err) 404 } 405 return serialNum, nil 406 } 407 408 func encodePem(isCSR bool, csrOrCert []byte, priv interface{}, pkcs8 bool) ( 409 csrOrCertPem []byte, privPem []byte, err error, 410 ) { 411 encodeMsg := "CERTIFICATE" 412 if isCSR { 413 encodeMsg = "CERTIFICATE REQUEST" 414 } 415 csrOrCertPem = pem.EncodeToMemory(&pem.Block{Type: encodeMsg, Bytes: csrOrCert}) 416 417 var encodedKey []byte 418 if pkcs8 { 419 if encodedKey, err = x509.MarshalPKCS8PrivateKey(priv); err != nil { 420 return nil, nil, err 421 } 422 privPem = pem.EncodeToMemory(&pem.Block{Type: blockTypePKCS8PrivateKey, Bytes: encodedKey}) 423 } else { 424 switch k := priv.(type) { 425 case *rsa.PrivateKey: 426 encodedKey = x509.MarshalPKCS1PrivateKey(k) 427 privPem = pem.EncodeToMemory(&pem.Block{Type: blockTypeRSAPrivateKey, Bytes: encodedKey}) 428 case *ecdsa.PrivateKey: 429 encodedKey, err = x509.MarshalECPrivateKey(k) 430 if err != nil { 431 return nil, nil, err 432 } 433 privPem = pem.EncodeToMemory(&pem.Block{Type: blockTypeECPrivateKey, Bytes: encodedKey}) 434 } 435 } 436 err = nil 437 return 438 }