Complete SSL certificate workflow

321 views
Skip to first unread message

Josh V

unread,
Aug 17, 2016, 12:01:01 PM8/17/16
to golang-nuts
Hi all,

I'm trying to come up with an example of how to create SSL certificates and keys from start to finish (including CertificateRequests) all using Go. I'll go ahead and get the obligatory "I'm pretty new to SSL" disclaimer out of the way... I've played with https://golang.org/src/crypto/tls/generate_cert.go quite a bit trying to understand what all needs to happen, but that program doesn't cover some cases I'd like to get working. Here's what I would like to build:
  • Server piece
    • Generates a new private
    • Generates a new x509.Certificate (with IsCA: true) using the new private key
    • Write both the cert and key to disk
    • Spin up an HTTP server to accept CSR->Certificate requests
    • Spin up an HTTPS server to accept requests from clients to test their newly generated certificates
  • Client piece
    • Generates a new private key
    • Creates a x509.CertificateRequest
    • POSTs the CertificateRequest off to the server's HTTP piece
    • Receives a response containing the client's fresh Certificate
    • Writes both the cert and the key to disk
    • Successfully connects to the server's HTTPS piece using the newly generated certificate
I've been working on a project that basically does (or tries to do) all of this, and things were looking promising for a while. I have (I guess what you'd call) a "root CA" cert/key that are used to create new client certificates from CSRs. The resulting client certificate, client key, and CA certificate connect to my server piece just fine when I use curl. But when I try to use those same files in the Go client, I get an "x509: certificate signed by unknown authority" error. I've tried as many variations on the tls.Config.ClientCAs and RootCAs as I can think of. Nothing seems to be just right, so I'm obviously missing something.

I've tried to whittle my project down to the basic concepts described above, which can be found at https://gist.github.com/codekoala/c793f020c27bded785fb39f0f2594ee2 ... I apologize in advance--it is horrendous code with lots of copy pasta and unhandled error cases. I just need to get this out there. If anyone can muster up the courage to take a peek at that gist and offer suggestions for how to achieve what I've described, please do.

I realize most people will immediately suggest "just use openssl on the command line" to get past these hurdles. I could certainly do that, but I'd prefer to keep it all in the standard library, if at all possible. Also, from my research, it seems like I should be making a root CA and then an intermediate CA that is used to process the actual CSRs and such. If anyone can offer insight into the correct way to do that with Go, I'm all eyes.

Thanks!

- Josh

Mi-e Foame

unread,
Aug 17, 2016, 12:58:18 PM8/17/16
to golang-nuts
Sorry, I should have mentioned that the primary goal here is to generate certificates for the specific purpose of client authentication.

Josh V

unread,
Aug 20, 2016, 1:48:11 PM8/20/16
to golang-nuts
I ended up figuring out what I needed to do. Here are some things that I did differently when it started working:
  • I created a self-signed root CA. This is possible with generate_cert.go with one minor modification: the KeyUsage must include x509.KeyUsageCertSign.
  • I created an intermediate CA. The x509.Certificate{} stuff was pretty much the same as it was for the root CA. The main difference when creating the intermediate CA certificate is that the x509.CreateCertificate call looks more like x509.CreateCertificate(rand.Reader, &intermediateTemplate, caCert, &intermediateTemplate.PublicKey, caKey) so the certificate will be signed by the root CA instead of being another self-signed certificate.
  • I created a server certificate using the intermediate CA, using a x509.CreateCertificate call similar to the one used to create the intermediate CA: x509.CreateCertificate(rand.Reader, serverTemplate, interCACert, serverTemplate.PublicKey, interCAKey). The x509.Certificate looked like this:
    x509.Certificate{
      SerialNumber: big.NewInt(1),
      Subject: &pkix.Name{CommonName: "server.site.local"},
      NotBefore: notBefore,
      NotAfter: notAfter,
      KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
      ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
      IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
    }

  • I created a client certificate very similarly to how the server cert was created: x509.CreateCertificate(rand.Reader, clientTemplate, interCACert, clientTemplate.PublicKey, interCAKey). The x509.Certificate looked like this:
    x509.Certificate{
      SerialNumber: big.NewInt(1),
      Subject: &pkix.Name{CommonName: "client.site.local"},
      NotBefore: notBefore,
      NotAfter: notAfter,
      KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
      ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
    }
  • When I created the server, the TLS config had a x509.CertPool containing the intermediate CA certificate as the ClientCAs
  • When I created the client, the TLS config had a x509.CertPool containing the intermediate CA certificate as the RootCAs

I did end up using x509.CertificateRequests to generate the server and client certificates. They aren't nearly as complicated as I tried to make them at first. Basically, I just used the x509.CertificateRequest's PublicKey and Subject to set those attributes when creating the x509.Certificate. Done and done.


I hope that helps anyone who stumbles upon this thread having similar problems.


- Josh


On Wednesday, August 17, 2016 at 10:01:01 AM UTC-6, Josh V wrote:

Manlio Perillo

unread,
Aug 20, 2016, 3:11:57 PM8/20/16
to golang-nuts
You can take a look at


Manlio

Josh V

unread,
Aug 20, 2016, 3:35:11 PM8/20/16
to golang-nuts
Thanks! I wish I would have found that repo a week ago. It would have saved me a lot of stress :)
Reply all
Reply to author
Forward
0 new messages