GopherCon 2018 - The Go Programmer's Guide to Secure Connections
Keegan Carruthers-Smith for the GopherCon Liveblog
Presenter: Liz Rice
Liveblogger: Keegan Carruthers-Smith
Title graphic: Amy Chen
A practical guide to what is happening under the covers when applications or users need to identify themselves, or need a secure channel for communications.
The motivation of the talk is to understand what is happening with these certificates and keys, how they are used to setup these secure connections.
The talk aims to answer:
- How do I secure my connections?
- What do these error messages mean?
- What the hell are all these .crt, .key, .csr and .pem files.
TLS is used to identify and encrypt connections. The code for the tutorial is at github.com/lizrice/secure-connections. It contains hooks into tls.Config to help illustrate what the client and server do when establishing a TLS connection. Additionally it illustrates how mutually authenticating TLS works (mTLS).
Establishing identity online is critical. For example when you online deposit money into your bank account, you want to ensure you are talking to your bank. Additionally the messages and amount of money you deposit should be encrypted in case traffic is intercepted.
Note: You often see TLS and SSL mentioned. TLS is the newer name for the protocol that used to be called SSL.
Go's TLS implementation exposes hooks into the TLS handshake:
Public/Private key encryption:
- Public key can be freely distributed and is used to encrypt.
- Private key must be kept private and is used to decrypt.
You can also use the private key to sign a message. The public key is then used to verify the signature. You have message + signature.
In TLS you send your public key and identify yourself. The server does not know who you are, so to establish identity it relies on a third-party it trusts called a Certificate Authority (CA). The CA will sign your certificate, which the server can verify.
A certificate is a X.509 certificate. X.509 is just the name of the standard. It contains:
- Subject Name
- Subject's public key
- Issuer (CA) name
Subject is who you are trying to identify. The CA is someone who you both trust.
Your certs should use a Subject Alternative Name (SAN). Common Name was
deprecated in 2000! You can ignore in Go 1.11 with
Creating keys and certificates
You use a trusted certificate authority. This is something like Let's Encrypt. Trusted certificate authorities have certificates on your computer and/or browser. Those certificates are used to validate certificates for public-facing domains. It is not for internal components in a distributed system.
If you want a signed certificate you create a Certificate Signing Request
(CSR) and send it to the CA. For example you can create a CSR with
openssl req -key private-key -new -out csr
The Certificate Authority then validates your identity. For example Lets Encrypt has challenges like asking you to change a DNS setting to prove you have control over what it will sign.
openssl doesn't easily support SANs. There are several alternatives:
minica. Minica's usecase is easy generation of key and
certs. Also it is only 300 lines of Go code, so if you want to create
certificates yourself progmatically you can just look at minica's source.
minica -domains liz-server
Will create keys, etc for the host
liz-server in a new directory called
liz-server. You will need to add
liz-server to your
/etc/hosts to point
These keys are then used to demonstrate TLS in the example code at github.com/lizrice/secure-connections. Try it out yourself.
Mutually-authenticated TLS (mTLS)
Normally the client only validates the identity of the server, but the server
doesn't validate the identity of the client. mTLS is adding the validation to
the TLS handshake. In the server-side
tls.Config you set
ClientAuth: tls.RequireAndVerifyClientCert. You then need to create a certificate for the
client and get it signed by a CA the server trusts.
tls.Config will need to
contain the CA's for client certificates
tls.Config is used by both clients and servers. Most fields are
shared, but some fields are specific to client and server. So take care to
read a fields documentation when using it.
The file extension used is inconsistent.
- Information type:
.keyfor private key...
- Or file format:
PEM files are Base64-Encoded and they will contain a header saying if it is a key, certificate, etc. You can understand the contents of the file with
openssl x509 -text -in cert.pem -noout
Common Error Messages
- Connection Refused: Check you're connecting to the right port. Can be due to another reason, but more than likely a port issue.
- Certificate signed by unknown authority: You have received a certificate, but you don't trust it. Check if you should trust the CA who signed the certificate, and possibly and to your trusted CA pool.
- Remote Error: It's the other end complaining.