Skip to content
Apache Kafka kf security 4 min read

TLS Encryption

By default, Kafka sends every byte — records, keys, metadata, and credentials — across the network in plaintext. Any party who can observe traffic between a producer, a broker, or a consumer can read your data and potentially steal authentication secrets. TLS (still called “SSL” throughout Kafka’s configuration keys) encrypts those connections, verifies broker identity, and optionally authenticates clients via mutual TLS (mTLS). This page walks through generating the certificate stores, configuring an SSL listener on the broker, and connecting plain and Spring Boot clients.

How TLS works in Kafka

A TLS connection relies on two stores. A keystore holds an entity’s private key and signed certificate — this is what the entity presents to prove who it is. A truststore holds the certificate authority (CA) certificates an entity trusts; it is used to validate the certificate the other side presents.

For one-way TLS (the common minimum), only the broker needs a keystore, and every client needs a truststore containing the CA that signed the broker’s certificate. For mutual TLS, clients also present their own keystore and the broker validates it against its truststore — this gives you encryption and client authentication in one mechanism.

Kafka uses ssl.* and security.protocol=SSL regardless of whether you think of it as TLS. The names are historical; the protocol negotiated is modern TLS 1.2/1.3.

Generating keystores and truststores

Use a single CA to sign the broker certificate, then build the stores with keytool and openssl. The example below creates a CA, a broker keystore, and a shared truststore.

# 1. Create a self-signed CA
openssl req -new -x509 -keyout ca-key -out ca-cert -days 3650 \
  -subj "/CN=Kafka-CA" -nodes

# 2. Generate the broker key pair in a keystore
keytool -keystore broker.keystore.jks -alias broker -validity 3650 \
  -genkey -keyalg RSA -storepass changeit -keypass changeit \
  -dname "CN=kafka-broker-1, O=DevCraftly" \
  -ext SAN=DNS:kafka-broker-1,DNS:localhost

# 3. Create a signing request, sign it with the CA, import results
keytool -keystore broker.keystore.jks -alias broker -certreq -file broker.csr \
  -storepass changeit
openssl x509 -req -CA ca-cert -CAkey ca-key -in broker.csr -out broker-signed.crt \
  -days 3650 -CAcreateserial
keytool -keystore broker.keystore.jks -alias CARoot -import -file ca-cert \
  -storepass changeit -noprompt
keytool -keystore broker.keystore.jks -alias broker -import -file broker-signed.crt \
  -storepass changeit -noprompt

# 4. Build a truststore that trusts the CA
keytool -keystore client.truststore.jks -alias CARoot -import -file ca-cert \
  -storepass changeit -noprompt

The certificate’s Subject Alternative Name (SAN) must match the hostname clients use to connect. A mismatch produces No subject alternative names matching ... found, the single most common TLS setup failure.

Configuring the broker

Add an SSL listener to server.properties and point Kafka at the broker keystore. To encrypt inter-broker traffic as well, set inter.broker.listener.name to the SSL listener.

# server.properties (KRaft mode)
listeners=SSL://0.0.0.0:9093,CONTROLLER://0.0.0.0:9094
advertised.listeners=SSL://kafka-broker-1:9093
listener.security.protocol.map=SSL:SSL,CONTROLLER:PLAINTEXT
inter.broker.listener.name=SSL

ssl.keystore.location=/etc/kafka/secrets/broker.keystore.jks
ssl.keystore.password=changeit
ssl.key.password=changeit
ssl.truststore.location=/etc/kafka/secrets/client.truststore.jks
ssl.truststore.password=changeit

# Require client certificates (mutual TLS). Use "requested" or "none" to relax.
ssl.client.auth=required
ssl.enabled.protocols=TLSv1.3,TLSv1.2

Setting ssl.client.auth=required turns on mTLS: clients without a valid certificate are rejected at the TLS handshake before any Kafka request is processed.

Connecting a plain Java client

A consumer or producer needs security.protocol=SSL and a truststore. For mTLS, add the client keystore properties too.

Properties props = new Properties();
props.put("bootstrap.servers", "kafka-broker-1:9093");
props.put("security.protocol", "SSL");
props.put("ssl.truststore.location", "/etc/kafka/secrets/client.truststore.jks");
props.put("ssl.truststore.password", "changeit");

// Mutual TLS: present the client's own certificate
props.put("ssl.keystore.location", "/etc/kafka/secrets/client.keystore.jks");
props.put("ssl.keystore.password", "changeit");
props.put("ssl.key.password", "changeit");

props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

try (var producer = new KafkaProducer<String, String>(props)) {
    producer.send(new ProducerRecord<>("orders", "k1", "encrypted-payload")).get();
    System.out.println("Sent record over TLS");
}

Output:

Sent record over TLS

You can verify the encrypted handshake from the command line before wiring up application code:

openssl s_client -connect kafka-broker-1:9093 -showcerts </dev/null

Output:

CONNECTED(00000003)
depth=1 CN = Kafka-CA
verify return:1
...
SSL handshake has read 2456 bytes and written 412 bytes
Verification: OK
Protocol  : TLSv1.3

Configuring Spring Boot

Spring for Apache Kafka exposes the same keys under spring.kafka.ssl and spring.kafka.properties. The security.protocol lives under properties since it applies to both producer and consumer factories.

spring:
  kafka:
    bootstrap-servers: kafka-broker-1:9093
    properties:
      security.protocol: SSL
    ssl:
      trust-store-location: file:/etc/kafka/secrets/client.truststore.jks
      trust-store-password: changeit
      # Mutual TLS — omit these for one-way TLS
      key-store-location: file:/etc/kafka/secrets/client.keystore.jks
      key-store-password: changeit
      key-password: changeit

With this in place, the auto-configured KafkaTemplate and @KafkaListener containers connect over TLS with no Java code changes. Use Spring records for your event payloads as usual.

public record OrderPlaced(String orderId, BigDecimal total) {}

SSL configuration reference

PropertySidePurpose
security.protocol=SSLclientSelects the encrypted protocol
ssl.truststore.locationbothCA certs used to validate the peer
ssl.keystore.locationbroker / mTLS clientPrivate key + signed cert presented
ssl.key.passwordbroker / mTLS clientPassword protecting the private key
ssl.client.authbrokernone, requested, or required (mTLS)
ssl.enabled.protocolsbothAllowed TLS versions, e.g. TLSv1.3
ssl.endpoint.identification.algorithmclienthttps (default) enforces hostname checks

Best Practices

  • Use a single internal CA to sign all broker and client certificates so you can rotate trust by replacing one CA cert in every truststore.
  • Always include correct SAN entries and keep ssl.endpoint.identification.algorithm=https enabled — disabling hostname verification reopens the door to man-in-the-middle attacks.
  • Restrict TLS to TLSv1.3,TLSv1.2 and remove legacy protocols and weak cipher suites.
  • Store keystore and truststore passwords in a secrets manager or mount them as files, never in source control or plain server.properties checked into Git.
  • Track certificate expiry and automate renewal; an expired broker certificate takes the whole cluster offline for clients.
  • Encrypt inter-broker traffic by setting inter.broker.listener.name to your SSL listener, not just the client-facing one.
  • Prefer mTLS (ssl.client.auth=required) when you want encryption and authentication together; pair it with ACLs for full authorization.
Last updated June 1, 2026
Was this helpful?