Using a simple private Public Key Infrastructure (PKI), plain TCP sockets can be secured with TLS. This can be demonstrated with trivial examples that run locally on a loopback device.
A simple echo TCP socket server that listens on port 7000 can be created with
$ socat PIPE TCP-LISTEN:7000,fork
When data is sent to the socket, it will echo it back. The
ncat tool from
nmap is used because it has TLS features that will be needed later.
$ echo "hello" | ncat localhost 7000 hello
The socket listening on port 7000 won't actually be secured, sorry, instead a new socket listening on port 6000 will provide a secure TLS tunnel/proxy.
The tunnel is created with
stunnel. It's configured with local file called
stunnel.conf which contains a section for the tunnel.
[service1] accept = 6000 connect = 7000 CAfile = ca.crt cert = service1.crt key = service1.key verify = 0
The tunnel is called
service1, it listens on port 6000 and creates a tunnel
to port 7000. The CA and
service1 certificates were created by the PKI. The
verify option is set to 0 meaning that peer (client) certificates will not be
required or verified.
The tunnel is started by running
stunnel with the path to the configuration
$ stunnel service1.conf
Trying to connect port 6000 like before won't work.
$ echo "hello" | ncat localhost 6000 Ncat: Connection reset by peer.
The server is expecting the client to start a TLS negotiation. Instead, the
characters "hello" are being sent, so the server disconnects. The error
stunnel is "SSL routines::wrong version number" which
isn't particularly helpful.
ncat supports TLS/SSL and it be used with the
$ echo "hello" | ncat --ssl localhost 6000 hello
A response is now returned through an encrypted TLS connection.
If this connection was made over an insecure network, there is no certainty
that the server being connected to is really
service1. With TLS, certificates
used by 'service1' can be verified to have been signed by a specific CA and
belonging to service1.
To do this with
--ssl-verify option tells
ncat to check the
server certificate. The certificate should be signed by a specific CA, so
ca.crt needs to be specified with the
--ssl-trustfile option. The name in
the certificate will also be validated, so
service1 needs to specified with
$ echo "hello" | ncat --ssl --ssl-verify --ssl-servername service1 --ssl-trustfile ca_public_key.pem localhost 6000 hello
Expecting a different server name should result in a certificate error.
echo "hello" | ncat --ssl --ssl-verify --ssl-servername service2 --ssl-trustfile ca_public_key.pem localhost 6000 Ncat: Certificate verification error. QUITTING.
A really useful tool when debugging TLS connections is
openssl test client.
openssl s_client -servername service1 -connect localhost:6000 -CAfile ca.crt
This will attempt to negotiate a connection and output a heaps of useful information about the certificate, the signatures, the alorithms used, the validation results and more.
TLS also supports a "peer authentication" mode in which the server checks the client certificates. Servers, or tunnels, can be configured to only allow clients with certificates signed by a specific CA to connect.
A secure tunnel configuration that uses strict peer authentication can be
added to the
[service1-secure] accept = 5000 connect = 7000 CAfile = ca.crt cert = service1.crt key = service1.key verify = 2
This tunnel is configuration is similar to the previous one except it'll listen
on a different port (5000) and
verify is set to 2, meaning it will require
client certificates and will validate they are signed by the CA (
After restarting the
stunnel process with the updated configuration,
connecting as before to the new tunnel on port 5000 does not work.
$ echo "hello" | ncat --ssl --ssl-verify --ssl-servername service1 --ssl-trustfile ca.crt localhost 5000
This time there is no response, this is because no client certificates are
being sent. The error logged by
stunnel is "peer did
not return a certificate".
To connect to this service, a client certificate pair signed by the CA is
required. The process to generate a certificate pair for the client is the same
process used to create the
service1 certificate pair, but this certificate
pair will be called
SUBJECT="/C=DE/ST=Berlin/L=Berlin/O=tarnbarford.net/OU=tarnbarford.net/CN=client1" openssl ecparam -genkey -name prime256v1 -noout -out client1.key openssl req -new -key client1.key -subj $SUBJECT -out client1.csr openssl x509 -req -days 365 -sha256 -in client1.csr -CA ca.crt -CAkey ca.key -set_serial 1 -out client1.crt
$ echo "hello" | ncat --ssl --ssl-verify --ssl-servername service1 --ssl-trustfile ca.crt --ssl-cert client1.crt --ssl-key client1.key localhost 5000 hello
A response is now returned, but only if the client and server certificates are signed by the CA.