Eine Einführung in Java SASL

Java Top

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs

1. Übersicht

In diesem Tutorial werden die Grundlagen der einfachen Authentifizierung und der Sicherheitsschicht (SASL) erläutert. Wir werden verstehen, wie Java die Einführung von SASL zur Sicherung der Kommunikation unterstützt.

Dabei verwenden wir eine einfache Client- und Serverkommunikation, um sie mit SASL zu sichern.

2. Was ist SASL ?

SASL ist ein Framework für Authentifizierung und Datensicherheit in Internetprotokollen . Ziel ist es, Internetprotokolle von bestimmten Authentifizierungsmechanismen zu entkoppeln. Wir werden Teile dieser Definition im Laufe der Zeit besser verstehen.

Das Bedürfnis nach Sicherheit in der Kommunikation ist implizit. Versuchen wir dies im Kontext der Client- und Serverkommunikation zu verstehen . In der Regel tauschen Client und Server Daten über das Netzwerk aus. Es ist unbedingt erforderlich, dass beide Parteien sich gegenseitig vertrauen und Daten sicher senden können.

2.1. Wo passt SASL hin ?

In einer Anwendung können wir SMTP verwenden, um E-Mails zu senden, und LDAP verwenden, um auf Verzeichnisdienste zuzugreifen. Jedes dieser Protokolle unterstützt jedoch möglicherweise einen anderen Authentifizierungsmechanismus wie Digest-MD5 oder Kerberos.

Was wäre, wenn es für Protokolle eine Möglichkeit gäbe, Authentifizierungsmechanismen deklarativer auszutauschen? Genau hier kommt SASL ins Spiel. Protokolle, die SASL unterstützen, können ausnahmslos jeden der SASL-Mechanismen unterstützen.

Daher können Anwendungen einen geeigneten Mechanismus aushandeln und diesen für die Authentifizierung und sichere Kommunikation übernehmen.

2.2. Wie funktioniert SASL ?

Nachdem wir gesehen haben, wo SASL in das allgemeine Sicherheitsschema passt, wollen wir verstehen, wie es funktioniert.

SASL ist ein Challenge-Response-Framework . Hier gibt der Server eine Herausforderung an den Client aus, und der Client sendet eine Antwort basierend auf der Herausforderung. Die Herausforderung und Antwort sind Bytearrays beliebiger Länge und können daher beliebige mechanismusspezifische Daten enthalten.

Dieser Austausch kann für mehrere Iterationen fortgesetzt werden und endet schließlich, wenn der Server keine weitere Herausforderung ausgibt.

Darüber hinaus können Client und Server nach der Authentifizierung eine Sicherheitsschicht aushandeln. Die gesamte nachfolgende Kommunikation kann dann diese Sicherheitsschicht nutzen. Beachten Sie jedoch, dass einige der Mechanismen möglicherweise nur die Authentifizierung unterstützen.

Es ist wichtig zu verstehen, dass SASL nur einen Rahmen für den Austausch von Herausforderungs- und Antwortdaten bietet . Es wird nichts über die Daten selbst oder deren Austausch erwähnt. Diese Details bleiben den Anwendungen überlassen, die SASL verwenden.

3. SASL-Unterstützung in Java

In Java gibt es APIs, die die Entwicklung von clientseitigen und serverseitigen Anwendungen mit SASL unterstützen. Die API ist nicht von den tatsächlichen Mechanismen selbst abhängig. Anwendungen, die die Java SASL-API verwenden, können einen Mechanismus basierend auf den erforderlichen Sicherheitsfunktionen auswählen.

3.1. Java SASL API

Die wichtigsten Schnittstellen, die im Rahmen des Pakets „javax.security.sasl“ zu beachten sind, sind SaslServer und SaslClient .

SaslServer repräsentiert den serverseitigen Mechanismus von SASL.

Mal sehen, wie wir einen SaslServer instanziieren können :

SaslServer ss = Sasl.createSaslServer( mechanism, protocol, serverName, props, callbackHandler);

Wir verwenden die Factory-Klasse Sasl, um SaslServer zu instanziieren . Die Methode createSaslServer akzeptiert mehrere Parameter:

  • Mechanismus - Der IANA-registrierte Name eines von SASL unterstützten Mechanismus
  • Protokoll - Der Name des Protokolls, für das die Authentifizierung durchgeführt wird
  • Servername - Der vollständig qualifizierte Hostname des Servers
  • Requisiten - Eine Reihe von Eigenschaften, die zum Konfigurieren des Authentifizierungsaustauschs verwendet werden
  • callbackHandler - Ein Callback-Handler, der vom ausgewählten Mechanismus verwendet wird, um weitere Informationen abzurufen

Von den oben genannten sind nur die ersten beiden obligatorisch, und der Rest ist nullbar.

SaslClient repräsentiert den clientseitigen Mechanismus von SASL. Mal sehen, wie wir einen SaslClient instanziieren können :

SaslClient sc = Sasl.createSaslClient( mechanisms, authorizationId, protocol, serverName, props, callbackHandler);

Auch hier verwenden wir die Factory-Klasse Sasl , um unseren SaslClient zu instanziieren . Die Liste der Parameter, die createSaslClient akzeptiert, ist fast dieselbe wie zuvor.

Es gibt jedoch einige subtile Unterschiede:

  • Mechanismen - hier ist eine Liste von Mechanismen, von denen aus versucht werden kann
  • authorisationId - Dies ist eine protokollabhängige Identifikation, die für die Autorisierung verwendet werden soll

Die übrigen Parameter haben eine ähnliche Bedeutung und Option.

3.2. Java SASL-Sicherheitsanbieter

Unter der Java SASL-API befinden sich die eigentlichen Mechanismen, die die Sicherheitsfunktionen bereitstellen. Die Implementierung dieser Mechanismen wird von Sicherheitsanbietern bereitgestellt, die bei der Java Cryptography Architecture (JCA) registriert sind.

Es können mehrere Sicherheitsanbieter bei der JCA registriert sein. Jeder dieser Mechanismen kann einen oder mehrere der SASL-Mechanismen unterstützen .

Java wird mit SunSASL als Sicherheitsanbieter ausgeliefert, der standardmäßig als JCA-Anbieter registriert wird. Dies kann jedoch bei anderen verfügbaren Anbietern entfernt oder neu angeordnet werden.

Darüber hinaus ist es immer möglich, einen benutzerdefinierten Sicherheitsanbieter bereitzustellen . Dazu müssen wir die Schnittstellen SaslClient und SaslServer implementieren . Auf diese Weise können wir auch unseren benutzerdefinierten Sicherheitsmechanismus implementieren!

4. SASL anhand eines Beispiels

Nachdem wir gesehen haben, wie ein SaslServer und ein SaslClient erstellt werden , ist es Zeit zu verstehen, wie man sie verwendet. Wir werden Client- und Serverkomponenten entwickeln. Diese tauschen Herausforderung und Antwort iterativ aus, um eine Authentifizierung zu erreichen. In unserem einfachen Beispiel verwenden wir hier den DIGEST-MD5-Mechanismus.

4.1. Client und Server CallbackHandler

Wie wir bereits gesehen haben, brauchen wir Implementierungen bieten Callbackhandler zu SaslServer und SaslClient . Nun Callbackhandler ist eine einfache Schnittstelle , die eine einzige Methode definiert - Griff . Diese Methode akzeptiert ein Array von Rückrufen .

Hier bietet Callback eine Möglichkeit für den Sicherheitsmechanismus, Authentifizierungsdaten von der aufrufenden Anwendung zu erfassen . Beispielsweise kann ein Sicherheitsmechanismus einen Benutzernamen und ein Kennwort erfordern. Es stehen einige Callback- Implementierungen wie NameCallback und PasswordCallback zur Verfügung.

Lassen Sie uns zunächst sehen, wie wir einen CallbackHandler für den Server definieren können :

public class ServerCallbackHandler implements CallbackHandler { @Override public void handle(Callback[] cbs) throws IOException, UnsupportedCallbackException { for (Callback cb : cbs) { if (cb instanceof AuthorizeCallback) { AuthorizeCallback ac = (AuthorizeCallback) cb; //Perform application-specific authorization action ac.setAuthorized(true); } else if (cb instanceof NameCallback) { NameCallback nc = (NameCallback) cb; //Collect username in application-specific manner nc.setName("username"); } else if (cb instanceof PasswordCallback) { PasswordCallback pc = (PasswordCallback) cb; //Collect password in application-specific manner pc.setPassword("password".toCharArray()); } else if (cb instanceof RealmCallback) { RealmCallback rc = (RealmCallback) cb; //Collect realm data in application-specific manner rc.setText("myServer"); } } } }

Sehen wir uns nun unsere Client-Seite des Callbackhandlers an :

public class ClientCallbackHandler implements CallbackHandler { @Override public void handle(Callback[] cbs) throws IOException, UnsupportedCallbackException { for (Callback cb : cbs) { if (cb instanceof NameCallback) { NameCallback nc = (NameCallback) cb; //Collect username in application-specific manner nc.setName("username"); } else if (cb instanceof PasswordCallback) { PasswordCallback pc = (PasswordCallback) cb; //Collect password in application-specific manner pc.setPassword("password".toCharArray()); } else if (cb instanceof RealmCallback) { RealmCallback rc = (RealmCallback) cb; //Collect realm data in application-specific manner rc.setText("myServer"); } } } }

Zur Verdeutlichung durchlaufen wir das Callback- Array und behandeln nur bestimmte . Diejenigen, die wir behandeln müssen, sind spezifisch für den verwendeten Mechanismus, der hier DIGEST-MD5 ist.

4.2. SASL-Authentifizierung

Also haben wir unseren Client und Server CallbackHandler geschrieben . Wir haben auch SaslClient und SaslServer für den DIGEST-MD5-Mechanismus instanziiert .

Jetzt ist die Zeit, sie in Aktion zu sehen:

@Test public void givenHandlers_whenStarted_thenAutenticationWorks() throws SaslException { byte[] challenge; byte[] response; challenge = saslServer.evaluateResponse(new byte[0]); response = saslClient.evaluateChallenge(challenge); challenge = saslServer.evaluateResponse(response); response = saslClient.evaluateChallenge(challenge); assertTrue(saslServer.isComplete()); assertTrue(saslClient.isComplete()); }

Versuchen wir zu verstehen, was hier passiert:

  • Zunächst erhält unser Client die Standardaufforderung vom Server
  • Der Kunde bewertet dann die Herausforderung und bereitet eine Antwort vor
  • Dieser Challenge-Response-Austausch wird für einen weiteren Zyklus fortgesetzt
  • Dabei verwenden Client und Server Callback-Handler, um zusätzliche Daten zu erfassen, die vom Mechanismus benötigt werden
  • Damit ist unsere Authentifizierung hier abgeschlossen, aber in Wirklichkeit kann sie über mehrere Zyklen iterieren

Ein typischer Austausch von Challenge- und Response-Byte-Arrays findet über das Netzwerk statt . Der Einfachheit halber haben wir hier jedoch von lokaler Kommunikation ausgegangen.

4.3. SASL Secure Communication

Wie bereits erwähnt, ist SASL ein Framework, das sichere Kommunikation über die reine Authentifizierung hinaus unterstützt. Dies ist jedoch nur möglich, wenn der zugrunde liegende Mechanismus dies unterstützt .

Lassen Sie uns zunächst prüfen, ob wir eine sichere Kommunikation aushandeln konnten:

String qop = (String) saslClient.getNegotiatedProperty(Sasl.QOP); assertEquals("auth-conf", qop);

Here, QOP stands for the quality of protection. This is something that the client and server negotiate during authentication. A value of “auth-int” indicates authentication and integrity. While, a value of “auth-conf” indicates authentication, integrity, and confidentiality.

Once we have a security layer, we can leverage that to secure our communication.

Let's see how we can secure outgoing communication in the client:

byte[] outgoing = "Baeldung".getBytes(); byte[] secureOutgoing = saslClient.wrap(outgoing, 0, outgoing.length); // Send secureOutgoing to the server over the network

And, similarly, the server can process incoming communication:

// Receive secureIncoming from the client over the network byte[] incoming = saslServer.unwrap(secureIncoming, 0, netIn.length); assertEquals("Baeldung", new String(incoming, StandardCharsets.UTF_8));

5. SASL in the Real World

So, we now have a fair understanding of what SASL is and how to use it in Java. But, typically, that's not what we'll end up using SASL for, at least in our daily routine.

As we saw earlier, SASL is primarily meant for protocols like LDAP and SMTP. Although, more and more applications and coming on board with SASL — for instance, Kafka. So, how do we use SASL to authenticate with such services?

Let's suppose we've configured Kafka Broker for SASL with PLAIN as the mechanism of choice. PLAIN simply means that it authenticates using a combination of username and password in plain text.

Let's now see how can we configure a Java client to use SASL/PLAIN to authenticate against the Kafka Broker.

We begin by providing a simple JAAS configuration, “kafka_jaas.conf”:

KafkaClient { org.apache.kafka.common.security.plain.PlainLoginModule required username="username" password="password"; };

We make use of this JAAS configuration while starting the JVM:

-Djava.security.auth.login.config=kafka_jaas.conf

Finally, we have to add a few properties to pass to our producer and consumer instances:

security.protocol=SASL_SSL sasl.mechanism=PLAIN

That's all there is to it. This is just a small part of Kafka client configurations, though. Apart from PLAIN, Kafka also supports GSSAPI/Kerberos for authentication.

6. SASL in Comparision

Although SASL is quite effective in providing a mechanism-neutral way of authenticating and securing client and server communication. However, SASL is not the only solution available in this regard.

Java itself provides other mechanisms to achieve this objective. We'll briefly discuss them and understand how they fare against SASL:

  • Java Secure Socket Extension (JSSE): JSSE is a set of packages in Java that implements Secure Sockets Layer (SSL) for Java. It provides data encryption, client and server authentication, and message integrity. Unlike SASL, JSSE relies on a Public Key Infrastructure (PKI) to work. Hence, SASL works out to be more flexible and lightweight than JSSE.
  • Java GSS API (JGSS): JGGS is the Java language binding for Generic Security Service Application Programming Interface (GSS-API). GSS-API is an IETF standard for applications to access security services. In Java, under GSS-API, Kerberos is the only mechanism supported. Kerberos again requires a Kerberised infrastructure to work. Compared to SASL, here yet, choices are limited and heavyweight.

Insgesamt ist SASL ein sehr leichtes Framework und bietet eine Vielzahl von Sicherheitsfunktionen durch steckbare Mechanismen. Anwendungen, die SASL einsetzen, haben je nach Bedarf viele Möglichkeiten, die richtigen Sicherheitsfunktionen zu implementieren.

7. Fazit

Zusammenfassend haben wir in diesem Tutorial die Grundlagen des SASL-Frameworks verstanden, das Authentifizierung und sichere Kommunikation bietet. Wir haben auch die in Java verfügbaren APIs für die Implementierung der Client- und Serverseite von SASL erörtert.

Wir haben gesehen, wie ein Sicherheitsmechanismus über einen JCA-Anbieter verwendet wird. Schließlich sprachen wir auch über die Verwendung von SASL bei der Arbeit mit verschiedenen Protokollen und Anwendungen.

Wie immer ist der Code auf GitHub zu finden.

Java unten

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs