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