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