Exercise
8: Using the Java Generic Security Services (GSS) API with SPNEGO
Goal
of this exercise:
Currently the only security mechansim available with Java GSS is "Kerberos". The goal of this exercise is to learn how to use other Java GSS mechanisms, such as SPNEGO, to secure the association. This feature is available from Java SE
6 onwards.
What is SPNEGO? 
Java GSS is a framework that can support multiple security mechanisms;
a way to negotiate a security mechanism underneath GSS-API is needed.
This is available via SPNEGO.
SPNEGO is the Simple
and Protected
GSS-API Negotiation Mechanism, standardized at IETF in 
RFC 4178. 
It's a
pseudo-security mechanism used to negotiate an underlying security
mechanism. It provides the flexibility for client and server to
securely negotiate a common GSS security mechanism.
Microsoft makes heavy use of SPNEGO. SPNEGO can be used to
inter-operate
with Microsoft Server over
HTTP, to support HTTP-based Cross-Platform authentication via the
Negotiate Protocol.
What do I need do to
use SPNEGO in Java GSS?
Currently when using Java GSS with Kerberos, we specify the
Kerberos OID to use Kerberos. 
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
In order to use
SPNEGO, you
only need to specify the SPNEGO OID as follows:-
Oid spnegoOid = new Oid("1.3.6.1.5.5.2");
And henceforth use the SPNEGO OID when creating a
GSSCredential, GSSContext, etc.
Steps
to follow:
  - Read the following code. This is located in src/GssSpNegoClient.java
Code listing for GssSpNegoClient.java.
  
    
      | 
 
        static class GssSpnegoClientAction implements
PrivilegedExceptionAction {...  public Object run() throws Exception {    // Create socket to server    Socket socket = new
Socket(hostName, port);    DataInputStream inStream =
new DataInputStream(socket.getInputStream());    DataOutputStream outStream =
new DataOutputStream(socket.getOutputStream());
    // Get service's principal
name    GSSManager manager =
GSSManager.getInstance();    Oid spnegoOid = new
Oid("1.3.6.1.5.5.2");    GSSName serverName =
manager.createName(serverPrinc, GSSName.NT_HOSTBASED_SERVICE,
spnegoOid);
    // Get the context for
authentication    GSSContext context =
manager.createContext(serverName, spnegoOid, null,      
GSSContext.DEFAULT_LIFETIME);    context.requestMutualAuth(true);
// Request mutual authentication    context.requestConf(true); //
Request confidentiality
    // Do the context
establishment loop    byte[] token = new byte[0];    while (!context.isEstablished())
{      token =
context.initSecContext(token, 0, token.length);      outStream.writeInt(token.length);      outStream.write(token);      outStream.flush();
      // Check if we're
done      if (!context.isEstablished())
{        token
= new
byte[inStream.readInt()];        inStream.readFully(token);      }    }
    // Context established!
    // Create MessageProp for use
with
unwrap (true means request confidentiality)    MessageProp prop = new
MessageProp(0, true);
    // Create encrypted message
and send
to server    byte[] reply = ...;    token =
context.wrap(reply,
0, reply.length, prop);
    outStream.writeInt(token.length);    outStream.write(token);    outStream.flush();
    // Read token from server    token = new
byte[inStream.readInt()];    inStream.readFully(token);
    // Unwrap (decrypt) token
sent by
server    byte[] input =
context.unwrap(token, 0, token.length, prop);    ...    context.dispose();    socket.close();    return null;  }} | 
  
  - Compile the sample code. 
 % javac GssSpNegoClient.java
 
 
 
- Read the following code. This is located in src/GssSpNegoServer.java
Code listing for GssSpNegoServer.java.
  
    
      | 
 
        static class GssSpNegoServerAction implements
PrivilegedExceptionAction {...  public Object run() throws Exception {    // Create server socket for
accepting connections    ServerSocket ss = new
ServerSocket(localPort);
    // Get own Kerberos
credentials for accepting connection    GSSManager manager =
GSSManager.getInstance();   
Oid spnegoOid = new
Oid("1.3.6.1.5.5.2");    GSSCredential serverCreds
=
manager.createCredential(null,      GSSCredential.DEFAULT_LIFETIME,
spnegoOid, GSSCredential.ACCEPT_ONLY);
    while (true) {      Socket socket =
ss.accept();      DataInputStream
inStream = new DataInputStream(socket.getInputStream());      DataOutputStream
outStream = new DataOutputStream(socket.getOutputStream());
      GSSContext
context = manager.createContext((GSSCredential)serverCreds);
      // Do the context
establishment loop      byte[] token =
null;      while (!context.isEstablished())
{        //
Read token        token
= new
byte[inStream.readInt()];        inStream.readFully(token);
        //
Process
token        token
= context.acceptSecContext(token, 0, token.length);
        //
Send a
token to the peer if one was generated by acceptSecContext        outStream.writeInt(token.length);        outStream.write(token);        outStream.flush();      }
      // Context
established!
      // Create
MessageProp
for use with unwrap (will be set upon return from unwrap)      MessageProp prop
= new
MessageProp(0, false);
      // Read token
from client      token = new
byte[inStream.readInt()];      inStream.readFully(token);      // Unwrap
(decrypt)
token sent by client      byte[]
input =
context.unwrap(token, 0, token.length, prop);      ...      // Create new
token
and send to client      byte[] reply =
...;      token =
context.wrap(reply, 0, reply.length, prop);
      outStream.writeInt(token.length);      outStream.write(token);      outStream.flush();      context.dispose();      socket.close();    }  }} 
 | 
  
  - Compile the sample code. 
 % javac GssSpNegoServer.java
 
 
- Launch a new window and start the server.   
 % xterm &
 % java -Djava.security.auth.login.config=jaas-krb5.conf \
 GssSpNegoServer
 
 
 
- Run the client application. GssClient takes two
parameters: the service name and the name of the server that the
service
is running on. For example, if the service is hostrunning on the machinej1hol-001, you would
enter the following. When prompted for the password, enter changeit. 
 % java -Djava.security.auth.login.config=jaas-krb5.conf \
 GssSpNegoClient host j1hol-001
 
 
 
 Output for running the GssSpNegoServer example.
  
    
      | 
 
        Authenticated principal:
[host/j1hol-001@J1LABS.EXAMPLE.COM]Waiting for incoming connections...Got connection from client /129.145.128.102SPNEGO Negotiated
Mechanism = 1.2.840.113554.1.2.2 Kerberos V5
Context Established!Client principal is test@J1LABS.EXAMPLE.COMServer principal is
host/j1hol-001@J1LABS.EXAMPLE.COMMutual authentication took place!Received data "Hello There!" of length 12Confidentiality applied: trueSending: Hello There! Thu May 06 12:11:15 PDT 2005 
 | 
  
Output for running the GssSpNegoClient example.
  
    
      | 
 
        Kerberos password for test: changeitAuthenticated principal: [test@J1LABS.EXAMPLE.COM]Connected to address j1hol-001/129.145.128.102SPNEGO Negotiated Mechanism =
1.2.840.113554.1.2.2 Kerberos V5Context Established!Client principal is test@J1LABS.EXAMPLE.COMServer principal is host@j1hol-001Mutual authentication took place!Sending message: Hello There!Will read token of size 93Received message: Hello There! Thu May 06 12:11:15
PDT 2005 
 | 
  
Summary:
In this exercise, you learned how to
write a client-server application that uses the Java GSS API with
SPNEGO to negotiate an underlying security mechanism, such as Kerberos,
and communicate securely using Kerberos as the underlying
authentication system.
Note: Microsoft
has implemented certain variations of the SPNEGO protocol, hence to
inter-operate with Microsoft, we have added an MS mode via a new system
property "sun.security.spnego.msinterop". This property is enabled to
"true"
by default. To disable it, you need to explicitly set this property to
"false". To enable SPNEGO debugging, you can set the system
property "sun.security.spnego.debug=true".