HashSHA256
HashSHA384
HashSHA512
+ HashMD5SHA1 // combined MD5 and SHA1 hash used for RSA signing in TLS.
)
// These are ASN1 DER structures:
// digestAlgorithm AlgorithmIdentifier,
// 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
// with the correct contents.
var hashPrefixes = [][]byte{
[]byte{0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
// HashSHA512
[]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.
hashLen = 48
case HashSHA512:
hashLen = 64
+ case HashMD5SHA1:
+ hashLen = 36
default:
return 0, nil, os.ErrorString("unknown hash function")
}
// TLS handshake message types.
const (
- typeClientHello uint8 = 1
- typeServerHello uint8 = 2
- typeCertificate uint8 = 11
- typeCertificateStatus uint8 = 22
- typeServerHelloDone uint8 = 14
- typeClientKeyExchange uint8 = 16
- typeFinished uint8 = 20
- typeNextProtocol uint8 = 67 // Not IANA assigned
+ typeClientHello uint8 = 1
+ typeServerHello uint8 = 2
+ typeCertificate uint8 = 11
+ typeCertificateRequest uint8 = 13
+ typeServerHelloDone uint8 = 14
+ typeCertificateVerify uint8 = 15
+ typeClientKeyExchange uint8 = 16
+ typeFinished uint8 = 20
+ typeCertificateStatus uint8 = 22
+ typeNextProtocol uint8 = 67 // Not IANA assigned
)
// TLS cipher suites.
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 {
HandshakeComplete bool
CipherSuite uint16
// Rand provides the source of entropy for nonces and RSA blinding.
Rand io.Reader
// 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
RootCAs *CASet
// NextProtos is a list of supported, application level protocols.
// ServerName is included in the client's handshake to support virtual
// hosting.
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 {
+ // Certificate contains a chain of one or more certificates. Leaf
+ // certificate first.
Certificate [][]byte
PrivateKey *rsa.PrivateKey
}
m = new(serverHelloMsg)
case typeCertificate:
m = new(certificateMsg)
+ case typeCertificateRequest:
+ m = new(certificateRequestMsg)
case typeCertificateStatus:
m = new(certificateStatusMsg)
case typeServerHelloDone:
m = new(serverHelloDoneMsg)
case typeClientKeyExchange:
m = new(clientKeyExchangeMsg)
+ case typeCertificateVerify:
+ m = new(certificateVerifyMsg)
case typeNextProtocol:
m = new(nextProtoMsg)
case typeFinished:
if err != nil {
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)
if !ok {
return c.sendAlert(alertUnexpectedMessage)
}
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)
preMasterSecret := make([]byte, 48)
preMasterSecret[0] = byte(hello.vers >> 8)
finishedHash.Write(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]
masterSecret, clientMAC, serverMAC, clientKey, serverKey :=
keysFromPreMasterSecret11(preMasterSecret, hello.random, serverHello.random, suite.hashLength, suite.cipherKeyLength)
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
+}
&serverHelloMsg{},
&certificateMsg{},
+ &certificateRequestMsg{},
+ &certificateVerifyMsg{},
&certificateStatusMsg{},
&clientKeyExchangeMsg{},
&finishedMsg{},
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 {
m := &certificateStatusMsg{}
if rand.Intn(10) > 5 {
"crypto/rsa"
"crypto/sha1"
"crypto/subtle"
+ "crypto/x509"
"io"
"os"
)
finishedHash.Write(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)
finishedHash.Write(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()
if err != nil {
return err
}
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)
_, err = io.ReadFull(config.Rand, preMasterSecret[2:])
if err != nil {
// func main() {
// http.HandleFunc("/", handler)
// 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 {
// log.Exit(err)
// }