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