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  }