We are building a server in Golang that opens a TCP port over SSL.
We would like to enable mutual-authentication between the client and the server. But also detect when a client attempts to connect to our server without a valid client certificate and return them a error message over SSL - such as "invalid client certificate detected, please contact company ABC for assistance".
Just to be clear: we are adamant about our requirement to return data over SSL to clients that fail to mutually authenticate with the server. We do not want to disconnect them.
The approach we have taken is to use the 'VerifyClientCertIfGiven' configuration setting for TLS.
Whereby, we verify the client certificate if it is given, but we still allow an SSL connection to be established if it isn't.
How can we find out:
Below is the code of our server:
package main
import(
"fmt"
"io/ioutil"
"crypto/tls"
"crypto/x509"
)
func main(){
// Configure SSL
cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
caCert, _ := ioutil.ReadFile("client.crt")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
config := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientCAs: caCertPool,
ClientAuth: tls.VerifyClientCertIfGiven,
}
config.BuildNameToCertificate()
// Listen on port 443
listener, _ := tls.Listen("tcp", ":443", config)
defer listener.Close()
// Accept incoming connection
conn, _ := listener.Accept()
defer conn.Close()
// Print ConnectionState
tlscon, _ := conn.(*tls.Conn)
fmt.Println(tlscon.ConnectionState())
}
And below is the code of our client:
package main
import (
"io/ioutil"
"crypto/tls"
"crypto/x509"
)
func main(){
//Configure SSL
cert, _ := tls.LoadX509KeyPair("client.crt", "client.key"
caCert, _ := io.util.ReadFile("server.crt")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
config := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
}
config.BuildNameToCertificate()
// Connect to server
conn, _ := tls.Dial("tcp", "127.0.0.1:443", config)
defer conn.Close()
}
The output:
{0 false false 0 false [] [] [] [] []}
We've tried implementing conn.ConnectionState().PeerCertificates on the server but in all our attempts, it was an empty byte array.
Thank you in advance. We appreciate your time trying to help us.
Kind Regards,
Julian
I spent the best of my afternoon on this one too.
Once the server accepts the connection you will need to explicitely call tlscon.Handshake()
to get the client's certificate, otherwise the tls package will do it automatically after the first i/o. https://golang.org/pkg/crypto/tls/#Conn.Handshake
You can see a example here.