Commit ac53c967 authored by Giovanni Bajo's avatar Giovanni Bajo Committed by Brad Fitzpatrick

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: 's avatarAdam Langley <agl@golang.org>
parent 3ea72fb1
......@@ -18,16 +18,18 @@ package main
import (
"bytes"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"flag"
"fmt"
"go/format"
"io/ioutil"
"log"
"math/big"
"net/http"
"os/exec"
"regexp"
"strings"
)
......@@ -41,7 +43,7 @@ func main() {
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, "const systemRootsPEM = `\n")
......@@ -78,36 +80,22 @@ func selectCerts() ([]*x509.Certificate, error) {
var certs []*x509.Certificate
for _, id := range ids {
sn, ok := big.NewInt(0).SetString(id.serialNumber, 0) // 0x prefix selects hex
if !ok {
return nil, fmt.Errorf("invalid serial number: %q", id.serialNumber)
}
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
if c, ok := scerts[id.fingerprint]; ok {
certs = append(certs, c)
} else {
fmt.Printf("WARNING: cannot find certificate: %s (fingerprint: %s)\n", id.name, id.fingerprint)
}
}
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")
data, err := cmd.Output()
if err != nil {
return nil, err
}
certs = make(map[string]*x509.Certificate)
for len(data) > 0 {
var block *pem.Block
block, data = pem.Decode(data)
......@@ -122,19 +110,23 @@ func sysCerts() (certs []*x509.Certificate, err error) {
if err != nil {
continue
}
certs = append(certs, cert)
fingerprint := sha256.Sum256(cert.Raw)
certs[hex.EncodeToString(fingerprint[:])] = cert
}
return certs, nil
}
type certID struct {
serialNumber string
subjectKeyID string
name string
fingerprint string
}
// fetchCertIDs fetches IDs of iOS X509 certificates from apple.com.
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 {
return nil, err
}
......@@ -144,31 +136,33 @@ func fetchCertIDs() ([]certID, error) {
return nil, err
}
text := string(body)
text = text[strings.Index(text, "<section id=trusted"):]
text = text[:strings.Index(text, "</section>")]
text = text[strings.Index(text, "<div id=trusted"):]
text = text[:strings.Index(text, "</div>")]
lines := strings.Split(text, "\n")
var ids []certID
var id certID
for i, ln := range lines {
if i == len(lines)-1 {
break
}
const sn = "Serial Number:"
if ln == sn {
id.serialNumber = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1)
continue
}
if strings.HasPrefix(ln, sn) {
// extract hex value from parentheses.
id.serialNumber = ln[strings.Index(ln, "(")+1 : len(ln)-1]
cols := make(map[string]int)
for i, rowmatch := range regexp.MustCompile("(?s)<tr>(.*?)</tr>").FindAllStringSubmatch(text, -1) {
row := rowmatch[1]
if i == 0 {
// Parse table header row to extract column names
for i, match := range regexp.MustCompile("(?s)<th>(.*?)</th>").FindAllStringSubmatch(row, -1) {
cols[match[1]] = i
}
continue
}
if strings.TrimSpace(ln) == "X509v3 Subject Key Identifier:" {
id.subjectKeyID = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1)
ids = append(ids, id)
id = certID{}
}
values := regexp.MustCompile("(?s)<td>(.*?)</td>").FindAllStringSubmatch(row, -1)
name := values[cols["Certificate name"]][1]
fingerprint := values[cols["Fingerprint (SHA-256)"]][1]
fingerprint = strings.Replace(fingerprint, "<br>", "", -1)
fingerprint = strings.Replace(fingerprint, "\n", "", -1)
fingerprint = strings.Replace(fingerprint, " ", "", -1)
fingerprint = strings.ToLower(fingerprint)
ids = append(ids, certID{
name: name,
fingerprint: fingerprint,
})
}
return ids, nil
}
......@@ -180,7 +174,7 @@ const header = `
// +build cgo
// +build darwin
// +build arm arm64
// +build arm arm64 ios
package x509
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment