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:
- Was a client certificate provided?
- If so, did it pass the mutual authentication checks performed by TLS?
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