| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build ignore |
| |
| // Generates root_darwin_armx.go. |
| // |
| // As of iOS 8, there is no API for querying the system trusted X.509 root |
| // certificates. We could use SecTrustEvaluate to verify that a trust chain |
| // exists for a certificate, but the x509 API requires returning the entire |
| // chain. |
| // |
| // Apple publishes the list of trusted root certificates for iOS on |
| // support.apple.com. So we parse the list and extract the certificates from |
| // an OS X machine and embed them into the x509 package. |
| package main |
| |
| import ( |
| "bytes" |
| "crypto/x509" |
| "encoding/pem" |
| "flag" |
| "fmt" |
| "go/format" |
| "io/ioutil" |
| "log" |
| "math/big" |
| "net/http" |
| "os/exec" |
| "strings" |
| ) |
| |
| var output = flag.String("output", "root_darwin_armx.go", "file name to write") |
| |
| func main() { |
| certs, err := selectCerts() |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| buf := new(bytes.Buffer) |
| |
| fmt.Fprintf(buf, "// Created by root_darwin_arm_gen --output %s; DO NOT EDIT\n", *output) |
| fmt.Fprintf(buf, "%s", header) |
| |
| fmt.Fprintf(buf, "const systemRootsPEM = `\n") |
| for _, cert := range certs { |
| b := &pem.Block{ |
| Type: "CERTIFICATE", |
| Bytes: cert.Raw, |
| } |
| if err := pem.Encode(buf, b); err != nil { |
| log.Fatal(err) |
| } |
| } |
| fmt.Fprintf(buf, "`") |
| |
| source, err := format.Source(buf.Bytes()) |
| if err != nil { |
| log.Fatal("source format error:", err) |
| } |
| if err := ioutil.WriteFile(*output, source, 0644); err != nil { |
| log.Fatal(err) |
| } |
| } |
| |
| func selectCerts() ([]*x509.Certificate, error) { |
| ids, err := fetchCertIDs() |
| if err != nil { |
| return nil, err |
| } |
| |
| scerts, err := sysCerts() |
| if err != nil { |
| return nil, err |
| } |
| |
| var certs []*x509.Certificate |
| for _, id := range ids { |
| sn, ok := big.NewInt(0).SetString(id.serialNumber, 0) // 0x prefix selects hex |
| if !ok { |
| return nil, fmt.Errorf("invalid serial number: %q", id.serialNumber) |
| } |
| ski, ok := big.NewInt(0).SetString(id.subjectKeyID, 0) |
| if !ok { |
| return nil, fmt.Errorf("invalid Subject Key ID: %q", id.subjectKeyID) |
| } |
| |
| for _, cert := range scerts { |
| if sn.Cmp(cert.SerialNumber) != 0 { |
| continue |
| } |
| cski := big.NewInt(0).SetBytes(cert.SubjectKeyId) |
| if ski.Cmp(cski) != 0 { |
| continue |
| } |
| certs = append(certs, cert) |
| break |
| } |
| } |
| return certs, nil |
| } |
| |
| func sysCerts() (certs []*x509.Certificate, err error) { |
| cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain") |
| data, err := cmd.Output() |
| if err != nil { |
| return nil, err |
| } |
| for len(data) > 0 { |
| var block *pem.Block |
| block, data = pem.Decode(data) |
| if block == nil { |
| break |
| } |
| if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { |
| continue |
| } |
| |
| cert, err := x509.ParseCertificate(block.Bytes) |
| if err != nil { |
| continue |
| } |
| certs = append(certs, cert) |
| } |
| return certs, nil |
| } |
| |
| type certID struct { |
| serialNumber string |
| subjectKeyID string |
| } |
| |
| // fetchCertIDs fetches IDs of iOS X509 certificates from apple.com. |
| func fetchCertIDs() ([]certID, error) { |
| resp, err := http.Get("https://support.apple.com/en-us/HT204132") |
| if err != nil { |
| return nil, err |
| } |
| defer resp.Body.Close() |
| body, err := ioutil.ReadAll(resp.Body) |
| if err != nil { |
| return nil, err |
| } |
| text := string(body) |
| text = text[strings.Index(text, "<section id=trusted"):] |
| text = text[:strings.Index(text, "</section>")] |
| |
| lines := strings.Split(text, "\n") |
| var ids []certID |
| var id certID |
| for i, ln := range lines { |
| if i == len(lines)-1 { |
| break |
| } |
| const sn = "Serial Number:" |
| if ln == sn { |
| id.serialNumber = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1) |
| continue |
| } |
| if strings.HasPrefix(ln, sn) { |
| // extract hex value from parentheses. |
| id.serialNumber = ln[strings.Index(ln, "(")+1 : len(ln)-1] |
| continue |
| } |
| if strings.TrimSpace(ln) == "X509v3 Subject Key Identifier:" { |
| id.subjectKeyID = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1) |
| ids = append(ids, id) |
| id = certID{} |
| } |
| } |
| return ids, nil |
| } |
| |
| const header = ` |
| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build cgo |
| // +build darwin |
| // +build arm arm64 |
| |
| package x509 |
| |
| func loadSystemRoots() (*CertPool, error) { |
| p := NewCertPool() |
| p.AppendCertsFromPEM([]byte(systemRootsPEM)) |
| return p, nil |
| } |
| ` |