JAAS with Client/Server socket example

What is below is a hack on the same example floating all around the internet for using JAAS with Kerberos (Active Directory, in this case). This extends the example to send the ticket over a network socket to the server. The nice thing about this is that you can use network tracing tools to see what happens and when, i.e., when does the server contact the KDC, if at all, etc. This assumes you have your keytab file set up as in the other examples. Reply to this post if you need those.

The client…

import java.io.*;
import java.net.*;
import java.security.PrivilegedAction;
import java.util.Properties;
import javax.security.auth.Subject;
import javax.security.auth.login.*;
import org.ietf.jgss.*;
import sun.misc.BASE64Encoder;
 
public class Client {
 
  public static void main( String[] args) {
    try {
      // Setup up the Kerberos properties.
      Properties props = new Properties();
      props.load( new FileInputStream( "client.properties"));
      System.setProperty( "sun.security.krb5.debug", "true");
      System.setProperty( "java.security.krb5.realm", props.getProperty( "realm")); 
      System.setProperty( "java.security.krb5.kdc", props.getProperty( "kdc"));
      System.setProperty( "java.security.auth.login.config", "./jaas.conf");
      System.setProperty( "javax.security.auth.useSubjectCredsOnly", "true");
      String username = props.getProperty( "client.principal.name");
      String password = props.getProperty( "client.password");
      // Oid mechanism = use Kerberos V5 as the security mechanism.
      krb5Oid = new Oid( "1.2.840.113554.1.2.2");
      Client client = new Client();
      // Login to the KDC.
      client.login( username, password);
      // Request the service ticket.
      client.initiateSecurityContext( props.getProperty( "service.principal.name"));
      // Write the ticket to disk for the server to read.
      String tmp = encodeAndWriteTicketToDisk( client.serviceTicket, "./security.token");
      Socket s = new Socket("localhost",7777);
      PrintWriter out = new PrintWriter(s.getOutputStream());   
      out.print(tmp);
      out.flush();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
 
  public Client() {
    super();
  }
 
  private static Oid krb5Oid;
 
  private Subject subject;
  private byte[] serviceTicket;
 
  // Authenticate against the KDC using JAAS.
  private void login( String username, String password) throws LoginException {
    LoginContext loginCtx = null;
    // "Client" references the JAAS configuration in the jaas.conf file.
    loginCtx = new LoginContext( "Client", new LoginCallbackHandler( username, password));
    System.out.println("trying to login");
    loginCtx.login();
    this.subject = loginCtx.getSubject();
  }
 
  // Begin the initiation of a security context with the target service.
  private void initiateSecurityContext( String servicePrincipalName)
      throws GSSException {
    GSSManager manager = GSSManager.getInstance();
    GSSName serverName = manager.createName( servicePrincipalName,
        GSSName.NT_HOSTBASED_SERVICE);
    final GSSContext context = manager.createContext( serverName, krb5Oid, null,
        GSSContext.DEFAULT_LIFETIME);
    // The GSS context initiation has to be performed as a privileged action.
    this.serviceTicket = Subject.doAs( subject, new PrivilegedAction() {
      public byte[] run() {
        try {
          byte[] token = new byte[0];
          // This is a one pass context initialisation.
          context.requestMutualAuth( false);
          context.requestCredDeleg( false);
          return context.initSecContext( token, 0, token.length);
        }
        catch ( GSSException e) {
          e.printStackTrace();
          return null;
        }
      }
    });
 
  }
 
  // Base64 encode the raw ticket and write it to the given file.
  private static String encodeAndWriteTicketToDisk( byte[] ticket, String filepath)
      throws IOException {
    BASE64Encoder encoder = new BASE64Encoder();    
    FileWriter writer = new FileWriter( new File( filepath));
    String encodedToken = encoder.encode( ticket);
    writer.write( encodedToken);
    writer.close();
    return encodedToken;
  }
}

…and the server…

import java.io.*;
import java.net.*;
import java.security.PrivilegedAction;
import java.util.Properties;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import sun.misc.BASE64Decoder;
 
public class Server {
 
  public static void main( String[] args) {
    try {
      // Setup up the Kerberos properties.
      Properties props = new Properties();
      props.load( new FileInputStream( "server.properties"));
      System.setProperty( "sun.security.krb5.debug", "true");
      System.setProperty( "java.security.krb5.realm", props.getProperty( "realm"));
      System.setProperty( "java.security.krb5.kdc", props.getProperty( "kdc"));
      System.setProperty( "java.security.auth.login.config", "./jaas.conf");
      System.setProperty( "javax.security.auth.useSubjectCredsOnly", "true");
      String password = props.getProperty( "service.password");
      // Oid mechanism = use Kerberos V5 as the security mechanism.
      krb5Oid = new Oid( "1.2.840.113554.1.2.2");
      Server server = new Server();
      // Login to the KDC.
      server.login( password);
      ServerSocket s = new ServerSocket(7777);
      while (true) {
        Socket s2 = s.accept();
        BufferedReader is = new BufferedReader(new InputStreamReader(s2.getInputStream()));
        StringBuffer buffer = new StringBuffer();
        String str = null;
        while ((str = is.readLine()) != null) {
          buffer.append( str + "\n");
        }
        is.close();
        BASE64Decoder decoder = new BASE64Decoder();
        //byte serviceTicket[] = loadTokenFromDisk();
        String clientName = server.acceptSecurityContext(decoder.decodeBuffer( buffer.toString()));
        System.out.println( "\nSecurity context successfully initialised!");
        System.out.println( "\nHello World " + clientName + "!");
      }
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
 
  // Load the security token from disk and decode it. Return the raw GSS token.
  private static byte[] loadTokenFromDisk() throws IOException {
    BufferedReader in = new BufferedReader( new FileReader( "security.token"));
    System.out.println( new File( "security.token").getAbsolutePath());
    String str;
    StringBuffer buffer = new StringBuffer();
    while ((str = in.readLine()) != null) {
       buffer.append( str + "\n");
    }
    in.close();
    //System.out.println( buffer.toString());
    BASE64Decoder decoder = new BASE64Decoder();
    return decoder.decodeBuffer( buffer.toString());
  }
 
  private static Oid krb5Oid;
 
  private Subject subject;
 
  // Authenticate against the KDC using JAAS.
  private void login( String password) throws LoginException {
    LoginContext loginCtx = null;
    // "Client" references the JAAS configuration in the jaas.conf file.
    loginCtx = new LoginContext( "Server",new LoginCallbackHandler( password));
    loginCtx.login();
    this.subject = loginCtx.getSubject();
  }
 
  private String acceptSecurityContext( final byte[] serviceTicket)
      throws GSSException {
    krb5Oid = new Oid( "1.2.840.113554.1.2.2");
 
    return Subject.doAs( subject, new PrivilegedAction() {
      public String run() {
        try {
          // Identify the server that communications are being made to.
          GSSManager manager = GSSManager.getInstance();
          GSSContext context = manager.createContext( (GSSCredential) null);
          context.acceptSecContext( serviceTicket, 0, serviceTicket.length);
          return context.getSrcName().toString() + " has connected to the " + context.getTargName() + " service " + context.getLifetime();
        }
        catch ( Exception e) {
          e.printStackTrace();
          return null;
        }
      }
    });
  }
}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.