Source Code for JAAS and Java GSS-API Tutorials
SampleServer.java
import org.ietf.jgss.*;
import java.io.*;
import java.net.Socket;
import java.net.ServerSocket;
/**
* A sample server application that uses JGSS to do mutual authentication
* with a client using Kerberos as the underlying mechanism. It then
* exchanges data securely with the client.
*
* Every message exchanged with the client includes a 4-byte application-
* level header that contains the big-endian integer value for the number
* of bytes that will follow as part of the JGSS token.
*
* The protocol is:
* 1. Context establishment loop:
* a. client sends init sec context token to server
* b. server sends accept sec context token to client
* ....
* 2. client sends a wrap token to the server.
* 3. server sends a mic token to the client for the application
* message that was contained in the wrap token.
*/
public class SampleServer {
public static void main(String[] args)
throws IOException, GSSException {
// Obtain the command-line arguments and parse the port number
if (args.length != 1) {
System.err.println("Usage: java <options> Login SampleServer <localPort>");
System.exit(-1);
}
int localPort = Integer.parseInt(args[0]);
ServerSocket ss = new ServerSocket(localPort);
GSSManager manager = GSSManager.getInstance();
while (true) {
System.out.println("Waiting for incoming connection...");
Socket socket = ss.accept();
DataInputStream inStream =
new DataInputStream(socket.getInputStream());
DataOutputStream outStream =
new DataOutputStream(socket.getOutputStream());
System.out.println("Got connection from client "
+ socket.getInetAddress());
/*
* Create a GSSContext to receive the incoming request
* from the client. Use null for the server credentials
* passed in. This tells the underlying mechanism
* to use whatever credentials it has available that
* can be used to accept this connection.
*/
GSSContext context = manager.createContext((GSSCredential)null);
// Do the context eastablishment loop
byte[] token = null;
while (!context.isEstablished()) {
token = new byte[inStream.readInt()];
System.out.println("Will read input token of size "
+ token.length
+ " for processing by acceptSecContext");
inStream.readFully(token);
token = context.acceptSecContext(token, 0, token.length);
// Send a token to the peer if one was generated by
// acceptSecContext
if (token != null) {
System.out.println("Will send token of size "
+ token.length
+ " from acceptSecContext.");
outStream.writeInt(token.length);
outStream.write(token);
outStream.flush();
}
}
System.out.print("Context Established! ");
System.out.println("Client is " + context.getSrcName());
System.out.println("Server is " + context.getTargName());
/*
* If mutual authentication did not take place, then
* only the client was authenticated to the
* server. Otherwise, both client and server were
* authenticated to each other.
*/
if (context.getMutualAuthState())
System.out.println("Mutual authentication took place!");
/*
* Create a MessageProp which unwrap will use to return
* information such as the Quality-of-Protection that was
* applied to the wrapped token, whether or not it was
* encrypted, etc. Since the initial MessageProp values
* are ignored, just set them to the defaults of 0 and false.
*/
MessageProp prop = new MessageProp(0, false);
/*
* Read the token. This uses the same token byte array
* as that used during context establishment.
*/
token = new byte[inStream.readInt()];
System.out.println("Will read token of size "
+ token.length);
inStream.readFully(token);
byte[] bytes = context.unwrap(token, 0, token.length, prop);
String str = new String(bytes);
System.out.println("Received data \""
+ str + "\" of length " + str.length());
System.out.println("Confidentiality applied: "
+ prop.getPrivacy());
/*
* Now generate a MIC and send it to the client. This is
* just for illustration purposes. The integrity of the
* incoming wrapped message is guaranteed irrespective of
* the confidentiality (encryption) that was used.
*/
/*
* First reset the QOP of the MessageProp to 0
* to ensure the default Quality-of-Protection
* is applied.
*/
prop.setQOP(0);
token = context.getMIC(bytes, 0, bytes.length, prop);
System.out.println("Will send MIC token of size "
+ token.length);
outStream.writeInt(token.length);
outStream.write(token);
outStream.flush();
System.out.println("Closing connection with client "
+ socket.getInetAddress());
context.dispose();
socket.close();
}
}
}
bcsLogin.conf
/**
* Login Configuration for JAAS.
*/
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required;
};
com.sun.security.jgss.accept {
com.sun.security.auth.module.Krb5LoginModule required storeKey=true;
};
SampleClient.java
import org.ietf.jgss.*;
import java.net.Socket;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
/**
* A sample client application that uses JGSS to do mutual authentication
* with a server using Kerberos as the underlying mechanism. It then
* exchanges data securely with the server.
*
* Every message sent to the server includes a 4-byte application-level
* header that contains the big-endian integer value for the number
* of bytes that will follow as part of the JGSS token.
*
* The protocol is:
* 1. Context establishment loop:
* a. client sends init sec context token to server
* b. server sends accept sec context token to client
* ....
* 2. client sends a wrap token to the server.
* 3. server sends a MIC token to the client for the application
* message that was contained in the wrap token.
*/
public class SampleClient {
public static void main(String[] args)
throws IOException, GSSException {
// Obtain the command-line arguments and parse the port number
if (args.length < 3) {
System.err.println("Usage: java <options> Login SampleClient "
+ " <server> <hostName> <port>");
System.exit(-1);
}
String server = args[0];
String hostName = args[1];
int port = Integer.parseInt(args[2]);
Socket socket = new Socket(hostName, port);
DataInputStream inStream =
new DataInputStream(socket.getInputStream());
DataOutputStream outStream =
new DataOutputStream(socket.getOutputStream());
System.out.println("Connected to server "
+ socket.getInetAddress());
/*
* This Oid is used to represent the Kerberos version 5 GSS-API
* mechanism. It is defined in RFC 1964. We will use this Oid
* whenever we need to indicate to the GSS-API that it must
* use Kerberos for some purpose.
*/
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
GSSManager manager = GSSManager.getInstance();
/*
* Create a GSSName out of the server's name. The null
* indicates that this application does not wish to make
* any claims about the syntax of this name and that the
* underlying mechanism should try to parse it as per whatever
* default syntax it chooses.
*/
GSSName serverName = manager.createName(server, null);
/*
* Create a GSSContext for mutual authentication with the
* server.
* - serverName is the GSSName that represents the server.
* - krb5Oid is the Oid that represents the mechanism to
* use. The client chooses the mechanism to use.
* - null is passed in for client credentials
* - DEFAULT_LIFETIME lets the mechanism decide how long the
* context can remain valid.
* Note: Passing in null for the credentials asks GSS-API to
* use the default credentials. This means that the mechanism
* will look among the credentials stored in the current Subject
* to find the right kind of credentials that it needs.
*/
GSSContext context = manager.createContext(serverName,
krb5Oid,
null,
GSSContext.DEFAULT_LIFETIME);
// Set the desired optional features on the context. The client
// chooses these options.
context.requestMutualAuth(true); // Mutual authentication
context.requestConf(true); // Will use confidentiality later
context.requestInteg(true); // Will use integrity later
// Do the context eastablishment loop
byte[] token = new byte[0];
while (!context.isEstablished()) {
// token is ignored on the first call
token = context.initSecContext(token, 0, token.length);
// Send a token to the server if one was generated by
// initSecContext
if (token != null) {
System.out.println("Will send token of size "
+ token.length
+ " from initSecContext.");
outStream.writeInt(token.length);
outStream.write(token);
outStream.flush();
}
// If the client is done with context establishment
// then there will be no more tokens to read in this loop
if (!context.isEstablished()) {
token = new byte[inStream.readInt()];
System.out.println("Will read input token of size "
+ token.length
+ " for processing by initSecContext");
inStream.readFully(token);
}
}
System.out.println("Context Established! ");
System.out.println("Client is " + context.getSrcName());
System.out.println("Server is " + context.getTargName());
/*
* If mutual authentication did not take place, then only the
* client was authenticated to the server. Otherwise, both
* client and server were authenticated to each other.
*/
if (context.getMutualAuthState())
System.out.println("Mutual authentication took place!");
byte[] messageBytes = "Hello There!\0".getBytes();
/*
* The first MessageProp argument is 0 to request
* the default Quality-of-Protection.
* The second argument is true to request
* privacy (encryption of the message).
*/
MessageProp prop = new MessageProp(0, true);
/*
* Encrypt the data and send it across. Integrity protection
* is always applied, irrespective of confidentiality
* (i.e., encryption).
* You can use the same token (byte array) as that used when
* establishing the context.
*/
token = context.wrap(messageBytes, 0, messageBytes.length, prop);
System.out.println("Will send wrap token of size " + token.length);
outStream.writeInt(token.length);
outStream.write(token);
outStream.flush();
/*
* Now we will allow the server to decrypt the message,
* calculate a MIC on the decrypted message and send it back
* to us for verification. This is unnecessary, but done here
* for illustration.
*/
token = new byte[inStream.readInt()];
System.out.println("Will read token of size " + token.length);
inStream.readFully(token);
context.verifyMIC(token, 0, token.length,
messageBytes, 0, messageBytes.length,
prop);
System.out.println("Verified received MIC for message.");
System.out.println("Exiting...");
context.dispose();
socket.close();
}
}
JaasAcn.java
import javax.security.auth.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import com.sun.security.auth.callback.TextCallbackHandler;
/**
* This JaasAcn application attempts to authenticate a user
* and reports whether or not the authentication was successful.
*/
public class JaasAcn {
public static void main(String[] args) {
// Obtain a LoginContext, needed for authentication. Tell it
// to use the LoginModule implementation specified by the
// entry named "JaasSample" in the JAAS login configuration
// file and to also use the specified CallbackHandler.
LoginContext lc = null;
try {
lc = new LoginContext("JaasSample", new TextCallbackHandler());
} catch (LoginException le) {
System.err.println("Cannot create LoginContext. "
+ le.getMessage());
System.exit(-1);
} catch (SecurityException se) {
System.err.println("Cannot create LoginContext. "
+ se.getMessage());
System.exit(-1);
}
try {
// attempt authentication
lc.login();
} catch (LoginException le) {
System.err.println("Authentication failed:");
System.err.println(" " + le.getMessage());
System.exit(-1);
}
System.out.println("Authentication succeeded!");
}
}
jass.conf
/** Login Configuration for the JaasAcn and
** JaasAzn Applications
**/
JaasSample {
com.sun.security.auth.module.Krb5LoginModule required;
};
jassacn.policy
/** Java Access Control Policy for the JaasAcn Application **/
grant codebase "file:./JaasAcn.jar" {
permission javax.security.auth.AuthPermission "createLoginContext.JaasSample";
};
JaasAzn.java
import javax.security.auth.Subject;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import com.sun.security.auth.callback.TextCallbackHandler;
import java.security.PrivilegedAction;
/**
* This JaasAzn application attempts to authenticate a user
* and reports whether or not the authentication was successful.
* If successful, it then sets up subsequent execution of
* code in the run method of the SampleAction class such that
* access control checks for security-sensitive operations will be
* based on the user running the code.
*/
public class JaasAzn {
public static void main(String[] args) {
// Obtain a LoginContext, needed for authentication. Tell it
// to use the LoginModule implementation specified by the
// entry named "JaasSample" in the JAAS login configuration
// file and to also use the specified CallbackHandler.
LoginContext lc = null;
try {
lc = new LoginContext("JaasSample", new TextCallbackHandler());
} catch (LoginException le) {
System.err.println("Cannot create LoginContext. "
+ le.getMessage());
System.exit(-1);
} catch (SecurityException se) {
System.err.println("Cannot create LoginContext. "
+ se.getMessage());
System.exit(-1);
}
try {
// attempt authentication
lc.login();
} catch (LoginException le) {
System.err.println("Authentication failed:");
System.err.println(" " + le.getMessage());
System.exit(-1);
}
System.out.println("Authentication succeeded!");
// now try to execute the SampleAction as the authenticated Subject
Subject mySubject = lc.getSubject();
PrivilegedAction action = new SampleAction();
Subject.doAsPrivileged(mySubject, action, null);
}
}
SampleAction.java
import java.io.File;
import java.security.PrivilegedAction;
/**
* This is a sample PrivilegedAction implementation, designed to be
* used with the JaasAzn class.
*/
public class SampleAction implements PrivilegedAction {
/**
* This sample PrivilegedAction performs the following operations:
* <ul>
* <li> Access the System property <i>java.home</i>
* <li> Access the System property <i>user.home</i>
* <li> Access the file <i>foo.txt</i>
* </ul>
*
* @return <code>null</code> in all cases.
*
* @exception SecurityException if the caller does not have permission
* to perform any of these operations.
*/
public Object run() {
System.out.println("\nYour java.home property value is: "
+System.getProperty("java.home"));
System.out.println("\nYour user.home property value is: "
+System.getProperty("user.home"));
File f = new File("foo.txt");
System.out.print("\nfoo.txt does ");
if (!f.exists())
System.out.print("not ");
System.out.println("exist in the current working directory.");
return null;
}
}
jassazn.policy
/** Java Access Control Policy for the JaasAzn Application **/
/** Code-Based Access Control Policy for JaasAzn **/
grant codebase "file:./JaasAzn.jar" {
permission javax.security.auth.AuthPermission
"createLoginContext.JaasSample";
permission javax.security.auth.AuthPermission "doAsPrivileged";
};
/** User-Based Access Control Policy for the SampleAction class
** instantiated by JaasAzn
**/
grant codebase "file:./SampleAction.jar",
Principal javax.security.auth.kerberos.KerberosPrincipal
"your_user_name@your_realm" {
permission java.util.PropertyPermission "java.home", "read";
permission java.util.PropertyPermission "user.home", "read";
permission java.io.FilePermission "foo.txt", "read";
};
Login.java
import java.io.*;
import java.lang.reflect.*;
import java.util.Arrays;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import javax.security.auth.Subject;
import com.sun.security.auth.callback.TextCallbackHandler;
/**
* <p> This class authenticates a <code>Subject</code> and then
* executes a specified application as that <code>Subject</code>.
* To use this class, the java interpreter would typically be invoked as:
*
* <pre>
* % java -Djava.security.manager \
* Login \
* <applicationclass> <applicationClass_args>
* </pre>
*
* <p> <i>applicationClass</i> represents the application to be executed
* as the authenticated <code>Subject</code>,
* and <i>applicationClass_args</i> are passed as arguments to
* <i>applicationClass</i>.
*
* <p> To perform the authentication, <code>Login</code> uses a
* <code>LoginContext</code>. A <code>LoginContext</code> relies on a
* <code>Configuration</code> to determine the modules that should be used
* to perform the actual authentication. The location of the Configuration
* is dependent upon each Configuration implementation.
* The default Configuration implementation
* (<code>com.sun.security.auth.login.ConfigFile</code>)
* allows the Configuration location to be specified (among other ways)
* via the <code>java.security.auth.login.config</code> system property.
* Therefore, the <code>Login</code> class can also be invoked as:
*
* <pre>
* % java -Djava.security.manager \
* -Djava.security.auth.login.config=<configuration_url> \
* Login \
* <your_application_class> <your_application_class_args>
* </pre>
*/
public class Login {
/**
* <p> Instantate a <code>LoginContext</code> using the
* provided application classname as the index for the login
* <code>Configuration</code>. Authenticate the <code>Subject</code>
* (three retries are allowed) and invoke
* <code>Subject.doAsPrivileged</code>
* with the authenticated <code>Subject</code> and a
* <code>PrivilegedExceptionAction</code>.
* The <code>PrivilegedExceptionAction</code>
* loads the provided application class, and then invokes
* its public static <code>main</code> method, passing it
* the application arguments.
*
* <p>
*
* @param args the arguments for <code>Login</code>. The first
* argument must be the class name of the application to be
* invoked once authentication has completed, and the
* subsequent arguments are the arguments to be passed
* to that application's public static <code>main</code> method.
*/
public static void main(String[] args) {
// check for the application's main class
if (args == null || args.length == 0) {
System.err.println("Invalid arguments: " +
"Did not provide name of application class.");
System.exit(-1);
}
LoginContext lc = null;
try {
lc = new LoginContext(args[0], new TextCallbackHandler());
} catch (LoginException le) {
System.err.println("Cannot create LoginContext. "
+ le.getMessage());
System.exit(-1);
} catch (SecurityException se) {
System.err .println("Cannot create LoginContext. "
+ se.getMessage());
System.exit(-1);
}
// the user has 3 attempts to authenticate successfully
int i;
for (i = 0; i < 3; i++) {
try {
// attempt authentication
lc.login();
// if we return with no exception, authentication succeeded
break;
} catch (AccountExpiredException aee) {
System.err.println("Your account has expired. " +
"Please notify your administrator.");
System.exit(-1);
} catch (CredentialExpiredException cee) {
System.err.println("Your credentials have expired.");
System.exit(-1);
} catch (FailedLoginException fle) {
System.err.println("Authentication Failed");
try {
Thread.currentThread().sleep(3000);
} catch (Exception e) {
// ignore
}
} catch (Exception e) {
System.err.println("Unexpected Exception - unable to continue");
e.printStackTrace();
System.exit(-1);
}
}
// did they fail three times?
if (i == 3) {
System.err.println("Sorry");
System.exit(-1);
}
// push the subject into the current ACC
try {
Subject.doAsPrivileged(lc.getSubject(),
new MyAction(args),
null);
} catch (java.security.PrivilegedActionException pae) {
pae.printStackTrace();
System.exit(-1);
}
System.exit(0);
}
}
class MyAction implements java.security.PrivilegedExceptionAction {
String[] origArgs;
public MyAction(String[] origArgs) {
this.origArgs = (String[])origArgs.clone();
}
public Object run() throws Exception {
// get the ContextClassLoader
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
// get the application class's main method
Class c = Class.forName(origArgs[0], true, cl);
Class[] PARAMS = { origArgs.getClass() };
java.lang.reflect.Method mainMethod = c.getMethod("main", PARAMS);
// invoke the main method with the remaining args
String[] appArgs = new String[origArgs.length - 1];
System.arraycopy(origArgs, 1, appArgs, 0, origArgs.length - 1);
Object[] args = { appArgs };
mainMethod.invoke(null /*ignored*/, args);
} catch (Exception e) {
throw new java.security.PrivilegedActionException(e);
}
// successful completion
return null;
}
}
Sample.java
import java.io.File;
public class Sample {
/**
* This sample class performs the following operations:
* <ul>
* <li> Access the System property <i>java.home</i>
* <li> Access the System property <i>user.home</i>
* <li> Access the file <i>foo.txt</i>
* </ul>
*
* @exception SecurityException if the caller does not have permission
* to perform any of these operations.
*/
public static void main (String[] args) throws SecurityException {
// If there were any arguments to read, we'd do it here.
System.out.println("\nYour java.home property value is: "
+System.getProperty("java.home"));
System.out.println("\nYour user.home property value is: "
+System.getProperty("user.home"));
File f = new File("foo.txt");
System.out.print("\nfoo.txt does ");
if (!f.exists())
System.out.print("not ");
System.out.println("exist in the current working directory.");
}
}
sample.conf
/** Login Configuration for the Sample Application **/
Sample {
com.sun.security.auth.module.Krb5LoginModule required;
};
sample.policy
/** Access Control Policy for the Sample Application **/
grant codebase "file:./Login.jar" {
permission java.security.AllPermission;
};
grant codebase "file:./Sample.jar",
Principal javax.security.auth.kerberos.KerberosPrincipal
"your_user_name@your_realm" {
permission java.util.PropertyPermission "java.home", "read";
permission java.util.PropertyPermission "user.home", "read";
permission java.io.FilePermission "foo.txt", "read";
};
csLogin.conf
/**
* Login Configuration for JAAS.
*/
SampleClient {
com.sun.security.auth.module.Krb5LoginModule required;
};
SampleServer {
com.sun.security.auth.module.Krb5LoginModule required storeKey=true principal="service_principal@your_realm";
};
client.policy
grant CodeBase "file:./Login.jar" {
permission java.security.AllPermission;
};
grant CodeBase "file:./SampleClient.jar",
Principal javax.security.auth.kerberos.KerberosPrincipal
"your_user_name@your_realm" {
permission java.net.SocketPermission "*", "connect";
permission javax.security.auth.kerberos.ServicePermission
"krbtgt/your_realm@your_realm",
"initiate";
permission javax.security.auth.kerberos.ServicePermission
"service_principal@your_realm",
"initiate";
};
server.policy
grant CodeBase "file:./Login.jar" {
permission java.security.AllPermission;
};
grant CodeBase "file:./SampleServer.jar"
Principal javax.security.auth.kerberos.KerberosPrincipal
"service_principal@your_realm" {
permission java.net.SocketPermission "*", "accept";
permission javax.security.auth.kerberos.ServicePermission
"service_principal@your_realm", "accept";
};
SampleServerImp.java
import org.ietf.jgss.*;
import java.io.*;
import java.net.Socket;
import java.net.ServerSocket;
import javax.security.auth.Subject;
import java.security.PrivilegedAction;
/**
* A sample server application that uses JGSS to do mutual authentication
* with a client using Kerberos as the underlying mechanism. It then
* exchanges data securely with the client.
*
* Every message exchanged with the client includes a 4-byte application-
* level header that contains the big-endian integer value for the number
* of bytes that will follow as part of the JGSS token.
*
* The protocol is:
* 1. Context establishment loop:
* a. client sends init sec context token to server
* b. server sends accept sec context token to client
* ....
* 2. client sends a wrap token to the server.
* 3. server sends a mic token to the client for the application
* message that was contained in the wrap token.
*/
public class SampleServerImp {
public static void main(String[] args)
throws IOException, GSSException {
// Obtain the command-line arguments and parse the port number
if (args.length != 1) {
System.err.println("Usage: java <options> Login SampleServer <localPort>");
System.exit(-1);
}
int localPort = Integer.parseInt(args[0]);
ServerSocket ss = new ServerSocket(localPort);
GSSManager manager = GSSManager.getInstance();
while (true) {
System.out.println("Waiting for incoming connection...");
Socket socket = ss.accept();
DataInputStream inStream =
new DataInputStream(socket.getInputStream());
DataOutputStream outStream =
new DataOutputStream(socket.getOutputStream());
System.out.println("Got connection from client "
+ socket.getInetAddress());
/*
* Create a GSSContext to receive the incoming request
* from the client. Use null for the server credentials
* passed in. This tells the underlying mechanism
* to use whatever credentials it has available that
* can be used to accept this connection.
*/
GSSContext context = manager.createContext((GSSCredential)null);
// Do the context eastablishment loop
byte[] token = null;
while (!context.isEstablished()) {
token = new byte[inStream.readInt()];
System.out.println("Will read input token of size "
+ token.length
+ " for processing by acceptSecContext");
inStream.readFully(token);
token = context.acceptSecContext(token, 0, token.length);
// Send a token to the peer if one was generated by
// acceptSecContext
if (token != null) {
System.out.println("Will send token of size "
+ token.length
+ " from acceptSecContext.");
outStream.writeInt(token.length);
outStream.write(token);
outStream.flush();
}
}
System.out.println("Context Established! ");
System.out.println("Client is " + context.getSrcName());
System.out.println("Server is " + context.getTargName());
/*
* If mutual authentication did not take place, then
* only the client was authenticated to the
* server. Otherwise, both client and server were
* authenticated to each other.
*/
if (context.getMutualAuthState())
System.out.println("Mutual authentication took place!");
/*
* Create a MessageProp which unwrap will use to return
* information such as the Quality-of-Protection that was
* applied to the wrapped token, whether or not it was
* encrypted, etc. Since the initial MessageProp values
* are ignored, just set them to the defaults of 0 and false.
*/
MessageProp prop = new MessageProp(0, false);
/*
* Read the token. This uses the same token byte array
* as that used during context establishment.
*/
token = new byte[inStream.readInt()];
System.out.println("Will read token of size "
+ token.length);
inStream.readFully(token);
byte[] bytes = context.unwrap(token, 0, token.length, prop);
String str = new String(bytes);
System.out.println("Received data \""
+ str + "\" of length " + str.length());
System.out.println("Confidentiality applied: "
+ prop.getPrivacy());
/*
* Now generate a MIC and send it to the client. This is
* just for illustration purposes. The integrity of the
* incoming wrapped message is guaranteed irrespective of
* the confidentiality (encryption) that was used.
*/
/*
* First reset the QOP of the MessageProp to 0
* to ensure the default Quality-of-Protection
* is applied.
*/
prop.setQOP(0);
token = context.getMIC(bytes, 0, bytes.length, prop);
System.out.println("Will send MIC token of size "
+ token.length);
outStream.writeInt(token.length);
outStream.write(token);
outStream.flush();
/*
* Impersonate client
*/
System.out.println("Impersonating client.");
/*
* Extract the KerberosPrincipal from the client GSSName and populate
* it in the principal set of a new Subject. Pass in a null for
* credentials. If we were to pass in the delegated GSSCredential
* instead of null, then the resulting Subject's private credential
* set would also be populated.
*/
GSSName clientGSSName = context.getSrcName();
System.out.println("clientGSSName: " + clientGSSName);
Subject client =
com.sun.security.jgss.GSSUtil.createSubject(clientGSSName,
null);
/*
* Construct an action that will read a file meant only for the
* client
*/
PrivilegedAction readFile =
new ReadFileAction(clientGSSName.toString());
/*
* Invoke the action via a doAsPrivileged. This allows the
* action to be executed as the client subject, and it also runs
* that code as privileged. This means that any permission checking
* that happens beyond this point applies only to the code being
* run as the client.
*/
Subject.doAsPrivileged(client, readFile, null);
/*
* Clean up
*/
System.out.println("Closing connection with client "
+ socket.getInetAddress());
context.dispose();
socket.close();
}
}
}
ReadFileAction.java
import java.security.PrivilegedAction;
import java.io.*;
/**
* This class implements the PrivilegedAction interface to demonstrate the
* reading of a file that belongs to the client. This code will be
* executed by the server while impersonating the client principal.
*/
public class ReadFileAction implements PrivilegedAction {
private String fileName;
/**
* Contructs a ReadFileAction instance.
*
* @param kerberosPrincipalName the name of the Kerberos principal
* who owns the file that will be read. The filename is constructed
* from the name of the principal.
*/
public ReadFileAction(String kerberosPrincipalName) {
/*
* Separate the realm component from the name and use the rest of
* it for constructing the filename. If the principal name is
* "joe@REALM" then the file that will be read is
* "data/joe_info.txt". The path separator "/" might be "\" in the
* case of Windows.
*/
int realmSeparatorPos = kerberosPrincipalName.lastIndexOf('@');
fileName = "data" + File.separatorChar
+ kerberosPrincipalName.substring(0, realmSeparatorPos)
+ "_info.txt";
}
/**
* Does the actual reading of the file. It displays the text contained
* in the file.
*/
public Object run() {
System.out.println("===============================================");
System.out.println("Reading file: " + fileName);
try {
BufferedReader reader = new BufferedReader(new FileReader(fileName));
String str = reader.readLine();
while (str != null) {
System.out.println(str);
str = reader.readLine();
}
} catch (IOException e) {
System.err.println(e);
}
System.out.println("===============================================");
return null;
}
}
serverimp.policy
grant CodeBase "file:./Login.jar" {
permission java.security.AllPermission;
};
grant CodeBase "file:./SampleServerImp.jar"
Principal javax.security.auth.kerberos.KerberosPrincipal
"service_principal@your_realm" {
permission java.net.SocketPermission "*", "accept";
permission javax.security.auth.kerberos.ServicePermission
"service_principal@your_realm", "accept";
permission javax.security.auth.AuthPermission "doAsPrivileged";
};
grant CodeBase "file:./ReadFileAction.jar"
Principal javax.security.auth.kerberos.KerberosPrincipal
"your_user_name@your_realm" {
permission java.io.FilePermission
"data/your_user_name_info.txt", "read";
};
csImpLogin.conf
/**
* Login Configuration for JAAS.
*/
SampleClient {
com.sun.security.auth.module.Krb5LoginModule required;
};
SampleServerImp {
com.sun.security.auth.module.Krb5LoginModule required storeKey=true principal="service_principal@your_realm";
};