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 }