Home | History | Annotate | Download | only in x509
      1 // Copyright 2015 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // +build ignore
      6 
      7 // Generates root_darwin_armx.go.
      8 //
      9 // As of iOS 8, there is no API for querying the system trusted X.509 root
     10 // certificates. We could use SecTrustEvaluate to verify that a trust chain
     11 // exists for a certificate, but the x509 API requires returning the entire
     12 // chain.
     13 //
     14 // Apple publishes the list of trusted root certificates for iOS on
     15 // support.apple.com. So we parse the list and extract the certificates from
     16 // an OS X machine and embed them into the x509 package.
     17 package main
     18 
     19 import (
     20 	"bytes"
     21 	"crypto/sha256"
     22 	"crypto/x509"
     23 	"encoding/hex"
     24 	"encoding/pem"
     25 	"flag"
     26 	"fmt"
     27 	"go/format"
     28 	"io/ioutil"
     29 	"log"
     30 	"net/http"
     31 	"os/exec"
     32 	"regexp"
     33 	"strings"
     34 )
     35 
     36 var output = flag.String("output", "root_darwin_armx.go", "file name to write")
     37 
     38 func main() {
     39 	certs, err := selectCerts()
     40 	if err != nil {
     41 		log.Fatal(err)
     42 	}
     43 
     44 	buf := new(bytes.Buffer)
     45 
     46 	fmt.Fprintf(buf, "// Code generated by root_darwin_arm_gen --output %s; DO NOT EDIT.\n", *output)
     47 	fmt.Fprintf(buf, "%s", header)
     48 
     49 	fmt.Fprintf(buf, "const systemRootsPEM = `\n")
     50 	for _, cert := range certs {
     51 		b := &pem.Block{
     52 			Type:  "CERTIFICATE",
     53 			Bytes: cert.Raw,
     54 		}
     55 		if err := pem.Encode(buf, b); err != nil {
     56 			log.Fatal(err)
     57 		}
     58 	}
     59 	fmt.Fprintf(buf, "`")
     60 
     61 	source, err := format.Source(buf.Bytes())
     62 	if err != nil {
     63 		log.Fatal("source format error:", err)
     64 	}
     65 	if err := ioutil.WriteFile(*output, source, 0644); err != nil {
     66 		log.Fatal(err)
     67 	}
     68 }
     69 
     70 func selectCerts() ([]*x509.Certificate, error) {
     71 	ids, err := fetchCertIDs()
     72 	if err != nil {
     73 		return nil, err
     74 	}
     75 
     76 	scerts, err := sysCerts()
     77 	if err != nil {
     78 		return nil, err
     79 	}
     80 
     81 	var certs []*x509.Certificate
     82 	for _, id := range ids {
     83 		if c, ok := scerts[id.fingerprint]; ok {
     84 			certs = append(certs, c)
     85 		} else {
     86 			fmt.Printf("WARNING: cannot find certificate: %s (fingerprint: %s)\n", id.name, id.fingerprint)
     87 		}
     88 	}
     89 	return certs, nil
     90 }
     91 
     92 func sysCerts() (certs map[string]*x509.Certificate, err error) {
     93 	cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain")
     94 	data, err := cmd.Output()
     95 	if err != nil {
     96 		return nil, err
     97 	}
     98 	certs = make(map[string]*x509.Certificate)
     99 	for len(data) > 0 {
    100 		var block *pem.Block
    101 		block, data = pem.Decode(data)
    102 		if block == nil {
    103 			break
    104 		}
    105 		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
    106 			continue
    107 		}
    108 
    109 		cert, err := x509.ParseCertificate(block.Bytes)
    110 		if err != nil {
    111 			continue
    112 		}
    113 
    114 		fingerprint := sha256.Sum256(cert.Raw)
    115 		certs[hex.EncodeToString(fingerprint[:])] = cert
    116 	}
    117 	return certs, nil
    118 }
    119 
    120 type certID struct {
    121 	name        string
    122 	fingerprint string
    123 }
    124 
    125 // fetchCertIDs fetches IDs of iOS X509 certificates from apple.com.
    126 func fetchCertIDs() ([]certID, error) {
    127 	// Download the iOS 11 support page. The index for all iOS versions is here:
    128 	// https://support.apple.com/en-us/HT204132
    129 	resp, err := http.Get("https://support.apple.com/en-us/HT208125")
    130 	if err != nil {
    131 		return nil, err
    132 	}
    133 	defer resp.Body.Close()
    134 	body, err := ioutil.ReadAll(resp.Body)
    135 	if err != nil {
    136 		return nil, err
    137 	}
    138 	text := string(body)
    139 	text = text[strings.Index(text, "<div id=trusted"):]
    140 	text = text[:strings.Index(text, "</div>")]
    141 
    142 	var ids []certID
    143 	cols := make(map[string]int)
    144 	for i, rowmatch := range regexp.MustCompile("(?s)<tr>(.*?)</tr>").FindAllStringSubmatch(text, -1) {
    145 		row := rowmatch[1]
    146 		if i == 0 {
    147 			// Parse table header row to extract column names
    148 			for i, match := range regexp.MustCompile("(?s)<th>(.*?)</th>").FindAllStringSubmatch(row, -1) {
    149 				cols[match[1]] = i
    150 			}
    151 			continue
    152 		}
    153 
    154 		values := regexp.MustCompile("(?s)<td>(.*?)</td>").FindAllStringSubmatch(row, -1)
    155 		name := values[cols["Certificate name"]][1]
    156 		fingerprint := values[cols["Fingerprint (SHA-256)"]][1]
    157 		fingerprint = strings.Replace(fingerprint, "<br>", "", -1)
    158 		fingerprint = strings.Replace(fingerprint, "\n", "", -1)
    159 		fingerprint = strings.Replace(fingerprint, " ", "", -1)
    160 		fingerprint = strings.ToLower(fingerprint)
    161 
    162 		ids = append(ids, certID{
    163 			name:        name,
    164 			fingerprint: fingerprint,
    165 		})
    166 	}
    167 	return ids, nil
    168 }
    169 
    170 const header = `
    171 // Copyright 2015 The Go Authors. All rights reserved.
    172 // Use of this source code is governed by a BSD-style
    173 // license that can be found in the LICENSE file.
    174 
    175 // +build cgo
    176 // +build darwin
    177 // +build arm arm64 ios
    178 
    179 package x509
    180 
    181 func loadSystemRoots() (*CertPool, error) {
    182 	p := NewCertPool()
    183 	p.AppendCertsFromPEM([]byte(systemRootsPEM))
    184 	return p, nil
    185 }
    186 `
    187