crypto: move certificate verification into x509.
People have a need to verify certificates in situations other than TLS client handshaking. Thus this CL moves certificate verification into x509 and expands its abilities. R=bradfitzgo CC=golang-dev https://golang.org/cl/4407046
This commit is contained in:
parent
4f813669cb
commit
24df228d7a
1
Makefile
1
Makefile
@ -7,7 +7,6 @@ include ../../../Make.inc
|
||||
TARG=crypto/tls
|
||||
GOFILES=\
|
||||
alert.go\
|
||||
ca_set.go\
|
||||
cipher_suites.go\
|
||||
common.go\
|
||||
conn.go\
|
||||
|
89
ca_set.go
89
ca_set.go
@ -1,89 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A CASet is a set of certificates.
|
||||
type CASet struct {
|
||||
bySubjectKeyId map[string][]*x509.Certificate
|
||||
byName map[string][]*x509.Certificate
|
||||
}
|
||||
|
||||
// NewCASet returns a new, empty CASet.
|
||||
func NewCASet() *CASet {
|
||||
return &CASet{
|
||||
make(map[string][]*x509.Certificate),
|
||||
make(map[string][]*x509.Certificate),
|
||||
}
|
||||
}
|
||||
|
||||
func nameToKey(name *x509.Name) string {
|
||||
return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName
|
||||
}
|
||||
|
||||
// FindVerifiedParent attempts to find the certificate in s which has signed
|
||||
// the given certificate. If no such certificate can be found or the signature
|
||||
// doesn't match, it returns nil.
|
||||
func (s *CASet) FindVerifiedParent(cert *x509.Certificate) (parent *x509.Certificate) {
|
||||
var candidates []*x509.Certificate
|
||||
|
||||
if len(cert.AuthorityKeyId) > 0 {
|
||||
candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)]
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
candidates = s.byName[nameToKey(&cert.Issuer)]
|
||||
}
|
||||
|
||||
for _, c := range candidates {
|
||||
if cert.CheckSignatureFrom(c) == nil {
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCert adds a certificate to the set
|
||||
func (s *CASet) AddCert(cert *x509.Certificate) {
|
||||
if len(cert.SubjectKeyId) > 0 {
|
||||
keyId := string(cert.SubjectKeyId)
|
||||
s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], cert)
|
||||
}
|
||||
name := nameToKey(&cert.Subject)
|
||||
s.byName[name] = append(s.byName[name], cert)
|
||||
}
|
||||
|
||||
// SetFromPEM attempts to parse a series of PEM encoded root certificates. It
|
||||
// appends any certificates found to s and returns true if any certificates
|
||||
// were successfully parsed. On many Linux systems, /etc/ssl/cert.pem will
|
||||
// contains the system wide set of root CAs in a format suitable for this
|
||||
// function.
|
||||
func (s *CASet) SetFromPEM(pemCerts []byte) (ok bool) {
|
||||
for len(pemCerts) > 0 {
|
||||
var block *pem.Block
|
||||
block, pemCerts = pem.Decode(pemCerts)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
s.AddCert(cert)
|
||||
ok = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
12
common.go
12
common.go
@ -122,7 +122,7 @@ type Config struct {
|
||||
// RootCAs defines the set of root certificate authorities
|
||||
// that clients use when verifying server certificates.
|
||||
// If RootCAs is nil, TLS uses the host's root CA set.
|
||||
RootCAs *CASet
|
||||
RootCAs *x509.CertPool
|
||||
|
||||
// NextProtos is a list of supported, application level protocols.
|
||||
NextProtos []string
|
||||
@ -158,7 +158,7 @@ func (c *Config) time() int64 {
|
||||
return t()
|
||||
}
|
||||
|
||||
func (c *Config) rootCAs() *CASet {
|
||||
func (c *Config) rootCAs() *x509.CertPool {
|
||||
s := c.RootCAs
|
||||
if s == nil {
|
||||
s = defaultRoots()
|
||||
@ -224,7 +224,7 @@ var certFiles = []string{
|
||||
|
||||
var once sync.Once
|
||||
|
||||
func defaultRoots() *CASet {
|
||||
func defaultRoots() *x509.CertPool {
|
||||
once.Do(initDefaults)
|
||||
return varDefaultRoots
|
||||
}
|
||||
@ -239,14 +239,14 @@ func initDefaults() {
|
||||
initDefaultCipherSuites()
|
||||
}
|
||||
|
||||
var varDefaultRoots *CASet
|
||||
var varDefaultRoots *x509.CertPool
|
||||
|
||||
func initDefaultRoots() {
|
||||
roots := NewCASet()
|
||||
roots := x509.NewCertPool()
|
||||
for _, file := range certFiles {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err == nil {
|
||||
roots.SetFromPEM(data)
|
||||
roots.AppendCertsFromPEM(data)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
3
conn.go
3
conn.go
@ -34,6 +34,9 @@ type Conn struct {
|
||||
cipherSuite uint16
|
||||
ocspResponse []byte // stapled OCSP response
|
||||
peerCertificates []*x509.Certificate
|
||||
// verifedChains contains the certificate chains that we built, as
|
||||
// opposed to the ones presented by the server.
|
||||
verifiedChains [][]*x509.Certificate
|
||||
|
||||
clientProtocol string
|
||||
clientProtocolFallback bool
|
||||
|
@ -88,7 +88,6 @@ func (c *Conn) clientHandshake() os.Error {
|
||||
finishedHash.Write(certMsg.marshal())
|
||||
|
||||
certs := make([]*x509.Certificate, len(certMsg.certificates))
|
||||
chain := NewCASet()
|
||||
for i, asn1Data := range certMsg.certificates {
|
||||
cert, err := x509.ParseCertificate(asn1Data)
|
||||
if err != nil {
|
||||
@ -96,47 +95,29 @@ func (c *Conn) clientHandshake() os.Error {
|
||||
return os.ErrorString("failed to parse certificate from server: " + err.String())
|
||||
}
|
||||
certs[i] = cert
|
||||
chain.AddCert(cert)
|
||||
}
|
||||
|
||||
// If we don't have a root CA set configured then anything is accepted.
|
||||
// TODO(rsc): Find certificates for OS X 10.6.
|
||||
for cur := certs[0]; c.config.RootCAs != nil; {
|
||||
parent := c.config.RootCAs.FindVerifiedParent(cur)
|
||||
if parent != nil {
|
||||
break
|
||||
if c.config.RootCAs != nil {
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: c.config.RootCAs,
|
||||
CurrentTime: c.config.Time(),
|
||||
DNSName: c.config.ServerName,
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
|
||||
parent = chain.FindVerifiedParent(cur)
|
||||
if parent == nil {
|
||||
for i, cert := range certs {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
c.verifiedChains, err = certs[0].Verify(opts)
|
||||
if err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return os.ErrorString("could not find root certificate for chain")
|
||||
return err
|
||||
}
|
||||
|
||||
if !parent.BasicConstraintsValid || !parent.IsCA {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return os.ErrorString("intermediate certificate does not have CA bit set")
|
||||
}
|
||||
// KeyUsage status flags are ignored. From Engineering
|
||||
// Security, Peter Gutmann: A European government CA marked its
|
||||
// signing certificates as being valid for encryption only, but
|
||||
// no-one noticed. Another European CA marked its signature
|
||||
// keys as not being valid for signatures. A different CA
|
||||
// marked its own trusted root certificate as being invalid for
|
||||
// certificate signing. Another national CA distributed a
|
||||
// certificate to be used to encrypt data for the country’s tax
|
||||
// authority that was marked as only being usable for digital
|
||||
// signatures but not for encryption. Yet another CA reversed
|
||||
// the order of the bit flags in the keyUsage due to confusion
|
||||
// over encoding endianness, essentially setting a random
|
||||
// keyUsage in certificates that it issued. Another CA created
|
||||
// a self-invalidating certificate by adding a certificate
|
||||
// policy statement stipulating that the certificate had to be
|
||||
// used strictly as specified in the keyUsage, and a keyUsage
|
||||
// containing a flag indicating that the RSA encryption key
|
||||
// could only be used for Diffie-Hellman key agreement.
|
||||
|
||||
cur = parent
|
||||
}
|
||||
|
||||
if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok {
|
||||
|
Loading…
Reference in New Issue
Block a user