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 loadSystemRoots() (*CertPool, error) { 188 p := NewCertPool() 189 p.AppendCertsFromPEM([]byte(systemRootsPEM)) 190 return p, nil 191 } 192 ` 193