github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/src/crypto/x509/root_darwin_arm_gen.go (about)

     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/x509"
    22  	"encoding/pem"
    23  	"flag"
    24  	"fmt"
    25  	"go/format"
    26  	"io/ioutil"
    27  	"log"
    28  	"math/big"
    29  	"net/http"
    30  	"os/exec"
    31  	"strings"
    32  )
    33  
    34  var output = flag.String("output", "root_darwin_armx.go", "file name to write")
    35  
    36  func main() {
    37  	certs, err := selectCerts()
    38  	if err != nil {
    39  		log.Fatal(err)
    40  	}
    41  
    42  	buf := new(bytes.Buffer)
    43  
    44  	fmt.Fprintf(buf, "// Created by root_darwin_arm_gen --output %s; DO NOT EDIT\n", *output)
    45  	fmt.Fprintf(buf, "%s", header)
    46  
    47  	fmt.Fprintf(buf, "const systemRootsPEM = `\n")
    48  	for _, cert := range certs {
    49  		b := &pem.Block{
    50  			Type:  "CERTIFICATE",
    51  			Bytes: cert.Raw,
    52  		}
    53  		if err := pem.Encode(buf, b); err != nil {
    54  			log.Fatal(err)
    55  		}
    56  	}
    57  	fmt.Fprintf(buf, "`")
    58  
    59  	source, err := format.Source(buf.Bytes())
    60  	if err != nil {
    61  		log.Fatal("source format error:", err)
    62  	}
    63  	if err := ioutil.WriteFile(*output, source, 0644); err != nil {
    64  		log.Fatal(err)
    65  	}
    66  }
    67  
    68  func selectCerts() ([]*x509.Certificate, error) {
    69  	ids, err := fetchCertIDs()
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	scerts, err := sysCerts()
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	var certs []*x509.Certificate
    80  	for _, id := range ids {
    81  		sn, ok := big.NewInt(0).SetString(id.serialNumber, 0) // 0x prefix selects hex
    82  		if !ok {
    83  			return nil, fmt.Errorf("invalid serial number: %q", id.serialNumber)
    84  		}
    85  		ski, ok := big.NewInt(0).SetString(id.subjectKeyID, 0)
    86  		if !ok {
    87  			return nil, fmt.Errorf("invalid Subject Key ID: %q", id.subjectKeyID)
    88  		}
    89  
    90  		for _, cert := range scerts {
    91  			if sn.Cmp(cert.SerialNumber) != 0 {
    92  				continue
    93  			}
    94  			cski := big.NewInt(0).SetBytes(cert.SubjectKeyId)
    95  			if ski.Cmp(cski) != 0 {
    96  				continue
    97  			}
    98  			certs = append(certs, cert)
    99  			break
   100  		}
   101  	}
   102  	return certs, nil
   103  }
   104  
   105  func sysCerts() (certs []*x509.Certificate, err error) {
   106  	cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain")
   107  	data, err := cmd.Output()
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	for len(data) > 0 {
   112  		var block *pem.Block
   113  		block, data = pem.Decode(data)
   114  		if block == nil {
   115  			break
   116  		}
   117  		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
   118  			continue
   119  		}
   120  
   121  		cert, err := x509.ParseCertificate(block.Bytes)
   122  		if err != nil {
   123  			continue
   124  		}
   125  		certs = append(certs, cert)
   126  	}
   127  	return certs, nil
   128  }
   129  
   130  type certID struct {
   131  	serialNumber string
   132  	subjectKeyID string
   133  }
   134  
   135  // fetchCertIDs fetches IDs of iOS X509 certificates from apple.com.
   136  func fetchCertIDs() ([]certID, error) {
   137  	resp, err := http.Get("https://support.apple.com/en-us/HT204132")
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	defer resp.Body.Close()
   142  	body, err := ioutil.ReadAll(resp.Body)
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	text := string(body)
   147  	text = text[strings.Index(text, "<section id=trusted"):]
   148  	text = text[:strings.Index(text, "</section>")]
   149  
   150  	lines := strings.Split(text, "\n")
   151  	var ids []certID
   152  	var id certID
   153  	for i, ln := range lines {
   154  		if i == len(lines)-1 {
   155  			break
   156  		}
   157  		const sn = "Serial Number:"
   158  		if ln == sn {
   159  			id.serialNumber = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1)
   160  			continue
   161  		}
   162  		if strings.HasPrefix(ln, sn) {
   163  			// extract hex value from parentheses.
   164  			id.serialNumber = ln[strings.Index(ln, "(")+1 : len(ln)-1]
   165  			continue
   166  		}
   167  		if strings.TrimSpace(ln) == "X509v3 Subject Key Identifier:" {
   168  			id.subjectKeyID = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1)
   169  			ids = append(ids, id)
   170  			id = certID{}
   171  		}
   172  	}
   173  	return ids, nil
   174  }
   175  
   176  const header = `
   177  // Copyright 2015 The Go Authors. All rights reserved.
   178  // Use of this source code is governed by a BSD-style
   179  // license that can be found in the LICENSE file.
   180  
   181  // +build cgo
   182  // +build darwin
   183  // +build arm arm64
   184  
   185  package x509
   186  
   187  func initSystemRoots() {
   188  	systemRoots = NewCertPool()
   189  	systemRoots.AppendCertsFromPEM([]byte(systemRootsPEM))
   190  }
   191  `