Applied Crypto
Hassle free single sign-on integrated with your enterprise windows domain Online Users: 11
(c) copyright
appliedcrypto.com

  Home Products Support  
Articles
References
SPNEGO SSO

Client side single sign-on using SPNEGO with Java and JGSS

Index

Introduction
The code...
Example output
Conclusion
About the author

Introduction

This article describes how to generate a SPNEGO/Kerberos token that can be used for authentication from either Java applications or applets with the purpose of doing single sign-on

We will show a method of getting the Kerberos tickets using the Microsoft ticket cache, which will enable full SSO support from Java applications and applets. But also Java file based ticket cache works.

The objective is to be able to generate a SPNEGO/Kerberos token at client side, just like what the Internet Explorer is capable of. This will extend SPNEGO/Kerberos support to client side Java applications and Java applets, which will enable SSO to such systems and platforms.

Web services can be handled by adding the SPNEGO/Kerberos token to the SOAP header. This will give Java applications using web services as communications protocol between a client and a web services server, a single sign-on (SSO) functionality

The JGSS based Tomcat SPNEGO/Kerberos Authentication and the Microsoft IIS are supported

The code...

The first thing we need to do is to initialize the ticket cache with a valid ticket granting ticket. Now we can choose between two ticket caches:

  1. a ticket cache located on the file system which is managed by Java. This ticket cache is per-user and is located in the "user.home" directory.
  2. the built-in ticket cache in windows 2000 or XP clients. This requires that the user is logged on to a valid domain

Java will use the file system based if this can be located in the "user.home" directory. Its created by running the commandline utility:

	%JAVA_HOME%/bin/kinit

It will prompt the user for a password. It contacts the Ticket granting service and use the password to decrypt the ticket granting ticket.

The same program can be called programatically by calling sun.security.krb5.internal.tools.Kinit.class

	String[] args = new String[2];
	args[0] = userName + "@" + System.getProperty("java.security.krb5.realm");
	args[1] = password;
	sun.security.krb5.internal.tools.Kinit kinit = 
		new sun.security.krb5.internal.tools.Kinit(args);

If the file is not created using above utilities or simply deleted, Java will use the windows based ticket cache by calling a native library

	// we need to make the system think we are running on top of window 2000.
	// otherwise, java wont try to use the native ticket cache.
	System.setProperty("os.name", "windows 2000");

	// first get the ticket granting ticket credentials
	Credentials c = Credentials.acquireDefaultCreds();
	System.out.println(c);
	
	// then get the ticket granting ticket from the cache.
	System.out.println("aquireTGT***");
	Credentials x = Credentials.acquireTGTFromCache(c.getServer(), null);
	System.out.println(x);

Due to a small bug with the client principal name not beeing initialized with the Realm, we need to create a Credentials object based on the returned from the TGS aquire method:

	// now construct the client principal name including realm information
	PrincipalName client = new PrincipalName(x.getClient().toString(), 
		PrincipalName.KRB_NT_PRINCIPAL);
	System.out.println("clientPrincipal***\r\n" + client );

	// then reconstruct the ticket granting ticket to contain 
	// the client principal name + realm information
	// this is a workaround for the bug in the KrbApReq DER 
	// encoding (when requesting service ticket)
	Credentials x1 = new Credentials(
		x.getTicket(), 
		client, 
		x.getServer(), 
		x.getSessionKey(), 
		x.getTicketFlags(), 
		new KerberosTime(x.getAuthTime()), 
		new KerberosTime(x.getStartTime()), 
		new KerberosTime(x.getEndTime()), 
		new KerberosTime(x.getRenewTill()), 
		new HostAddresses(x.getClientAddresses()));

Finally aquire the service ticket - the service credentials

	Credentials s = Credentials.acquireServiceCreds("HTTP/spnego.test.net@TEST.NET", x1);
	System.out.println(s);

Now we have the service ticket, which contains encrypted data which can be decrypted only by the specified service principal - the server

In order to send this over the wire as specified on the GSS-API specification, we first need to wrap the service ticket into a KRB-AP-REQ token. This token, which we will call the 'inner token', is again wrapped and stored inside the SPNEGO/Kerberos token according to RFC specifications.

First we generate the KRB-AP-REQ from the service credentials object:

	KrbApReq apreq = new KrbApReq(s, true, true, true);

Then we encode the AP-REQ into the inner token

		
	DerOutputStream innerContextToken = new DerOutputStream();
	// write the TOK_ID for KRB_AP_REQ as defined in RFC1964
	innerContextToken.write(new byte[]{0x01, 0x00});
	// write the KRB_AP_REQ
	innerContextToken.write(apreq.getMessage());
		
	DerOutputStream initialContextTokenContent = new DerOutputStream();
	// write thisMech
	// -- MechType is OBJECT IDENTIFIER
	// -- representing "Kerberos V5"
	initialContextTokenContent.putOID(new ObjectIdentifier("1.2.840.113554.1.2.2"));
	// write the innerContextToken
	initialContextTokenContent.write(innerContextToken.toByteArray());
		
	// write the Application 0 tag and length	
	DerOutputStream initialContextToken = new DerOutputStream();
	initialContextToken.write((byte)0x60, initialContextTokenContent);
		
	System.out.println(HexDump.xdump(initialContextToken.toByteArray()));

Note that the resulting initialContextToken is what server side uses to verify the kerberos ticket

The SPNEGO/Kerberos token can then be created from the inner token

	Spnego spnego = new Spnego();
	spnego.setServiceCredentials(s);
	System.out.println(HexDump.xdump(spnego.getEncodedNegTokenInit()));

The only thing we need now is to send the token using some URL connection code. The server side must pull the token from the request an pass it on to the SpnegoContext handler, which decodes and calls the appropriate JGSS methods.

Example output

An example of the system output where client and server are setup to run in debug mode.

The provided output is provided as an example and will not reflect the system output of the library running in production mode

Conclusion

We have shown how to either create a client side ticket cache or grab the existing ticket cache if client is running in a windows environment.

We have shown how to build an 'inner token' which is the internals of the SPNEGO/Kerberos token.

We have shown how to use the SPNEGO/Kerberos library to create a SPNEGO/Kerberos token, which can be used for authentication

The content of this article can be used to create Java applications or applets which uses the native windows ticket cache to enable true SSO between windows and non Internet Explorer browsers.

This can be used in thick java applications, applets running in java plugins (which enables SPNEGO/Kerberos support in java enabled browsers).

The client SPNEGO/Kerberos library has been tested with the Tomcat SPNEGO/Kerberos authenticator and with Microsoft Internet Information Server (IIS).

For more information about the SPNEGO/Kerberos library, you can contact spnego_AT_it-practice.dk

About the author

Bo Friis is working as a security consultant for IT Practice in Denmark. He has specialized in security protocols and implementations. He is working on security solutions for various customers and is the co. architect of PortalProtect. He has designed and developed the initial version of opensign and openlogon, a set of applets that supports digital signature using X.509 certificates over the XMLDSIG standard. The result was donated to the open source OpenOCES project.

Bo Friis holds a Masters degree in Cryptography from the University of Aarhus in Denmark. He also holds a Master of Science degree in Electrical Engineering from the Technical University of Denmark.

He can be reached at email:jbf_AT_practice.dk

(c) copyright appliedcrypto.com AppliedCrypto News RSS feed