title: How to create a test server which uses TLS client authentication in Go?
body: <p>I'd like to write a unit test for an HTTP handler which extracts certain information from a device's certificate. I've found this gist, <a href="https://gist.github.com/ncw/9253562" rel="nofollow noreferrer">https://gist.github.com/ncw/9253562</a>, which uses <code>openssl</code> to generate the certificates and simply reads the resulting files in its <code>client.go</code> and <code>server.go</code>. To make things a bit more transparent, however, I'd like to generate the certificates using Go's standard library.</p>
  
  <p>Here is my attempt so far at the unit test (available at <a href="https://github.com/kurtpeek/client-auth-test" rel="nofollow noreferrer">https://github.com/kurtpeek/client-auth-test</a>):</p>
  
  <pre class="lang-golang prettyprint-override"><code>package main
  
  import (
      "crypto"
      "crypto/rand"
      "crypto/rsa"
      "crypto/sha1"
      "crypto/tls"
      "crypto/x509"
      "crypto/x509/pkix"
      "encoding/asn1"
      "encoding/pem"
      "io"
      "math/big"
      "net"
      "net/http"
      "net/http/httptest"
      "testing"
      "time"
  
      "github.com/stretchr/testify/assert"
      "github.com/stretchr/testify/require"
  )
  
  func TestDeviceFromTLS(t *testing.T) {
      deviceKeyPEM, csrPEM := generateKeyAndCSR(t)
  
      caKey, caKeyPEM := generateKey(t)
      caCert, caCertPEM := generateRootCert(t, caKey)
  
      deviceCertPEM := signCSR(t, csrPEM, caKey, caCert)
  
      serverCert, err := tls.X509KeyPair(caCertPEM, caKeyPEM)
      require.NoError(t, err)
  
      clientPool := x509.NewCertPool()
      clientPool.AddCert(caCert)
  
      ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
          assert.Len(t, r.TLS.PeerCertificates, 1)
      }))
      ts.TLS = &amp;tls.Config{
          Certificates: []tls.Certificate{serverCert},
          ClientAuth:   tls.RequireAndVerifyClientCert,
          ClientCAs:    clientPool,
      }
      ts.StartTLS()
      defer ts.Close()
  
      deviceCert, err := tls.X509KeyPair(deviceCertPEM, deviceKeyPEM)
      require.NoError(t, err)
  
      pool := x509.NewCertPool()
      pool.AddCert(caCert)
  
      client := ts.Client()
      client.Transport.(*http.Transport).TLSClientConfig = &amp;tls.Config{
          Certificates: []tls.Certificate{deviceCert},
          RootCAs:      pool,
      }
  
      req, err := http.NewRequest(http.MethodPut, ts.URL, nil)
      resp, err := client.Do(req)
      require.NoError(t, err)
      defer resp.Body.Close()
  
      assert.Exactly(t, http.StatusOK, resp.StatusCode)
  }
  
  func generateKeyAndCSR(t *testing.T) ([]byte, []byte) {
      rsaKey, err := rsa.GenerateKey(rand.Reader, 1024)
      require.NoError(t, err)
  
      key := pem.EncodeToMemory(&amp;pem.Block{
          Type:  "RSA PRIVATE KEY",
          Bytes: x509.MarshalPKCS1PrivateKey(rsaKey),
      })
  
      template := &amp;x509.CertificateRequest{
          Subject: pkix.Name{
              Country:      []string{"US"},
              Locality:     []string{"San Francisco"},
              Organization: []string{"Awesomeness, Inc."},
              Province:     []string{"California"},
          },
          SignatureAlgorithm: x509.SHA256WithRSA,
          IPAddresses:        []net.IP{net.ParseIP("127.0.0.1")},
      }
  
      req, err := x509.CreateCertificateRequest(rand.Reader, template, rsaKey)
      require.NoError(t, err)
  
      csr := pem.EncodeToMemory(&amp;pem.Block{
          Type:  "CERTIFICATE REQUEST",
          Bytes: req,
      })
  
      return key, csr
  }
  
  func generateRootCert(t *testing.T, key crypto.Signer) (*x509.Certificate, []byte) {
      subjectKeyIdentifier := calculateSubjectKeyIdentifier(t, key.Public())
  
      template := &amp;x509.Certificate{
          SerialNumber: generateSerial(t),
          Subject: pkix.Name{
              Organization: []string{"Awesomeness, Inc."},
              Country:      []string{"US"},
              Locality:     []string{"San Francisco"},
          },
          NotBefore:             time.Now(),
          NotAfter:              time.Now().AddDate(10, 0, 0),
          SubjectKeyId:          subjectKeyIdentifier,
          AuthorityKeyId:        subjectKeyIdentifier,
          KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
          ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
          BasicConstraintsValid: true,
          IsCA:                  true,
          MaxPathLenZero:        true,
      }
  
      der, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
      require.NoError(t, err)
  
      rootCert, err := x509.ParseCertificate(der)
      require.NoError(t, err)
  
      rootCertPEM := pem.EncodeToMemory(&amp;pem.Block{
          Type:  "CERTIFICATE",
          Bytes: der,
      })
  
      return rootCert, rootCertPEM
  }
  
  // generateSerial generates a serial number using the maximum number of octets (20) allowed by RFC 5280 4.1.2.2
  // (Adapted from https://github.com/cloudflare/cfssl/blob/828c23c22cbca1f7632b9ba85174aaa26e745340/signer/local/local.go#L407-L418)
  func generateSerial(t *testing.T) *big.Int {
      serialNumber := make([]byte, 20)
      _, err := io.ReadFull(rand.Reader, serialNumber)
      require.NoError(t, err)
  
      return new(big.Int).SetBytes(serialNumber)
  }
  
  // calculateSubjectKeyIdentifier implements a common method to generate a key identifier
  // from a public key, namely, by composing it from the 160-bit SHA-1 hash of the bit string
  // of the public key (cf. https://tools.ietf.org/html/rfc5280#section-4.2.1.2).
  // (Adapted from https://github.com/jsha/minica/blob/master/main.go).
  func calculateSubjectKeyIdentifier(t *testing.T, pubKey crypto.PublicKey) []byte {
      spkiASN1, err := x509.MarshalPKIXPublicKey(pubKey)
      require.NoError(t, err)
  
      var spki struct {
          Algorithm        pkix.AlgorithmIdentifier
          SubjectPublicKey asn1.BitString
      }
      _, err = asn1.Unmarshal(spkiASN1, &amp;spki)
      require.NoError(t, err)
  
      skid := sha1.Sum(spki.SubjectPublicKey.Bytes)
      return skid[:]
  }
  
  // signCSR signs a certificate signing request with the given CA certificate and private key
  func signCSR(t *testing.T, csr []byte, caKey crypto.Signer, caCert *x509.Certificate) []byte {
      block, _ := pem.Decode(csr)
      require.NotNil(t, block, "failed to decode CSR")
  
      certificateRequest, err := x509.ParseCertificateRequest(block.Bytes)
      require.NoError(t, err)
  
      require.NoError(t, certificateRequest.CheckSignature())
  
      template := x509.Certificate{
          Subject:               certificateRequest.Subject,
          PublicKeyAlgorithm:    certificateRequest.PublicKeyAlgorithm,
          PublicKey:             certificateRequest.PublicKey,
          SignatureAlgorithm:    certificateRequest.SignatureAlgorithm,
          Signature:             certificateRequest.Signature,
          SerialNumber:          generateSerial(t),
          Issuer:                caCert.Issuer,
          NotBefore:             time.Now(),
          NotAfter:              time.Now().AddDate(10, 0, 0),
          KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
          ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
          SubjectKeyId:          calculateSubjectKeyIdentifier(t, certificateRequest.PublicKey),
          BasicConstraintsValid: true,
          IPAddresses:           certificateRequest.IPAddresses,
      }
  
      derBytes, err := x509.CreateCertificate(rand.Reader, &amp;template, caCert, certificateRequest.PublicKey, caKey)
      require.NoError(t, err)
  
      return pem.EncodeToMemory(&amp;pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
  }
  
  // generateKey generates a 1024-bit RSA private key
  func generateKey(t *testing.T) (crypto.Signer, []byte) {
      key, err := rsa.GenerateKey(rand.Reader, 1024)
      require.NoError(t, err)
  
      keyPEM := pem.EncodeToMemory(&amp;pem.Block{
          Type:  "RSA PRIVATE KEY",
          Bytes: x509.MarshalPKCS1PrivateKey(key),
      })
  
      return key, keyPEM
  }
  </code></pre>
  
  <p>However, when I run it, I get the following error:</p>
  
  <pre><code>&gt; go test ./...
  2020/04/06 15:12:30 http: TLS handshake error from 127.0.0.1:58685: remote error: tls: bad certificate
  --- FAIL: TestDeviceFromTLS (0.05s)
      main_test.go:64: 
              Error Trace:    main_test.go:64
              Error:          Received unexpected error:
                              Put https://127.0.0.1:58684: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs
              Test:           TestDeviceFromTLS
  FAIL
  FAIL    github.com/kurtpeek/client-auth-test    0.379s
  </code></pre>
  
  <p>I'm not really sure what to make of the error message</p>
  
  <blockquote>
    <p>cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs</p>
  </blockquote>
  
  <p>because I am passing in the <code>IPAddresses</code> field when creating the certificate. Any ideas on what is wrong here?</p>