Gatling’s TLS and certificate handling can be surprisingly flexible, allowing you to bypass strict validation for testing purposes or to simulate real-world scenarios with custom certificate authorities.

Let’s see how it works with a live example. Imagine you’re testing an API that uses a self-signed certificate or one issued by an internal CA. Your standard Gatling simulation would fail with an SSLHandshakeException.

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._

class TlsSimulation extends Simulation {

  val httpProtocol = http
    .baseUrl("https://your-internal-api.local")
    .doNotTrackIds

  val scn = scenario("TLS Test")
    .exec(http("GET Root")
      .get("/")
      .check(status.is(200)))

  setUp(scn.inject(atOnceUsers(1)))
    .protocols(httpProtocol)
}

Running this would likely result in an error like:

javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

To overcome this, Gatling leverages the underlying JVM’s SSL context. You can configure it directly within the httpProtocol.

The most common way to handle untrusted certificates in a test environment is to simply disable SSL verification. This is highly discouraged for production environments but is invaluable for testing internal services with self-signed certificates or when you haven’t yet configured your production certificates.

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._

class TlsSimulationDisabledVerification extends Simulation {

  val httpProtocol = http
    .baseUrl("https://your-internal-api.local")
    .doNotTrackIds
    .disableUrlSelector // Added to ensure consistent behavior across versions
    .trustManagerType("X509") // Explicitly set trust manager type
    .sslTrustStorePath("/dev/null") // Point to a non-existent path to effectively disable trust store checks
    .sslTrustStorePassword(None) // No password needed for a non-existent store

  val scn = scenario("TLS Test")
    .exec(http("GET Root")
      .get("/")
      .check(status.is(200)))

  setUp(scn.inject(atOnceUsers(1)))
    .protocols(httpProtocol)
}

Here, sslTrustStorePath("/dev/null") and sslTrustStorePassword(None) are the key. By providing a path to a non-existent trust store and no password, you’re instructing the JVM to effectively ignore any trust store validation. The trustManagerType("X509") is often implied but good to be explicit.

For more controlled testing, you might need to trust a specific certificate authority (CA) that isn’t in the default JVM trust store. This is common when your internal services use certificates signed by your company’s private CA.

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._

class TlsSimulationCustomCA extends Simulation {

  // Ensure your custom CA certificate is in PEM format and accessible
  // Example: openssl x509 -in my_custom_ca.crt -out custom_ca.pem
  // You might need to convert CRT to PEM if it's not already.

  val httpProtocol = http
    .baseUrl("https://your-internal-api.local")
    .doNotTrackIds
    .disableUrlSelector
    .sslTrustStorePath("src/test/resources/custom_ca.pem") // Path to your custom CA certificate file
    .sslTrustStorePassword(None) // If your CA cert doesn't have a password, or you're using a file and not a JKS/PKCS12 store

  val scn = scenario("TLS Test")
    .exec(http("GET Root")
      .get("/")
      .check(status.is(200)))

  setUp(scn.inject(atOnceUsers(1)))
    .protocols(httpProtocol)
}

In this case, sslTrustStorePath("src/test/resources/custom_ca.pem") points to the .pem file containing your custom CA’s public certificate. Gatling (via the JVM) will then trust any certificate signed by this CA. If your trust store is a Java KeyStore (JKS) or PKCS12 file, you’d use sslTrustStorePath("path/to/your/truststore.jks") and sslTrustStorePassword("yourpassword").

You can also configure the client certificate used for mutual TLS (mTLS) authentication. This is where the client (Gatling) presents its own certificate to the server for authentication.

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._

class TlsSimulationMtls extends Simulation {

  // Ensure your client certificate and private key are in PEM format
  // Example: cat client.crt client.key > client_bundle.pem

  val httpProtocol = http
    .baseUrl("https://your-internal-api.local")
    .doNotTrackIds
    .disableUrlSelector
    .sslClientAuth.certificate("src/test/resources/client_bundle.pem") // Path to your bundled client cert and key
    .sslClientAuth.password("yourclientpassword") // Password for your client private key, if any

  val scn = scenario("TLS Test")
    .exec(http("GET Root")
      .get("/")
      .check(status.is(200)))

  setUp(scn.inject(atOnceUsers(1)))
    .protocols(httpProtocol)
}

Here, sslClientAuth.certificate("src/test/resources/client_bundle.pem") specifies the file containing your client’s certificate and its private key. If the private key is password-protected, you provide that password via sslClientAuth.password("yourclientpassword"). The certificate and key are often bundled together in a single PEM file for simplicity.

It’s important to note that Gatling’s TLS configuration directly maps to the underlying javax.net.ssl.SSLContext and TrustManager/KeyManager implementations in Java. When you specify sslTrustStorePath or sslClientAuth.certificate, Gatling is essentially creating and configuring these Java SSL components. This means you can use any certificate format that the JVM’s default SSL providers can handle, including JKS, PKCS12, and PEM files. The key is to ensure the file paths are correct and any necessary passwords are provided.

The next hurdle you might encounter is dealing with specific TLS versions or cipher suites, which can be configured using sslProtocol and sslEnabledProtocols within the httpProtocol.

Want structured learning?

Take the full Gatling course →