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  }