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
|
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\
|
||||||
|
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
|
// 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
conn.go
3
conn.go
@ -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
|
||||||
|
@ -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 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 {
|
if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok {
|
||||||
|
Loading…
Reference in New Issue
Block a user