| // Copyright 2011 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package x509 | |
| import ( | |
| "bytes" | |
| "encoding/pem" | |
| "errors" | |
| "runtime" | |
| ) | |
| // CertPool is a set of certificates. | |
| type CertPool struct { | |
| byName map[string][]int | |
| certs []*Certificate | |
| } | |
| // NewCertPool returns a new, empty CertPool. | |
| func NewCertPool() *CertPool { | |
| return &CertPool{ | |
| byName: make(map[string][]int), | |
| } | |
| } | |
| func (s *CertPool) copy() *CertPool { | |
| p := &CertPool{ | |
| byName: make(map[string][]int, len(s.byName)), | |
| certs: make([]*Certificate, len(s.certs)), | |
| } | |
| for k, v := range s.byName { | |
| indexes := make([]int, len(v)) | |
| copy(indexes, v) | |
| p.byName[k] = indexes | |
| } | |
| copy(p.certs, s.certs) | |
| return p | |
| } | |
| // SystemCertPool returns a copy of the system cert pool. | |
| // | |
| // On Unix systems other than macOS the environment variables SSL_CERT_FILE and | |
| // SSL_CERT_DIR can be used to override the system default locations for the SSL | |
| // certificate file and SSL certificate files directory, respectively. The | |
| // latter can be a colon-separated list. | |
| // | |
| // Any mutations to the returned pool are not written to disk and do not affect | |
| // any other pool returned by SystemCertPool. | |
| // | |
| // New changes in the system cert pool might not be reflected in subsequent calls. | |
| func SystemCertPool() (*CertPool, error) { | |
| if runtime.GOOS == "windows" { | |
| // Issue 16736, 18609: | |
| return nil, errors.New("crypto/x509: system root pool is not available on Windows") | |
| } | |
| if sysRoots := systemRootsPool(); sysRoots != nil { | |
| return sysRoots.copy(), nil | |
| } | |
| return loadSystemRoots() | |
| } | |
| // findPotentialParents returns the indexes of certificates in s which might | |
| // have signed cert. | |
| func (s *CertPool) findPotentialParents(cert *Certificate) []int { | |
| if s == nil { | |
| return nil | |
| } | |
| // consider all candidates where cert.Issuer matches cert.Subject. | |
| // when picking possible candidates the list is built in the order | |
| // of match plausibility as to save cycles in buildChains: | |
| // AKID and SKID match | |
| // AKID present, SKID missing / AKID missing, SKID present | |
| // AKID and SKID don't match | |
| var matchingKeyID, oneKeyID, mismatchKeyID []int | |
| for _, c := range s.byName[string(cert.RawIssuer)] { | |
| candidate := s.certs[c] | |
| kidMatch := bytes.Equal(candidate.SubjectKeyId, cert.AuthorityKeyId) | |
| switch { | |
| case kidMatch: | |
| matchingKeyID = append(matchingKeyID, c) | |
| case (len(candidate.SubjectKeyId) == 0 && len(cert.AuthorityKeyId) > 0) || | |
| (len(candidate.SubjectKeyId) > 0 && len(cert.AuthorityKeyId) == 0): | |
| oneKeyID = append(oneKeyID, c) | |
| default: | |
| mismatchKeyID = append(mismatchKeyID, c) | |
| } | |
| } | |
| found := len(matchingKeyID) + len(oneKeyID) + len(mismatchKeyID) | |
| if found == 0 { | |
| return nil | |
| } | |
| candidates := make([]int, 0, found) | |
| candidates = append(candidates, matchingKeyID...) | |
| candidates = append(candidates, oneKeyID...) | |
| candidates = append(candidates, mismatchKeyID...) | |
| return candidates | |
| } | |
| func (s *CertPool) contains(cert *Certificate) bool { | |
| if s == nil { | |
| return false | |
| } | |
| candidates := s.byName[string(cert.RawSubject)] | |
| for _, c := range candidates { | |
| if s.certs[c].Equal(cert) { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| // AddCert adds a certificate to a pool. | |
| func (s *CertPool) AddCert(cert *Certificate) { | |
| if cert == nil { | |
| panic("adding nil Certificate to CertPool") | |
| } | |
| // Check that the certificate isn't being added twice. | |
| if s.contains(cert) { | |
| return | |
| } | |
| n := len(s.certs) | |
| s.certs = append(s.certs, cert) | |
| name := string(cert.RawSubject) | |
| s.byName[name] = append(s.byName[name], n) | |
| } | |
| // AppendCertsFromPEM attempts to parse a series of PEM encoded certificates. | |
| // It appends any certificates found to s and reports whether any certificates | |
| // were successfully parsed. | |
| // | |
| // On many Linux systems, /etc/ssl/cert.pem will contain the system wide set | |
| // of root CAs in a format suitable for this function. | |
| func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) { | |
| for len(pemCerts) > 0 { | |
| var block *pem.Block | |
| block, pemCerts = pem.Decode(pemCerts) | |
| if block == nil { | |
| break | |
| } | |
| if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { | |
| continue | |
| } | |
| cert, err := ParseCertificate(block.Bytes) | |
| if err != nil { | |
| continue | |
| } | |
| s.AddCert(cert) | |
| ok = true | |
| } | |
| return | |
| } | |
| // Subjects returns a list of the DER-encoded subjects of | |
| // all of the certificates in the pool. | |
| func (s *CertPool) Subjects() [][]byte { | |
| res := make([][]byte, len(s.certs)) | |
| for i, c := range s.certs { | |
| res[i] = c.RawSubject | |
| } | |
| return res | |
| } |