17 Creating Certificate-based Authentication (SSL/TLS) Middleware in Go
In this article, we will learn how to create middleware for certificate-based authentication (SSL/TLS) using the httprouter library in Go. SSL/TLS is a widely used protocol for enhancing internet communication security, ensuring that only authorized parties can communicate with the server. This middleware will help us verify whether a client provides a valid certificate before accessing an endpoint.
What is Certificate-based Authentication?
Certificate-based authentication is a method of authentication that uses digital certificates to verify a client’s identity. These certificates typically contain a public key that encrypts communication between the client and the server, ensuring that only authorized clients can communicate securely.
SSL (Secure Socket Layer) and TLS (Transport Layer Security) are the main protocols used to secure network communications. By using certificates, the server can confirm that the client holds a valid certificate, reducing the risk of attacks like Man-in-the-Middle (MITM).
Steps to Create Middleware
To build this middleware, we will use the following components:
httprouter: A lightweight and fast HTTP router library in Go.crypto/tls: A package for handling certificates and TLS communication.net/http: To create an HTTP server in Go.
1. Installation and Project Setup
First, install the httprouter library by running the following command in your terminal:
1go get -u github.com/julienschmidt/httprouterThen, create a Go file named main.go to set up the HTTP server and middleware.
2. Creating SSL/TLS Middleware
This middleware will verify the client certificate. If the certificate is valid, the request proceeds to the next handler; otherwise, it responds with an HTTP 403 Forbidden status.
1package main
2
3import (
4 "crypto/tls"
5 "fmt"
6 "log"
7 "net/http"
8
9 "github.com/julienschmidt/httprouter"
10)
11
12// CertificateAuthMiddleware verifies the client certificate
13func CertificateAuthMiddleware(next httprouter.Handle) httprouter.Handle {
14 return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
15 // Retrieve the client certificate from the TLS connection
16 clientCert := r.TLS.PeerCertificates
17 if len(clientCert) == 0 {
18 http.Error(w, "Client certificate required", http.StatusUnauthorized)
19 return
20 }
21
22 // Log client certificate information (can be extended further)
23 fmt.Println("Client certificate subject:", clientCert[0].Subject)
24
25 // If the certificate is valid, proceed to the next handler
26 next(w, r, ps)
27 }
28}
29
30func main() {
31 // Create a router
32 router := httprouter.New()
33
34 // Add middleware
35 router.GET("/secure", CertificateAuthMiddleware(func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
36 w.Write([]byte("You have access to the secure resource!"))
37 }))
38
39 // Set up the server with TLS
40 server := &http.Server{
41 Addr: ":8080",
42 Handler: router,
43 TLSConfig: &tls.Config{MinVersion: tls.VersionTLS13},
44 }
45
46 // Run the HTTPS server
47 log.Println("Server running at https://localhost:8080")
48 err := server.ListenAndServeTLS("certs/server.crt", "certs/server.key")
49 if err != nil {
50 log.Fatal("ListenAndServeTLS failed: ", err)
51 }
52}3. Unit Testing the Middleware
Next, let’s write unit tests to ensure our middleware functions correctly.
Create a file main_test.go to write test cases using testing and net/http/httptest:
1package main
2
3import (
4 "crypto/tls"
5 "net/http"
6 "net/http/httptest"
7 "testing"
8)
9
10// TestCertificateAuthMiddleware tests SSL/TLS middleware
11func TestCertificateAuthMiddleware(t *testing.T) {
12 // Create a test router
13 router := httprouter.New()
14 router.GET("/secure", CertificateAuthMiddleware(func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
15 w.Write([]byte("You have access to the secure resource!"))
16 }))
17
18 // Create a test server
19 server := httptest.NewServer(router)
20 defer server.Close()
21
22 // Make an HTTPS request to the test server without a client certificate
23 req, err := http.NewRequest("GET", server.URL+"/secure", nil)
24 if err != nil {
25 t.Fatalf("Error creating request: %v", err)
26 }
27
28 // Test that the server denies access without a certificate
29 resp, err := http.DefaultClient.Do(req)
30 if err != nil {
31 t.Fatalf("Error making request: %v", err)
32 }
33
34 if resp.StatusCode != http.StatusUnauthorized {
35 t.Errorf("Expected status %v, got %v", http.StatusUnauthorized, resp.StatusCode)
36 }
37}4. Running the Server and Tests
Before running the server, ensure you have a valid certificate in the certs/ directory. If not, you can generate a self-signed certificate for testing purposes using OpenSSL:
1openssl req -x509 -newkey rsa:4096 -keyout certs/server.key -out certs/server.crt -days 365Then, run the application using:
1go run main.goTo execute the unit test, run:
1go test -v5. Conclusion
In this article, we have learned how to create middleware for Certificate-based Authentication using Go with the httprouter library. We also created unit tests to ensure the middleware functions correctly. By understanding this concept, you can add an extra layer of security to your application, particularly for APIs that require certificate-based authentication.
For more Go tutorials and HTTP router usage, check out these related articles: