Client side single sign-on using SPNEGO with Java and
JGSS
Index
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:
- 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.
- 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
|