A Guide to Java GSS API – Java GSS API指南

最后修改: 2019年 8月 12日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll understand the Generic Security Service API (GSS API) and how we can implement it in Java. We’ll see how we can secure network communication using the GSS API in Java.

在本教程中,我们将了解通用安全服务API(GSS API)以及我们如何在Java中实现它。我们将看到如何使用Java中的GSS API确保网络通信的安全。

In the process, we’ll create simple client and server components, securing them with GSS API.

在这个过程中,我们将创建简单的客户端和服务器组件,用GSS API保护它们。

2. What Is GSS API?

2.什么是GSS API?

So, what really is the Generic Security Service API? GSS API provides a generic framework for applications to use different security mechanisms like Kerberos, NTLM, and SPNEGO in a pluggable manner. Consequently, it helps applications to decouple themselves from the security mechanisms directly.

那么,什么是真正的通用安全服务API?GSS API为应用程序提供了一个通用框架,以可插拔的方式使用不同的安全机制,如Kerberos、NTLM和SPNEGO。因此,它可以帮助应用程序直接从安全机制中解脱出来。

To clarify, security here spans authentication, data integrity, and confidentiality.

为了澄清,这里的安全包括认证、数据完整性和保密性。

2.1. Why Do We Need GSS API?

2.1.为什么我们需要GSS API?

Security mechanisms like Kerberos, NTLM, and Digest-MD5 are quite different in their capabilities and implementations. Typically, an application supporting one of these mechanisms finds it quite daunting to switch to another.

像Kerberos、NTLM和Digest-MD5这样的安全机制在能力和实现上有很大不同。通常情况下,支持这些机制之一的应用程序发现切换到另一个机制是相当令人生畏的。

This is where a generic framework like GSS API provides applications with an abstraction. Therefore applications using GSS API can negotiate a suitable security mechanism and use that for communication. All that without actually having to implement any mechanism-specific details.

这就是像GSS API这样的通用框架为应用程序提供抽象的地方。因此,使用GSS API的应用程序可以协商一个合适的安全机制,并将其用于通信。所有这些都不需要实际实现任何特定机制的细节。

2.2. How Does GSS API Work?

2.2.GSS API是如何工作的?

GSS API is a token-based mechanism. It works by the exchange of security tokens between peers. This exchange typically happens over a network but GSS API is agnostic to those details.

GSS API是一种基于令牌的机制。它通过对等体之间交换安全令牌来工作。这种交换通常是通过网络进行的,但GSS API对这些细节并不了解。

These tokens are generated and processed by the specific implementations of the GSS API. The syntax and semantics of these tokens are specific to the security mechanism negotiated between the peers:

这些令牌是由GSS API的具体实现产生和处理的。这些令牌的语法和语义是针对对等体之间协商的安全机制的

The central theme of GSS API revolves around a security context. We can establish this context between peers through the exchange of tokens. We may need multiple exchanges of tokens between peers to establish the context.

GSS API的中心主题是围绕着安全环境。我们可以通过交换令牌在对等体之间建立这种上下文。我们可能需要在对等体之间多次交换令牌以建立上下文。

Once successfully established at both the ends, we can use the security context to exchange data securely. This may include data integrity checks and data encryption, depending upon the underlying security mechanism.

一旦在两端成功建立,我们可以使用安全上下文来安全地交换数据。这可能包括数据完整性检查和数据加密,这取决于基础安全机制。

3. GSS API Support in Java

3.Java中的GSS API支持

Java supports GSS API as part of the package “org.ietf.jgss”. The package name may seem peculiar. That’s because the Java bindings for GSS API are defined in an IETF specification. The specification itself is independent of the security mechanism.

Java支持GSS API,是 “org.ietf.jgss “包的一部分。这个包的名字可能看起来很特别。这是因为GSS API的Java绑定被定义在一个IETF规范中。该规范本身是独立于安全机制的。

One of the popular security mechanism for Java GSS is Kerberos v5.

Java GSS的流行安全机制之一是Kerberos v5。

3.1. Java GSS API

3.1. Java GSS API

Let’s try to understand some of the core APIs that builds Java GSS:

让我们试着了解一下构建Java GSS的一些核心API。

  • GSSContext encapsulates the GSS API security context and provides services available under the context
  • GSSCredential encapsulates the GSS API credentials for an entity that is necessary to establish the security context
  • GSSName encapsulates the GSS API principal entity which provides an abstraction for different namespace used by underlying mechanisms

Apart from the above interfaces, there are few other important classes to note:

除了上述接口外,还有一些其他重要的类需要注意。

  • GSSManager serves as the factory class for other important GSS API classes like GSSName, GSSCredential, and GSSContext
  • Oid represents the Universal Object Identifiers (OIDs) which are hierarchical identifiers used within GSS API to identify mechanisms and name formats
  • MessageProp wraps properties to indicate GSSContext on things like Quality of Protection (QoP) and confidentiality for data exchange
  • ChannelBinding encapsulates the optional channel binding information used to strengthen the quality with which peer entity authentication is provided

3.2. Java GSS Security Provider

3.2.Java GSS安全供应商

While the Java GSS defines the core framework for implementing the GSS API in Java, it does not provide an implementation. Java adopts Provider-based pluggable implementations for security services including Java GSS.

虽然 Java GSS 定义了在 Java 中实现 GSS API 的核心框架,但它并没有提供实现。Java采用基于Provider的可插拔的安全服务实现,包括Java GSS。

There can be one or more such security providers registered with the Java Cryptography Architecture (JCA). Each security provider may implement one or more security services, like Java GSSAPI and security mechanisms underneath.

可以有一个或多个这样的安全提供者在Java Cryptography Architecture(JCA)注册。每个安全提供者可以实现一个或多个安全服务,如Java GSSAPI和下面的安全机制。

There is a default GSS provider that ships with the JDK. However, there are other vendor-specific GSS providers with different security mechanisms which we can use. One such provider is IBM Java GSS. We have to register such a security provider with JCA to be able to use them.

JDK中有一个默认的GSS提供者。然而,还有其他特定供应商的GSS提供者,它们具有不同的安全机制,我们可以使用。一个这样的提供者是IBM Java GSS。我们必须向JCA注册这样的安全提供者,以便能够使用它们。

Moreover, if required, we can implement our own security provider with possibly custom security mechanisms. However, this is hardly needed in practice.

此外,如果需要,我们可以用可能的自定义安全机制实现我们自己的安全提供者。然而,这在实践中几乎是不需要的。

4. GSS API Through an Example

4.通过一个例子看GSS API

Now, we’ll see Java GSS in action through an example. We’ll create a simple client and server application. The client is more commonly referred to as initiator and server as an acceptor in GSS. We’ll use Java GSS and Kerberos v5 underneath for authentication.

现在,我们将通过一个例子来看看Java GSS的运作。我们将创建一个简单的客户端和服务器应用程序。在GSS中,客户端通常被称为发起者,服务器被称为接受者。我们将使用Java GSS和下面的Kerberos v5进行认证。

4.1. GSS Context for Client and Server

4.1.客户端和服务器的GSS上下文

To begin with, we’ll have to establish a GSSContext, both at the server and client-side of the application.

首先,我们必须建立一个GSSContext,包括在应用程序的服务器和客户端

Let’s first see how we can do this at the client-side:

让我们首先看看我们如何在客户端做到这一点。

GSSManager manager = GSSManager.getInstance();
String serverPrinciple = "HTTP/localhost@EXAMPLE.COM";
GSSName serverName = manager.createName(serverPrinciple, null);
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
GSSContext clientContext = manager.createContext(
  serverName, krb5Oid, (GSSCredential)null, GSSContext.DEFAULT_LIFETIME);
clientContext.requestMutualAuth(true);
clientContext.requestConf(true);
clientContext.requestInteg(true);

There is quite a lot of things happening here, let’s break them down:

这里发生了相当多的事情,让我们把它们分解一下。

  • We begin by creating an instance of the GSSManager
  • Then we use this instance to create GSSContext, passing along:
    • a GSSName representing the server principal, note the Kerberos specific principal name here
    • the Oid of mechanism to use, Kerberos v5 here
    • the initiator’s credentials, null here means that default credentials will be used
    • the lifetime for the established context
  • Finally, we prepare the context for mutual authentication, confidentiality, and data integrity

Similarly, we have to define the server-side context:

同样地,我们必须定义服务器端的上下文。

GSSManager manager = GSSManager.getInstance();
GSSContext serverContext = manager.createContext((GSSCredential) null);

As we can see, this is much simpler than the client-side context. The only difference here is that we need acceptor’s credentials which we have used as null. As before, null means that the default credentials will be used.

我们可以看到,这比客户端的上下文要简单得多。这里唯一的区别是,我们需要接受者的凭证,我们将其作为null使用。和以前一样,null意味着将使用默认的凭证。

4.2. GSS API Authentication

4.2.GSS API认证

Although we have created the server and client-side GSSContext, please note that they are unestablished at this stage.

尽管我们已经创建了服务器和客户端的GSSContext,但请注意它们在这个阶段是未建立的。

To establish these contexts, we need to exchange tokens specific to the security mechanism specified, that is Kerberos v5:

为了建立这些上下文,我们需要交换特定于安全机制的令牌,也就是Kerberos v5。

// On the client-side
clientToken = clientContext.initSecContext(new byte[0], 0, 0);
sendToServer(clientToken); // This is supposed to be send over the network
		
// On the server-side
serverToken = serverContext.acceptSecContext(clientToken, 0, clientToken.length);
sendToClient(serverToken); // This is supposed to be send over the network
		
// Back on the client side
clientContext.initSecContext(serverToken, 0, serverToken.length);

This finally makes the context established at both the ends:

这最终使上下文在两端建立起来。

assertTrue(serverContext.isEstablished());
assertTrue(clientContext.isEstablished());

4.3. GSS API Secure Communication

4.3.GSS API安全通信

Now, that we have context established at both the ends, we can start sending data with integrity and confidentiality:

现在,我们已经在两端建立了上下文,我们可以开始发送具有完整性和保密性的数据

// On the client-side
byte[] messageBytes = "Baeldung".getBytes();
MessageProp clientProp = new MessageProp(0, true);
byte[] clientToken = clientContext.wrap(messageBytes, 0, messageBytes.length, clientProp);
sendToClient(serverToken); // This is supposed to be send over the network
       
// On the server-side 
MessageProp serverProp = new MessageProp(0, false);
byte[] bytes = serverContext.unwrap(clientToken, 0, clientToken.length, serverProp);
String string = new String(bytes);
assertEquals("Baeldung", string);

There are a couple of things happening here, let’s analyze:

这里发生了几件事情,我们来分析一下。

  • MessageProp is used by the client to set the wrap method and generate the token
  • The method wrap also adds cryptographic MIC of the data, the MIC is bundled as part of the token
  • That token is sent to the server (possibly over a network call)
  • The server leverages MessageProp again to set the unwrap method and get data back
  • Also, the method unwrap verifies the MIC for the received data, ensuring the data integrity

Hence, the client and server are able to exchange data with integrity and confidentiality.

因此,客户端和服务器能够以完整性和保密性交换数据。

4.4. Kerberos Set-up for the Example

4.4.例子的Kerberos设置

Now, a GSS mechanism like Kerberos is typically expected to fetch credentials from an existing Subject. The class Subject here is a JAAS abstraction representing an entity like a person or a service. This is usually populated during a JAAS-based authentication.

现在,像Kerberos这样的GSS机制通常要从现有的Subject中获取凭证。这里的Subject类是一个JAAS抽象,代表一个实体,如一个人或一个服务。这通常是在基于JAAS的认证过程中被填充的。

However, for our example, we’ll not directly use a JAAS-based authentication. We’ll let Kerberos obtain credentials directly, in our case using a keytab file. There is a JVM system parameter to achieve that:

然而,在我们的例子中,我们将不直接使用基于JAAS的认证。我们将让Kerberos直接获得凭证,在我们的例子中使用一个keytab文件。有一个JVM系统参数可以实现这一点。

-Djavax.security.auth.useSubjectCredsOnly=false

However, the defaults Kerberos implementation provided by Sun Microsystem relies on JAAS to provide authentication.

然而,Sun Microsystem提供的默认Kerberos实现依赖于JAAS来提供认证。

This may sound contrary to what we just discussed. Please note that we can explicitly use JAAS in our application which will populate the Subject. Or leave it to the underlying mechanism to authenticate directly, where it anyways uses JAAS. Hence, we need to provide a JAAS configuration file to the underlying mechanism:

这听起来可能与我们刚才讨论的内容相反。请注意,我们可以在我们的应用程序中明确使用JAAS,这将填充Subject。或者让底层机制直接进行认证,无论如何它都会使用JAAS。因此,我们需要向底层机制提供一个JAAS配置文件。

com.sun.security.jgss.initiate  {
  com.sun.security.auth.module.Krb5LoginModule required
  useKeyTab=true
  keyTab=example.keytab
  principal="client/localhost"
  storeKey=true;
};
com.sun.security.jgss.accept  {
  com.sun.security.auth.module.Krb5LoginModule required
  useKeyTab=true
  keyTab=example.keytab
  storeKey=true
  principal="HTTP/localhost";
};

This configuration is straight-forward, where we have defined Kerberos as the required login module for both initiator and acceptor. Additionally, we have configured to use the respective principals from a keytab file. We can pass this JAAS configuration to JVM as a system parameter:

这个配置很简单,我们把Kerberos定义为发起者和接受者的必要登录模块。此外,我们还配置了使用keytab文件中各自的原则。我们可以把这个JAAS配置作为一个系统参数传递给JVM。

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

Here, the assumption is that we have access to a Kerberos KDC. In the KDC we have set up the required principals and obtained the keytab file to use, let’s say “example.keytab”.

这里的假设是,我们可以访问一个 Kerberos KDC。在 KDC 中,我们已经设置了所需的委托人,并获得了要使用的 keytab 文件,比方说 “example.keytab”.

Additionally, we need the Kerberos configuration file pointing to the right KDC:

此外,我们需要Kerberos配置文件指向正确的KDC。

[libdefaults]
default_realm = EXAMPLE.COM
udp_preference_limit = 1
[realms]
EXAMPLE.COM = {
    kdc = localhost:52135
}

This simple configuration defines a KDC running on port 52135 with a default realm as EXAMPLE.COM. We can pass this to JVM as a system parameter:

这个简单的配置定义了一个运行在52135端口的KDC,默认境界为EXAMPLE.COM。我们可以把它作为一个系统参数传递给 JVM。

-Djava.security.krb5.conf=krb5.conf

4.5. Running the Example

4.5.运行该实例

To run the example, we have to make use of the Kerberos artifacts discussed in the last section.

为了运行这个例子,我们必须利用上一节中讨论的Kerberos工件

Also, we need to pass the required JVM parameters:

此外,我们还需要传递所需的JVM参数。

java -Djava.security.krb5.conf=krb5.conf \
  -Djavax.security.auth.useSubjectCredsOnly=false \
  -Djava.security.auth.login.config=login.conf \
  com.baeldung.jgss.JgssUnitTest

This is sufficient for Kerberos to perform the authentication with credentials from keytab and GSS to establish the contexts.

这足以让Kerberos用keytab的凭证进行认证,并让GSS建立上下文。

5. GSS API in Real World

5.真实世界中的GSS API

While GSS API promises to solve a host of security problems through pluggable mechanisms, there are few use cases which have been more widely adopted:

虽然GSS API承诺通过可插拔机制解决一系列安全问题,但有几个用例已经被更广泛地采用。

  • It’s widely used in SASL as a security mechanism, especially where Kerberos is the underlying mechanism of choice. Kerberos is a widely used authentication mechanism, especially within an enterprise network. It is really useful to leverage a Kerberised infrastructure to authenticate a new application. Hence, GSS API bridges that gap nicely.
  • It’s also used in conjugation with SPNEGO to negotiate a security mechanism when one is not known beforehand. In this regard, SPNEGO is a pseudo mechanism of GSS API in a sense. This is widely supported in all modern browsers making them capable of leveraging Kerberos-based authentication.

6. GSS API in Comparision

6.GSS API的比较

GSS API is quite effective in providing security services to applications in a pluggable manner. However, it’s not the only choice to achieve this in Java.

GSS API在以可插拔的方式向应用程序提供安全服务方面相当有效。然而,它并不是在Java中实现这一目标的唯一选择。

Let’s understand what else Java has to offer and how do they compare against GSS API:

让我们了解一下Java还能提供什么,以及它们与GSS API相比有什么不同。

  • 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 GSS API, JSSE relies on a Public Key Infrastructure (PKI) to work. Hence, the GSS API works out to be more flexible and lightweight than JSSE.
  • Java Simple Authentication and Security Layer (SASL): SASL is a framework for authentication and data security for internet protocols which decouples them from specific authentication mechanisms. This is similar in scope to GSS API. However, Java GSS has limited support for underlying security mechanisms through available security providers.

Overall, GSS API is pretty powerful in providing security services in mechanism agnostic manner. However, support for more security mechanisms in Java will take this further in adoption.

总的来说,GSS API在以机制无关的方式提供安全服务方面是相当强大的。然而,对Java中更多安全机制的支持将使其进一步被采用。

7. Conclusion

7.结语

To sum up, in this tutorial, we understood the basics of GSS API as a security framework. We went through the Java API for GSS and understood how we can leverage them. In the process, we created simple client and server components that performed mutual authentication and exchanged data securely.

综上所述,在本教程中,我们了解了GSS API作为一个安全框架的基础知识。我们浏览了GSS的Java API,并了解了我们如何利用它们。在这个过程中,我们创建了简单的客户端和服务器组件,进行相互认证并安全地交换数据。

Further, we also saw what are the practical applications of GSS API and what are the alternatives available in Java.

此外,我们还看到了GSS API的实际应用以及Java中的替代方案。

As always, the code can be found over on GitHub.

一如既往,代码可以在GitHub上找到over