bundler.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  1. // Package bundler implements certificate bundling functionality for
  2. // CFSSL.
  3. package bundler
  4. import (
  5. "bytes"
  6. "crypto"
  7. "crypto/ecdsa"
  8. "crypto/rsa"
  9. "crypto/tls"
  10. "crypto/x509"
  11. "encoding/pem"
  12. goerr "errors"
  13. "fmt"
  14. "io/ioutil"
  15. "net"
  16. "net/http"
  17. "os"
  18. "path/filepath"
  19. "strconv"
  20. "strings"
  21. "time"
  22. "github.com/cloudflare/cfssl/errors"
  23. "github.com/cloudflare/cfssl/helpers"
  24. "github.com/cloudflare/cfssl/log"
  25. "github.com/cloudflare/cfssl/ubiquity"
  26. )
  27. // IntermediateStash contains the path to the directory where
  28. // downloaded intermediates should be saved.
  29. // When unspecified, downloaded intermediates are not saved.
  30. var IntermediateStash string
  31. // BundleFlavor is named optimization strategy on certificate chain selection when bundling.
  32. type BundleFlavor string
  33. const (
  34. // Optimal means the shortest chain with newest intermediates and
  35. // the most advanced crypto.
  36. Optimal BundleFlavor = "optimal"
  37. // Ubiquitous is aimed to provide the chain which is accepted
  38. // by the most platforms.
  39. Ubiquitous BundleFlavor = "ubiquitous"
  40. // Force means the bundler only verfiies the input as a valid bundle, not optimization is done.
  41. Force BundleFlavor = "force"
  42. )
  43. const (
  44. sha2Warning = "The bundle contains certificates signed with advanced hash functions such as SHA2, which are problematic for certain operating systems, e.g. Windows XP SP2."
  45. ecdsaWarning = "The bundle contains ECDSA signatures, which are problematic for certain operating systems, e.g. Windows XP, Android 2.2 and Android 2.3."
  46. expiringWarningStub = "The bundle is expiring within 30 days."
  47. untrustedWarningStub = "The bundle may not be trusted by the following platform(s):"
  48. ubiquityWarning = "Unable to measure bundle ubiquity: No platform metadata present."
  49. )
  50. // A Bundler contains the certificate pools for producing certificate
  51. // bundles. It contains any intermediates and root certificates that
  52. // should be used.
  53. type Bundler struct {
  54. RootPool *x509.CertPool
  55. IntermediatePool *x509.CertPool
  56. KnownIssuers map[string]bool
  57. opts options
  58. }
  59. type options struct {
  60. keyUsages []x509.ExtKeyUsage
  61. }
  62. var defaultOptions = options{
  63. keyUsages: []x509.ExtKeyUsage{
  64. x509.ExtKeyUsageAny,
  65. },
  66. }
  67. // An Option sets options such as allowed key usages, etc.
  68. type Option func(*options)
  69. // WithKeyUsages lets you set which Extended Key Usage values are acceptable. By
  70. // default x509.ExtKeyUsageAny will be used.
  71. func WithKeyUsages(usages ...x509.ExtKeyUsage) Option {
  72. return func(o *options) {
  73. o.keyUsages = usages
  74. }
  75. }
  76. // NewBundler creates a new Bundler from the files passed in; these
  77. // files should contain a list of valid root certificates and a list
  78. // of valid intermediate certificates, respectively.
  79. func NewBundler(caBundleFile, intBundleFile string, opt ...Option) (*Bundler, error) {
  80. var caBundle, intBundle []byte
  81. var err error
  82. if caBundleFile != "" {
  83. log.Debug("Loading CA bundle: ", caBundleFile)
  84. caBundle, err = ioutil.ReadFile(caBundleFile)
  85. if err != nil {
  86. log.Errorf("root bundle failed to load: %v", err)
  87. return nil, errors.Wrap(errors.RootError, errors.ReadFailed, err)
  88. }
  89. }
  90. if intBundleFile != "" {
  91. log.Debug("Loading Intermediate bundle: ", intBundleFile)
  92. intBundle, err = ioutil.ReadFile(intBundleFile)
  93. if err != nil {
  94. log.Errorf("intermediate bundle failed to load: %v", err)
  95. return nil, errors.Wrap(errors.IntermediatesError, errors.ReadFailed, err)
  96. }
  97. }
  98. if IntermediateStash != "" {
  99. if _, err = os.Stat(IntermediateStash); err != nil && os.IsNotExist(err) {
  100. log.Infof("intermediate stash directory %s doesn't exist, creating", IntermediateStash)
  101. err = os.MkdirAll(IntermediateStash, 0755)
  102. if err != nil {
  103. log.Errorf("failed to create intermediate stash directory %s: %v",
  104. IntermediateStash, err)
  105. return nil, err
  106. }
  107. log.Infof("intermediate stash directory %s created", IntermediateStash)
  108. }
  109. }
  110. return NewBundlerFromPEM(caBundle, intBundle, opt...)
  111. }
  112. // NewBundlerFromPEM creates a new Bundler from PEM-encoded root certificates and
  113. // intermediate certificates.
  114. // If caBundlePEM is nil, the resulting Bundler can only do "Force" bundle.
  115. func NewBundlerFromPEM(caBundlePEM, intBundlePEM []byte, opt ...Option) (*Bundler, error) {
  116. opts := defaultOptions
  117. for _, o := range opt {
  118. o(&opts)
  119. }
  120. log.Debug("parsing root certificates from PEM")
  121. roots, err := helpers.ParseCertificatesPEM(caBundlePEM)
  122. if err != nil {
  123. log.Errorf("failed to parse root bundle: %v", err)
  124. return nil, errors.New(errors.RootError, errors.ParseFailed)
  125. }
  126. log.Debug("parse intermediate certificates from PEM")
  127. intermediates, err := helpers.ParseCertificatesPEM(intBundlePEM)
  128. if err != nil {
  129. log.Errorf("failed to parse intermediate bundle: %v", err)
  130. return nil, errors.New(errors.IntermediatesError, errors.ParseFailed)
  131. }
  132. b := &Bundler{
  133. KnownIssuers: map[string]bool{},
  134. IntermediatePool: x509.NewCertPool(),
  135. opts: opts,
  136. }
  137. log.Debug("building certificate pools")
  138. // RootPool will be nil if caBundlePEM is nil, also
  139. // that translates to caBundleFile is "".
  140. // Systems root store will be used.
  141. if caBundlePEM != nil {
  142. b.RootPool = x509.NewCertPool()
  143. }
  144. for _, c := range roots {
  145. b.RootPool.AddCert(c)
  146. b.KnownIssuers[string(c.Signature)] = true
  147. }
  148. for _, c := range intermediates {
  149. b.IntermediatePool.AddCert(c)
  150. b.KnownIssuers[string(c.Signature)] = true
  151. }
  152. log.Debug("bundler set up")
  153. return b, nil
  154. }
  155. // VerifyOptions generates an x509 VerifyOptions structure that can be
  156. // used for verifying certificates.
  157. func (b *Bundler) VerifyOptions() x509.VerifyOptions {
  158. return x509.VerifyOptions{
  159. Roots: b.RootPool,
  160. Intermediates: b.IntermediatePool,
  161. KeyUsages: b.opts.keyUsages,
  162. }
  163. }
  164. // BundleFromFile takes a set of files containing the PEM-encoded leaf certificate
  165. // (optionally along with some intermediate certs), the PEM-encoded private key
  166. // and returns the bundle built from that key and the certificate(s).
  167. func (b *Bundler) BundleFromFile(bundleFile, keyFile string, flavor BundleFlavor, password string) (*Bundle, error) {
  168. log.Debug("Loading Certificate: ", bundleFile)
  169. certsRaw, err := ioutil.ReadFile(bundleFile)
  170. if err != nil {
  171. return nil, errors.Wrap(errors.CertificateError, errors.ReadFailed, err)
  172. }
  173. var keyPEM []byte
  174. // Load private key PEM only if a file is given
  175. if keyFile != "" {
  176. log.Debug("Loading private key: ", keyFile)
  177. keyPEM, err = ioutil.ReadFile(keyFile)
  178. if err != nil {
  179. log.Debugf("failed to read private key: ", err)
  180. return nil, errors.Wrap(errors.PrivateKeyError, errors.ReadFailed, err)
  181. }
  182. if len(keyPEM) == 0 {
  183. log.Debug("key is empty")
  184. return nil, errors.Wrap(errors.PrivateKeyError, errors.DecodeFailed, err)
  185. }
  186. }
  187. return b.BundleFromPEMorDER(certsRaw, keyPEM, flavor, password)
  188. }
  189. // BundleFromPEMorDER builds a certificate bundle from the set of byte
  190. // slices containing the PEM or DER-encoded certificate(s), private key.
  191. func (b *Bundler) BundleFromPEMorDER(certsRaw, keyPEM []byte, flavor BundleFlavor, password string) (*Bundle, error) {
  192. log.Debug("bundling from PEM files")
  193. var key crypto.Signer
  194. var err error
  195. if len(keyPEM) != 0 {
  196. key, err = helpers.ParsePrivateKeyPEM(keyPEM)
  197. if err != nil {
  198. log.Debugf("failed to parse private key: %v", err)
  199. return nil, err
  200. }
  201. }
  202. certs, err := helpers.ParseCertificatesPEM(certsRaw)
  203. if err != nil {
  204. // If PEM doesn't work try DER
  205. var keyDER crypto.Signer
  206. var errDER error
  207. certs, keyDER, errDER = helpers.ParseCertificatesDER(certsRaw, password)
  208. // Only use DER key if no key read from file
  209. if key == nil && keyDER != nil {
  210. key = keyDER
  211. }
  212. if errDER != nil {
  213. log.Debugf("failed to parse certificates: %v", err)
  214. // If neither parser works pass along PEM error
  215. return nil, err
  216. }
  217. }
  218. if len(certs) == 0 {
  219. log.Debugf("no certificates found")
  220. return nil, errors.New(errors.CertificateError, errors.DecodeFailed)
  221. }
  222. log.Debugf("bundle ready")
  223. return b.Bundle(certs, key, flavor)
  224. }
  225. // BundleFromRemote fetches the certificate served by the server at
  226. // serverName (or ip, if the ip argument is not the empty string). It
  227. // is expected that the method will be able to make a connection at
  228. // port 443. The certificate used by the server in this connection is
  229. // used to build the bundle, which will necessarily be keyless.
  230. func (b *Bundler) BundleFromRemote(serverName, ip string, flavor BundleFlavor) (*Bundle, error) {
  231. config := &tls.Config{
  232. RootCAs: b.RootPool,
  233. ServerName: serverName,
  234. }
  235. // Dial by IP if present
  236. var dialName string
  237. if ip != "" {
  238. dialName = ip + ":443"
  239. } else {
  240. dialName = serverName + ":443"
  241. }
  242. log.Debugf("bundling from remote %s", dialName)
  243. dialer := &net.Dialer{Timeout: time.Duration(5) * time.Second}
  244. conn, err := tls.DialWithDialer(dialer, "tcp", dialName, config)
  245. var dialError string
  246. // If there's an error in tls.Dial, try again with
  247. // InsecureSkipVerify to fetch the remote bundle to (re-)bundle
  248. // with. If the bundle is indeed not usable (expired, mismatched
  249. // hostnames, etc.), report the error. Otherwise, create a
  250. // working bundle and insert the tls error in the bundle.Status.
  251. if err != nil {
  252. log.Debugf("dial failed: %v", err)
  253. // record the error msg
  254. dialError = fmt.Sprintf("Failed rigid TLS handshake with %s: %v", dialName, err)
  255. // dial again with InsecureSkipVerify
  256. log.Debugf("try again with InsecureSkipVerify.")
  257. config.InsecureSkipVerify = true
  258. conn, err = tls.DialWithDialer(dialer, "tcp", dialName, config)
  259. if err != nil {
  260. log.Debugf("dial with InsecureSkipVerify failed: %v", err)
  261. return nil, errors.Wrap(errors.DialError, errors.Unknown, err)
  262. }
  263. }
  264. connState := conn.ConnectionState()
  265. certs := connState.PeerCertificates
  266. err = conn.VerifyHostname(serverName)
  267. if err != nil {
  268. log.Debugf("failed to verify hostname: %v", err)
  269. return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
  270. }
  271. // Bundle with remote certs. Inject the initial dial error, if any, to the status reporting.
  272. bundle, err := b.Bundle(certs, nil, flavor)
  273. if err != nil {
  274. return nil, err
  275. } else if dialError != "" {
  276. bundle.Status.Messages = append(bundle.Status.Messages, dialError)
  277. }
  278. return bundle, err
  279. }
  280. type fetchedIntermediate struct {
  281. Cert *x509.Certificate
  282. Name string
  283. }
  284. // fetchRemoteCertificate retrieves a single URL pointing to a certificate
  285. // and attempts to first parse it as a DER-encoded certificate; if
  286. // this fails, it attempts to decode it as a PEM-encoded certificate.
  287. func fetchRemoteCertificate(certURL string) (fi *fetchedIntermediate, err error) {
  288. log.Debugf("fetching remote certificate: %s", certURL)
  289. var resp *http.Response
  290. resp, err = http.Get(certURL)
  291. if err != nil {
  292. log.Debugf("failed HTTP get: %v", err)
  293. return
  294. }
  295. defer resp.Body.Close()
  296. var certData []byte
  297. certData, err = ioutil.ReadAll(resp.Body)
  298. if err != nil {
  299. log.Debugf("failed to read response body: %v", err)
  300. return
  301. }
  302. log.Debugf("attempting to parse certificate as DER")
  303. crt, err := x509.ParseCertificate(certData)
  304. if err != nil {
  305. log.Debugf("attempting to parse certificate as PEM")
  306. crt, err = helpers.ParseCertificatePEM(certData)
  307. if err != nil {
  308. log.Debugf("failed to parse certificate: %v", err)
  309. return
  310. }
  311. }
  312. log.Debugf("certificate fetch succeeds")
  313. fi = &fetchedIntermediate{Cert: crt, Name: constructCertFileName(crt)}
  314. return
  315. }
  316. func reverse(certs []*x509.Certificate) []*x509.Certificate {
  317. n := len(certs)
  318. if n == 0 {
  319. return certs
  320. }
  321. rcerts := []*x509.Certificate{}
  322. for i := n - 1; i >= 0; i-- {
  323. rcerts = append(rcerts, certs[i])
  324. }
  325. return rcerts
  326. }
  327. // Check if the certs form a partial cert chain: every cert verifies
  328. // the signature of the one in front of it.
  329. func partialVerify(certs []*x509.Certificate) bool {
  330. n := len(certs)
  331. if n == 0 {
  332. return false
  333. }
  334. for i := 0; i < n-1; i++ {
  335. if certs[i].CheckSignatureFrom(certs[i+1]) != nil {
  336. return false
  337. }
  338. }
  339. return true
  340. }
  341. func isSelfSigned(cert *x509.Certificate) bool {
  342. return cert.CheckSignatureFrom(cert) == nil
  343. }
  344. func isChainRootNode(cert *x509.Certificate) bool {
  345. if isSelfSigned(cert) {
  346. return true
  347. }
  348. return false
  349. }
  350. func (b *Bundler) verifyChain(chain []*fetchedIntermediate) bool {
  351. // This process will verify if the root of the (partial) chain is in our root pool,
  352. // and will fail otherwise.
  353. log.Debugf("verifying chain")
  354. for vchain := chain[:]; len(vchain) > 0; vchain = vchain[1:] {
  355. cert := vchain[0]
  356. // If this is a certificate in one of the pools, skip it.
  357. if b.KnownIssuers[string(cert.Cert.Signature)] {
  358. log.Debugf("certificate is known")
  359. continue
  360. }
  361. _, err := cert.Cert.Verify(b.VerifyOptions())
  362. if err != nil {
  363. log.Debugf("certificate failed verification: %v", err)
  364. return false
  365. } else if len(chain) == len(vchain) && isChainRootNode(cert.Cert) {
  366. // The first certificate in the chain is a root; it shouldn't be stored.
  367. log.Debug("looking at root certificate, will not store")
  368. continue
  369. }
  370. // leaf cert has an empty name, don't store leaf cert.
  371. if cert.Name == "" {
  372. continue
  373. }
  374. log.Debug("add certificate to intermediate pool:", cert.Name)
  375. b.IntermediatePool.AddCert(cert.Cert)
  376. b.KnownIssuers[string(cert.Cert.Signature)] = true
  377. if IntermediateStash != "" {
  378. fileName := filepath.Join(IntermediateStash, cert.Name)
  379. var block = pem.Block{Type: "CERTIFICATE", Bytes: cert.Cert.Raw}
  380. log.Debugf("write intermediate to stash directory: %s", fileName)
  381. // If the write fails, verification should not fail.
  382. err = ioutil.WriteFile(fileName, pem.EncodeToMemory(&block), 0644)
  383. if err != nil {
  384. log.Errorf("failed to write new intermediate: %v", err)
  385. } else {
  386. log.Info("stashed new intermediate ", cert.Name)
  387. }
  388. }
  389. }
  390. return true
  391. }
  392. // constructCertFileName returns a uniquely identifying file name for a certificate
  393. func constructCertFileName(cert *x509.Certificate) string {
  394. // construct the filename as the CN with no period and space
  395. name := strings.Replace(cert.Subject.CommonName, ".", "", -1)
  396. name = strings.Replace(name, " ", "", -1)
  397. // add SKI and serial number as extra identifier
  398. name += fmt.Sprintf("_%x", cert.SubjectKeyId)
  399. name += fmt.Sprintf("_%x", cert.SerialNumber.Bytes())
  400. name += ".crt"
  401. return name
  402. }
  403. // fetchIntermediates goes through each of the URLs in the AIA "Issuing
  404. // CA" extensions and fetches those certificates. If those
  405. // certificates are not present in either the root pool or
  406. // intermediate pool, the certificate is saved to file and added to
  407. // the list of intermediates to be used for verification. This will
  408. // not add any new certificates to the root pool; if the ultimate
  409. // issuer is not trusted, fetching the certicate here will not change
  410. // that.
  411. func (b *Bundler) fetchIntermediates(certs []*x509.Certificate) (err error) {
  412. if IntermediateStash != "" {
  413. log.Debugf("searching intermediates")
  414. if _, err := os.Stat(IntermediateStash); err != nil && os.IsNotExist(err) {
  415. log.Infof("intermediate stash directory %s doesn't exist, creating", IntermediateStash)
  416. err = os.MkdirAll(IntermediateStash, 0755)
  417. if err != nil {
  418. log.Errorf("failed to create intermediate stash directory %s: %v", IntermediateStash, err)
  419. return err
  420. }
  421. log.Infof("intermediate stash directory %s created", IntermediateStash)
  422. }
  423. }
  424. // stores URLs and certificate signatures that have been seen
  425. seen := map[string]bool{}
  426. var foundChains int
  427. // Construct a verify chain as a reversed partial bundle,
  428. // such that the certs are ordered by promxity to the root CAs.
  429. var chain []*fetchedIntermediate
  430. for i, cert := range certs {
  431. var name string
  432. // Only construct filenames for non-leaf intermediate certs
  433. // so they will be saved to disk if necessary.
  434. // Leaf cert gets a empty name and will be skipped.
  435. if i > 0 {
  436. name = constructCertFileName(cert)
  437. }
  438. chain = append([]*fetchedIntermediate{{cert, name}}, chain...)
  439. seen[string(cert.Signature)] = true
  440. }
  441. // Verify the chain and store valid intermediates in the chain.
  442. // If it doesn't verify, fetch the intermediates and extend the chain
  443. // in a DFS manner and verify each time we hit a root.
  444. for {
  445. if len(chain) == 0 {
  446. log.Debugf("search complete")
  447. if foundChains == 0 {
  448. return x509.UnknownAuthorityError{}
  449. }
  450. return nil
  451. }
  452. current := chain[0]
  453. var advanced bool
  454. if b.verifyChain(chain) {
  455. foundChains++
  456. }
  457. log.Debugf("walk AIA issuers")
  458. for _, url := range current.Cert.IssuingCertificateURL {
  459. if seen[url] {
  460. log.Debugf("url %s has been seen", url)
  461. continue
  462. }
  463. crt, err := fetchRemoteCertificate(url)
  464. if err != nil {
  465. continue
  466. } else if seen[string(crt.Cert.Signature)] {
  467. log.Debugf("fetched certificate is known")
  468. continue
  469. }
  470. seen[url] = true
  471. seen[string(crt.Cert.Signature)] = true
  472. chain = append([]*fetchedIntermediate{crt}, chain...)
  473. advanced = true
  474. break
  475. }
  476. if !advanced {
  477. log.Debugf("didn't advance, stepping back")
  478. chain = chain[1:]
  479. }
  480. }
  481. }
  482. // Bundle takes an X509 certificate (already in the
  483. // Certificate structure), a private key as crypto.Signer in one of the appropriate
  484. // formats (i.e. *rsa.PrivateKey or *ecdsa.PrivateKey, or even a opaque key), using them to
  485. // build a certificate bundle.
  486. func (b *Bundler) Bundle(certs []*x509.Certificate, key crypto.Signer, flavor BundleFlavor) (*Bundle, error) {
  487. log.Infof("bundling certificate for %+v", certs[0].Subject)
  488. if len(certs) == 0 {
  489. return nil, nil
  490. }
  491. // Detect reverse ordering of the cert chain.
  492. if len(certs) > 1 && !partialVerify(certs) {
  493. rcerts := reverse(certs)
  494. if partialVerify(rcerts) {
  495. certs = rcerts
  496. }
  497. }
  498. var ok bool
  499. cert := certs[0]
  500. if key != nil {
  501. switch {
  502. case cert.PublicKeyAlgorithm == x509.RSA:
  503. var rsaPublicKey *rsa.PublicKey
  504. if rsaPublicKey, ok = key.Public().(*rsa.PublicKey); !ok {
  505. return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
  506. }
  507. if cert.PublicKey.(*rsa.PublicKey).N.Cmp(rsaPublicKey.N) != 0 {
  508. return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
  509. }
  510. case cert.PublicKeyAlgorithm == x509.ECDSA:
  511. var ecdsaPublicKey *ecdsa.PublicKey
  512. if ecdsaPublicKey, ok = key.Public().(*ecdsa.PublicKey); !ok {
  513. return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
  514. }
  515. if cert.PublicKey.(*ecdsa.PublicKey).X.Cmp(ecdsaPublicKey.X) != 0 {
  516. return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
  517. }
  518. default:
  519. return nil, errors.New(errors.PrivateKeyError, errors.NotRSAOrECC)
  520. }
  521. } else {
  522. switch {
  523. case cert.PublicKeyAlgorithm == x509.RSA:
  524. case cert.PublicKeyAlgorithm == x509.ECDSA:
  525. default:
  526. return nil, errors.New(errors.PrivateKeyError, errors.NotRSAOrECC)
  527. }
  528. }
  529. bundle := new(Bundle)
  530. bundle.Cert = cert
  531. bundle.Key = key
  532. bundle.Issuer = &cert.Issuer
  533. bundle.Subject = &cert.Subject
  534. bundle.buildHostnames()
  535. if flavor == Force {
  536. // force bundle checks the certificates
  537. // forms a verification chain.
  538. if !partialVerify(certs) {
  539. return nil,
  540. errors.Wrap(errors.CertificateError, errors.VerifyFailed,
  541. goerr.New("Unable to verify the certificate chain"))
  542. }
  543. bundle.Chain = certs
  544. } else {
  545. // disallow self-signed cert
  546. if cert.CheckSignatureFrom(cert) == nil {
  547. return nil, errors.New(errors.CertificateError, errors.SelfSigned)
  548. }
  549. chains, err := cert.Verify(b.VerifyOptions())
  550. if err != nil {
  551. log.Debugf("verification failed: %v", err)
  552. // If the error was an unknown authority, try to fetch
  553. // the intermediate specified in the AIA and add it to
  554. // the intermediates bundle.
  555. if _, ok := err.(x509.UnknownAuthorityError); !ok {
  556. return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
  557. }
  558. log.Debugf("searching for intermediates via AIA issuer")
  559. searchErr := b.fetchIntermediates(certs)
  560. if searchErr != nil {
  561. log.Debugf("search failed: %v", searchErr)
  562. return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
  563. }
  564. log.Debugf("verifying new chain")
  565. chains, err = cert.Verify(b.VerifyOptions())
  566. if err != nil {
  567. log.Debugf("failed to verify chain: %v", err)
  568. return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
  569. }
  570. log.Debugf("verify ok")
  571. }
  572. var matchingChains [][]*x509.Certificate
  573. switch flavor {
  574. case Optimal:
  575. matchingChains = optimalChains(chains)
  576. case Ubiquitous:
  577. if len(ubiquity.Platforms) == 0 {
  578. log.Warning("No metadata, Ubiquitous falls back to Optimal.")
  579. }
  580. matchingChains = ubiquitousChains(chains)
  581. default:
  582. matchingChains = ubiquitousChains(chains)
  583. }
  584. bundle.Chain = matchingChains[0]
  585. }
  586. statusCode := int(errors.Success)
  587. var messages []string
  588. // Check if bundle is expiring.
  589. expiringCerts := checkExpiringCerts(bundle.Chain)
  590. if len(expiringCerts) > 0 {
  591. statusCode |= errors.BundleExpiringBit
  592. messages = append(messages, expirationWarning(expiringCerts))
  593. }
  594. // Check if bundle contains SHA2 certs.
  595. if ubiquity.ChainHashUbiquity(bundle.Chain) <= ubiquity.SHA2Ubiquity {
  596. statusCode |= errors.BundleNotUbiquitousBit
  597. messages = append(messages, sha2Warning)
  598. }
  599. // Check if bundle contains ECDSA signatures.
  600. if ubiquity.ChainKeyAlgoUbiquity(bundle.Chain) <= ubiquity.ECDSA256Ubiquity {
  601. statusCode |= errors.BundleNotUbiquitousBit
  602. messages = append(messages, ecdsaWarning)
  603. }
  604. // when forcing a bundle, bundle ubiquity doesn't matter
  605. // also we don't retrieve the anchoring root of the bundle
  606. var untrusted []string
  607. if flavor != Force {
  608. // Add root store presence info
  609. root := bundle.Chain[len(bundle.Chain)-1]
  610. bundle.Root = root
  611. log.Infof("the anchoring root is %v", root.Subject)
  612. // Check if there is any platform that doesn't trust the chain.
  613. // Also, an warning will be generated if ubiquity.Platforms is nil,
  614. untrusted = ubiquity.UntrustedPlatforms(root)
  615. untrustedMsg := untrustedPlatformsWarning(untrusted)
  616. if len(untrustedMsg) > 0 {
  617. log.Debug("Populate untrusted platform warning.")
  618. statusCode |= errors.BundleNotUbiquitousBit
  619. messages = append(messages, untrustedMsg)
  620. }
  621. }
  622. // Check if there is any platform that rejects the chain because of SHA1 deprecation.
  623. sha1Msgs := ubiquity.SHA1DeprecationMessages(bundle.Chain)
  624. if len(sha1Msgs) > 0 {
  625. log.Debug("Populate SHA1 deprecation warning.")
  626. statusCode |= errors.BundleNotUbiquitousBit
  627. messages = append(messages, sha1Msgs...)
  628. }
  629. bundle.Status = &BundleStatus{ExpiringSKIs: getSKIs(bundle.Chain, expiringCerts), Code: statusCode, Messages: messages, Untrusted: untrusted}
  630. // attempt to not to include the root certificate for optimization
  631. if flavor != Force {
  632. // Include at least one intermediate if the leaf has enabled OCSP and is not CA.
  633. if bundle.Cert.OCSPServer != nil && !bundle.Cert.IsCA && len(bundle.Chain) <= 2 {
  634. // No op. Return one intermediate if there is one.
  635. } else {
  636. // do not include the root.
  637. bundle.Chain = bundle.Chain[:len(bundle.Chain)-1]
  638. }
  639. }
  640. bundle.Status.IsRebundled = diff(bundle.Chain, certs)
  641. bundle.Expires = helpers.ExpiryTime(bundle.Chain)
  642. bundle.LeafExpires = bundle.Chain[0].NotAfter
  643. log.Debugf("bundle complete")
  644. return bundle, nil
  645. }
  646. // checkExpiringCerts returns indices of certs that are expiring within 30 days.
  647. func checkExpiringCerts(chain []*x509.Certificate) (expiringIntermediates []int) {
  648. now := time.Now()
  649. for i, cert := range chain {
  650. if cert.NotAfter.Sub(now).Hours() < 720 {
  651. expiringIntermediates = append(expiringIntermediates, i)
  652. }
  653. }
  654. return
  655. }
  656. // getSKIs returns a list of cert subject key id in the bundle chain with matched indices.
  657. func getSKIs(chain []*x509.Certificate, indices []int) (skis []string) {
  658. for _, index := range indices {
  659. ski := fmt.Sprintf("%X", chain[index].SubjectKeyId)
  660. skis = append(skis, ski)
  661. }
  662. return
  663. }
  664. // expirationWarning generates a warning message with expiring certs.
  665. func expirationWarning(expiringIntermediates []int) (ret string) {
  666. if len(expiringIntermediates) == 0 {
  667. return
  668. }
  669. ret = expiringWarningStub
  670. if len(expiringIntermediates) > 1 {
  671. ret = ret + "The expiring certs are"
  672. } else {
  673. ret = ret + "The expiring cert is"
  674. }
  675. for _, index := range expiringIntermediates {
  676. ret = ret + " #" + strconv.Itoa(index+1)
  677. }
  678. ret = ret + " in the chain."
  679. return
  680. }
  681. // untrustedPlatformsWarning generates a warning message with untrusted platform names.
  682. func untrustedPlatformsWarning(platforms []string) string {
  683. if len(ubiquity.Platforms) == 0 {
  684. return ubiquityWarning
  685. }
  686. if len(platforms) == 0 {
  687. return ""
  688. }
  689. msg := untrustedWarningStub
  690. for i, platform := range platforms {
  691. if i > 0 {
  692. msg += ","
  693. }
  694. msg += " " + platform
  695. }
  696. msg += "."
  697. return msg
  698. }
  699. // Optimal chains are the shortest chains, with newest intermediates and most advanced crypto suite being the tie breaker.
  700. func optimalChains(chains [][]*x509.Certificate) [][]*x509.Certificate {
  701. // Find shortest chains
  702. chains = ubiquity.Filter(chains, ubiquity.CompareChainLength)
  703. // Find the chains with longest expiry.
  704. chains = ubiquity.Filter(chains, ubiquity.CompareChainExpiry)
  705. // Find the chains with more advanced crypto suite
  706. chains = ubiquity.Filter(chains, ubiquity.CompareChainCryptoSuite)
  707. return chains
  708. }
  709. // Ubiquitous chains are the chains with highest platform coverage and break ties with the optimal strategy.
  710. func ubiquitousChains(chains [][]*x509.Certificate) [][]*x509.Certificate {
  711. // Filter out chains with highest cross platform ubiquity.
  712. chains = ubiquity.Filter(chains, ubiquity.ComparePlatformUbiquity)
  713. // Prefer that all intermediates are SHA-2 certs if the leaf is a SHA-2 cert, in order to improve ubiquity.
  714. chains = ubiquity.Filter(chains, ubiquity.CompareSHA2Homogeneity)
  715. // Filter shortest chains
  716. chains = ubiquity.Filter(chains, ubiquity.CompareChainLength)
  717. // Filter chains with highest signature hash ubiquity.
  718. chains = ubiquity.Filter(chains, ubiquity.CompareChainHashUbiquity)
  719. // Filter chains with highest keyAlgo ubiquity.
  720. chains = ubiquity.Filter(chains, ubiquity.CompareChainKeyAlgoUbiquity)
  721. // Filter chains with intermediates that last longer.
  722. chains = ubiquity.Filter(chains, ubiquity.CompareExpiryUbiquity)
  723. // Use the optimal strategy as final tie breaker.
  724. return optimalChains(chains)
  725. }
  726. // diff checkes if two input cert chains are not identical
  727. func diff(chain1, chain2 []*x509.Certificate) bool {
  728. // Check if bundled one is different from the input.
  729. diff := false
  730. if len(chain1) != len(chain2) {
  731. diff = true
  732. } else {
  733. for i := 0; i < len(chain1); i++ {
  734. cert1 := chain1[i]
  735. cert2 := chain2[i]
  736. // Use signature to differentiate.
  737. if !bytes.Equal(cert1.Signature, cert2.Signature) {
  738. diff = true
  739. break
  740. }
  741. }
  742. }
  743. return diff
  744. }