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 }