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/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 `
    192