github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/pki/authority.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package pki
     5  
     6  import (
     7  	"crypto"
     8  	"crypto/rand"
     9  	"crypto/tls"
    10  	"crypto/x509"
    11  	"crypto/x509/pkix"
    12  	"fmt"
    13  	"strings"
    14  	"sync"
    15  	"time"
    16  
    17  	"github.com/juju/errors"
    18  )
    19  
    20  const (
    21  	DefaultLeafGroup      = "controller"
    22  	ControllerIPLeafGroup = "controllerip"
    23  )
    24  
    25  // Authority represents a secure means of issuing groups of common interest
    26  // certificates that share a certificate authority. Authority should
    27  // only be shared around between trusted parties. Authority should be considered
    28  // thread safe.
    29  type Authority interface {
    30  	// Leaf Authority implements the Leaf interface
    31  	Leaf
    32  
    33  	// LeafForGroup returns the leaf associated with the given group. Returns
    34  	// error if no leaf exists for the given group.
    35  	LeafForGroup(string) (Leaf, error)
    36  
    37  	// LeafGroupFromPemCertKey loads an already existing certificate key pair as
    38  	// a new leaf at the given group. Returns error if a leaf for the given
    39  	// group already exists or an error occurred loading the pem data.
    40  	LeafGroupFromPemCertKey(group string, certPem, key []byte) (Leaf, error)
    41  
    42  	// LeafRequestForGroup starts a new leaf request for the given group. If a
    43  	// leaf already exists it will be overwritten with this request when
    44  	// committed.
    45  	LeafRequestForGroup(string) LeafRequest
    46  
    47  	// LeafRange is a method for safely iterating over all the leafs for the
    48  	// given Authority. Supplied function should return false to stop iteration
    49  	// early.
    50  	LeafRange(func(leaf Leaf) bool)
    51  }
    52  
    53  // DefaultAuthority is a juju implementation of the Authority interface. It's
    54  // main difference is the ability to set a common leaf private key so all leafs
    55  // use the same key.
    56  type DefaultAuthority struct {
    57  	authority       Leaf
    58  	leafs           sync.Map
    59  	leafSignerMutex sync.Mutex
    60  	leafSigner      crypto.Signer
    61  }
    62  
    63  // Organisation default organisation set on all certificates
    64  var Organisation = []string{"Juju"}
    65  
    66  // LeafSubjectTemplate is the default pkix.Name used for all leaf certificates
    67  // made from a DefaultAuthority
    68  var LeafSubjectTemplate = pkix.Name{
    69  	Organization: Organisation,
    70  	CommonName:   "Juju server certificate",
    71  }
    72  
    73  // Certificate implements Leaf interface method. Returns the CA's certificate
    74  func (a *DefaultAuthority) Certificate() *x509.Certificate {
    75  	return a.authority.Certificate()
    76  }
    77  
    78  // Chain implements Leaf interface method. Returns the CA's chain if it is an
    79  // intermediate.
    80  func (a *DefaultAuthority) Chain() []*x509.Certificate {
    81  	return a.authority.Chain()
    82  }
    83  
    84  func (a *DefaultAuthority) ChainWithAuthority() []*x509.Certificate {
    85  	chain := a.authority.Chain()
    86  	if chain == nil {
    87  		chain = []*x509.Certificate{}
    88  	}
    89  	return append(chain, a.authority.Certificate())
    90  }
    91  
    92  // leafMaker is responsible for providing a method to make new leafs after
    93  // request signing.
    94  func (a *DefaultAuthority) leafMaker(groupKey string) LeafMaker {
    95  	return func(cert *x509.Certificate, chain []*x509.Certificate,
    96  		signer crypto.Signer) (Leaf, error) {
    97  		leaf := NewDefaultLeaf(groupKey, cert, chain, signer)
    98  		a.leafs.Store(groupKey, leaf)
    99  		return leaf, nil
   100  	}
   101  }
   102  
   103  // LeafRequestForGroup implements Authority interface method. Starts a new leaf
   104  // request for the given group overwritting any existing leaf when the request
   105  // is committed.
   106  func (a *DefaultAuthority) LeafRequestForGroup(group string) LeafRequest {
   107  	groupKey := strings.ToLower(group)
   108  	subject := MakeX509NameFromDefaults(&LeafSubjectTemplate,
   109  		&pkix.Name{
   110  			CommonName: fmt.Sprintf("%s - %s", LeafSubjectTemplate.CommonName, groupKey),
   111  		})
   112  	a.leafSignerMutex.Lock()
   113  	defer a.leafSignerMutex.Unlock()
   114  	if a.leafSigner != nil {
   115  		return NewDefaultLeafRequestWithSigner(subject, a.leafSigner,
   116  			NewDefaultRequestSigner(a.Certificate(), a.ChainWithAuthority(), a.Signer()),
   117  			a.leafMaker(groupKey))
   118  	}
   119  	return NewDefaultLeafRequest(subject,
   120  		NewDefaultRequestSigner(a.Certificate(), a.ChainWithAuthority(), a.Signer()),
   121  		a.leafMaker(groupKey))
   122  }
   123  
   124  // LeafForGroup implements Authority interface method.
   125  func (a *DefaultAuthority) LeafForGroup(group string) (Leaf, error) {
   126  	groupKey := strings.ToLower(group)
   127  	leaf, has := a.leafs.Load(groupKey)
   128  	if !has {
   129  		return nil, errors.NotFoundf("no leaf for group key %s", groupKey)
   130  	}
   131  	return leaf.(Leaf), nil
   132  }
   133  
   134  // LeafGroupFromPemCertKey implements Authority interface method.
   135  func (a *DefaultAuthority) LeafGroupFromPemCertKey(group string,
   136  	certPem, key []byte) (Leaf, error) {
   137  
   138  	groupKey := strings.ToLower(group)
   139  	certs, signers, err := UnmarshalPemData(append(certPem, key...))
   140  	if err != nil {
   141  		return nil, errors.Trace(err)
   142  	}
   143  
   144  	if len(certs) == 0 {
   145  		return nil, errors.New("found zero certificates in pem bundle")
   146  	}
   147  	if len(signers) != 1 {
   148  		return nil, errors.New("expected at least one private key in bundle")
   149  	}
   150  	if !PublicKeysEqual(signers[0].Public(), certs[0].PublicKey) {
   151  		return nil, errors.New("public keys of first certificate and key do not match")
   152  	}
   153  
   154  	leaf := NewDefaultLeaf(groupKey, certs[0], certs[1:], signers[0])
   155  
   156  	if _, exists := a.leafs.LoadOrStore(groupKey, leaf); exists {
   157  		return nil, errors.AlreadyExistsf("leaf for group %s", group)
   158  	}
   159  	return leaf, nil
   160  }
   161  
   162  // LeafRange implements Authority interface method.
   163  func (a *DefaultAuthority) LeafRange(ranger func(leaf Leaf) bool) {
   164  	a.leafs.Range(func(_, val interface{}) bool {
   165  		return ranger(val.(Leaf))
   166  	})
   167  }
   168  
   169  // Helper method to generate a new certificate authority using the provided
   170  // common name and signer.
   171  func NewCA(commonName string, signer crypto.Signer) (*x509.Certificate, error) {
   172  	template := &x509.Certificate{}
   173  	if err := assetTagCertificate(template); err != nil {
   174  		return nil, errors.Annotate(err, "failed tagging new CA certificate")
   175  	}
   176  
   177  	template.Subject = pkix.Name{
   178  		CommonName:   commonName,
   179  		Organization: Organisation,
   180  	}
   181  	now := time.Now()
   182  	template.NotBefore = now.Add(NotBeforeJitter)
   183  	template.NotAfter = now.AddDate(DefaultValidityYears, 0, 0)
   184  	template.KeyUsage = x509.KeyUsageKeyEncipherment |
   185  		x509.KeyUsageDigitalSignature |
   186  		x509.KeyUsageCertSign
   187  	template.BasicConstraintsValid = true
   188  	template.IsCA = true
   189  
   190  	der, err := x509.CreateCertificate(rand.Reader, template, template,
   191  		signer.Public(), signer)
   192  	if err != nil {
   193  		return nil, errors.Annotate(err, "failed creating CA certificate")
   194  	}
   195  
   196  	caCert, err := x509.ParseCertificate(der)
   197  	if err != nil {
   198  		return nil, errors.Trace(err)
   199  	}
   200  
   201  	return caCert, nil
   202  }
   203  
   204  // NewDefaultAuthority generates a new DefaultAuthority for the supplied CA
   205  // cert and keys. Error is returned when the supplied certificate is not a CA.
   206  func NewDefaultAuthority(authority *x509.Certificate, signer crypto.Signer,
   207  	chain ...*x509.Certificate) (*DefaultAuthority, error) {
   208  	if !authority.IsCA {
   209  		return nil, errors.NotValidf("%s is not a certificate authority",
   210  			authority.Subject)
   211  	}
   212  
   213  	return &DefaultAuthority{
   214  		authority: NewDefaultLeaf("", authority, chain, signer),
   215  	}, nil
   216  }
   217  
   218  // NewDefaultAuthorityPem generates a new DefaultAuthority for the supplied pem
   219  // block. The pem block must contain a valid CA certificate and associated
   220  // private key.
   221  func NewDefaultAuthorityPem(pemBlock []byte) (*DefaultAuthority, error) {
   222  	leaf, err := NewDefaultLeafPem("", pemBlock)
   223  	if err != nil {
   224  		return nil, errors.Annotate(err, "generating CA leaf")
   225  	}
   226  
   227  	if !leaf.Certificate().IsCA {
   228  		return nil, errors.Errorf("certificate %s is not a CA",
   229  			leaf.Certificate().Subject.CommonName)
   230  	}
   231  
   232  	return NewDefaultAuthority(leaf.Certificate(), leaf.Signer(), leaf.Chain()...)
   233  }
   234  
   235  // NewDefaultAuthorityPemCAKey generates a new DefaultAuthority for the supplied
   236  // pem ca and key. Returns error if the supplied cert is not a ca or passing of
   237  // the pem data fails.
   238  func NewDefaultAuthorityPemCAKey(caPem, keyPem []byte) (*DefaultAuthority, error) {
   239  	return NewDefaultAuthorityPem(append(caPem, keyPem...))
   240  }
   241  
   242  // SetLeafSigner sets a default signer to use for all new created leafs on this
   243  // authority.
   244  func (a *DefaultAuthority) SetLeafSigner(signer crypto.Signer) {
   245  	a.leafSignerMutex.Lock()
   246  	defer a.leafSignerMutex.Unlock()
   247  	a.leafSigner = signer
   248  }
   249  
   250  // Signer implements Leaf interface method. Returns the signer used for this
   251  // authority.
   252  func (a *DefaultAuthority) Signer() crypto.Signer {
   253  	return a.authority.Signer()
   254  }
   255  
   256  // TLSCertificate implements Leaf interface method. Returns a tls certificate
   257  // that can be used in tls connections.
   258  func (a *DefaultAuthority) TLSCertificate() *tls.Certificate {
   259  	return a.authority.TLSCertificate()
   260  }
   261  
   262  // ToPemParts implements the Leaf interface method. Returns this authority split
   263  // into certificate and key pem components.
   264  func (a *DefaultAuthority) ToPemParts() (cert, key []byte, err error) {
   265  	return a.authority.ToPemParts()
   266  }