github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/config/certs/trusted.go (about)

     1  // Copyright 2019 Google LLC
     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  //   https://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 certs
    16  
    17  import (
    18  	"crypto/ecdsa"
    19  	"crypto/elliptic"
    20  	"crypto/rand"
    21  	"crypto/x509"
    22  	"crypto/x509/pkix"
    23  	"encoding/pem"
    24  	"errors"
    25  	"fmt"
    26  	"math/big"
    27  	"os"
    28  	"time"
    29  
    30  	log "github.com/golang/glog"
    31  
    32  	cpb "github.com/google/fleetspeak/fleetspeak/src/config/proto/fleetspeak_config"
    33  )
    34  
    35  // GetTrustedCert returns the trusted certificate associated with cfg, creating
    36  // it if necessary.  If available, priv is the private key associated with cert.
    37  func GetTrustedCert(cfg *cpb.Config) (cert *x509.Certificate, priv any, pem []byte, err error) {
    38  	if cfg.TrustedCertFile == "" {
    39  		return nil, nil, nil, errors.New("trusted_cert_file not set")
    40  	}
    41  	if _, err := os.Stat(cfg.TrustedCertFile); err != nil {
    42  		if os.IsNotExist(err) {
    43  			// Attempt to create a CA certificate.
    44  			if err := makeCACert(cfg); err != nil {
    45  				return nil, nil, nil, err
    46  			}
    47  		} else {
    48  			return nil, nil, nil, fmt.Errorf("unable to stat trusted_cert_file [%s]: %v", cfg.TrustedCertFile, err)
    49  		}
    50  	}
    51  	return getTrustedCert(cfg)
    52  }
    53  
    54  func makeCACert(cfg *cpb.Config) error {
    55  	if cfg.TrustedCertKeyFile == "" {
    56  		return errors.New("unable to create a CA cert: trusted_cert_key_file not set")
    57  	}
    58  	privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    59  	if err != nil {
    60  		return fmt.Errorf("unable to create a CA cert: key generation failed: %v", err)
    61  	}
    62  	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
    63  	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
    64  	if err != nil {
    65  		return fmt.Errorf("unable to create a CA cert: serial number generation failed: %v", err)
    66  	}
    67  	tmpl := x509.Certificate{
    68  		Version:               1,
    69  		SerialNumber:          serialNumber,
    70  		Subject:               pkix.Name{CommonName: fmt.Sprintf("%s Fleetspeak CA", cfg.ConfigurationName)},
    71  		NotBefore:             time.Now(),
    72  		NotAfter:              time.Now().Add(10 * 24 * 365 * time.Hour), // 10 years from now
    73  		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
    74  		IsCA:                  true,
    75  		BasicConstraintsValid: true,
    76  	}
    77  	cert, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, privKey.Public(), privKey)
    78  	if err != nil {
    79  		return fmt.Errorf("unable to create a CA cert: %v", err)
    80  	}
    81  	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert})
    82  
    83  	key, err := x509.MarshalECPrivateKey(privKey)
    84  	if err != nil {
    85  		return fmt.Errorf("unable to create CA cert: failed to marshal private key: %v", err)
    86  	}
    87  	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: key})
    88  
    89  	if err := os.WriteFile(cfg.TrustedCertFile, certPEM, 0644); err != nil {
    90  		return fmt.Errorf("failed to write CA cert file [%s]: %v", cfg.TrustedCertFile, err)
    91  	}
    92  	if err := os.WriteFile(cfg.TrustedCertKeyFile, keyPEM, 0600); err != nil {
    93  		return fmt.Errorf("failed to write CA key file [%s]: %v", cfg.TrustedCertKeyFile, err)
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  func getTrustedCert(cfg *cpb.Config) (cert *x509.Certificate, priv any, certPEM []byte, err error) {
   100  	// Read and validate the cert.
   101  	certPEM, err = os.ReadFile(cfg.TrustedCertFile)
   102  	if err != nil {
   103  		return nil, nil, nil, fmt.Errorf("unable to read trusted certificate file [%s]: %v", cfg.TrustedCertFile, err)
   104  	}
   105  	certBlock, _ := pem.Decode(certPEM)
   106  	if certBlock == nil || certBlock.Type != "CERTIFICATE" {
   107  		return nil, nil, nil, fmt.Errorf("trusted certificate file [%s] does not appear to contain a PEM format certificate", cfg.TrustedCertFile)
   108  	}
   109  	cert, err = x509.ParseCertificate(certBlock.Bytes)
   110  	if err != nil {
   111  		return nil, nil, nil, fmt.Errorf("unable to parse trusted certificate file [%s]: %v", cfg.TrustedCertFile, err)
   112  	}
   113  
   114  	// Read the key file, if present.
   115  	keyPEM, err := os.ReadFile(cfg.TrustedCertKeyFile)
   116  	if err != nil {
   117  		log.Infof("unable to read the trusted certificate key file [%s], server certificate creation disabled: %v", cfg.TrustedCertKeyFile, err)
   118  		return cert, nil, certPEM, nil
   119  	}
   120  
   121  	keyBlock, _ := pem.Decode(keyPEM)
   122  	if keyBlock == nil {
   123  		return nil, nil, nil, fmt.Errorf("trusted certificate key file [%s] does not appear to contain a PEM format key", cfg.TrustedCertKeyFile)
   124  	}
   125  	switch keyBlock.Type {
   126  	case "RSA PRIVATE KEY":
   127  		priv, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
   128  		if err != nil {
   129  			return nil, nil, nil, fmt.Errorf("unable to parse RSA key in certificate key file [%s]: %v", cfg.TrustedCertKeyFile, err)
   130  		}
   131  		return cert, priv, certPEM, nil
   132  	case "EC PRIVATE KEY":
   133  		priv, err = x509.ParseECPrivateKey(keyBlock.Bytes)
   134  		if err != nil {
   135  			return nil, nil, nil, fmt.Errorf("unable to parse EC key in certificate key file [%s]: %v", cfg.TrustedCertKeyFile, err)
   136  		}
   137  		return cert, priv, certPEM, nil
   138  	default:
   139  		return nil, nil, nil, fmt.Errorf("unsupport PEM block type [%s] in certificate key file [%s]", keyBlock.Type, cfg.TrustedCertKeyFile)
   140  	}
   141  }