github.com/sealerio/sealer@v0.11.1-0.20240507115618-f4f89c5853ae/pkg/clustercert/cert/generator.go (about)

     1  // Copyright © 2022 Alibaba Group Holding Ltd.
     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  package cert
    16  
    17  import (
    18  	"crypto"
    19  	"crypto/ecdsa"
    20  	"crypto/elliptic"
    21  	"crypto/rand"
    22  	"crypto/rsa"
    23  	"crypto/x509"
    24  	"crypto/x509/pkix"
    25  	"encoding/pem"
    26  	"fmt"
    27  	"math"
    28  	"math/big"
    29  	"net"
    30  	"os"
    31  	"path"
    32  	"path/filepath"
    33  	"time"
    34  
    35  	"github.com/pkg/errors"
    36  	"github.com/sirupsen/logrus"
    37  	"k8s.io/client-go/util/keyutil"
    38  )
    39  
    40  const (
    41  	// PrivateKeyBlockType is a possible value for pem.Block.Type.
    42  	PrivateKeyBlockType = "PRIVATE KEY"
    43  	// PublicKeyBlockType is a possible value for pem.Block.Type.
    44  	PublicKeyBlockType = "PUBLIC KEY"
    45  	// CertificateBlockType is a possible value for pem.Block.Type.
    46  	CertificateBlockType = "CERTIFICATE"
    47  	// RSAPrivateKeyBlockType is a possible value for pem.Block.Type.
    48  	RSAPrivateKeyBlockType = "RSA PRIVATE KEY"
    49  	rsaKeySize             = 2048
    50  	duration365d           = time.Hour * 24 * 365
    51  )
    52  
    53  // KeyPairFileGenerator write symmetric encryption key, like: sa.key and sa.pub
    54  type KeyPairFileGenerator struct {
    55  	path string
    56  	name string
    57  }
    58  
    59  func NewKeyPairFileGenerator(certPath string, certName string) KeyPairFileGenerator {
    60  	return KeyPairFileGenerator{
    61  		path: certPath,
    62  		name: certName,
    63  	}
    64  }
    65  
    66  func (k KeyPairFileGenerator) GenerateAll() error {
    67  	_, err := os.Stat(path.Join(k.path, "sa.key"))
    68  	if !os.IsNotExist(err) {
    69  		logrus.Info("sa.key sa.pub already exist")
    70  		return nil
    71  	}
    72  
    73  	key, err := NewPrivateKey(x509.RSA)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	err = k.writePrivateKey(key)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	return k.writePublicKey(key.Public())
    84  }
    85  
    86  func (k KeyPairFileGenerator) writePrivateKey(key crypto.Signer) error {
    87  	if key == nil {
    88  		return errors.New("private key cannot be nil when writing to file")
    89  	}
    90  
    91  	privateKeyPath := PathForKey(k.path, k.name)
    92  	encoded, err := keyutil.MarshalPrivateKeyToPEM(key)
    93  	if err != nil {
    94  		return fmt.Errorf("unable to marshal private key to PEM :%v", err)
    95  	}
    96  	if err := keyutil.WriteKey(privateKeyPath, encoded); err != nil {
    97  		return fmt.Errorf("unable to write private key to file %s :%v", privateKeyPath, err)
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  func (k KeyPairFileGenerator) writePublicKey(key crypto.PublicKey) error {
   104  	if key == nil {
   105  		return errors.New("public key cannot be nil when writing to file")
   106  	}
   107  
   108  	der, err := x509.MarshalPKIXPublicKey(key)
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	block := pem.Block{
   114  		Type:  PublicKeyBlockType,
   115  		Bytes: der,
   116  	}
   117  
   118  	publicKeyPath := PathForPublicKey(k.path, k.name)
   119  	if err := keyutil.WriteKey(publicKeyPath, pem.EncodeToMemory(&block)); err != nil {
   120  		return fmt.Errorf("unable to write public key to file %s %v", publicKeyPath, err)
   121  	}
   122  
   123  	return nil
   124  }
   125  
   126  // AltNames contains the domain names and IP addresses that will be added
   127  // to the API Server's x509 certificate SubAltNames field. The values will
   128  // be passed directly to the x509.Certificate object.
   129  type AltNames struct {
   130  	DNSNames map[string]string
   131  	IPs      map[string]net.IP
   132  }
   133  
   134  // CertificateDescriptor contains the basic fields required for creating a certificate
   135  type CertificateDescriptor struct {
   136  	CommonName   string
   137  	DNSNames     []string
   138  	Organization []string
   139  	Year         time.Duration
   140  	AltNames     AltNames
   141  	Usages       []x509.ExtKeyUsage
   142  }
   143  
   144  type CertificateGenerator interface {
   145  	Generate() (*x509.Certificate, crypto.Signer, error)
   146  }
   147  
   148  type AuthorityCertificateGenerator struct {
   149  	config CertificateDescriptor
   150  }
   151  
   152  func (m AuthorityCertificateGenerator) Generate() (*x509.Certificate, crypto.Signer, error) {
   153  	key, err := NewPrivateKey(x509.UnknownPublicKeyAlgorithm)
   154  	if err != nil {
   155  		return nil, nil, fmt.Errorf("unable to create private key while generating CA certificate: %v", err)
   156  	}
   157  
   158  	cert, err := m.generateSelfSignedCACert(key)
   159  	if err != nil {
   160  		return nil, nil, fmt.Errorf("unable to generate cert %v", err)
   161  	}
   162  
   163  	return cert, key, nil
   164  }
   165  
   166  func (m AuthorityCertificateGenerator) generateSelfSignedCACert(key crypto.Signer) (*x509.Certificate, error) {
   167  	now := time.Now()
   168  	tmpl := x509.Certificate{
   169  		SerialNumber: new(big.Int).SetInt64(0),
   170  		Subject: pkix.Name{
   171  			CommonName:   m.config.CommonName,
   172  			Organization: m.config.Organization,
   173  		},
   174  		DNSNames:              m.config.DNSNames,
   175  		NotBefore:             now.UTC(),
   176  		NotAfter:              now.Add(duration365d * m.config.Year).UTC(),
   177  		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
   178  		BasicConstraintsValid: true,
   179  		IsCA:                  true,
   180  	}
   181  
   182  	certDERBytes, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, key.Public(), key)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	return x509.ParseCertificate(certDERBytes)
   187  }
   188  
   189  func NewAuthorityCertificateGenerator(config CertificateDescriptor) CertificateGenerator {
   190  	return AuthorityCertificateGenerator{
   191  		config: config,
   192  	}
   193  }
   194  
   195  type CommonCertificateGenerator struct {
   196  	config CertificateDescriptor
   197  	caCert *x509.Certificate
   198  	caKey  crypto.Signer
   199  }
   200  
   201  func (m CommonCertificateGenerator) Generate() (*x509.Certificate, crypto.Signer, error) {
   202  	key, err := NewPrivateKey(x509.UnknownPublicKeyAlgorithm)
   203  	if err != nil {
   204  		return nil, nil, fmt.Errorf("unable to create private key while generating common certificate: %v", err)
   205  	}
   206  
   207  	cert, err := m.generateSignedCert(key)
   208  	if err != nil {
   209  		return nil, nil, fmt.Errorf("failed to generate signed cert: %v", err)
   210  	}
   211  
   212  	return cert, key, nil
   213  }
   214  
   215  // generateSignedCert creates a signed certificate using the given CA certificate and key
   216  func (m CommonCertificateGenerator) generateSignedCert(key crypto.Signer) (*x509.Certificate, error) {
   217  	var dnsNames []string
   218  	var ips []net.IP
   219  
   220  	for _, v := range m.config.AltNames.DNSNames {
   221  		dnsNames = append(dnsNames, v)
   222  	}
   223  	for _, v := range m.config.AltNames.IPs {
   224  		ips = append(ips, v)
   225  	}
   226  
   227  	serial, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	certTmpl := x509.Certificate{
   233  		Subject: pkix.Name{
   234  			CommonName:   m.config.CommonName,
   235  			Organization: m.config.Organization,
   236  		},
   237  		DNSNames:     dnsNames,
   238  		IPAddresses:  ips,
   239  		SerialNumber: serial,
   240  		NotBefore:    m.caCert.NotBefore,
   241  		NotAfter:     time.Now().Add(duration365d * m.config.Year).UTC(),
   242  		KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
   243  		ExtKeyUsage:  m.config.Usages,
   244  	}
   245  	certDERBytes, err := x509.CreateCertificate(rand.Reader, &certTmpl, m.caCert, key.Public(), m.caKey)
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  	return x509.ParseCertificate(certDERBytes)
   250  }
   251  
   252  func NewCommonCertificateGenerator(config CertificateDescriptor, caCert *x509.Certificate, caKey crypto.Signer) (CertificateGenerator, error) {
   253  	if config.CommonName == "" {
   254  		return nil, errors.New("must specify a CommonName for cert")
   255  	}
   256  
   257  	if len(config.Usages) == 0 {
   258  		return nil, errors.New("must specify at least one ExtKeyUsage")
   259  	}
   260  
   261  	return CommonCertificateGenerator{
   262  		config: config,
   263  		caCert: caCert,
   264  		caKey:  caKey,
   265  	}, nil
   266  }
   267  
   268  func PathForCert(pkiPath, name string) string {
   269  	return filepath.Join(pkiPath, fmt.Sprintf("%s.crt", name))
   270  }
   271  
   272  func PathForKey(pkiPath, name string) string {
   273  	return filepath.Join(pkiPath, fmt.Sprintf("%s.key", name))
   274  }
   275  
   276  func PathForPublicKey(pkiPath, name string) string {
   277  	return filepath.Join(pkiPath, fmt.Sprintf("%s.pub", name))
   278  }
   279  
   280  // NewPrivateKey creates an RSA private key
   281  func NewPrivateKey(keyType x509.PublicKeyAlgorithm) (crypto.Signer, error) {
   282  	if keyType == x509.ECDSA {
   283  		return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   284  	}
   285  
   286  	return rsa.GenerateKey(rand.Reader, rsaKeySize)
   287  }
   288  
   289  // EncodeCertPEM returns PEM-encoded certificate data
   290  func EncodeCertPEM(cert *x509.Certificate) []byte {
   291  	block := pem.Block{
   292  		Type:  CertificateBlockType,
   293  		Bytes: cert.Raw,
   294  	}
   295  	return pem.EncodeToMemory(&block)
   296  }