IBM Java Generic Security Service API (JGSS) 1.0.1
Application Developer's Guide
Contents
Note: Before using this information and the product it supports, be sure to read the general information under Notices.
This edition of the Developers Guide applies to the IBM 32-bit SDK for AIX, Java 2 Technology Edition, Version 1.4.2, and to all subsequent releases and modifications until otherwise indicated in new editions.
(c) Copyright Sun Microsystems, Inc. 1998, 2004, 901 San Antonio Rd., Palo Alto, CA 94303 USA. All rights reserved.
(c) Copyright International Business Machines Corporation, 1998, 2004. All rights reserved.
U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
Introduction
IBM JGSS 1.0.1 is a Java Generic Security Service Application Programming Interface (GSSAPI) framework with Kerberos V5 as the underlying default security mechanism. GSSAPI is a standardized abstract interface under which can be plugged different security mechanisms based on private-key, public-key and other security technologies. GSSAPI shields secure applications from the complexities and peculiarities of the different underlying security mechanisms. GSSAPI provides identity and message origin authentication, message integrity, and message confidentiality.
IBM JGSS 1.0.1 also features optional Java Authentication and Authorization Service (JAAS) Kerberos login interface and authorization checks. JAAS augments the access control features of Java 2, which is based on CodeSource with access controls based on authenticated principal identities.
Additional Documentation
Readers of this guide be familiar with the high-level GSSAPI specification and the Java™ bindings specification. IBM JGSS 1.0.1 is primarily based on and conforms to these specifications. Readers should also have read the documentation that accompanies IBM JGSS 1.0.1:
Application Programming Interfaces (APIs)
The User's Guide lists several publicly accessible packages contained in the product. However, application developers should use only the APIs from the standardized org.ietf.jgss package. Use of this package will make their applications conform to the specifications and ensure their optimum interoperability.
Transporting Tokens
Some of the important JGSS operations generate tokens in the form of Java byte arrays. It is the responsibility of the application to forward the tokens from one JGSS peer to the other. The protocol used by the application for transporting tokens is not constrained by JGSS in any way and JGSS tokens can be transported together with other application (non-JGSS) data. But only JGSS-specific tokens can be fed into JGSS operations.
Summary of Application Programming Steps
A GSSAPI application follows the GSSAPI operational model and consists of GSSAPI constructs in the following order:
- Creation of a GSSManager: an instance of GSSManager acts as a factory for creating other JGSS object instances
- Creation of a GSSName: a GSSName represents the identity of a JGSS principal. Some JGSS operations are capable of locating and using a default principal when a null GSSName is specified.
- Creation of a GSSCredential: a GSSCredential embodies the mechanism-specific credentials of the principal.
- Creation of a GSSContext: a GSSContext is used for context establishment and subsequent per-message services.
- Selection of optional services on the context: services such as mutual authentication are optional and must be explicitly requested for.
- Context establishment: the initiator authenticates itself to the acceptor; optionally, when mutual authentication is requested, the acceptor in turn authenticates itself to the initiator.
- Use of per-message services: the initiator and the acceptor exchange secure messages over the established context.
- Deletion of context: the context is deleted when it is no longer needed.
These steps apply to both the initiator and the acceptor and are covered in detail in the sections that follow. The code segments below illustrate the use of the high-level JGSS APIs and assume the importation of the org.ietf.jgss package. Many of the high-level APIs are overloaded but only the most commonly used forms of those methods are illustrated. Application developers are encouraged to use whichever versions of the APIs best suit their needs.
GSSManager Creation
GSSManager is an abstract class that serves as a factory for the creation of GSSName, GSSCredential, and GSSContext. It also has methods for determining the supported security mechanisms and name types. The IBM JGSS framework includes an implementation of GSSManager. An instance of this default GSSManager can be created by using the GSSManager getInstance static method:
GSSManager manager = GSSManager.getInstance();
GSSName Creation
A GSSName represents the identity of a GSSAPI principal. GSSManager has several overloaded methods for creating a GSSName. These methods create a GSSName either from a string or a contiguous array of bytes. The methods interpret the name string or byte array according a name type that must be specified. Finally, each name creation method has a variety for creating a mechanism name (MN), as per shown in the Javadoc for a given mechanism. The byte-array methods are typically used to reconstitute an exported name; the name is typically a mechanism name and of type GSSName.NT_EXPORT_NAME.
To create a GSSName for the user "foo"
GSSName fooName = manager.createName("foo", GSSName.NT_USER_NAME);To create a Kerberos V5 mechanism name for the same user,
Oid krb5Mech = Oid.getInstance("1.2.840.113554.1.2.2"); GSSName fooName = manager.createName("foo", GSSName.NT_USER_NAME, krb5Mech);A mechanism name can be created from a non-mechanism name by using the canonical method of a GSSName:
GSSName fooName = manager.createName("foo", GSSName.NT_USER_NAME); GSSName fooKrb5Name = fooName.canonicalize(krb5Mech);
Kerberos service name strings must be specified as either <service> or <service@host> where <service> is the name of the service and <host> is the hostname of the machine on which the service runs. The hostname may or may not be fully qualified. Where the "@<host>" portion is omitted, the local hostname is used.
GSSCredential Creation
GSSManager has two credential creation methods. The only difference between the two methods is that one returns credentials for a single mechanism whereas the other returns credentials for an array of mechanisms. Specifying a null mechanism will return credentials for the default mechanism. Specifying a null array of mechanisms will result in credentials for the default set of mechanisms. The only IBM JGSS default mechanism is the Kerberos V5 mechanism.
Only one of three credentials types (initiate, accept, initiate and accept) can be created at a time. A context initiator creates initiate credentials, the acceptor creates accept credentials, and an acceptor that will also behave as initiator creates initiate and accept credentials.
To obtain Kerberos V5 credentials for the initiator "foo" with the default validity period:
GSSCredential fooCreds = manager.createCredential(fooName, GSSCredential.DEFAULT_LIFETIME, krb5Mech,GSSCredential.INITIATE);
To obtain an all-default acceptor credential:
GSSCredential serverCreds = manager.createCredential(null, GSSCredential.DEFAULT_LIFETIME, (Oid)null, GSSCredential.ACCEPT);
GSSContext Creation
GSSManager has three methods for creating a context, one for use by the context initiator, one by the acceptor, and one for recreating a previously exported context. An initiator context cannot be used for context acceptance and vice-versa. Each of the first two methods requires a credential as input, but null can be specified for the default credential to be used.
To create a context, valid for the default period, with which "foo" can initiate a context with its peer "superSecureServer" running on the host "securityCentral":
GSSName serverName = manager.createName("superSecureServer@securityCentral", GSSName.NT_HOSTBASED_SERVICE, krb5Mech); GSSContext fooContext = manager.createContext(serverName, krb5Mech, fooCreds, GSSCredential.DEFAULT_LIFETIME);
To create a context for "superSecureServer" to accept contexts initiated by any peer,
GSSContext serverAcceptorContext = manager.createContext(serverCreds);
Note the name of the acceptor context created for the server. It indicates that a server can simultaneously possess and use both an acceptor and an initiator context.
Selection of Optional Security Services
A number of security services - delegation, mutual authentication, replay detection, and out-of-sequence detection - are optional and must be requested explicitly. An optional service is requested by using the applicable requestX method on the context, where X is the service being requested. For example, "foo" uses the following calls to request that mutual authentication and delegation services be enabled on fooContext:
fooContext.requestMutualAuth(true);
fooContext.requestCredDeleg(true);
Only an initiator can request these optional services and the request must be made before context establishment begins.
Context Establishment
The two communicating peers must establish a security context over which they can use per-message services. The initiator calls initSecContext on its context, which returns a token to the application. The initiator's application transports the context token to the acceptor's application. The acceptor's application calls acceptSecContext on the acceptor's context, specifying the context token received from the initiator. Depending on the underlying mechanism and the optional services selected by the initiator, the acceptSecContext can produce a token which the acceptor's application has to forward to the initiator's application; the initiator's application then calls initSecContext one more time with the received token.
Indeed, multiple calls can be made to both initSecContext and acceptSecContext and multiple tokens exchanged between the two peers during context establishment. Therefore, context establishment is typically accomplished in a programming construct in which initSecContext or acceptSecContext is called in a loop until the context is established.
Being the initiator, foo's side of the context establishment can be coded as:
byte array[] inToken = null; // The input token is null for the first call
int inTokenLen = 0;
do { byte array[] outToken = fooContext.initSecContext(inToken, 0, inTokenLen);
if (outToken != null) send(outToken); // transport token to acceptor
inToken = receive(); // receive token from acceptor
inTokenLen = inToken.length;
} while (!fooContext.isEstablished());
The acceptor code for establishing context can be coded:
do { byte array[] inToken = receive(); // receive token from initiator
byte array[] outToken = serverAcceptorContext.acceptSecContext(inToken, 0, inToken.length);
if (outToken != null) send(outToken); // transport token to initiator
} while (!isEstablished());
Per-message Services
Having established a security context, the two communicating peers can now exchange secure messages over the established context. Either of the two parties can be the originator of a secure message irrespective of their role (initiator or acceptor) in the context establishment process. A message is made secure by computing a cryptographic message integrity code (MIC) over the message. The message can also be encrypted for privacy.
JGSS provides two sets of methods, the wrap and the getMIC methods, for securing messages. A wrap method computes a MIC and optionally encrypts the message. It returns a token that contains both the MIC and ciphertext (if the message was encrypted) or the original plain text (if the message was not encrypted). The caller specifies , using the MessageProp input whether or not the message is to be encrypted. A getMIC method, on the other hand, only computes a MIC over the message. The token it returns is made up entirely of the computed MIC and does not include the original message. Therefore, in addition to transporting the MIC token to the peer, the peer must somehow be made aware of the original message so that it can verify the MIC.
Here is how foo might wrap a message for delivery to superSecureServer:
byte array[] message = "Ready to roll!".getBytes();
MessageProp mprop = new MessageProp(true); // foo wants the message encrypted, too
byte array[] wrappedMessage = fooContext.wrap(message, 0, message.length, mprop);
send(wrappedMessage); // transfer the wrapped message to superSecureServer
Here is how superSecureServer might obtain a MIC for delivery to foo:
byte array[] message = "You bet!".getBytes();
MessageProp mprop = null; // superSecureServer is content with the default quality of protection
byte array[] mic = serverAcceptorContext.getMIC(message, 0, message.length, mprop);
send(mic); // send the MIC to foo. foo will also require the original message to verify the MIC
The receiver of a wrapped message uses the unwrap method to decode the message. The method verifies the cryptographic MIC embedded in the message and returns the original message over which the MIC was computed by the sender. If the message was encrypted, the unwrap method decrypts the message before verifying the MIC and returning the original plain text message. The receiver of a MIC token uses the verifyMIC method to verify the MIC over a given a message.
The peer applications use their own protocol to deliver JGSS context and message tokens to each other. They should also define a protocol for determining the type of a given message token, that is, whether the token is a MIC or a wrapped message. For example, part of such a protocol may be as simple (and rigid) as that used by
Simple Authentication And Security Layer (SASL) GSSAPI applications in which the context acceptor is always the first to send a per-message (wrapped) token following context establishment.
superSecureServer unwraps the wrapped token received from foo:
MessageProp mprop = new MessageProp(false);
byte array[] plaintextFromFoo = serverAcceptorContext.unwrap(wrappedTokenFromFoo, 0, wrappedTokenFromFoo.length, mprop);
// superSecureServer can now examine mprop to determine the message properties (such as whether the message was encrypted) applied by foo.
foo verifies the MIC received from superSecureServer thus:
MessageProp mprop = new MessageProp(false);
fooContext.verifyMIC(micFromFoo, 0, micFromFoo.length, messageFromFoo, 0, messageFromFoo.length, mprop);
// foo can now examine mprop to determine the message properties applied by superSecureServer. In particular, it can assert that the message was not encrypted because getMIC should never encrypt a message.
Context Deletion
A context object should be deleted when the context is no longer needed. In JGSS, each peer unilaterally decides when to delete a context and does not need to inform its peer - JGSS does not define a "delete context" token. A context is deleted by calling its dispose method to free up any resources used by the context. Unless is set to null by the application, a disposed context might still be accessible but any attempt to use it will result in an exception.
Enabling The JAAS Login Facility
IBM JGSS includes an optional JAAS login facility, which saves principal credentials and secret keys in the Subject of the application's JAAS login context. JGSS retrieves credentials and secret keys from the Subject by default. However, this feature can be disabled by setting the Java™ property javax.security.auth.useSubjectCredsOnly to false.
To use the JAAS login facility, an application must follow the JAAS programming model. It must create a JAAS login context and operate within the confines of a JAAS Subject doAs construct as illustrated in the following code segment:
static class JGSSOperations implements PrivilegedExceptionAction { public JGSSOperations() {} public Object run () throws GSSException { // JGSS application code goes/runs here
}
}
public static void main(String args[]) throws Exception { // Create a login context that will use the callback handler com.ibm.security.auth.callback.Krb5CallbackHandler
// There must be a JAAS configuration for "JGSSClient"
LoginContext loginContext = new LoginContext("JGSSClient", new Krb5CallbackHandler()); // Run the entire JGSS application in JAAS privileged mode
Subject.doAsPrivileged(Subject.getSubject(), new JGSSOperations(), null);
}
Debugging
The IBM JGSS framework includes a debug class with APIs that applications can use. The class categorizes debugging and only outputs information when the requested debug category is in effect. Its default constructor reads the Java™ property com.ibm.security.jgss.debug to determine which categories to turn on.
To request debug information for the application category,
import com.ibm.security.jgss.debug;
Debug debug = new Debug(); // gets categories from Java™ property
// Lots of work required to set up someBuffer. Test that the category is on before setting up for debugging
if (debug.on(Debug.OPTS_CAT_APPLICATION)) { // Jump hoops to set up someBuffer …
debug.out(Debug.OPTS_CAT_APPLICATION, someBuffer); // someBuffer may be a byte array or a String.
}
Appendix A: Sample Programs
The source code for the following sample programs are provided in the zip file:
- Sample Client Program
- Sample Server Program
- Sample JAAS-Enabled Client Program
- Sample JAAS-Enabled Server Program
Notices
This information was developed for products and services offered in the U.S.A. IBM may not offer the products, services, or features discussed in this document in other countries. Consult your local IBM representative for information on the products and services currently available in your area. Any reference to an IBM product, program, or service is not intended to state or imply that only that IBM product, program, or service may be used. Any functionally equivalent product, program, or service that does not infringe any IBM intellectual property right may be used instead. However, it is the user's responsibility to evaluate and verify the operation of any non-IBM product, program, or service.
IBM may have patents or pending patent applications covering subject matter in this document. The furnishing of this document does not give you any license to these patents. You can send license inquiries, in writing, to:
IBM Director of Licensing
IBM Corporation
North Castle Drive, Armonk
NY 10504-1758 U.S.A.
For license inquiries regarding double-byte (DBCS) information, contact the IBM Intellectual Property Department in your country or send inquiries, in writing, to:
IBM World Trade Asia Corporation Licensing
2-31 Roppongi 3-chome, Minato-ku
Tokyo 106-0032, Japan
The following paragraph does not apply to the United Kingdom or any other country where such provisions are inconsistent with local law:
INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THIS PUBLICATION "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Some states do not allow disclaimer of express or implied warranties in certain transactions, therefore, this statement may not apply to you.
This information could include technical inaccuracies or typographical errors. Changes are periodically made to the information herein; these changes will be incorporated in new editions of the information. IBM may make improvements and/or changes in the product(s) and/or the program(s) described in this information at any time without notice.
Any references in this information to non-IBM Web sites are provided for convenience only and do not in any manner serve as an endorsement of those Web sites. The materials at those Web sites are not part of the materials for this IBM product and use of those Web sites is at your own risk.
IBM may use or distribute any of the information you supply in any way it believes appropriate without incurring any obligation to you.
Licensees of this program who wish to have information about it for the purpose of enabling (i) the exchange of information between independently created programs and other programs (including this one) and (ii) the mutual use of the information which has been exchanged, should contact:
JIMMAIL@uk.ibm.com
[Hursley Java Technology Center (JTC) contact]
Such information may be available, subject to appropriate terms and conditions, including in some cases, payment of a fee.
The licensed program described in this document and all licensed material available for it are provided by IBM under terms of the IBM Customer Agreement, IBM International Program License Agreement or any equivalent agreement between us.
Any performance data contained herein was determined in a controlled environment. Therefore, the results obtained in other operating environments may vary significantly. Some measurements may have been made on development-level systems and there is no guarantee that these measurements will be the same on generally available systems. Furthermore, some measurement may have been estimated through extrapolation. Actual results may vary. Users of this document should verify the applicable data for their specific environment.
Information concerning non-IBM products was obtained from the suppliers of those products, their published announcements or other publicly available sources. IBM has not tested those products and cannot confirm the accuracy of performance, compatibility or any other claims related to non-IBM products. Questions on the capabilities of non-IBM products should be addressed to the suppliers of those products.
Trademarks
IBM is a trademark or registered trademark of International Business Machines Corporation in the United States, or other countries, or both.
Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.
Other company, product, or service names may be trademarks or service marks of others.