github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/crypto/x509/root_ios_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 // +build ignore 6 7 // Generates root_ios.go. 8 // 9 // As of iOS 13, there is no API for querying the system trusted X.509 root 10 // certificates. 11 // 12 // Apple publishes the trusted root certificates for iOS and macOS on 13 // opensource.apple.com so we embed them into the x509 package. 14 // 15 // Note that this ignores distrusted and revoked certificates. 16 package main 17 18 import ( 19 "archive/tar" 20 "bytes" 21 "compress/gzip" 22 "crypto/sha256" 23 "crypto/tls" 24 "crypto/x509" 25 "encoding/pem" 26 "flag" 27 "fmt" 28 "go/format" 29 "io" 30 "log" 31 "net/http" 32 "os" 33 "path" 34 "sort" 35 "strings" 36 "time" 37 ) 38 39 func main() { 40 var output = flag.String("output", "root_ios.go", "file name to write") 41 var version = flag.String("version", "", "security_certificates version") 42 flag.Parse() 43 if *version == "" { 44 log.Fatal("Select the latest security_certificates version from " + 45 "https://opensource.apple.com/source/security_certificates/") 46 } 47 48 url := "https://opensource.apple.com/tarballs/security_certificates/security_certificates-%s.tar.gz" 49 hc := &http.Client{Timeout: 1 * time.Minute} 50 resp, err := hc.Get(fmt.Sprintf(url, *version)) 51 if err != nil { 52 log.Fatal(err) 53 } 54 defer resp.Body.Close() 55 if resp.StatusCode != http.StatusOK { 56 log.Fatalf("HTTP status not OK: %s", resp.Status) 57 } 58 59 zr, err := gzip.NewReader(resp.Body) 60 if err != nil { 61 log.Fatal(err) 62 } 63 defer zr.Close() 64 65 var certs []*x509.Certificate 66 pool := x509.NewCertPool() 67 68 tr := tar.NewReader(zr) 69 for { 70 hdr, err := tr.Next() 71 if err == io.EOF { 72 break 73 } 74 if err != nil { 75 log.Fatal(err) 76 } 77 78 rootsDirectory := fmt.Sprintf("security_certificates-%s/certificates/roots/", *version) 79 if dir, file := path.Split(hdr.Name); hdr.Typeflag != tar.TypeReg || 80 dir != rootsDirectory || strings.HasPrefix(file, ".") { 81 continue 82 } 83 84 der, err := io.ReadAll(tr) 85 if err != nil { 86 log.Fatal(err) 87 } 88 89 c, err := x509.ParseCertificate(der) 90 if err != nil { 91 log.Printf("Failed to parse certificate %q: %v", hdr.Name, err) 92 continue 93 } 94 95 certs = append(certs, c) 96 pool.AddCert(c) 97 } 98 99 // Quick smoke test to check the pool is well formed, and that we didn't end 100 // up trusting roots in the removed folder. 101 for _, c := range certs { 102 if c.Subject.CommonName == "Symantec Class 2 Public Primary Certification Authority - G4" { 103 log.Fatal("The pool includes a removed root!") 104 } 105 } 106 conn, err := tls.Dial("tcp", "mail.google.com:443", &tls.Config{ 107 RootCAs: pool, 108 }) 109 if err != nil { 110 log.Fatal(err) 111 } 112 conn.Close() 113 114 certName := func(c *x509.Certificate) string { 115 if c.Subject.CommonName != "" { 116 return c.Subject.CommonName 117 } 118 if len(c.Subject.OrganizationalUnit) > 0 { 119 return c.Subject.OrganizationalUnit[0] 120 } 121 return c.Subject.Organization[0] 122 } 123 sort.Slice(certs, func(i, j int) bool { 124 if strings.ToLower(certName(certs[i])) != strings.ToLower(certName(certs[j])) { 125 return strings.ToLower(certName(certs[i])) < strings.ToLower(certName(certs[j])) 126 } 127 if !certs[i].NotBefore.Equal(certs[j].NotBefore) { 128 return certs[i].NotBefore.Before(certs[j].NotBefore) 129 } 130 fi, fj := sha256.Sum256(certs[i].Raw), sha256.Sum256(certs[j].Raw) 131 return bytes.Compare(fi[:], fj[:]) < 0 132 }) 133 134 out := new(bytes.Buffer) 135 fmt.Fprintf(out, header, *version) 136 fmt.Fprintf(out, "const systemRootsPEM = `\n") 137 138 for _, c := range certs { 139 fmt.Fprintf(out, "# %q\n", certName(c)) 140 h := sha256.Sum256(c.Raw) 141 fmt.Fprintf(out, "# % X\n", h[:len(h)/2]) 142 fmt.Fprintf(out, "# % X\n", h[len(h)/2:]) 143 b := &pem.Block{ 144 Type: "CERTIFICATE", 145 Bytes: c.Raw, 146 } 147 if err := pem.Encode(out, b); err != nil { 148 log.Fatal(err) 149 } 150 } 151 152 fmt.Fprintf(out, "`") 153 154 source, err := format.Source(out.Bytes()) 155 if err != nil { 156 log.Fatal(err) 157 } 158 if err := os.WriteFile(*output, source, 0644); err != nil { 159 log.Fatal(err) 160 } 161 } 162 163 const header = `// Code generated by root_ios_gen.go -version %s; DO NOT EDIT. 164 // Update the version in root.go and regenerate with "go generate". 165 166 // +build ios 167 // +build !x509omitbundledroots 168 169 package x509 170 171 func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { 172 return nil, nil 173 } 174 175 func loadSystemRoots() (*CertPool, error) { 176 p := NewCertPool() 177 p.AppendCertsFromPEM([]byte(systemRootsPEM)) 178 return p, nil 179 } 180 `