istio.io/istio@v0.0.0-20240520182934-d79c90f27776/security/pkg/pki/util/generate_csr.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Provides utility methods to generate X.509 certificates with different 16 // options. This implementation is Largely inspired from 17 // https://golang.org/src/crypto/tls/generate_cert.go. 18 19 package util 20 21 import ( 22 "crypto" 23 "crypto/ecdsa" 24 "crypto/elliptic" 25 "crypto/rand" 26 "crypto/rsa" 27 "crypto/x509" 28 "crypto/x509/pkix" 29 "errors" 30 "fmt" 31 "os" 32 "strings" 33 34 "istio.io/istio/pkg/log" 35 ) 36 37 // minimumRsaKeySize is the minimum RSA key size to generate certificates 38 // to ensure proper security 39 const minimumRsaKeySize = 2048 40 41 // GenCSR generates a X.509 certificate sign request and private key with the given options. 42 func GenCSR(options CertOptions) ([]byte, []byte, error) { 43 var priv any 44 var err error 45 if options.ECSigAlg != "" { 46 switch options.ECSigAlg { 47 case EcdsaSigAlg: 48 var curve elliptic.Curve 49 switch options.ECCCurve { 50 case P384Curve: 51 curve = elliptic.P384() 52 default: 53 curve = elliptic.P256() 54 } 55 priv, err = ecdsa.GenerateKey(curve, rand.Reader) 56 if err != nil { 57 return nil, nil, fmt.Errorf("EC key generation failed (%v)", err) 58 } 59 default: 60 return nil, nil, errors.New("csr cert generation fails due to unsupported EC signature algorithm") 61 } 62 } else { 63 if options.RSAKeySize < minimumRsaKeySize { 64 return nil, nil, fmt.Errorf("requested key size does not meet the minimum required size of %d (requested: %d)", minimumRsaKeySize, options.RSAKeySize) 65 } 66 67 priv, err = rsa.GenerateKey(rand.Reader, options.RSAKeySize) 68 if err != nil { 69 return nil, nil, fmt.Errorf("RSA key generation failed (%v)", err) 70 } 71 } 72 template, err := GenCSRTemplate(options) 73 if err != nil { 74 return nil, nil, fmt.Errorf("CSR template creation failed (%v)", err) 75 } 76 77 csrBytes, err := x509.CreateCertificateRequest(rand.Reader, template, crypto.PrivateKey(priv)) 78 if err != nil { 79 return nil, nil, fmt.Errorf("CSR creation failed (%v)", err) 80 } 81 82 csr, privKey, err := encodePem(true, csrBytes, priv, options.PKCS8Key) 83 return csr, privKey, err 84 } 85 86 // GenCSRTemplate generates a certificateRequest template with the given options. 87 func GenCSRTemplate(options CertOptions) (*x509.CertificateRequest, error) { 88 template := &x509.CertificateRequest{ 89 Subject: pkix.Name{ 90 Organization: []string{options.Org}, 91 }, 92 } 93 94 if h := options.Host; len(h) > 0 { 95 s, err := BuildSubjectAltNameExtension(h) 96 if err != nil { 97 return nil, err 98 } 99 if options.IsDualUse { 100 cn, err := DualUseCommonName(h) 101 if err != nil { 102 // log and continue 103 log.Errorf("dual-use failed for CSR template - omitting CN (%v)", err) 104 } else { 105 template.Subject.CommonName = cn 106 } 107 } 108 template.ExtraExtensions = []pkix.Extension{*s} 109 } 110 111 return template, nil 112 } 113 114 // AppendRootCerts appends root certificates in RootCertFile to the input certificate. 115 func AppendRootCerts(pemCert []byte, rootCertFile string) ([]byte, error) { 116 rootCerts := pemCert 117 if len(rootCertFile) > 0 { 118 log.Debugf("append root certificates from %v", rootCertFile) 119 certBytes, err := os.ReadFile(rootCertFile) 120 if err != nil && !os.IsNotExist(err) { 121 return rootCerts, fmt.Errorf("failed to read root certificates (%v)", err) 122 } 123 rootCerts = AppendCertByte(pemCert, certBytes) 124 } 125 return rootCerts, nil 126 } 127 128 // AppendCertByte: Append x.509 rootCert in bytes to existing certificate chain (in bytes) 129 func AppendCertByte(pemCert []byte, rootCert []byte) []byte { 130 rootCerts := []byte{} 131 if len(pemCert) > 0 { 132 // Copy the input certificate 133 rootCerts = []byte(strings.TrimSuffix(string(pemCert), "\n") + "\n") 134 } 135 rootCerts = append(rootCerts, rootCert...) 136 return rootCerts 137 }