Commit c47123d0 authored by Mikkel Krautz's avatar Mikkel Krautz Committed by Adam Langley

crypto/tls: client certificate support.

This changeset implements client certificate support in crypto/tls
for both handshake_server.go and handshake_client.go

The updated server implementation sends an empty CertificateAuthorities
field in the CertificateRequest, thus allowing clients to send any
certificates they wish. Likewise, the client code will only respond
with its certificate when the server requests a certificate with this
field empty.

R=agl, rsc, agl1
CC=golang-dev
https://golang.org/cl/1975042
parent e235a04f
...@@ -146,6 +146,7 @@ const ( ...@@ -146,6 +146,7 @@ const (
HashSHA256 HashSHA256
HashSHA384 HashSHA384
HashSHA512 HashSHA512
HashMD5SHA1 // combined MD5 and SHA1 hash used for RSA signing in TLS.
) )
// These are ASN1 DER structures: // These are ASN1 DER structures:
...@@ -153,7 +154,7 @@ const ( ...@@ -153,7 +154,7 @@ const (
// digestAlgorithm AlgorithmIdentifier, // digestAlgorithm AlgorithmIdentifier,
// digest OCTET STRING // digest OCTET STRING
// } // }
// For performance, we don't use the generic ASN1 encoding. Rather, we // For performance, we don't use the generic ASN1 encoder. Rather, we
// precompute a prefix of the digest value that makes a valid ASN1 DER string // precompute a prefix of the digest value that makes a valid ASN1 DER string
// with the correct contents. // with the correct contents.
var hashPrefixes = [][]byte{ var hashPrefixes = [][]byte{
...@@ -167,6 +168,8 @@ var hashPrefixes = [][]byte{ ...@@ -167,6 +168,8 @@ var hashPrefixes = [][]byte{
[]byte{0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}, []byte{0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
// HashSHA512 // HashSHA512
[]byte{0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}, []byte{0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
// HashMD5SHA1
[]byte{}, // A special TLS case which doesn't use an ASN1 prefix.
} }
// SignPKCS1v15 calcuates the signature of hashed using RSASSA-PSS-SIGN from RSA PKCS#1 v1.5. // SignPKCS1v15 calcuates the signature of hashed using RSASSA-PSS-SIGN from RSA PKCS#1 v1.5.
...@@ -252,6 +255,8 @@ func pkcs1v15HashInfo(hash PKCS1v15Hash, inLen int) (hashLen int, prefix []byte, ...@@ -252,6 +255,8 @@ func pkcs1v15HashInfo(hash PKCS1v15Hash, inLen int) (hashLen int, prefix []byte,
hashLen = 48 hashLen = 48
case HashSHA512: case HashSHA512:
hashLen = 64 hashLen = 64
case HashMD5SHA1:
hashLen = 36
default: default:
return 0, nil, os.ErrorString("unknown hash function") return 0, nil, os.ErrorString("unknown hash function")
} }
......
...@@ -38,10 +38,12 @@ const ( ...@@ -38,10 +38,12 @@ const (
typeClientHello uint8 = 1 typeClientHello uint8 = 1
typeServerHello uint8 = 2 typeServerHello uint8 = 2
typeCertificate uint8 = 11 typeCertificate uint8 = 11
typeCertificateStatus uint8 = 22 typeCertificateRequest uint8 = 13
typeServerHelloDone uint8 = 14 typeServerHelloDone uint8 = 14
typeCertificateVerify uint8 = 15
typeClientKeyExchange uint8 = 16 typeClientKeyExchange uint8 = 16
typeFinished uint8 = 20 typeFinished uint8 = 20
typeCertificateStatus uint8 = 22
typeNextProtocol uint8 = 67 // Not IANA assigned typeNextProtocol uint8 = 67 // Not IANA assigned
) )
...@@ -67,6 +69,15 @@ const ( ...@@ -67,6 +69,15 @@ const (
statusTypeOCSP uint8 = 1 statusTypeOCSP uint8 = 1
) )
// Certificate types (for certificateRequestMsg)
const (
certTypeRSASign = 1 // A certificate containing an RSA key
certTypeDSSSign = 2 // A certificate containing a DSA key
certTypeRSAFixedDH = 3 // A certificate containing a static DH key
certTypeDSSFixedDH = 4 // A certficiate containing a static DH key
// Rest of these are reserved by the TLS spec
)
type ConnectionState struct { type ConnectionState struct {
HandshakeComplete bool HandshakeComplete bool
CipherSuite uint16 CipherSuite uint16
...@@ -80,6 +91,7 @@ type Config struct { ...@@ -80,6 +91,7 @@ type Config struct {
Rand io.Reader Rand io.Reader
// Time returns the current time as the number of seconds since the epoch. // Time returns the current time as the number of seconds since the epoch.
Time func() int64 Time func() int64
// Certificates contains one or more certificate chains.
Certificates []Certificate Certificates []Certificate
RootCAs *CASet RootCAs *CASet
// NextProtos is a list of supported, application level protocols. // NextProtos is a list of supported, application level protocols.
...@@ -88,9 +100,16 @@ type Config struct { ...@@ -88,9 +100,16 @@ type Config struct {
// ServerName is included in the client's handshake to support virtual // ServerName is included in the client's handshake to support virtual
// hosting. // hosting.
ServerName string ServerName string
// AuthenticateClient determines if a server will request a certificate
// from the client. It does not require that the client send a
// certificate nor, if it does, that the certificate is anything more
// than self-signed.
AuthenticateClient bool
} }
type Certificate struct { type Certificate struct {
// Certificate contains a chain of one or more certificates. Leaf
// certificate first.
Certificate [][]byte Certificate [][]byte
PrivateKey *rsa.PrivateKey PrivateKey *rsa.PrivateKey
} }
......
...@@ -534,12 +534,16 @@ func (c *Conn) readHandshake() (interface{}, os.Error) { ...@@ -534,12 +534,16 @@ func (c *Conn) readHandshake() (interface{}, os.Error) {
m = new(serverHelloMsg) m = new(serverHelloMsg)
case typeCertificate: case typeCertificate:
m = new(certificateMsg) m = new(certificateMsg)
case typeCertificateRequest:
m = new(certificateRequestMsg)
case typeCertificateStatus: case typeCertificateStatus:
m = new(certificateStatusMsg) m = new(certificateStatusMsg)
case typeServerHelloDone: case typeServerHelloDone:
m = new(serverHelloDoneMsg) m = new(serverHelloDoneMsg)
case typeClientKeyExchange: case typeClientKeyExchange:
m = new(clientKeyExchangeMsg) m = new(clientKeyExchangeMsg)
case typeCertificateVerify:
m = new(certificateVerifyMsg)
case typeNextProtocol: case typeNextProtocol:
m = new(nextProtoMsg) m = new(nextProtoMsg)
case typeFinished: case typeFinished:
......
...@@ -130,12 +130,64 @@ func (c *Conn) clientHandshake() os.Error { ...@@ -130,12 +130,64 @@ func (c *Conn) clientHandshake() os.Error {
if err != nil { if err != nil {
return err return err
} }
transmitCert := false
certReq, ok := msg.(*certificateRequestMsg)
if ok {
// We only accept certificates with RSA keys.
rsaAvail := false
for _, certType := range certReq.certificateTypes {
if certType == certTypeRSASign {
rsaAvail = true
break
}
}
// For now, only send a certificate back if the server gives us an
// empty list of certificateAuthorities.
//
// RFC 4346 on the certificateAuthorities field:
// A list of the distinguished names of acceptable certificate
// authorities. These distinguished names may specify a desired
// distinguished name for a root CA or for a subordinate CA; thus,
// this message can be used to describe both known roots and a
// desired authorization space. If the certificate_authorities
// list is empty then the client MAY send any certificate of the
// appropriate ClientCertificateType, unless there is some
// external arrangement to the contrary.
if rsaAvail && len(certReq.certificateAuthorities) == 0 {
transmitCert = true
}
finishedHash.Write(certReq.marshal())
msg, err = c.readHandshake()
if err != nil {
return err
}
}
shd, ok := msg.(*serverHelloDoneMsg) shd, ok := msg.(*serverHelloDoneMsg)
if !ok { if !ok {
return c.sendAlert(alertUnexpectedMessage) return c.sendAlert(alertUnexpectedMessage)
} }
finishedHash.Write(shd.marshal()) finishedHash.Write(shd.marshal())
var cert *x509.Certificate
if transmitCert {
certMsg = new(certificateMsg)
if len(c.config.Certificates) > 0 {
cert, err = x509.ParseCertificate(c.config.Certificates[0].Certificate[0])
if err == nil && cert.PublicKeyAlgorithm == x509.RSA {
certMsg.certificates = c.config.Certificates[0].Certificate
} else {
cert = nil
}
}
finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal())
}
ckx := new(clientKeyExchangeMsg) ckx := new(clientKeyExchangeMsg)
preMasterSecret := make([]byte, 48) preMasterSecret := make([]byte, 48)
preMasterSecret[0] = byte(hello.vers >> 8) preMasterSecret[0] = byte(hello.vers >> 8)
...@@ -153,6 +205,21 @@ func (c *Conn) clientHandshake() os.Error { ...@@ -153,6 +205,21 @@ func (c *Conn) clientHandshake() os.Error {
finishedHash.Write(ckx.marshal()) finishedHash.Write(ckx.marshal())
c.writeRecord(recordTypeHandshake, ckx.marshal()) c.writeRecord(recordTypeHandshake, ckx.marshal())
if cert != nil {
certVerify := new(certificateVerifyMsg)
var digest [36]byte
copy(digest[0:16], finishedHash.serverMD5.Sum())
copy(digest[16:36], finishedHash.serverSHA1.Sum())
signed, err := rsa.SignPKCS1v15(c.config.Rand, c.config.Certificates[0].PrivateKey, rsa.HashMD5SHA1, digest[0:])
if err != nil {
return c.sendAlert(alertInternalError)
}
certVerify.signature = signed
finishedHash.Write(certVerify.marshal())
c.writeRecord(recordTypeHandshake, certVerify.marshal())
}
suite := cipherSuites[0] suite := cipherSuites[0]
masterSecret, clientMAC, serverMAC, clientKey, serverKey := masterSecret, clientMAC, serverMAC, clientKey, serverKey :=
keysFromPreMasterSecret11(preMasterSecret, hello.random, serverHello.random, suite.hashLength, suite.cipherKeyLength) keysFromPreMasterSecret11(preMasterSecret, hello.random, serverHello.random, suite.hashLength, suite.cipherKeyLength)
......
...@@ -668,3 +668,153 @@ func (m *nextProtoMsg) unmarshal(data []byte) bool { ...@@ -668,3 +668,153 @@ func (m *nextProtoMsg) unmarshal(data []byte) bool {
return true return true
} }
type certificateRequestMsg struct {
raw []byte
certificateTypes []byte
certificateAuthorities [][]byte
}
func (m *certificateRequestMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
}
// See http://tools.ietf.org/html/rfc4346#section-7.4.4
length := 1 + len(m.certificateTypes) + 2
for _, ca := range m.certificateAuthorities {
length += 2 + len(ca)
}
x = make([]byte, 4+length)
x[0] = typeCertificateRequest
x[1] = uint8(length >> 16)
x[2] = uint8(length >> 8)
x[3] = uint8(length)
x[4] = uint8(len(m.certificateTypes))
copy(x[5:], m.certificateTypes)
y := x[5+len(m.certificateTypes):]
numCA := len(m.certificateAuthorities)
y[0] = uint8(numCA >> 8)
y[1] = uint8(numCA)
y = y[2:]
for _, ca := range m.certificateAuthorities {
y[0] = uint8(len(ca) >> 8)
y[1] = uint8(len(ca))
y = y[2:]
copy(y, ca)
y = y[len(ca):]
}
m.raw = x
return
}
func (m *certificateRequestMsg) unmarshal(data []byte) bool {
m.raw = data
if len(data) < 5 {
return false
}
length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
if uint32(len(data))-4 != length {
return false
}
numCertTypes := int(data[4])
data = data[5:]
if numCertTypes == 0 || len(data) <= numCertTypes {
return false
}
m.certificateTypes = make([]byte, numCertTypes)
if copy(m.certificateTypes, data) != numCertTypes {
return false
}
data = data[numCertTypes:]
if len(data) < 2 {
return false
}
numCAs := uint16(data[0])<<16 | uint16(data[1])
data = data[2:]
m.certificateAuthorities = make([][]byte, numCAs)
for i := uint16(0); i < numCAs; i++ {
if len(data) < 2 {
return false
}
caLen := uint16(data[0])<<16 | uint16(data[1])
data = data[2:]
if len(data) < int(caLen) {
return false
}
ca := make([]byte, caLen)
copy(ca, data)
m.certificateAuthorities[i] = ca
data = data[caLen:]
}
if len(data) > 0 {
return false
}
return true
}
type certificateVerifyMsg struct {
raw []byte
signature []byte
}
func (m *certificateVerifyMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
}
// See http://tools.ietf.org/html/rfc4346#section-7.4.8
siglength := len(m.signature)
length := 2 + siglength
x = make([]byte, 4+length)
x[0] = typeCertificateVerify
x[1] = uint8(length >> 16)
x[2] = uint8(length >> 8)
x[3] = uint8(length)
x[4] = uint8(siglength >> 8)
x[5] = uint8(siglength)
copy(x[6:], m.signature)
m.raw = x
return
}
func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
m.raw = data
if len(data) < 6 {
return false
}
length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
if uint32(len(data))-4 != length {
return false
}
siglength := int(data[4])<<8 + int(data[5])
if len(data)-6 != siglength {
return false
}
m.signature = data[6:]
return true
}
...@@ -16,6 +16,8 @@ var tests = []interface{}{ ...@@ -16,6 +16,8 @@ var tests = []interface{}{
&serverHelloMsg{}, &serverHelloMsg{},
&certificateMsg{}, &certificateMsg{},
&certificateRequestMsg{},
&certificateVerifyMsg{},
&certificateStatusMsg{}, &certificateStatusMsg{},
&clientKeyExchangeMsg{}, &clientKeyExchangeMsg{},
&finishedMsg{}, &finishedMsg{},
...@@ -148,6 +150,23 @@ func (*certificateMsg) Generate(rand *rand.Rand, size int) reflect.Value { ...@@ -148,6 +150,23 @@ func (*certificateMsg) Generate(rand *rand.Rand, size int) reflect.Value {
return reflect.NewValue(m) return reflect.NewValue(m)
} }
func (*certificateRequestMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m := &certificateRequestMsg{}
m.certificateTypes = randomBytes(rand.Intn(5)+1, rand)
numCAs := rand.Intn(100)
m.certificateAuthorities = make([][]byte, numCAs)
for i := 0; i < numCAs; i++ {
m.certificateAuthorities[i] = randomBytes(rand.Intn(15)+1, rand)
}
return reflect.NewValue(m)
}
func (*certificateVerifyMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m := &certificateVerifyMsg{}
m.signature = randomBytes(rand.Intn(15)+1, rand)
return reflect.NewValue(m)
}
func (*certificateStatusMsg) Generate(rand *rand.Rand, size int) reflect.Value { func (*certificateStatusMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m := &certificateStatusMsg{} m := &certificateStatusMsg{}
if rand.Intn(10) > 5 { if rand.Intn(10) > 5 {
......
...@@ -18,6 +18,7 @@ import ( ...@@ -18,6 +18,7 @@ import (
"crypto/rsa" "crypto/rsa"
"crypto/sha1" "crypto/sha1"
"crypto/subtle" "crypto/subtle"
"crypto/x509"
"io" "io"
"os" "os"
) )
...@@ -112,10 +113,62 @@ func (c *Conn) serverHandshake() os.Error { ...@@ -112,10 +113,62 @@ func (c *Conn) serverHandshake() os.Error {
finishedHash.Write(certMsg.marshal()) finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal())
if config.AuthenticateClient {
// Request a client certificate
certReq := new(certificateRequestMsg)
certReq.certificateTypes = []byte{certTypeRSASign}
// An empty list of certificateAuthorities signals to
// the client that it may send any certificate in response
// to our request.
finishedHash.Write(certReq.marshal())
c.writeRecord(recordTypeHandshake, certReq.marshal())
}
helloDone := new(serverHelloDoneMsg) helloDone := new(serverHelloDoneMsg)
finishedHash.Write(helloDone.marshal()) finishedHash.Write(helloDone.marshal())
c.writeRecord(recordTypeHandshake, helloDone.marshal()) c.writeRecord(recordTypeHandshake, helloDone.marshal())
var pub *rsa.PublicKey
if config.AuthenticateClient {
// Get client certificate
msg, err = c.readHandshake()
if err != nil {
return err
}
certMsg, ok = msg.(*certificateMsg)
if !ok {
return c.sendAlert(alertUnexpectedMessage)
}
finishedHash.Write(certMsg.marshal())
certs := make([]*x509.Certificate, len(certMsg.certificates))
for i, asn1Data := range certMsg.certificates {
cert, err := x509.ParseCertificate(asn1Data)
if err != nil {
return c.sendAlert(alertBadCertificate)
}
certs[i] = cert
}
// TODO(agl): do better validation of certs: max path length, name restrictions etc.
for i := 1; i < len(certs); i++ {
if err := certs[i-1].CheckSignatureFrom(certs[i]); err != nil {
return c.sendAlert(alertBadCertificate)
}
}
if len(certs) > 0 {
key, ok := certs[0].PublicKey.(*rsa.PublicKey)
if !ok {
return c.sendAlert(alertUnsupportedCertificate)
}
pub = key
c.peerCertificates = certs
}
}
// Get client key exchange
msg, err = c.readHandshake() msg, err = c.readHandshake()
if err != nil { if err != nil {
return err return err
...@@ -126,6 +179,33 @@ func (c *Conn) serverHandshake() os.Error { ...@@ -126,6 +179,33 @@ func (c *Conn) serverHandshake() os.Error {
} }
finishedHash.Write(ckx.marshal()) finishedHash.Write(ckx.marshal())
// If we received a client cert in response to our certificate request message,
// the client will send us a certificateVerifyMsg immediately after the
// clientKeyExchangeMsg. This message is a MD5SHA1 digest of all preceeding
// handshake-layer messages that is signed using the private key corresponding
// to the client's certificate. This allows us to verify that the client is in
// posession of the private key of the certificate.
if len(c.peerCertificates) > 0 {
msg, err = c.readHandshake()
if err != nil {
return err
}
certVerify, ok := msg.(*certificateVerifyMsg)
if !ok {
return c.sendAlert(alertUnexpectedMessage)
}
digest := make([]byte, 36)
copy(digest[0:16], finishedHash.serverMD5.Sum())
copy(digest[16:36], finishedHash.serverSHA1.Sum())
err = rsa.VerifyPKCS1v15(pub, rsa.HashMD5SHA1, digest, certVerify.signature)
if err != nil {
return c.sendAlert(alertBadCertificate)
}
finishedHash.Write(certVerify.marshal())
}
preMasterSecret := make([]byte, 48) preMasterSecret := make([]byte, 48)
_, err = io.ReadFull(config.Rand, preMasterSecret[2:]) _, err = io.ReadFull(config.Rand, preMasterSecret[2:])
if err != nil { if err != nil {
......
...@@ -661,7 +661,7 @@ func ListenAndServe(addr string, handler Handler) os.Error { ...@@ -661,7 +661,7 @@ func ListenAndServe(addr string, handler Handler) os.Error {
// func main() { // func main() {
// http.HandleFunc("/", handler) // http.HandleFunc("/", handler)
// log.Stdoutf("About to listen on 10443. Go to https://127.0.0.1:10443/") // log.Stdoutf("About to listen on 10443. Go to https://127.0.0.1:10443/")
// err := http.ListenAndServe(":10443", "cert.pem", "key.pem", nil) // err := http.ListenAndServeTLS(":10443", "cert.pem", "key.pem", nil)
// if err != nil { // if err != nil {
// log.Exit(err) // log.Exit(err)
// } // }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment