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  `