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:
go get -u github.com/julienschmidt/httprouter
Then, 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.
package main
import (
"crypto/tls"
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
)
// CertificateAuthMiddleware verifies the client certificate
func CertificateAuthMiddleware(next httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
// Retrieve the client certificate from the TLS connection
clientCert := r.TLS.PeerCertificates
if len(clientCert) == 0 {
http.Error(w, "Client certificate required", http.StatusUnauthorized)
return
}
// Log client certificate information (can be extended further)
fmt.Println("Client certificate subject:", clientCert[0].Subject)
// If the certificate is valid, proceed to the next handler
next(w, r, ps)
}
}
func main() {
// Create a router
router := httprouter.New()
// Add middleware
router.GET("/secure", CertificateAuthMiddleware(func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
w.Write([]byte("You have access to the secure resource!"))
}))
// Set up the server with TLS
server := &http.Server{
Addr: ":8080",
Handler: router,
TLSConfig: &tls.Config{MinVersion: tls.VersionTLS13},
}
// Run the HTTPS server
log.Println("Server running at https://localhost:8080")
err := server.ListenAndServeTLS("certs/server.crt", "certs/server.key")
if err != nil {
log.Fatal("ListenAndServeTLS failed: ", err)
}
}
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
:
package main
import (
"crypto/tls"
"net/http"
"net/http/httptest"
"testing"
)
// TestCertificateAuthMiddleware tests SSL/TLS middleware
func TestCertificateAuthMiddleware(t *testing.T) {
// Create a test router
router := httprouter.New()
router.GET("/secure", CertificateAuthMiddleware(func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
w.Write([]byte("You have access to the secure resource!"))
}))
// Create a test server
server := httptest.NewServer(router)
defer server.Close()
// Make an HTTPS request to the test server without a client certificate
req, err := http.NewRequest("GET", server.URL+"/secure", nil)
if err != nil {
t.Fatalf("Error creating request: %v", err)
}
// Test that the server denies access without a certificate
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatalf("Error making request: %v", err)
}
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("Expected status %v, got %v", http.StatusUnauthorized, resp.StatusCode)
}
}
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:
openssl req -x509 -newkey rsa:4096 -keyout certs/server.key -out certs/server.crt -days 365
Then, run the application using:
go run main.go
To execute the unit test, run:
go test -v
5. 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: