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  `