/** * @author Terry Fleury (tfleury@ncsa.uiuc.edu) * @version 1.0 2006-09-06 * * This class provides several utility methods to assist with "Single * Sign-On" authentication of users utilizing a MyProxy server. It also has * a convenience method to create a session password upon successful * authentication of a user. Most of these methods are simply wrappers for * MyProxy methods. * * To compile, you will need the following jars in your CLASSPATH: * cog-jglobus.jar - From GT4 Java WS Core Binary installer: * http://www.globus.org/toolkit/downloads * commons-lang.jar - From Apache Jakarta Commons Lang project: * http://jakarta.apache.org/commons/lang * log4j.jar - Available lots of places, but you can use the one from * the GT4 Java WS Core Binary package * * To run, you will need the following ADDITIONAL jars in your CLASSPATH: * cryptix32.jar - From GT4 Java WS Core Binary * cryptix-asn1.jar - From GT4 Java WS Core Binary * puretls.jar - From GT4 Java WS Core Binary */ package edu.uiuc.ncsa.myproxy; // In standard Java import org.ietf.jgss.GSSCredential; import java.rmi.dgc.VMID; // In cog-jglobus.jar import org.globus.myproxy.MyProxy; import org.globus.myproxy.DestroyParams; import org.globus.myproxy.GetParams; import org.globus.myproxy.InitParams; import org.globus.myproxy.InfoParams; import org.globus.myproxy.CredentialInfo; // In commons-lang.jar import org.apache.commons.lang.RandomStringUtils; // In log4j.jar import org.apache.log4j.Logger; import org.apache.log4j.Level; /** * The SSOUtils (Single Sign-On Utilities) class consists of several static * methods to assist with the creation and authentication of session * passwords using a MyProxy server. To use these methods, you will need to * have a MyProxy server set up with MyProxyCA to create user credentials * on-the-fly, and also configured with "check_multiple_credentials" so as * to be able to store multiple (randomly named) credentials for a given * username. The two main methods you will use are * createSessionPassword and authenticateUser. * The first method takes in a username/password and returns a new random * session password for that username. It also has the "side effect" of * authenticating the username (if the returned session password is not * empty). The second method takes in a username/password and returns * true/false for if that username authenticates with the MyProxy server. * Note that in each method the "password" can be either the username's long * lived password OR a previously created session password. * * Example usage: * *
 * import edu.uiuc.ncsa.myproxy.SSOUtils;
 *
 * String username;
 * String password;
 * String sessionpassword;
 *
 * sessionpassword = SSOUtils.createSessionPassword(
 *     "myproxy-test.ncsa.uiuc.edu",7512,username,password);
 *
 * if (sessionpassword.length() > 0) {
 *     // We successfully authenticated the given username/password
 *     // AND created a new random session password for that username.
 *     // We can use the new session password in subsequent authentication
 *     // attempts of the username.
 * }
 *
 * if (SSOUtils.authenticateUser("myproxy-test.ncsa.uiuc.edu",7512,
 *     username,sessionpassword)) {
 *     // We successfuly authenticated the given username/session password.
 *     // Note that we could also authenticate the original password 
 *     // with this method.
 * }
 * 
* * Another useful utility method is enableSSL which allows the * use of "https" in your Java URLs. */ public class SSOUtils { /** * Default lifetime of a session password, in seconds (= 4 hours). */ public static final int SSOLIFETIME = 14400; // 4 hours static Logger logger = Logger.getLogger(SSOUtils.class); /** * A short "main" test application to demonstrate how to use the utility * methods of the class. This main method can be used to test out the * functionality of the utility methods of the SSOUtils class. It takes * in a username, password, and optional MyProxy host. It creates a * session password based on that username/password (using some other * authentication mechanism) at the MyProxy host. It then tests * authentication with username/session password. The MyProxy host is * hard-coded but can be overridden by providing an alternate MyProxy * host as the third command line argument. * @param args The command line arguments. Must include USERNAME as the * first argument and PASSWORD as the second argument. May * also include a MYPROXYHOST (FQDN) as the third argument, * otherwise a default MyProxy server will be used. */ public static void main(String[] args) { // Set up the logger for outputting 'debug' messages logger.setLevel((Level)Level.DEBUG); // Check number of command line arguments if ((args.length < 2) || (args.length > 3)) { System.out.println("Usage: java edu.uiuc.ncsa.myproxy.SSOUtils " + " {MyProxyHost}\n"); } else { String username = args[0]; String password = args[1]; String myproxyhost = "myproxy-test.ncsa.uiuc.edu"; if (args.length == 3) { myproxyhost = args[2]; } // First, authenticate with the username/password just to make // sure that the provided username/password are valid. if (authenticateUser(myproxyhost,0, // Use default port username,password)) { System.out.println("*** Initial authentication of " + "username/password successful!"); // Next, using the username/password, create a session // password by storing a credential associated with the // username on the MyProxy server. String sessionpassword = createSessionPassword( myproxyhost,0,username,password); if (sessionpassword.length() > 0) { System.out.println("*** Successfully created a " + "session password = " + sessionpassword); // Finally, attempt to authenticate the username using // this new session password. if (authenticateUser(myproxyhost,0, username,sessionpassword)) { System.out.println("*** Authentication of username/" + "session password successful!"); } else { System.out.println("ERROR! Couldn't authenticate " + username + "/" + sessionpassword); } } else { System.out.println("ERROR! Couldn't create " + "session password for " + username); } } else { System.out.println("ERROR! Couldn't authenticate " + username + "/" + password); } } } /** * Allows for the use of "https" in URLs. If you want to use "https" in * your java.net.URL instances, then you need to call this * method first. This is simply a convenience method. * @return The reference position in which the SSL provider was added * (>= 0), or -1 if the SSL provider was not added because it * was already installed. */ public static int enableSSL() { System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); return java.security.Security.addProvider( new com.sun.net.ssl.internal.ssl.Provider()); } /** * Create a session password by storing a randomly named credential with * a random password on a MyProxy server. This method hides the gory * details of creating a session password for a particular * username/password combination. First, it * attempts to get a credential from the specified MyProxy server using * the specified username and password. Note * that this password can be the username's long lived password or a * previously created session password. Then, it delegates the * fetched credential back to the same MyProxy server using a random * credential name and random password. If the credential is * successfully put to the MyProxy server, the random (session) password * is returned. Otherwise, the empty string ("") is returned. You * should use this method if you only want a session password and have * no need of the user credential associated with that session password. * @param host The FQDN of the MyProxy host. * @param port The MyProxy port. If 0, then the default MyProxy port * (7512) will be used. * @param username The username for storing the credential. * @param password The password corresponding to the username. This * can be either the user's long lived password or a * previously created session password associated with * that username. * @return The random session password which was used to successfully * put a credential for the username to the MyProxy server, or * the empty string ("") if there was some sort of error. */ public static String createSessionPassword(String host, int port, String username, String password) { String randpass = ""; String randname = ""; GSSCredential credential = null; credential = getCredFromMyProxy(host,port,username,password); if (credential != null) { randpass = RandomStringUtils.randomAlphanumeric(16); randname = putRandomNamedCredToMyProxy(host,port,credential, username,randpass,SSOLIFETIME); if (randname.length() == 0) { randpass = ""; } } return randpass; } /** * Authenticate a username/password with a MyProxy server. This is a * convenience function which returns a boolean success/fail for a * username/password combination rather than * the credential for that username. Note that this method * can take either the user's long lived password or a session password. * @param host The FQDN of the MyProxy host. * @param port The MyProxy port. If 0, then the default MyProxy port * (7512) will be used. * @param username The username for storing the credential. * @param password The password corresponding to the username. This * can be either the user's long lived password or a * previously created session password associated with * that username. * @return True if the username/password successfully authenticates with * the MyProxy server, false otherwise. */ public static boolean authenticateUser(String host, int port, String username, String password) { boolean retval; retval = (getCredFromMyProxy(host,port,username,password) != null); return retval; } /** * Destroy a previously created session password. This method is a bit * of a hack. It enables you to delete a credential from a MyProxy * server by providing only the username and session password. * Typically, the destroy() method requires the name of the credential * to be deleted. But session passwords are created with a random * credential name that is not typically available to the developer. So * this method takes the long way. It first gets all stored credentials * for a username. We then have a list of credential names. It then * tries to get() each named credential utilizing the passed-in username * and (session) password. If the get() is successful, then we found * the credential associated with the session password, so we can do a * standard MyProxy destroy() call. * @param host The FQDN of the MyProxy host. * @param port The MyProxy port. If 0, then the default MyProxy port * (7512) will be used. * @param username The username for destroying the credential. * @param password The password corresponding to the username. This * is a previously created session password which was * stored with a random credential name (which you don't * know). * @return True if the credential associated with the passed-in session * password is successfully deleted from the MyProxy server, * thus rendering the session password useless. False if there * was any sort of error. */ public static boolean destroySessionPassword(String host, int port, String username, String password) { boolean retval = false; /* Assume failure */ if (port == 0) { port = MyProxy.DEFAULT_PORT; } logger.debug("destroySessionPassword() called, connecting to '" + host + ":" + port + " with username = '"+ username + "' and session password = '"+ password + "' ..."); /* Make sure there is a credential associated with the password */ GSSCredential credential = getCredFromMyProxy(host,port,username,password); if (credential != null) { CredentialInfo[] info = null; MyProxy myProxyServer = new MyProxy(host,port); /* Get a list of all stored credential for the username */ InfoParams infoParams = new InfoParams(); infoParams.setLifetime(0); infoParams.setUserName(username); infoParams.setPassphrase(password); try { logger.debug("In destroySessionPassword(), trying to get info "+ "from " + host + ":" + port + " for username = '"+ username + "' using session password = '" + password + "' ..."); info = myProxyServer.info(credential,infoParams); logger.debug("'INFO' Succeeded!"); } catch (Exception e) { info = null; logger.error("When trying to destroy credential on '" + host + ":" + port + "', info() failed because "+ e.getMessage()); } /* Scan through the list of stored creds for ones with a * * credname (i.e. NOT the default unnamed cred). Try to do * * a get() with the username, password, and credname. If * * successful, then destroy that credential */ if (info != null) { for (int i = 0; i < info.length; i++) { if (info[i].getName().length() > 0) { /* Make sure that there is a non-empty credname */ GSSCredential tempcred; GetParams getParams = new GetParams(username,password); getParams.setCredentialName(info[i].getName()); try { tempcred = myProxyServer.get(credential,getParams); } catch (Exception e) { tempcred = null; } if (tempcred != null) { /* Found it! Destroy it using the credname. */ DestroyParams destroyParams = new DestroyParams(username,password); destroyParams.setCredentialName(info[i].getName()); try { logger.debug("Trying to destroy cred named '" + info[i].getName() + "' from " + host + ":" + port + " for username = '"+ username + "' and session password = '"+ password + "' ..."); myProxyServer.destroy(credential,destroyParams); logger.debug("'DESTROY' Succeeded!"); retval = true; } catch (Exception e) { logger.error("Failed to destroy session "+ "password '" + password + "' because "+ e.getMessage()); } } } } } } return retval; } /** * Fetch a credential from a MyProxy server. This is really just * a wrapper for MyProxy's old static get method which is * now deprecated. If any of host, username, * or password is not specified (i.e. null or empty * string), then an error is logged and the return credential is null. * If the port is not specified (i.e. 0), then the default * MyProxy port (7512) will be used. If the lifetime is * not specified (i.e. 0), then a default lifetime * (SSOLIFETIME) will be used. * @param host The FQDN of the MyProxy host. * @param port The MyProxy port. If 0, then the default MyProxy port * (7512) will be used. * @param username The username for the credential. * @param password The password corresponding to the username. * @param lifetime The requested lifetime of the fetched credential, in * seconds. If set to 0, then a default value will be * used. * @return A GSS credential for the proxy credential from the MyProxy * server, or "null" if there was an error. * @see #getCredFromMyProxy(String,int,String,String) */ public static GSSCredential getCredFromMyProxy(String host, int port, String username, String password, int lifetime) { GSSCredential retcred = null; if (port == 0) { port = MyProxy.DEFAULT_PORT; } if (lifetime == 0) { lifetime = SSOLIFETIME; } if ((host.length() == 0) || (username.length() == 0) || (password.length() == 0)) { logger.error("In getCredFromMyProxy, host, username, and/or " + "password was not specified." ); } else { try { MyProxy myProxyServer = new MyProxy(host,port); logger.debug("Trying to get credential from " + host + ":" + port + " for username = '"+ username + "' using password = '" + password + "' and lifetime = '" + lifetime + "' ..."); retcred = myProxyServer.get(username,password,lifetime); logger.debug("'GET' Succeeded!"); } catch (Exception e) { logger.error("Could not get proxy from '" + host + ":" + port + "' because "+ e.getMessage()); } } return retcred; } /** * Fetch a credential from a MyProxy server. This is a convenience * method which calls the above getCredFromMyProxy with a default * lifetime (SSOLIFETIME). * @param host The FQDN of the MyProxy host. * @param port The MyProxy port. If 0, then the default MyProxy port * (7512) will be used. * @param username The username for the credential. * @param password The password corresponding to the username. * @return A GSS credential for the proxy credential from the MyProxy * server, or "null" if there was an error. * @see #getCredFromMyProxy(String,int,String,String,int) */ public static GSSCredential getCredFromMyProxy(String host, int port, String username, String password) { return getCredFromMyProxy(host,port,username,password,SSOLIFETIME); } /** * Delegate a credential to a MyProxy server under a given credential * name. This method puts a GSSCredential to a specified MyProxy * server:port under the given username with a * specified password, lifetime (in seconds), * and credential name. If any of host, * username, or password is not specified * (i.e. null or empty string), then an error is logged and failure is * retured. If the port is not specified (i.e. 0), then * the default MyProxy port (7512) will be used. If the * lifetime is not specified (i.e. 0), then a default * lifetime (SSOLIFETIME) will be used. If the credential * name is empty (""), then the credential is stored as the * user's "default" credential. * @param host The FQDN of the MyProxy host. * @param port The MyProxy port. If 0, then the default MyProxy port * (7512) will be used. * @param credential A GSSCredential to be delegated to a MyProxy server. * @param username The username for storing the credential. * @param password The password corresponding to the username. * @param lifetime The requested lifetime of the stored credential, in * seconds. If set to 0, then a default value will be * used. * @param credname The name under which to store the credential. If the * empty string (""), then the credential stored is the * "default" credential for the username. Note: Use only * alphanumeric characters for this field. * @return True if successfully put credential to MyProxy server, false * if there was some sort of error. */ public static boolean putNamedCredToMyProxy( String host, int port, GSSCredential credential, String username, String password, int lifetime, String credname) { boolean retbool = false; // Assume failure if (port == 0) { port = MyProxy.DEFAULT_PORT; } if (lifetime == 0) { lifetime = SSOLIFETIME; } if ((host.length() == 0) || (username.length() == 0) || (password.length() == 0)) { logger.error("In getCredFromMyProxy, host, username, or " + "password was not given." ); } else { try { MyProxy myProxyServer = new MyProxy(host,port); InitParams params = new InitParams(); params.setUserName(username); params.setPassphrase(password); params.setLifetime(lifetime); if (credname.length() > 0) { params.setCredentialName(credname); } logger.debug("Trying to put credential '" + credential + "' to " + host + ":" + port + " with username = '"+ username + "', credname = '" + credname + "', and lifetime = '" + lifetime + "' ..."); myProxyServer.put(credential,params); logger.debug("'PUT' Succeeded!"); retbool = true; } catch (Exception e) { logger.error("Failed! Could not put credential to '" + host + ":" + port + "' because "+ e.getMessage()); } } return retbool; } /** * Delegate a credential to a MyProxy server. This is a convenience * method which calls putNamedCredToMyProxy with a blank * credential name. This stores the credential under the "default" * credential for the given username. * @param host The FQDN of the MyProxy host. * @param port The MyProxy port. If 0, then the default MyProxy port * (7512) will be used. * @param credential A GSSCredential to be delegated to a MyProxy server. * @param username The username for storing the credential. * @param password The password corresponding to the username. * @param lifetime The requested lifetime of the stored credential, in * seconds. If set to 0, then a default value will be * used. * @return True if successfully put credential to MyProxy server, false * if there was some sort of error. * @see #putNamedCredToMyProxy(String,int,GSSCredential,String,String,int,String) */ public static boolean putCredToMyProxy( String host, int port, GSSCredential credential, String username, String password, int lifetime) { return putNamedCredToMyProxy(host,port,credential, username,password,lifetime,""); } /** * Delegate a credential to a MyProxy server under a random * credential name. This is a convenience method which calls * putNamedCredToMyProxy with a random credential name. If * the credential is successfully put to the MyProxy server, then the * random credential name utilized is returned. Otherwise, the empty * string ("") is returned. * @param host The FQDN of the MyProxy host. * @param port The MyProxy port. If 0, then the default MyProxy port * (7512) will be used. * @param credential A GSSCredential to be delegated to a MyProxy server. * @param username The username for storing the credential. * @param password The password corresponding to the username. * @param lifetime The requested lifetime of the stored credential, in * seconds. If set to 0, then a default value will be * used. * @return The random credential name utilized when successfully putting * the credential to the MyProxy server, or the empty string * ("") if failed to put the credential. * @see #putNamedCredToMyProxy(String,int,GSSCredential,String,String,int,String) */ public static String putRandomNamedCredToMyProxy( String host, int port, GSSCredential credential, String username, String password, int lifetime) { String randname = new java.rmi.dgc.VMID().toString() + RandomStringUtils.randomAlphanumeric(4); if (!putNamedCredToMyProxy(host,port,credential,username, password,lifetime,randname)) { randname = ""; } return randname; } }