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.*andsecurity.protocol=SSLregardless 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
| Property | Side | Purpose |
|---|---|---|
security.protocol=SSL | client | Selects the encrypted protocol |
ssl.truststore.location | both | CA certs used to validate the peer |
ssl.keystore.location | broker / mTLS client | Private key + signed cert presented |
ssl.key.password | broker / mTLS client | Password protecting the private key |
ssl.client.auth | broker | none, requested, or required (mTLS) |
ssl.enabled.protocols | both | Allowed TLS versions, e.g. TLSv1.3 |
ssl.endpoint.identification.algorithm | client | https (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=httpsenabled — disabling hostname verification reopens the door to man-in-the-middle attacks. - Restrict TLS to
TLSv1.3,TLSv1.2and 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.propertieschecked 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.nameto 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.