github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/container/lxd/certificate.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package lxd 5 6 import ( 7 "crypto/sha256" 8 "crypto/x509" 9 "encoding/base64" 10 "encoding/pem" 11 "fmt" 12 "io" 13 14 "github.com/canonical/lxd/shared/api" 15 "github.com/juju/errors" 16 ) 17 18 // Certificate holds the information for a single certificate that a client may 19 // use to connect to a remote server. 20 type Certificate struct { 21 // Name is the name that LXD will use for the cert. 22 Name string 23 // CertPEM is the PEM-encoded x.509 cert. 24 CertPEM []byte 25 // KeyPEM is the PEM-encoded x.509 private key. 26 KeyPEM []byte 27 } 28 29 // GenerateClientCertificate creates and returns a new certificate for client 30 // communication with an LXD server. 31 func GenerateClientCertificate() (*Certificate, error) { 32 cert, key, err := GenerateMemCert(true, true) 33 if err != nil { 34 return nil, errors.Trace(err) 35 } 36 return NewCertificate(cert, key), nil 37 } 38 39 // NewCertificate creates a new Certificate for the given cert and key. 40 func NewCertificate(certPEM, keyPEM []byte) *Certificate { 41 return &Certificate{ 42 CertPEM: certPEM, 43 KeyPEM: keyPEM, 44 } 45 } 46 47 // Validate ensures that the cert is valid. 48 func (c *Certificate) Validate() error { 49 if len(c.CertPEM) == 0 { 50 return errors.NotValidf("missing cert PEM") 51 } 52 if len(c.KeyPEM) == 0 { 53 return errors.NotValidf("missing key PEM") 54 } 55 return nil 56 } 57 58 // WriteCertPEM writes the cert's x.509 PEM data to the given writer. 59 func (c *Certificate) WriteCertPEM(out io.Writer) error { 60 _, err := out.Write(c.CertPEM) 61 return errors.Trace(err) 62 } 63 64 // WriteKeyPEM writes the key's x.509 PEM data to the given writer. 65 func (c *Certificate) WriteKeyPEM(out io.Writer) error { 66 _, err := out.Write(c.KeyPEM) 67 return errors.Trace(err) 68 } 69 70 // Fingerprint returns the cert's LXD fingerprint. 71 func (c *Certificate) Fingerprint() (string, error) { 72 x509Cert, err := c.X509() 73 if err != nil { 74 return "", errors.Trace(err) 75 } 76 data := sha256.Sum256(x509Cert.Raw) 77 return fmt.Sprintf("%x", data), nil 78 } 79 80 // X509 returns the x.509 certificate. 81 func (c *Certificate) X509() (*x509.Certificate, error) { 82 block, _ := pem.Decode(c.CertPEM) 83 if block == nil { 84 return nil, errors.Errorf("invalid cert PEM (%d bytes)", len(c.CertPEM)) 85 } 86 87 x509Cert, err := x509.ParseCertificate(block.Bytes) 88 if err != nil { 89 return nil, errors.Trace(err) 90 } 91 return x509Cert, nil 92 } 93 94 // AsCreateRequest creates a payload for the LXD API, suitable for posting the 95 // client certificate to an LXD server. 96 func (c *Certificate) AsCreateRequest() (api.CertificatesPost, error) { 97 block, _ := pem.Decode(c.CertPEM) 98 if block == nil { 99 return api.CertificatesPost{}, errors.New("failed to decode certificate PEM") 100 } 101 102 return api.CertificatesPost{ 103 CertificatePut: api.CertificatePut{ 104 Name: c.Name, 105 Type: "client", 106 Certificate: base64.StdEncoding.EncodeToString(block.Bytes), 107 }, 108 }, nil 109 }