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