Currently, I am working on REST APIs and want to make them sso enabled. For this I'm using OpenAM as well as trying to write a
Java Client using Kerberos, JAAS and GSS API.
I came to know that with Open AM i can only
test PCA apps and therefore i can only execute GET APIs using the browser. Therefore, to test the rest of the PUT, POST and DELETE APIs i have written a java client using JAAS/GSS and Kerberos.
I have attached my code with the post.
My concern is that :
As the subject of Login Context I get this:
Principal: akaagarwsso@DC2K3.COM
Private Credential: Ticket (hex) =
0000: 61 82 03 8A 30 82 03 86 A0 03 02 01 05 A1 0B 1B a...0...........
0010: 09 44 43 32 4B 33 2E 43 4F 4D A2 1E 30 1C A0 03 .DC2K3.COM..0...
0020: 02 01 02 A1 15 30 13 1B 06 6B 72 62 74 67 74 1B .....0...krbtgt.
0030: 09 44 43 32 4B 33 2E 43 4F 4D A3 82 03 50 30 82 .DC2K3.COM...P0.
0040: 03 4C A0 03 02 01 17 A1 03 02 01 02 A2 82 03 3E .L.............>
0050: 04 82 03 3A CC FA 72 CC 99 D6 04 CD 28 8B DE 53 ...:..r.....(..S
................................................................
Client Principal = akaagarwsso@DC2K3.COM
Server Principal = krbtgt/DC2K3.COM@DC2K3.COM
Session Key = EncryptionKey: keyType=3 keyBytes (hex dump)=
0000: F2 EF F1 E6 6D DA 58 10
Forwardable Ticket false
Forwarded Ticket false
Proxiable Ticket false
Proxy Ticket false
Postdated Ticket false
Renewable Ticket false
Initial Ticket false
Auth Time = Fri Aug 24 04:01:36 PDT 2012
Start Time = Fri Aug 24 04:01:36 PDT 2012
End Time = Fri Aug 24 14:01:36 PDT 2012
Renew Till = null
Client Addresses Null
I am getting the principal key in return. But when I execute the API, I get 500 Internal Server Error. This generally comes when the session has expired for SSO and I try to hit the API on the browser. So this means that the reason for this Internal Server Error is that as soon as I send the key to login to the server, the session expires or may be the credentials are incorrect.
FYI:
AD Info:
ucbu-vm87.dc2k3.com
Domain: dc2k3.com
User: akaagarwsso
Jaas.conf:
GSSClient{
com.sun.security.auth.module.Krb5LoginModule required
useTicketCache=false;
};
and the CallBackHandler:
import java.io.*;
import java.security.*;
import javax.security.auth.*;
import javax.security.auth.callback.*;
public class BeanCallbackHandler implements CallbackHandler {
// Store username and password.
String name = null;
String password = null;
public BeanCallbackHandler(String name, String password) {
System.out.println("The servername and password are::::" + name + " and " + password);
this.name = name;
this.password = password;
}// BeanCallbackHandler
public void handle(Callback[] callbacks)
throws UnsupportedCallbackException, IOException {
for (int i = 0; i < callbacks.length; i++) {
Callback callBack = callbacks[i];
// Handles user-name call-back.
if (callBack instanceof NameCallback) {
NameCallback nameCallback = (NameCallback) callBack;
nameCallback.setName(name);
// Handles password callback.
} else if (callBack instanceof PasswordCallback) {
PasswordCallback passwordCallback = (PasswordCallback) callBack;
passwordCallback.setPassword(password.toCharArray());
} else {
throw new UnsupportedCallbackException(callBack,
"Call back not supported");
}// else
}// for
}// handle
}// BeanCallbackHandler
I also tried to create a simple html using Ajax call and when i opened that html in browser and hit a GET API, it also gave me 500 internal server error. For this thing what i could see in the firebug is that the cookie was not getting passed and probably that was the reason it was not able to authenticate.
However, I couldn't figure out the reason of failure for the JAAS based java app.
I don't understand what am i missing in both the approaches. Any help and guidance would be really appreciable.
Following i sthe code for Test.java:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.ietf.jgss.GSSException;
public class test {
private static GSSClient gssClient;
/**
* @param args
* @throws IOException
* @throws HttpException
* @throws InterruptedException
*/
public static void main(String[] args) throws HttpException, IOException, InterruptedException {
String theClientCred = authenticateAndGetKerberosToken();
String serviceAddress = "https://ucbu-aricent-vm199.dc2k3.com:8443/vmrest/user";
// String serviceAddress1 = "https://ucbu-aricent-vm266.pankaj.aricent-lab.cisco.com/vmrest/users?templateAlias=voicemailusertemplate";
//String message = "Akanksha";
// System.out.println(message);
//try {
//byte[] wrapedMsg = gssClient.wrapMessage(message.getBytes());
//serviceAddress += Base64.encodeBase64URLSafeString(wrapedMsg);
System.out.println("The service address is:::"+serviceAddress);
//} catch (GSSException e) {
//e.printStackTrace();
//}
//File file1 = new File("C:/Documents and Settings/ssouser/Desktop/Code/user.xml");
//GetMethod get = new GetMethod(serviceAddress);
GetMethod get = new GetMethod(serviceAddress)
{
@Override
public boolean getFollowRedirects()
{
return true;
}
};
get.setRequestHeader("Authorization", theClientCred);
get.setRequestHeader("Content-type", "application/xml; charset=ISO-8859-1");
//post.setRequestBody(new FileInputStream(file1));
HttpClient httpclient = new HttpClient();
// httpclient.getState().setCookiePolicy(CookiePolicy.COMPATIBILITY);
try {
int result = httpclient.executeMethod(get);
String s = get.getResponseBodyAsString();
System.out.println("Response status code: " + result);
System.out.println("Response body: " + s);
//System.out.println("After decryptionm: " + new String(gssClient.unWrapMessage(Base64 .decodeBase64(get.getResponseBodyAsString()))));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/* finally {
// Release current connection to the connection pool once you are done
get.releaseConnection();
}*/
// Thread.sleep(1000000);
}
private static String authenticateAndGetKerberosToken() {
System.out.println("I'm INNNNNN");
gssClient = new GSSClient("akaagarwsso", "Ecsbulab1");
byte[] b = gssClient.login();
System.err.println("The bytes are::: " + b);
String serviceTicket = Base64.encodeBase64String(b);
return serviceTicket;
}
}//End of Class
And the Code for GSSClient.java:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Properties;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.callback.*;
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.MessageProp;
import org.ietf.jgss.Oid;
public class GSSClient implements java.security.PrivilegedAction<byte[]> {
// Handles callback from the JAAS framework.
private BeanCallbackHandler beanCallbackHandler = null;
// The main object that handles all JAAS login.
LoginContext peerLC = null;
// Socket and streams used for communication.
Socket socket = null;
DataInputStream inStream;
DataOutputStream outStream;
// This and remote clients.
String clientName = null;
String serverName = null;
// Address and port of remote server.
String serverAddress = null;
int serverPort;
// The name of the client configuration.
String confName = null;
private GSSContext clientGSSContext = null;
/**
* @param clientName, Client SPN
* @param password, client password
*/
public GSSClient(String clientName, String password){
System.err.println("The clientname in GSSClient ::"+ clientName);
Properties env = new Properties();
try {
env.load(this.getClass().getClassLoader().getResourceAsStream("gssclient.properties"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.clientName = clientName;
this.serverName = (String)env.getProperty("Server.SPN");
this.serverAddress = (String)env.getProperty("Server.Address");
this.confName = (String)env.getProperty("JAAS.Config.Name");
beanCallbackHandler = new BeanCallbackHandler(clientName, password);
setSystemProperties((String)env.getProperty("KDC.Realm"),
(String)env.getProperty("KDC.Address"),
(String)env.getProperty("JAAS.Config.Path"));
}
/**
* The GSSClient constructor only sets all the required parameters.
*
* @param clientName, Client SPN
* @param password, client password
* @param serverName, Remote server/service SPN
* @param serverAddress, Remote Server address IP or Hostname
* @param kerberosRealm, KDC realm or domain name
* @param kdcAddress, KDC Server address IP or Hostname
* @param confFile, fll path of JAAS COnfig file
* @param confName, JAAS config name
*/
public GSSClient(String clientName, String password, String serverName,
String serverAddress, String kerberosRealm,
String kdcAddress, String confFile, String confName){
// The beanCallbackHandler will require the name and password of the
// client.
beanCallbackHandler = new BeanCallbackHandler(clientName, password);
this.clientName = clientName;
this.serverName = serverName;
this.serverAddress = serverAddress;
this.confName = confName;
setSystemProperties(kerberosRealm, kdcAddress, confFile);
}// KerberoseLoginBean
private void setSystemProperties(String kerberosRealm, String kdcAddress,
String confFile) {
System.setProperty("java.security.krb5.realm", kerberosRealm);
System.setProperty("java.security.krb5.kdc", kdcAddress);
System.setProperty("java.security.auth.login.config", confFile);
System.setProperty("sun.security.krb5.debug", "true");
}
public byte[] login() {
try {
System.out.println("I'm inside LOGIN");
peerLC = new LoginContext(confName, beanCallbackHandler);
peerLC.login();
System.err.println("2222222222222"+peerLC.getSubject());
System.err.println("3333333333333"+peerLC.getSubject().getPrincipals());
return Subject.doAs(peerLC.getSubject(), this);
} catch (Exception e) {
System.out
.println(">>>> GSSClient....Secure Context not established..");
e.printStackTrace();
return null;
}// catch
}// establishSecureContextWithServer
// This is the only method in PrivilegedAction interface.
// It receives control only in case of successful authentication of the
// client.
public byte[] run() {
try {
GSSManager manager = GSSManager.getInstance();
Oid kerberos = new Oid("1.2.840.113554.1.2.2");
System.err.println("INSIDE RUN!!!");
GSSName clientPeerName = manager.createName(
// Name of the client for which we want to create this GSSName
// object.
clientName,
// Type of GSSName. Our client is a Windows user,
// which we can specifiy using GSSName.NT_USER_NAME
// property.
GSSName.NT_USER_NAME);
// GSSName remotePeerName2 = manager.createName("FakeSERver",
// GSSName.NT_USER_NAME);
GSSName remotePeerName = manager.createName(serverName,
GSSName.NT_USER_NAME);
System.err.println(">>> GSSClient... Getting client credentials");
GSSCredential peerCredentials = manager.createCredential(
// The GSSName object of the client.
clientPeerName,
// Time for which credentials whill be valid.
10 * 600,
// Kerberos mecahnism identifier.
kerberos,
// The client only intiates the secure context request.
GSSCredential.INITIATE_ONLY);
System.err
.println(">>> GSSClient...GSSManager creating security context");
clientGSSContext= manager.createContext(remotePeerName, kerberos,
peerCredentials, GSSContext.DEFAULT_LIFETIME);
// peerContext.requestCredDeleg(true);
clientGSSContext.requestConf(true);
clientGSSContext.requestMutualAuth(false);// I don't want to authenticate the
// Server.
byte[] byteToken = new byte[0];
byteToken = clientGSSContext.initSecContext(byteToken, 0,
byteToken.length);
return byteToken;
}// try
catch (org.ietf.jgss.GSSException ge) {
ge.printStackTrace();
System.err.println(">>> GSSClient... GSS Exception "
+ ge.getMessage());
}
catch (java.lang.Exception e) {
e.printStackTrace();
System.err.println(">>> GSSClient... Exception " + e.getMessage());
}// catch
return null;
}// run
// It returns the established login context to client.
public LoginContext getLoginContext() {
System.err.println("INSIDE GET LOGIN CONTEXT");
return peerLC;
}// getloginContext
public byte[] wrapMessage(byte[] msg) throws GSSException {
MessageProp msgProp = new MessageProp(0, false);
return clientGSSContext.wrap(msg, 0, msg.length, msgProp);
}
public byte[] unWrapMessage(byte[] msg) throws GSSException {
MessageProp msgProp = new MessageProp(0, false);
return clientGSSContext.unwrap(msg, 0, msg.length, msgProp);
}
}// End of GSSClient
Thanks in advance!
Best Regards,
Akanksha A