// 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_ios.go. // // As of iOS 13, there is no API for querying the system trusted X.509 root // certificates. // // Apple publishes the trusted root certificates for iOS and macOS on // opensource.apple.com so we embed them into the x509 package. // // Note that this ignores distrusted and revoked certificates. package main import ( "archive/tar" "bytes" "compress/gzip" "crypto/sha256" "crypto/tls" "crypto/x509" "encoding/pem" "flag" "fmt" "go/format" "io" "log" "net/http" "os" "path" "sort" "strings" "time" ) func main() { var output = flag.String("output", "root_ios.go", "file name to write") var version = flag.String("version", "", "security_certificates version") flag.Parse() if *version == "" { log.Fatal("Select the latest security_certificates version from " + "https://opensource.apple.com/source/security_certificates/") } url := "https://opensource.apple.com/tarballs/security_certificates/security_certificates-%s.tar.gz" hc := &http.Client{Timeout: 1 * time.Minute} resp, err := hc.Get(fmt.Sprintf(url, *version)) if err != nil { log.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { log.Fatalf("HTTP status not OK: %s", resp.Status) } zr, err := gzip.NewReader(resp.Body) if err != nil { log.Fatal(err) } defer zr.Close() var certs []*x509.Certificate pool := x509.NewCertPool() tr := tar.NewReader(zr) for { hdr, err := tr.Next() if err == io.EOF { break } if err != nil { log.Fatal(err) } rootsDirectory := fmt.Sprintf("security_certificates-%s/certificates/roots/", *version) if dir, file := path.Split(hdr.Name); hdr.Typeflag != tar.TypeReg || dir != rootsDirectory || strings.HasPrefix(file, ".") { continue } der, err := io.ReadAll(tr) if err != nil { log.Fatal(err) } c, err := x509.ParseCertificate(der) if err != nil { log.Printf("Failed to parse certificate %q: %v", hdr.Name, err) continue } certs = append(certs, c) pool.AddCert(c) } // Quick smoke test to check the pool is well formed, and that we didn't end // up trusting roots in the removed folder. for _, c := range certs { if c.Subject.CommonName == "Symantec Class 2 Public Primary Certification Authority - G4" { log.Fatal("The pool includes a removed root!") } } conn, err := tls.Dial("tcp", "mail.google.com:443", &tls.Config{ RootCAs: pool, }) if err != nil { log.Fatal(err) } conn.Close() certName := func(c *x509.Certificate) string { if c.Subject.CommonName != "" { return c.Subject.CommonName } if len(c.Subject.OrganizationalUnit) > 0 { return c.Subject.OrganizationalUnit[0] } return c.Subject.Organization[0] } sort.Slice(certs, func(i, j int) bool { if strings.ToLower(certName(certs[i])) != strings.ToLower(certName(certs[j])) { return strings.ToLower(certName(certs[i])) < strings.ToLower(certName(certs[j])) } if !certs[i].NotBefore.Equal(certs[j].NotBefore) { return certs[i].NotBefore.Before(certs[j].NotBefore) } fi, fj := sha256.Sum256(certs[i].Raw), sha256.Sum256(certs[j].Raw) return bytes.Compare(fi[:], fj[:]) < 0 }) out := new(bytes.Buffer) fmt.Fprintf(out, header, *version) fmt.Fprintf(out, "const systemRootsPEM = `\n") for _, c := range certs { fmt.Fprintf(out, "# %q\n", certName(c)) h := sha256.Sum256(c.Raw) fmt.Fprintf(out, "# % X\n", h[:len(h)/2]) fmt.Fprintf(out, "# % X\n", h[len(h)/2:]) b := &pem.Block{ Type: "CERTIFICATE", Bytes: c.Raw, } if err := pem.Encode(out, b); err != nil { log.Fatal(err) } } fmt.Fprintf(out, "`") source, err := format.Source(out.Bytes()) if err != nil { log.Fatal(err) } if err := os.WriteFile(*output, source, 0644); err != nil { log.Fatal(err) } } const header = `// Code generated by root_ios_gen.go -version %s; DO NOT EDIT. // Update the version in root.go and regenerate with "go generate". // +build ios // +build !x509omitbundledroots package x509 func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { return nil, nil } func loadSystemRoots() (*CertPool, error) { p := NewCertPool() p.AppendCertsFromPEM([]byte(systemRootsPEM)) return p, nil } `