github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/tools/lxdclient/cert.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // +build go1.3
     5  
     6  package lxdclient
     7  
     8  import (
     9  	"crypto/sha256"
    10  	"crypto/x509"
    11  	"encoding/pem"
    12  	"fmt"
    13  	"io"
    14  
    15  	"github.com/juju/errors"
    16  )
    17  
    18  const (
    19  	certDefaultName = "juju-client-certificate"
    20  )
    21  
    22  // Cert holds the information for a single certificate a client
    23  // may use to connect to a remote server.
    24  type Cert struct {
    25  	// Name is the name that LXD will use for the cert.
    26  	Name string
    27  
    28  	// CertPEM is the PEM-encoded x.509 cert.
    29  	CertPEM []byte
    30  
    31  	// KeyPEM is the PEM-encoded x.509 private key.
    32  	KeyPEM []byte
    33  }
    34  
    35  // NewCertificate creates a new Certificate for the given cert and key.
    36  func NewCert(certPEM, keyPEM []byte) Cert {
    37  	return Cert{
    38  		CertPEM: certPEM,
    39  		KeyPEM:  keyPEM,
    40  	}
    41  }
    42  
    43  // WithDefaults updates a copy of the remote with default values
    44  // where needed.
    45  func (cert Cert) WithDefaults() (Cert, error) {
    46  	// Note that cert is a value receiver, so it is an implicit copy.
    47  
    48  	if cert.Name == "" {
    49  		// The certificate Name will get overridden later by code that
    50  		// knows what environment this certificate is being used for.
    51  		cert.Name = certDefaultName
    52  	}
    53  
    54  	// WithDefaults doesn't populate CertPEM or KeyPEM because those aren't
    55  	// used when contacting the LXD service via the unix socket.
    56  	return cert, nil
    57  }
    58  
    59  // Validate ensures that the cert is valid.
    60  func (cert Cert) Validate() error {
    61  	if len(cert.CertPEM) == 0 {
    62  		return errors.NotValidf("missing cert PEM")
    63  	}
    64  	if len(cert.KeyPEM) == 0 {
    65  		return errors.NotValidf("missing key PEM")
    66  	}
    67  
    68  	// TODO(ericsnow) Ensure cert and key are valid?
    69  
    70  	return nil
    71  }
    72  
    73  // WriteCertPEM writes the cert's x.509 PEM data to the given writer.
    74  func (cert Cert) WriteCertPEM(out io.Writer) error {
    75  	if _, err := out.Write(cert.CertPEM); err != nil {
    76  		return errors.Trace(err)
    77  	}
    78  	return nil
    79  }
    80  
    81  // WriteKeytPEM writes the key's x.509 PEM data to the given writer.
    82  func (cert Cert) WriteKeyPEM(out io.Writer) error {
    83  	if _, err := out.Write(cert.KeyPEM); err != nil {
    84  		return errors.Trace(err)
    85  	}
    86  	return nil
    87  }
    88  
    89  // Fingerprint returns the cert's LXD fingerprint.
    90  func (cert Cert) Fingerprint() (string, error) {
    91  	// See: https://github.com/lxc/lxd/blob/master/lxd/certificates.go
    92  	x509Cert, err := cert.X509()
    93  	if err != nil {
    94  		return "", errors.Trace(err)
    95  	}
    96  	data := sha256.Sum256(x509Cert.Raw)
    97  	return fmt.Sprintf("%x", data), nil
    98  }
    99  
   100  // X509 returns the x.509 certificate.
   101  func (cert Cert) X509() (*x509.Certificate, error) {
   102  	block, _ := pem.Decode(cert.CertPEM)
   103  	if block == nil {
   104  		return nil, errors.Errorf("invalid cert PEM (%d bytes)", len(cert.CertPEM))
   105  	}
   106  
   107  	x509Cert, err := x509.ParseCertificate(block.Bytes)
   108  	if err != nil {
   109  		return nil, errors.Trace(err)
   110  	}
   111  
   112  	return x509Cert, nil
   113  }