In modernen verteilten Systemen ist die Absicherung der Kommunikation zwischen Diensten ein zentrales Anliegen. Während HTTPS eine Einweg-Authentifizierung (Client vertraut dem Server) bietet, ermöglicht mutual TLS (mTLS) eine beidseitige Authentifizierung, bei der auch der Server den Client authentifiziert. Dies erhöht die Sicherheit erheblich – besonders in Microservice-Architekturen oder bei hochsensiblen Anwendungen.
Dieser Artikel zeigt, wie mTLS in einer Spring Boot-Anwendung für REST-Clients integriert wird, und erläutert dabei die notwendigen Schritte, Konfigurationsoptionen und typische Stolperfallen.
Was ist mTLS?
Mutual TLS (mTLS) ist eine Erweiterung des TLS-Protokolls, bei der beide Kommunikationspartner ein X.509-Zertifikat vorweisen müssen, um sich gegenseitig zu identifizieren. Während klassisches TLS (wie bei HTTPS-Webseiten) nur das Server-Zertifikat prüft, verlangt mTLS, dass auch der Client ein gültiges Zertifikat vorweist.
Vorteile von mTLS:
- Beidseitige Authentifizierung
- Keine Notwendigkeit für Benutzernamen/Passwörter
- Schutz gegen Man-in-the-Middle-Angriffe
- Integrität der übermittelten Daten durch TLS
Anwendungsfall
In diesem Beispiel betreiben wir eine Spring Boot REST-Client-Anwendung, die mit einem geschützten mTLS-gesicherten REST-Endpunkt kommunizieren möchte. Die Gegenstelle verlangt, dass sich der Client mit einem Zertifikat authentifiziert.
Voraussetzungen
- Java 17 oder höher
- Spring Boot (ab Version 2.7 oder 3.x)
- Eigene Zertifikate für Client und Server
- Zugriff auf den Serverzertifikat-Truststore
Schritt 1: Erstellen der Zertifikate (für Testzwecke)
Für Testzwecke können Zertifikate mit OpenSSL erstellt werden.
# Root CA
openssl req -x509 -new -nodes -keyout rootCA.key -out rootCA.crt -days 3650 -subj "/CN=MyRootCA"
# Server-Zertifikat
openssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr -subj "/CN=localhost"
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -set_serial 01 -out server.crt -days 365
# Client-Zertifikat
openssl req -newkey rsa:2048 -nodes -keyout client.key -out client.csr -subj "/CN=my-client"
openssl x509 -req -in client.csr -CA rootCA.crt -CAkey rootCA.key -set_serial 02 -out client.crt -days 365
Code-Sprache: PHP (php)
Wandle die Zertifikate in ein Java KeyStore-Format (.p12 oder .jks):
# Client keystore (enthält private key + client cert)
openssl pkcs12 -export -in client.crt -inkey client.key -out client-keystore.p12 -name client -CAfile rootCA.crt -caname root -passout pass:changeit
# Truststore (mit Root-CA des Servers)
keytool -importcert -file rootCA.crt -alias rootCA -keystore truststore.p12 -storepass changeit -storetype PKCS12 -noprompt
Code-Sprache: CSS (css)
Schritt 2: Konfiguration des REST-Clients
Spring Boot verwendet intern typischerweise den RestTemplate
oder den moderneren WebClient
. Beide können für mTLS konfiguriert werden.
Beispiel mit RestTemplate
:
@Bean
public RestTemplate restTemplate() throws Exception {
char[] keyStorePassword = "changeit".toCharArray();
KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (InputStream keyStoreStream = new FileInputStream("client-keystore.p12")) {
keyStore.load(keyStoreStream, keyStorePassword);
}
KeyStore trustStore = KeyStore.getInstance("PKCS12");
try (InputStream trustStoreStream = new FileInputStream("truststore.p12")) {
trustStore.load(trustStoreStream, keyStorePassword);
}
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(keyStore, keyStorePassword)
.loadTrustMaterial(trustStore, null)
.build();
HttpClient httpClient = HttpClients.custom()
.setSSLContext(sslContext)
.build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
return new RestTemplate(factory);
}
Code-Sprache: PHP (php)
🔐 Achte darauf, Zertifikat und Truststore niemals hart im Code zu hinterlegen. Nutze z. B.
application.properties
.
Beispiel mit WebClient
:
@Bean
public WebClient webClient() throws Exception {
char[] password = "changeit".toCharArray();
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream("client-keystore.p12"), password);
KeyStore trustStore = KeyStore.getInstance("PKCS12");
trustStore.load(new FileInputStream("truststore.p12"), password);
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(keyStore, password)
.loadTrustMaterial(trustStore, null)
.build();
HttpClient httpClient = HttpClients.custom()
.setSSLContext(sslContext)
.build();
ClientHttpConnector connector = new HttpComponentsClientHttpConnector(httpClient);
return WebClient.builder()
.clientConnector(connector)
.build();
}
Code-Sprache: PHP (php)
Schritt 3: Nutzung des REST-Clients
Sobald der Client konfiguriert ist, kannst du ihn wie gewohnt verwenden:
@Autowired
private RestTemplate restTemplate;
public void callSecureEndpoint() {
String url = "https://secure-api.internal.example.com/data";
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
System.out.println("Antwort: " + response.getBody());
}
Code-Sprache: JavaScript (javascript)
Tipps und Best Practices
🔄 Zertifikatrotation
Zertifikate sollten regelmäßig erneuert werden. Verwende möglichst kurze Gültigkeitszeiträume und automatisierte Erneuerung z. B. via HashiCorp Vault oder cert-manager.
🧪 Testumgebung
Für lokale Tests kann ein mTLS-Server mit Tools wie mitmproxy, MockServer oder selbstgebauter Spring Boot REST-API mit aktiviertem mTLS genutzt werden.
🧰 Logging aktivieren
Bei Problemen mit TLS hilft das Aktivieren von Debug-Logs:
-Djavax.net.debug=ssl:handshake
So kannst du genau sehen, welche Zertifikate verwendet werden, ob die Gegenstelle das Client-Zertifikat akzeptiert etc.
Fehlersuche (Troubleshooting)
javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
→ Der Server akzeptiert das Client-Zertifikat nicht. Stelle sicher, dass der Server die CA des Clients vertraut.
SSLHandshakeException: PKIX path building failed
→ Der Client kann dem Server-Zertifikat nicht vertrauen. Überprüfe den Truststore und dessen Inhalte.
KeyStoreException: problem accessing keystore
→ Falsches Passwort oder beschädigte Datei. Stelle sicher, dass Pfad und Passwort korrekt sind.
Fazit
Mutual TLS bietet eine starke, zertifikatsbasierte Authentifizierung zwischen REST-Diensten und ist besonders für hochsichere oder interne Service-Kommunikation sinnvoll. In Spring Boot ist die Integration für REST-Clients dank der flexiblen HTTP-Client-Infrastruktur relativ einfach umsetzbar – erfordert jedoch sorgfältiges Management von Zertifikaten und Keystores.
Wer regelmäßig mit sensiblen Daten oder internen APIs arbeitet, sollte mTLS ernsthaft in Betracht ziehen – nicht nur als Sicherheitsmaßnahme, sondern auch als professionellen Standard für Service-to-Service-Kommunikation.