// Copyright 2015 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. // +build ignore // Generates root_darwin_armx.go. // // As of iOS 8, there is no API for querying the system trusted X.509 root // certificates. We could use SecTrustEvaluate to verify that a trust chain // exists for a certificate, but the x509 API requires returning the entire // chain. // // Apple publishes the list of trusted root certificates for iOS on // support.apple.com. So we parse the list and extract the certificates from // an OS X machine and embed them into the x509 package. package main import ( "bytes" "crypto/sha256" "crypto/x509" "encoding/hex" "encoding/pem" "flag" "fmt" "go/format" "io/ioutil" "log" "net/http" "os/exec" "regexp" "strings" ) var output = flag.String("output", "root_darwin_armx.go", "file name to write") func main() { certs, err := selectCerts() if err != nil { log.Fatal(err) } buf := new(bytes.Buffer) fmt.Fprintf(buf, "// Code generated by root_darwin_arm_gen --output %s; DO NOT EDIT.\n", *output) fmt.Fprintf(buf, "%s", header) fmt.Fprintf(buf, "const systemRootsPEM = `\n") for _, cert := range certs { b := &pem.Block{ Type: "CERTIFICATE", Bytes: cert.Raw, } if err := pem.Encode(buf, b); err != nil { log.Fatal(err) } } fmt.Fprintf(buf, "`") source, err := format.Source(buf.Bytes()) if err != nil { log.Fatal("source format error:", err) } if err := ioutil.WriteFile(*output, source, 0644); err != nil { log.Fatal(err) } } func selectCerts() ([]*x509.Certificate, error) { ids, err := fetchCertIDs() if err != nil { return nil, err } scerts, err := sysCerts() if err != nil { return nil, err } var certs []*x509.Certificate for _, id := range ids { if c, ok := scerts[id.fingerprint]; ok { certs = append(certs, c) } else { fmt.Printf("WARNING: cannot find certificate: %s (fingerprint: %s)\n", id.name, id.fingerprint) } } return certs, nil } func sysCerts() (certs map[string]*x509.Certificate, err error) { cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain") data, err := cmd.Output() if err != nil { return nil, err } certs = make(map[string]*x509.Certificate) for len(data) > 0 { var block *pem.Block block, data = pem.Decode(data) if block == nil { break } if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { continue } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { continue } fingerprint := sha256.Sum256(cert.Raw) certs[hex.EncodeToString(fingerprint[:])] = cert } return certs, nil } type certID struct { name string fingerprint string } // fetchCertIDs fetches IDs of iOS X509 certificates from apple.com. func fetchCertIDs() ([]certID, error) { // Download the iOS 11 support page. The index for all iOS versions is here: // https://support.apple.com/en-us/HT204132 resp, err := http.Get("https://support.apple.com/en-us/HT208125") if err != nil { return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } text := string(body) text = text[strings.Index(text, "<div id=trusted"):] text = text[:strings.Index(text, "</div>")] var ids []certID cols := make(map[string]int) for i, rowmatch := range regexp.MustCompile("(?s)<tr>(.*?)</tr>").FindAllStringSubmatch(text, -1) { row := rowmatch[1] if i == 0 { // Parse table header row to extract column names for i, match := range regexp.MustCompile("(?s)<th>(.*?)</th>").FindAllStringSubmatch(row, -1) { cols[match[1]] = i } continue } values := regexp.MustCompile("(?s)<td>(.*?)</td>").FindAllStringSubmatch(row, -1) name := values[cols["Certificate name"]][1] fingerprint := values[cols["Fingerprint (SHA-256)"]][1] fingerprint = strings.ReplaceAll(fingerprint, "<br>", "") fingerprint = strings.ReplaceAll(fingerprint, "\n", "") fingerprint = strings.ReplaceAll(fingerprint, " ", "") fingerprint = strings.ToLower(fingerprint) ids = append(ids, certID{ name: name, fingerprint: fingerprint, }) } return ids, nil } const header = ` // Copyright 2015 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. // +build cgo // +build darwin // +build arm arm64 ios package x509 func loadSystemRoots() (*CertPool, error) { p := NewCertPool() p.AppendCertsFromPEM([]byte(systemRootsPEM)) return p, nil } `