// UnmarshalJSON converts the JSON representation of a FixError back to a FixError
func UnmarshalJSON(b []byte) (*FixError, error) {
	var u struct {
		Type  string
		Cert  []byte
		Chain [][]byte
		URL   string
		Bad   []byte
		Error string
		Code  int
	}
	err := json.Unmarshal(b, &u)
	if err != nil {
		return nil, err
	}

	ferr := &FixError{}
	switch u.Type {
	case "None":
		ferr.Type = None
	case "ParseFailure":
		ferr.Type = ParseFailure
	case "CannotFetchURL":
		ferr.Type = CannotFetchURL
	case "FixFailed":
		ferr.Type = FixFailed
	case "PostFailed":
		ferr.Type = PostFailed
	case "LogPostFailed":
		ferr.Type = LogPostFailed
	case "VerifyFailed":
		ferr.Type = VerifyFailed
	default:
		return nil, errors.New("cannot parse FixError Type")
	}

	if u.Cert != nil {
		cert, err := x509.ParseCertificate(u.Cert)
		if err != nil {
			return nil, fmt.Errorf("cannot parse FixError Cert: %s", err)
		}
		ferr.Cert = cert
	}

	for _, c := range u.Chain {
		cert, err := x509.ParseCertificate(c)
		if err != nil {
			return nil, fmt.Errorf("cannot parse FixError Chain: %s", err)
		}
		ferr.Chain = append(ferr.Chain, cert)
	}

	ferr.URL = u.URL
	ferr.Bad = u.Bad
	if u.Error != "" {
		ferr.Error = errors.New(u.Error)
	}
	ferr.Code = u.Code

	return ferr, nil
}
// Assumes chains to be stores in a file in JSON encoded with the certificates
// in DER format.
func processChains(file string, fl *fixchain.FixAndLog) {
	f, err := os.Open(file)
	if err != nil {
		log.Fatalf("Can't open %q: %s", file, err)
	}
	defer f.Close()

	type Chain struct {
		Chain [][]byte
	}

	dec := json.NewDecoder(f)
	for {
		var m Chain
		if err := dec.Decode(&m); err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		}
		var chain []*x509.Certificate
		for _, derBytes := range m.Chain {
			cert, err := x509.ParseCertificate(derBytes)
			switch err.(type) {
			case nil, x509.NonFatalErrors:
				// ignore
			default:
				log.Fatalf("can't parse certificate: %s %#v",
					err, derBytes)
			}

			chain = append(chain, cert)
		}
		fl.QueueAllCertsInChain(chain)
	}
}
Example #3
0
// Processes the given |entry| in the specified log.
func (s *Scanner) processEntry(entry ct.LogEntry, foundCert func(*ct.LogEntry), foundPrecert func(*ct.LogEntry)) {
	atomic.AddInt64(&s.certsProcessed, 1)
	switch entry.Leaf.TimestampedEntry.EntryType {
	case ct.X509LogEntryType:
		if s.opts.PrecertOnly {
			// Only interested in precerts and this is an X.509 cert, early-out.
			return
		}
		cert, err := x509.ParseCertificate(entry.Leaf.TimestampedEntry.X509Entry)
		if err = s.handleParseEntryError(err, entry.Leaf.TimestampedEntry.EntryType, entry.Index); err != nil {
			// We hit an unparseable entry, already logged inside handleParseEntryError()
			return
		}
		if s.opts.Matcher.CertificateMatches(cert) {
			entry.X509Cert = cert
			foundCert(&entry)
		}
	case ct.PrecertLogEntryType:
		c, err := x509.ParseTBSCertificate(entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate)
		if err = s.handleParseEntryError(err, entry.Leaf.TimestampedEntry.EntryType, entry.Index); err != nil {
			// We hit an unparseable entry, already logged inside handleParseEntryError()
			return
		}
		precert := &ct.Precertificate{
			Raw:            entry.Chain[0],
			TBSCertificate: *c,
			IssuerKeyHash:  entry.Leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash}
		if s.opts.Matcher.PrecertificateMatches(precert) {
			entry.Precert = precert
			foundPrecert(&entry)
		}
		atomic.AddInt64(&s.precertsSeen, 1)
	}
}
Example #4
0
func (edb *EntriesDatabase) InsertCensysEntry(entry *censysdata.CensysEntry) error {
	cert, err := x509.ParseCertificate(entry.CertBytes)
	if err != nil {
		return err
	}

	txn, certId, err := edb.insertCertificate(cert)
	if err != nil {
		if edb.Verbose {
			fmt.Printf("Error inserting cert, %s\n", err)
		}
		if txn != nil {
			txn.Rollback()
		}
		return err
	}

	//
	// Insert the appropriate CensysEntry
	//
	certEntry := &CensysEntry{
		CertID:    certId,
		EntryTime: *entry.Timestamp,
	}
	// Ignore errors on insertion for Censys entry markers
	err = txn.Insert(certEntry)
	if errorIsNotDuplicate(err) {
		return err
	}

	return txn.Commit()
}
func (l *Logger) getRoots() (*x509.CertPool, error) {
	rootsJSON, err := l.client.Get(l.url + "/ct/v1/get-roots")
	if err != nil {
		return nil, fmt.Errorf("can't get roots from %s: %s", l.url, err)
	}
	defer rootsJSON.Body.Close()
	j, err := ioutil.ReadAll(rootsJSON.Body)
	if err != nil {
		return nil, fmt.Errorf("can't read body from %s: %s", l.url, err)
	}
	if rootsJSON.StatusCode != 200 {
		return nil, fmt.Errorf("can't deal with status other than 200 from %s: %d\nbody: %s", l.url, rootsJSON.StatusCode, string(j))
	}
	type Certificates struct {
		Certificates [][]byte
	}
	var certs Certificates
	err = json.Unmarshal(j, &certs)
	if err != nil {
		return nil, fmt.Errorf("can't parse json (%s) from %s: %s", err, l.url, j)
	}
	ret := x509.NewCertPool()
	for i := 0; i < len(certs.Certificates); i++ {
		r, err := x509.ParseCertificate(certs.Certificates[i])
		switch err.(type) {
		case nil, x509.NonFatalErrors:
			// ignore
		default:
			return nil, fmt.Errorf("can't parse certificate from %s: %s %#v", l.url, err, certs.Certificates[i])
		}
		ret.AddCert(r)
	}
	return ret, nil
}
func urlReplacement(url string) []*x509.Certificate {
	cs, ok := replacements[url]
	if !ok {
		return nil
	}
	var r []*x509.Certificate
	for _, c := range cs {
		s, _ := pem.Decode([]byte(c))
		if s == nil {
			log.Fatalf("Can't decode built-in: %s", c)
			return nil
		}
		cert, err := x509.ParseCertificate(s.Bytes)
		if err != nil {
			log.Fatalf("Can't parse built-in: %s\n%s", c, err)
			return nil
		}
		if cert == nil {
			log.Fatalf("Parse didn't produce a cert: %s", c)
			return nil
		}
		r = append(r, cert)
	}
	return r
}
// CertificateFromPEM takes a string representing a certificate in PEM format
// and returns the corresponding x509.Certificate object.
func CertificateFromPEM(pemBytes string) (*x509.Certificate, error) {
	block, _ := pem.Decode([]byte(pemBytes))
	if block == nil {
		return nil, errors.New("failed to decode PEM")
	}
	return x509.ParseCertificate(block.Bytes)
}
Example #8
0
func (edb *EntriesDatabase) InsertCTEntry(entry *ct.LogEntry) error {
	var cert *x509.Certificate
	var err error

	switch entry.Leaf.TimestampedEntry.EntryType {
	case ct.X509LogEntryType:
		cert, err = x509.ParseCertificate(entry.Leaf.TimestampedEntry.X509Entry)
	case ct.PrecertLogEntryType:
		cert, err = x509.ParseTBSCertificate(entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate)
	}

	if err != nil {
		return err
	}

	// Skip unimportant entries, if configured
	if edb.IssuerFilter != nil && !strings.HasPrefix(cert.Issuer.CommonName, *edb.IssuerFilter) {
		return nil
	}

	backoff := &backoff.Backoff{
		Jitter: true,
	}

	for count := 0; count < 10; count++ {
		txn, certId, err := edb.insertCertificate(cert)
		if err != nil {
			if edb.Verbose {
				fmt.Printf("Error inserting cert, retrying (%d/10) %s\n", count, err)
			}
			if txn != nil {
				txn.Rollback()
			}
			time.Sleep(backoff.Duration())
			continue
		}

		//
		// Insert the appropriate CertificateLogEntry, ignoring errors if there was a collision
		//

		certLogEntry := &CertificateLogEntry{
			CertID:    certId,
			LogID:     edb.LogId,
			EntryID:   uint64(entry.Index),
			EntryTime: Uint64ToTimestamp(entry.Leaf.TimestampedEntry.Timestamp),
		}
		err = txn.Insert(certLogEntry)
		if errorIsNotDuplicate(err) {
			txn.Rollback()
			continue
		}

		return txn.Commit()
	}

	return err
}
Example #9
0
// Get the certs that correspond to the given url.
func (fix *toFix) getIntermediates(url string) ([]*x509.Certificate, *FixError) {
	var icerts []*x509.Certificate
	// PKCS#7 additions as (at time of writing) there is no standard Go PKCS#7
	// implementation
	r := urlReplacement(url)
	if r != nil {
		return r, nil
	}

	body, err := fix.cache.getURL(url)
	if err != nil {
		return nil, &FixError{
			Type:  CannotFetchURL,
			Cert:  fix.cert,
			Chain: fix.chain.certs,
			URL:   url,
			Error: err,
		}
	}

	icert, err := x509.ParseCertificate(body)
	if err != nil {
		s, _ := pem.Decode(body)
		if s != nil {
			icert, err = x509.ParseCertificate(s.Bytes)
		}
	}

	if err != nil {
		return nil, &FixError{
			Type:  ParseFailure,
			Cert:  fix.cert,
			Chain: fix.chain.certs,
			URL:   url,
			Bad:   body,
			Error: err,
		}
	}

	icerts = append(icerts, icert)
	return icerts, nil
}
Example #10
0
func getRoots(ctx context.Context, logClient *client.LogClient) {
	roots, err := logClient.GetAcceptedRoots(ctx)
	if err != nil {
		log.Fatal(err)
	}
	for _, root := range roots {
		if *textOut {
			cert, err := x509.ParseCertificate(root)
			if err != nil {
				log.Printf("Error parsing certificate: %q", err.Error())
				continue
			}
			fmt.Printf("%s\n", x509util.CertificateToString(cert))
		} else {
			if err := pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: root}); err != nil {
				log.Printf("Failed to PEM encode cert: %q", err.Error())
			}
		}
	}
}
Example #11
0
func sysCerts() (certs []*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
	}
	for len(data) > 0 {
		var block *pem.Block
		block, data = pem.Decode(data)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}

		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			continue
		}
		certs = append(certs, cert)
	}
	return certs, nil
}
Example #12
0
// X509Certificate returns the X.509 Certificate contained within the
// MerkleTreeLeaf.
// Returns a pointer to an x509.Certificate or a non-nil error.
func (m *MerkleTreeLeaf) X509Certificate() (*x509.Certificate, error) {
	return x509.ParseCertificate(m.TimestampedEntry.X509Entry)
}
func (rt testRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
	url := fmt.Sprintf("%s://%s%s", request.URL.Scheme, request.URL.Host, request.URL.Path)
	switch url {
	case "https://ct.googleapis.com/pilot/ct/v1/get-roots":
		b := stringRootsToJSON([]string{verisignRoot, testRoot})
		return &http.Response{
			Status:        "200 OK",
			StatusCode:    200,
			Proto:         request.Proto,
			ProtoMajor:    request.ProtoMajor,
			ProtoMinor:    request.ProtoMinor,
			Body:          &bytesReadCloser{bytes.NewReader(b)},
			ContentLength: int64(len(b)),
			Request:       request,
		}, nil
	case "https://ct.googleapis.com/pilot/ct/v1/add-chain":
		body, err := ioutil.ReadAll(request.Body)
		request.Body.Close()
		if err != nil {
			errStr := fmt.Sprintf("#%d: Could not read request body: %s", rt.testIndex, err.Error())
			rt.t.Error(errStr)
			return nil, errors.New(errStr)
		}

		type Chain struct {
			Chain [][]byte
		}
		var chainBytes Chain
		err = json.Unmarshal(body, &chainBytes)
		if err != nil {
			errStr := fmt.Sprintf("#%d: Could not unmarshal json: %s", rt.testIndex, err.Error())
			rt.t.Error(errStr)
			return nil, errors.New(errStr)
		}
		var chain []*x509.Certificate
		for _, certBytes := range chainBytes.Chain {
			cert, err := x509.ParseCertificate(certBytes)
			if err != nil {
				errStr := fmt.Sprintf("#%d: Could not parse certificate: %s", rt.testIndex, err.Error())
				rt.t.Error(errStr)
				return nil, errors.New(errStr)
			}
			chain = append(chain, cert)
		}

	TryNextExpected:
		for i, expChain := range rt.test.expLoggedChains {
			if rt.seen[i] || len(chain) != len(expChain) {
				continue
			}
			for j, cert := range chain {
				if !strings.Contains(nameToKey(&cert.Subject), expChain[j]) {
					continue TryNextExpected
				}
			}
			rt.seen[i] = true
			goto Return
		}
		rt.t.Errorf("#%d: Logged chain was not expected: %s", rt.testIndex, chainToDebugString(chain))
	Return:
		return &http.Response{
			Status:        "200 OK",
			StatusCode:    200,
			Proto:         request.Proto,
			ProtoMajor:    request.ProtoMajor,
			ProtoMinor:    request.ProtoMinor,
			Body:          &bytesReadCloser{bytes.NewReader([]byte(""))},
			ContentLength: 0,
			Request:       request,
		}, nil
	default:
		var cert string
		switch url {
		case "http://www.thawte.com/repository/Thawte_SGC_CA.crt":
			cert = thawteIntermediate
		case "http://crt.comodoca.com/EssentialSSLCA_2.crt":
			cert = comodoIntermediate
		case "http://crt.comodoca.com/ComodoUTNSGCCA.crt":
			cert = comodoRoot
		case "http://www.example.com/intermediate2.crt":
			cert = testIntermediate2
		case "http://www.example.com/intermediate1.crt":
			cert = testIntermediate1
		case "http://www.example.com/ca.crt":
			cert = testRoot
		case "http://www.example.com/a.crt":
			cert = testA
		case "http://www.example.com/b.crt":
			cert = testB
		default:
			return nil, fmt.Errorf("can't reach url %s", url)
		}

		return &http.Response{
			Status:        "200 OK",
			StatusCode:    200,
			Proto:         request.Proto,
			ProtoMajor:    request.ProtoMajor,
			ProtoMinor:    request.ProtoMinor,
			Body:          &bytesReadCloser{bytes.NewReader([]byte(cert))},
			ContentLength: int64(len([]byte(cert))),
			Request:       request,
		}, nil
	}
}
Example #14
0
func main() {
	var (
		err    error
		offset int
	)
	// create a certificate transparency client
	ctLog := client.New("http://ct.googleapis.com/aviator", nil)

	httpCli := &http.Client{
		Transport: &http.Transport{
			DisableCompression: true,
			DisableKeepAlives:  false,
		},
		Timeout: 10 * time.Second,
	}

	if len(os.Args) > 1 {
		offset, err = strconv.Atoi(os.Args[1])
		if err != nil {
			log.Fatal(err)
		}
	}
	for {
		log.Printf("retrieving CT logs %d to %d", offset, offset+100)
		rawEnts, err := ctLog.GetEntries(int64(offset), int64(offset+100))
		if err != nil {
			log.Fatal(err)
		}

		// loop over CT records
		for i, ent := range rawEnts {
			log.Printf("CT index=%d", offset+i)
			var cert *x509.Certificate
			switch ent.Leaf.TimestampedEntry.EntryType {
			case ct.X509LogEntryType:
				cert, err = x509.ParseCertificate(ent.Leaf.TimestampedEntry.X509Entry)
			case ct.PrecertLogEntryType:
				cert, err = x509.ParseTBSCertificate(ent.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate)
			}
			if err != nil {
				log.Fatal(err)
			}
			log.Printf("CN=%s", cert.Subject.CommonName)
			log.Printf("Not Before=%s", cert.NotBefore)
			log.Printf("Not After=%s", cert.NotAfter)

			// Format the PEM certificate
			payload := base64.StdEncoding.EncodeToString(cert.Raw)
			buf := new(bytes.Buffer)
			fmt.Fprintf(buf, "-----BEGIN CERTIFICATE-----\n")
			for len(payload) > 0 {
				chunkLen := len(payload)
				if chunkLen > 64 {
					chunkLen = 64
				}
				fmt.Fprintf(buf, "%s\n", payload[0:chunkLen])
				payload = payload[chunkLen:]
			}
			fmt.Fprintf(buf, "-----END CERTIFICATE-----")

			// create a mime/multipart form with the certificate
			var b bytes.Buffer
			w := multipart.NewWriter(&b)
			fw, err := w.CreateFormFile("certificate", certificate.SHA256Hash(cert.Raw))
			if err != nil {
				log.Fatal(err)
			}
			_, err = io.Copy(fw, buf)
			if err != nil {
				log.Fatal(err)
			}
			w.Close()

			// post the form to the tls-observatory api
			r, err := http.NewRequest("POST", "https://tls-observatory.services.mozilla.com/api/v1/certificate", &b)
			if err != nil {
				log.Fatal(err)
			}
			r.Header.Set("Content-Type", w.FormDataContentType())
			resp, err := httpCli.Do(r)
			if err != nil {
				log.Printf("%v\n\n", err)
				continue
			}
			defer resp.Body.Close()
			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				log.Fatal(err)
			}
			if resp.StatusCode != http.StatusCreated {
				log.Fatalf("Expected HTTP 201 Created, got %q\n%s", resp.Status, body)
			}

			// parse the returned cert
			var tlsobs_cert certificate.Certificate
			err = json.Unmarshal(body, &tlsobs_cert)
			if err != nil {
				log.Fatal(err)
			}
			log.Printf("https://tls-observatory.services.mozilla.com/api/v1/certificate?id=%d\n\n", tlsobs_cert.ID)
		}
		offset += 100
	}
}
Example #15
0
func ExampleCertificate_Verify() {
	// Verifying with a custom list of root certificates.

	const rootPEM = `
-----BEGIN CERTIFICATE-----
MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy
bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP
VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv
h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE
ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ
EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC
DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7
qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD
VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g
K4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI
KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n
ZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB
BQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY
/iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/
zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza
HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto
WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6
yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx
-----END CERTIFICATE-----`

	const certPEM = `
-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgIIE31FZVaPXTUwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl
cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwMTI5MTMyNzQzWhcNMTQwNTI5MDAwMDAw
WjBpMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN
TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEYMBYGA1UEAwwPbWFp
bC5nb29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfRrObuSW5T7q
5CnSEqefEmtH4CCv6+5EckuriNr1CjfVvqzwfAhopXkLrq45EQm8vkmf7W96XJhC
7ZM0dYi1/qOCAU8wggFLMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAa
BgNVHREEEzARgg9tYWlsLmdvb2dsZS5jb20wCwYDVR0PBAQDAgeAMGgGCCsGAQUF
BwEBBFwwWjArBggrBgEFBQcwAoYfaHR0cDovL3BraS5nb29nbGUuY29tL0dJQUcy
LmNydDArBggrBgEFBQcwAYYfaHR0cDovL2NsaWVudHMxLmdvb2dsZS5jb20vb2Nz
cDAdBgNVHQ4EFgQUiJxtimAuTfwb+aUtBn5UYKreKvMwDAYDVR0TAQH/BAIwADAf
BgNVHSMEGDAWgBRK3QYWG7z2aLV29YG2u2IaulqBLzAXBgNVHSAEEDAOMAwGCisG
AQQB1nkCBQEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3BraS5nb29nbGUuY29t
L0dJQUcyLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAH6RYHxHdcGpMpFE3oxDoFnP+
gtuBCHan2yE2GRbJ2Cw8Lw0MmuKqHlf9RSeYfd3BXeKkj1qO6TVKwCh+0HdZk283
TZZyzmEOyclm3UGFYe82P/iDFt+CeQ3NpmBg+GoaVCuWAARJN/KfglbLyyYygcQq
0SgeDh8dRKUiaW3HQSoYvTvdTuqzwK4CXsr3b5/dAOY8uMuG/IAR3FgwTbZ1dtoW
RvOTa8hYiU6A475WuZKyEHcwnGYe57u2I2KbMgcKjPniocj4QzgYsVAVKW3IwaOh
yE+vPxsiUkvQHdO2fojCkY8jg70jxM+gu59tPDNbw3Uh/2Ij310FgTHsnGQMyA==
-----END CERTIFICATE-----`

	// First, create the set of root certificates. For this example we only
	// have one. It's also possible to omit this in order to use the
	// default root set of the current operating system.
	roots := x509.NewCertPool()
	ok := roots.AppendCertsFromPEM([]byte(rootPEM))
	if !ok {
		panic("failed to parse root certificate")
	}

	block, _ := pem.Decode([]byte(certPEM))
	if block == nil {
		panic("failed to parse certificate PEM")
	}
	cert, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		panic("failed to parse certificate: " + err.Error())
	}

	opts := x509.VerifyOptions{
		DNSName: "mail.google.com",
		Roots:   roots,
	}

	if _, err := cert.Verify(opts); err != nil {
		panic("failed to verify certificate: " + err.Error())
	}
}