KubeMQ
Client SDKsKotlinHow-to guidesTLS

mTLS Setup

Configure mutual TLS (mTLS) for a KubeMQ Kotlin client with file-based or PEM-bytes certificates.

Overview

Standard TLS only proves the server's identity — the server itself accepts any client that knows the address and client ID. Mutual TLS (mTLS) closes that gap: the client also presents a certificate, so the server verifies who is connecting before accepting the connection. That matters on zero-trust networks and in regulated environments where "reaches the port" isn't an acceptable authorization model — the certificate becomes the credential.

The tls { } builder block's caCertFile, certFile, and keyFile (or the caCertPem / certPem / keyPem byte-array variants) wire in three artifacts at client construction: the CA certificate (to verify the server, same as one-way TLS) plus the client's own certificate and private key (for the server to verify in return). Verification happens during the handshake, before any messaging traffic flows.

Gotchas: the certificate and key must be a matched pair signed by a CA the server trusts — a mismatch fails the handshake outright; all three inputs must be valid, unexpired PEM, and expiry breaks connections with no warning; and the CA that signed the client cert isn't necessarily the CA that verifies the server — mixing them up causes "works with TLS, fails with mTLS" confusion.

Prerequisites

  • KubeMQ server running with mTLS enabled
  • Kotlin SDK installed (implementation("io.kubemq.sdk:kubemq-sdk-kotlin:1.0.1"))
  • CA certificate, client certificate, and client private key files

Code

MtlsSetupExample.kt
package io.kubemq.sdk.examples.tls

import io.kubemq.sdk.client.KubeMQClient
import kotlinx.coroutines.runBlocking

private const val ADDRESS = "localhost:50001"
private const val CLIENT_ID = "kotlin-tls-mtls-setup"
private const val CA_CERT_FILE = "/path/to/ca.pem"
private const val CLIENT_CERT_FILE = "/path/to/client.pem"
private const val CLIENT_KEY_FILE = "/path/to/client.key"

fun main() = runBlocking {
    connectWithMutualTls()
    connectWithMutualTlsFromPemBytes()
    println("mTLS setup examples completed.")
}

private fun connectWithMutualTls() {
    println("=== Mutual TLS (mTLS) Connection ===\n")

    // Create a client with mTLS (client cert + key + CA cert)
    try {
        val client = KubeMQClient.queues {
            address = ADDRESS
            clientId = CLIENT_ID
            tls {
                caCertFile = CA_CERT_FILE
                certFile = CLIENT_CERT_FILE
                keyFile = CLIENT_KEY_FILE
            }
        }
        println("mTLS client configured with file paths:")
        println("  CA: $CA_CERT_FILE")
        println("  Cert: $CLIENT_CERT_FILE")
        println("  Key: $CLIENT_KEY_FILE")
        // Uncomment when mTLS server is available:
        // client.use { println("Connected: ${it.ping().host}") }
        client.close()
    } catch (e: Exception) {
        println("mTLS connection failed: ${e.message}")
    }
}

private fun connectWithMutualTlsFromPemBytes() {
    println("\n=== Mutual TLS from PEM bytes ===\n")

    // Load certs from PEM bytes instead of files
    val caBytes = "-----BEGIN CERTIFICATE-----\n... CA cert ...\n-----END CERTIFICATE-----".toByteArray()
    val certBytes = "-----BEGIN CERTIFICATE-----\n... client cert ...\n-----END CERTIFICATE-----".toByteArray()
    val keyBytes = "-----BEGIN PRIVATE KEY-----\n... client key ...\n-----END PRIVATE KEY-----".toByteArray()

    try {
        val client = KubeMQClient.queues {
            address = ADDRESS
            clientId = "$CLIENT_ID-pem"
            tls {
                caCertPem = caBytes
                certPem = certBytes
                keyPem = keyBytes
            }
        }
        println("mTLS client configured with PEM bytes.")
        // Uncomment when mTLS server is available:
        // client.use { println("Connected: ${it.ping().host}") }
        client.close()
    } catch (e: Exception) {
        println("mTLS PEM connection failed: ${e.message}")
    }
}

How It Works

  • mTLS requires three components: CA certificate, client certificate, and client private key.
  • File-based configuration: Use caCertFile, certFile, and keyFile with file paths.
  • PEM-bytes configuration: Use caCertPem, certPem, and keyPem with byte arrays loaded from any source.
  • The PEM-bytes approach is useful when certificates come from secrets managers, environment variables, or Kubernetes secrets.
  • Both client and server verify each other's certificates for mutual authentication.

Was this page helpful?

On this page