...

Source file src/crypto/x509/root_darwin_arm_gen.go

     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.ReplaceAll(fingerprint, "<br>", "")
   158			fingerprint = strings.ReplaceAll(fingerprint, "\n", "")
   159			fingerprint = strings.ReplaceAll(fingerprint, " ", "")
   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	

View as plain text