Skip to content

Commit c581ec4

Browse files
jeffallenagl
authored andcommitted
crypto/tls: Improve TLS Client Authentication
Fix incorrect marshal/unmarshal of certificateRequest. Add support for configuring client-auth on the server side. Fix the certificate selection in the client side. Update generate_cert.go to new time package Fixes #2521. R=krautz, agl, bradfitz CC=golang-dev, mikkel https://golang.org/cl/5448093
1 parent 8f1cb09 commit c581ec4

File tree

7 files changed

+915
-128
lines changed

7 files changed

+915
-128
lines changed

src/pkg/crypto/tls/common.go

+26-6
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,18 @@ type ConnectionState struct {
111111
VerifiedChains [][]*x509.Certificate
112112
}
113113

114+
// ClientAuthType declares the policy the server will follow for
115+
// TLS Client Authentication.
116+
type ClientAuthType int
117+
118+
const (
119+
NoClientCert ClientAuthType = iota
120+
RequestClientCert
121+
RequireAnyClientCert
122+
VerifyClientCertIfGiven
123+
RequireAndVerifyClientCert
124+
)
125+
114126
// A Config structure is used to configure a TLS client or server. After one
115127
// has been passed to a TLS function it must not be modified.
116128
type Config struct {
@@ -120,7 +132,7 @@ type Config struct {
120132
Rand io.Reader
121133

122134
// Time returns the current time as the number of seconds since the epoch.
123-
// If Time is nil, TLS uses the system time.Seconds.
135+
// If Time is nil, TLS uses time.Now.
124136
Time func() time.Time
125137

126138
// Certificates contains one or more certificate chains
@@ -148,11 +160,14 @@ type Config struct {
148160
// hosting.
149161
ServerName string
150162

151-
// AuthenticateClient controls whether a server will request a certificate
152-
// from the client. It does not require that the client send a
153-
// certificate nor does it require that the certificate sent be
154-
// anything more than self-signed.
155-
AuthenticateClient bool
163+
// ClientAuth determines the server's policy for
164+
// TLS Client Authentication. The default is NoClientCert.
165+
ClientAuth ClientAuthType
166+
167+
// ClientCAs defines the set of root certificate authorities
168+
// that servers use if required to verify a client certificate
169+
// by the policy in ClientAuth.
170+
ClientCAs *x509.CertPool
156171

157172
// InsecureSkipVerify controls whether a client verifies the
158173
// server's certificate chain and host name.
@@ -259,6 +274,11 @@ type Certificate struct {
259274
// OCSPStaple contains an optional OCSP response which will be served
260275
// to clients that request it.
261276
OCSPStaple []byte
277+
// Leaf is the parsed form of the leaf certificate, which may be
278+
// initialized using x509.ParseCertificate to reduce per-handshake
279+
// processing for TLS clients doing client authentication. If nil, the
280+
// leaf certificate will be parsed as needed.
281+
Leaf *x509.Certificate
262282
}
263283

264284
// A TLS record.

src/pkg/crypto/tls/handshake_client.go

+54-29
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
package tls
66

77
import (
8+
"bytes"
89
"crypto"
910
"crypto/rsa"
1011
"crypto/subtle"
1112
"crypto/x509"
1213
"errors"
1314
"io"
15+
"strconv"
1416
)
1517

1618
func (c *Conn) clientHandshake() error {
@@ -162,10 +164,23 @@ func (c *Conn) clientHandshake() error {
162164
}
163165
}
164166

165-
transmitCert := false
167+
var certToSend *Certificate
166168
certReq, ok := msg.(*certificateRequestMsg)
167169
if ok {
168-
// We only accept certificates with RSA keys.
170+
// RFC 4346 on the certificateAuthorities field:
171+
// A list of the distinguished names of acceptable certificate
172+
// authorities. These distinguished names may specify a desired
173+
// distinguished name for a root CA or for a subordinate CA;
174+
// thus, this message can be used to describe both known roots
175+
// and a desired authorization space. If the
176+
// certificate_authorities list is empty then the client MAY
177+
// send any certificate of the appropriate
178+
// ClientCertificateType, unless there is some external
179+
// arrangement to the contrary.
180+
181+
finishedHash.Write(certReq.marshal())
182+
183+
// For now, we only know how to sign challenges with RSA
169184
rsaAvail := false
170185
for _, certType := range certReq.certificateTypes {
171186
if certType == certTypeRSASign {
@@ -174,23 +189,41 @@ func (c *Conn) clientHandshake() error {
174189
}
175190
}
176191

177-
// For now, only send a certificate back if the server gives us an
178-
// empty list of certificateAuthorities.
179-
//
180-
// RFC 4346 on the certificateAuthorities field:
181-
// A list of the distinguished names of acceptable certificate
182-
// authorities. These distinguished names may specify a desired
183-
// distinguished name for a root CA or for a subordinate CA; thus,
184-
// this message can be used to describe both known roots and a
185-
// desired authorization space. If the certificate_authorities
186-
// list is empty then the client MAY send any certificate of the
187-
// appropriate ClientCertificateType, unless there is some
188-
// external arrangement to the contrary.
189-
if rsaAvail && len(certReq.certificateAuthorities) == 0 {
190-
transmitCert = true
191-
}
192+
// We need to search our list of client certs for one
193+
// where SignatureAlgorithm is RSA and the Issuer is in
194+
// certReq.certificateAuthorities
195+
findCert:
196+
for i, cert := range c.config.Certificates {
197+
if !rsaAvail {
198+
continue
199+
}
192200

193-
finishedHash.Write(certReq.marshal())
201+
leaf := cert.Leaf
202+
if leaf == nil {
203+
if leaf, err = x509.ParseCertificate(cert.Certificate[0]); err != nil {
204+
c.sendAlert(alertInternalError)
205+
return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error())
206+
}
207+
}
208+
209+
if leaf.PublicKeyAlgorithm != x509.RSA {
210+
continue
211+
}
212+
213+
if len(certReq.certificateAuthorities) == 0 {
214+
// they gave us an empty list, so just take the
215+
// first RSA cert from c.config.Certificates
216+
certToSend = &cert
217+
break
218+
}
219+
220+
for _, ca := range certReq.certificateAuthorities {
221+
if bytes.Equal(leaf.RawIssuer, ca) {
222+
certToSend = &cert
223+
break findCert
224+
}
225+
}
226+
}
194227

195228
msg, err = c.readHandshake()
196229
if err != nil {
@@ -204,17 +237,9 @@ func (c *Conn) clientHandshake() error {
204237
}
205238
finishedHash.Write(shd.marshal())
206239

207-
var cert *x509.Certificate
208-
if transmitCert {
240+
if certToSend != nil {
209241
certMsg = new(certificateMsg)
210-
if len(c.config.Certificates) > 0 {
211-
cert, err = x509.ParseCertificate(c.config.Certificates[0].Certificate[0])
212-
if err == nil && cert.PublicKeyAlgorithm == x509.RSA {
213-
certMsg.certificates = c.config.Certificates[0].Certificate
214-
} else {
215-
cert = nil
216-
}
217-
}
242+
certMsg.certificates = certToSend.Certificate
218243
finishedHash.Write(certMsg.marshal())
219244
c.writeRecord(recordTypeHandshake, certMsg.marshal())
220245
}
@@ -229,7 +254,7 @@ func (c *Conn) clientHandshake() error {
229254
c.writeRecord(recordTypeHandshake, ckx.marshal())
230255
}
231256

232-
if cert != nil {
257+
if certToSend != nil {
233258
certVerify := new(certificateVerifyMsg)
234259
digest := make([]byte, 0, 36)
235260
digest = finishedHash.serverMD5.Sum(digest)

src/pkg/crypto/tls/handshake_messages.go

+21-19
Original file line numberDiff line numberDiff line change
@@ -881,9 +881,11 @@ func (m *certificateRequestMsg) marshal() (x []byte) {
881881

882882
// See http://tools.ietf.org/html/rfc4346#section-7.4.4
883883
length := 1 + len(m.certificateTypes) + 2
884+
casLength := 0
884885
for _, ca := range m.certificateAuthorities {
885-
length += 2 + len(ca)
886+
casLength += 2 + len(ca)
886887
}
888+
length += casLength
887889

888890
x = make([]byte, 4+length)
889891
x[0] = typeCertificateRequest
@@ -895,10 +897,8 @@ func (m *certificateRequestMsg) marshal() (x []byte) {
895897

896898
copy(x[5:], m.certificateTypes)
897899
y := x[5+len(m.certificateTypes):]
898-
899-
numCA := len(m.certificateAuthorities)
900-
y[0] = uint8(numCA >> 8)
901-
y[1] = uint8(numCA)
900+
y[0] = uint8(casLength >> 8)
901+
y[1] = uint8(casLength)
902902
y = y[2:]
903903
for _, ca := range m.certificateAuthorities {
904904
y[0] = uint8(len(ca) >> 8)
@@ -909,7 +909,6 @@ func (m *certificateRequestMsg) marshal() (x []byte) {
909909
}
910910

911911
m.raw = x
912-
913912
return
914913
}
915914

@@ -937,31 +936,34 @@ func (m *certificateRequestMsg) unmarshal(data []byte) bool {
937936
}
938937

939938
data = data[numCertTypes:]
939+
940940
if len(data) < 2 {
941941
return false
942942
}
943-
944-
numCAs := uint16(data[0])<<16 | uint16(data[1])
943+
casLength := uint16(data[0])<<8 | uint16(data[1])
945944
data = data[2:]
945+
if len(data) < int(casLength) {
946+
return false
947+
}
948+
cas := make([]byte, casLength)
949+
copy(cas, data)
950+
data = data[casLength:]
946951

947-
m.certificateAuthorities = make([][]byte, numCAs)
948-
for i := uint16(0); i < numCAs; i++ {
949-
if len(data) < 2 {
952+
m.certificateAuthorities = nil
953+
for len(cas) > 0 {
954+
if len(cas) < 2 {
950955
return false
951956
}
952-
caLen := uint16(data[0])<<16 | uint16(data[1])
957+
caLen := uint16(cas[0])<<8 | uint16(cas[1])
958+
cas = cas[2:]
953959

954-
data = data[2:]
955-
if len(data) < int(caLen) {
960+
if len(cas) < int(caLen) {
956961
return false
957962
}
958963

959-
ca := make([]byte, caLen)
960-
copy(ca, data)
961-
m.certificateAuthorities[i] = ca
962-
data = data[caLen:]
964+
m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen])
965+
cas = cas[caLen:]
963966
}
964-
965967
if len(data) > 0 {
966968
return false
967969
}

0 commit comments

Comments
 (0)