crypto/x509: update iOS root certs.

Apple changed the format of its support page, so we need to
restructure the HTML parser. The HTML table is now parsed using
regular expressions, and certificates are then found in macOS
trust store by their fingerprint.

Fixes #22181

Change-Id: I29e7a40d37770bb005d728f1832299c528691f7e
Reviewed-on: https://go-review.googlesource.com/77252
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Adam Langley <agl@golang.org>
This commit is contained in:
Giovanni Bajo 2017-11-13 01:54:08 +01:00 committed by Brad Fitzpatrick
parent 3ea72fb1a1
commit ac53c9673d
2 changed files with 3637 additions and 4238 deletions

View File

@ -18,16 +18,18 @@ package main
import ( import (
"bytes" "bytes"
"crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/hex"
"encoding/pem" "encoding/pem"
"flag" "flag"
"fmt" "fmt"
"go/format" "go/format"
"io/ioutil" "io/ioutil"
"log" "log"
"math/big"
"net/http" "net/http"
"os/exec" "os/exec"
"regexp"
"strings" "strings"
) )
@ -41,7 +43,7 @@ func main() {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
fmt.Fprintf(buf, "// Created by root_darwin_arm_gen --output %s; DO NOT EDIT\n", *output) fmt.Fprintf(buf, "// Code generated by root_darwin_arm_gen --output %s; DO NOT EDIT.\n", *output)
fmt.Fprintf(buf, "%s", header) fmt.Fprintf(buf, "%s", header)
fmt.Fprintf(buf, "const systemRootsPEM = `\n") fmt.Fprintf(buf, "const systemRootsPEM = `\n")
@ -78,36 +80,22 @@ func selectCerts() ([]*x509.Certificate, error) {
var certs []*x509.Certificate var certs []*x509.Certificate
for _, id := range ids { for _, id := range ids {
sn, ok := big.NewInt(0).SetString(id.serialNumber, 0) // 0x prefix selects hex if c, ok := scerts[id.fingerprint]; ok {
if !ok { certs = append(certs, c)
return nil, fmt.Errorf("invalid serial number: %q", id.serialNumber) } else {
} fmt.Printf("WARNING: cannot find certificate: %s (fingerprint: %s)\n", id.name, id.fingerprint)
ski, ok := big.NewInt(0).SetString(id.subjectKeyID, 0)
if !ok {
return nil, fmt.Errorf("invalid Subject Key ID: %q", id.subjectKeyID)
}
for _, cert := range scerts {
if sn.Cmp(cert.SerialNumber) != 0 {
continue
}
cski := big.NewInt(0).SetBytes(cert.SubjectKeyId)
if ski.Cmp(cski) != 0 {
continue
}
certs = append(certs, cert)
break
} }
} }
return certs, nil return certs, nil
} }
func sysCerts() (certs []*x509.Certificate, err error) { func sysCerts() (certs map[string]*x509.Certificate, err error) {
cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain") cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain")
data, err := cmd.Output() data, err := cmd.Output()
if err != nil { if err != nil {
return nil, err return nil, err
} }
certs = make(map[string]*x509.Certificate)
for len(data) > 0 { for len(data) > 0 {
var block *pem.Block var block *pem.Block
block, data = pem.Decode(data) block, data = pem.Decode(data)
@ -122,19 +110,23 @@ func sysCerts() (certs []*x509.Certificate, err error) {
if err != nil { if err != nil {
continue continue
} }
certs = append(certs, cert)
fingerprint := sha256.Sum256(cert.Raw)
certs[hex.EncodeToString(fingerprint[:])] = cert
} }
return certs, nil return certs, nil
} }
type certID struct { type certID struct {
serialNumber string name string
subjectKeyID string fingerprint string
} }
// fetchCertIDs fetches IDs of iOS X509 certificates from apple.com. // fetchCertIDs fetches IDs of iOS X509 certificates from apple.com.
func fetchCertIDs() ([]certID, error) { func fetchCertIDs() ([]certID, error) {
resp, err := http.Get("https://support.apple.com/en-us/HT204132") // Download the iOS 11 support page. The index for all iOS versions is here:
// https://support.apple.com/en-us/HT204132
resp, err := http.Get("https://support.apple.com/en-us/HT208125")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -144,31 +136,33 @@ func fetchCertIDs() ([]certID, error) {
return nil, err return nil, err
} }
text := string(body) text := string(body)
text = text[strings.Index(text, "<section id=trusted"):] text = text[strings.Index(text, "<div id=trusted"):]
text = text[:strings.Index(text, "</section>")] text = text[:strings.Index(text, "</div>")]
lines := strings.Split(text, "\n")
var ids []certID var ids []certID
var id certID cols := make(map[string]int)
for i, ln := range lines { for i, rowmatch := range regexp.MustCompile("(?s)<tr>(.*?)</tr>").FindAllStringSubmatch(text, -1) {
if i == len(lines)-1 { row := rowmatch[1]
break if i == 0 {
} // Parse table header row to extract column names
const sn = "Serial Number:" for i, match := range regexp.MustCompile("(?s)<th>(.*?)</th>").FindAllStringSubmatch(row, -1) {
if ln == sn { cols[match[1]] = i
id.serialNumber = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1) }
continue continue
} }
if strings.HasPrefix(ln, sn) {
// extract hex value from parentheses. values := regexp.MustCompile("(?s)<td>(.*?)</td>").FindAllStringSubmatch(row, -1)
id.serialNumber = ln[strings.Index(ln, "(")+1 : len(ln)-1] name := values[cols["Certificate name"]][1]
continue fingerprint := values[cols["Fingerprint (SHA-256)"]][1]
} fingerprint = strings.Replace(fingerprint, "<br>", "", -1)
if strings.TrimSpace(ln) == "X509v3 Subject Key Identifier:" { fingerprint = strings.Replace(fingerprint, "\n", "", -1)
id.subjectKeyID = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1) fingerprint = strings.Replace(fingerprint, " ", "", -1)
ids = append(ids, id) fingerprint = strings.ToLower(fingerprint)
id = certID{}
} ids = append(ids, certID{
name: name,
fingerprint: fingerprint,
})
} }
return ids, nil return ids, nil
} }
@ -180,7 +174,7 @@ const header = `
// +build cgo // +build cgo
// +build darwin // +build darwin
// +build arm arm64 // +build arm arm64 ios
package x509 package x509

File diff suppressed because it is too large Load Diff