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:
Adam Langley 2011-04-19 09:57:58 -04:00
parent 4f813669cb
commit 24df228d7a
5 changed files with 24 additions and 130 deletions

View File

@ -7,7 +7,6 @@ include ../../../Make.inc
TARG=crypto/tls TARG=crypto/tls
GOFILES=\ GOFILES=\
alert.go\ alert.go\
ca_set.go\
cipher_suites.go\ cipher_suites.go\
common.go\ common.go\
conn.go\ conn.go\

View File

@ -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
}

View File

@ -122,7 +122,7 @@ type Config struct {
// RootCAs defines the set of root certificate authorities // RootCAs defines the set of root certificate authorities
// that clients use when verifying server certificates. // that clients use when verifying server certificates.
// If RootCAs is nil, TLS uses the host's root CA set. // 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 is a list of supported, application level protocols.
NextProtos []string NextProtos []string
@ -158,7 +158,7 @@ func (c *Config) time() int64 {
return t() return t()
} }
func (c *Config) rootCAs() *CASet { func (c *Config) rootCAs() *x509.CertPool {
s := c.RootCAs s := c.RootCAs
if s == nil { if s == nil {
s = defaultRoots() s = defaultRoots()
@ -224,7 +224,7 @@ var certFiles = []string{
var once sync.Once var once sync.Once
func defaultRoots() *CASet { func defaultRoots() *x509.CertPool {
once.Do(initDefaults) once.Do(initDefaults)
return varDefaultRoots return varDefaultRoots
} }
@ -239,14 +239,14 @@ func initDefaults() {
initDefaultCipherSuites() initDefaultCipherSuites()
} }
var varDefaultRoots *CASet var varDefaultRoots *x509.CertPool
func initDefaultRoots() { func initDefaultRoots() {
roots := NewCASet() roots := x509.NewCertPool()
for _, file := range certFiles { for _, file := range certFiles {
data, err := ioutil.ReadFile(file) data, err := ioutil.ReadFile(file)
if err == nil { if err == nil {
roots.SetFromPEM(data) roots.AppendCertsFromPEM(data)
break break
} }
} }

View File

@ -34,6 +34,9 @@ type Conn struct {
cipherSuite uint16 cipherSuite uint16
ocspResponse []byte // stapled OCSP response ocspResponse []byte // stapled OCSP response
peerCertificates []*x509.Certificate 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 clientProtocol string
clientProtocolFallback bool clientProtocolFallback bool

View File

@ -88,7 +88,6 @@ func (c *Conn) clientHandshake() os.Error {
finishedHash.Write(certMsg.marshal()) finishedHash.Write(certMsg.marshal())
certs := make([]*x509.Certificate, len(certMsg.certificates)) certs := make([]*x509.Certificate, len(certMsg.certificates))
chain := NewCASet()
for i, asn1Data := range certMsg.certificates { for i, asn1Data := range certMsg.certificates {
cert, err := x509.ParseCertificate(asn1Data) cert, err := x509.ParseCertificate(asn1Data)
if err != nil { if err != nil {
@ -96,47 +95,29 @@ func (c *Conn) clientHandshake() os.Error {
return os.ErrorString("failed to parse certificate from server: " + err.String()) return os.ErrorString("failed to parse certificate from server: " + err.String())
} }
certs[i] = cert certs[i] = cert
chain.AddCert(cert)
} }
// If we don't have a root CA set configured then anything is accepted. // If we don't have a root CA set configured then anything is accepted.
// TODO(rsc): Find certificates for OS X 10.6. // TODO(rsc): Find certificates for OS X 10.6.
for cur := certs[0]; c.config.RootCAs != nil; { if c.config.RootCAs != nil {
parent := c.config.RootCAs.FindVerifiedParent(cur) opts := x509.VerifyOptions{
if parent != nil { Roots: c.config.RootCAs,
break CurrentTime: c.config.Time(),
DNSName: c.config.ServerName,
Intermediates: x509.NewCertPool(),
} }
parent = chain.FindVerifiedParent(cur) for i, cert := range certs {
if parent == nil { if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
c.verifiedChains, err = certs[0].Verify(opts)
if err != nil {
c.sendAlert(alertBadCertificate) 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 countrys 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 { if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok {