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  }