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