bea.com | products | dev2dev | support | askBEA
 Download Docs   Site Map   Glossary 
Search

Programming WebLogic Security

 Previous Next Contents Index View as PDF  

Writing Secure Java Clients (Fat Clients)

The following topics are discussed in this section:

 


Introduction

Whether the client is an application, applet, Enterprise JavaBean (EJB), or servlet that requires authentication, WebLogic Server uses the Java Authentication and Authorization Service (JAAS) classes to reliably and securely authenticate to the client. JAAS implements a Java version of the Pluggable Authentication Module (PAM) framework, which permits applications to remain independent from underlying authentication technologies. Therefore, the PAM framework allows the use of new or updated authentication technologies without requiring modifications to your application.

WebLogic Server uses JAAS for remote fat-client authentication, and internally for authentication. Therefore, only developers of custom Authentication providers and developers of remote fat client applications need to be involved with JAAS directly. Users of thin clients or developers of within-container fat client applications (for example, those calling an Enterprise JavaBean (EJB) from a servlet) do not require the direct use or knowledge of JAAS.

By default, WebLogic Server to uses SSL on the HTTPS port. The SSL protocol encrypts the data transmitted between the client and WebLogic Server so that the username and password do not flow in clear text.

Note: In order to implement security in a WebLogic client you must install the WebLogic Server software distribution kit on the Java client.

 


Use of JSSE with WebLogic Server

JSSE is a set of packages that support and implement the SSL and TLS v1, making those capabilities programmatically available. BEA WebLogic Server provides Secure Sockets Layer (SSL) support for encrypting data transmitted between WebLogic Server clients and servers, Java clients, Web browsers, and other servers.

WebLogic's Java Secure Socket Extension (JSSE) implementation can be used by WebLogic clients, but is not required. Other JSSE implementations can be used for their client-side code outside the server as well.

The SSL implementation that WebLogic Server uses is static to the server configuration and is not replaceable by customer applications.

The WebLogic packages that implement JSSE are as follows:

 


JAAS Authentication

JAAS is a standard extension to the security in the Java Software Development Kit version 1.3. JAAS provides the ability to enforce access controls based on user identity. JAAS is provided in WebLogic Server as an alternative to the JNDI authentication mechanism.

WebLogic Server uses the authentication portion of the standard JAAS. The JAAS LoginContext provides support for the ordered execution of all configured authentication provider LoginModule instances and is responsible for the management of the completion status of each configured provider.

Note: JAAS is the preferred method of authentication, however, the WebLogic-supplied LoginModule only supports username and password authentication. Thus, for client certificate authentication (also referred to as two-way SSL authentication), you should use JNDI. To use JAAS for client authentication, you must write a custom LoginModule that does certificate authentication.

Note: Before compiling your JAAS applications on WebLogic Server, add the following paths to your system's CLASSPATH to obtain required JAR files:

Note: WebLogic Server provides full container support for JAAS authentication and supports full use of JAAS authentication and authorization in application code.

Supported JAAS Classes

WebLogic Server supports the full JAAS 1.0 Reference Implementation with respect to authentication and authorization. While WebLogic Server does not protect any resources using JAAS authorization (it uses WebLogic security), you can use JAAS authorization in application code to protect the application's resources.

For more information about JAAS, see the Java Authentication and Authorization Service Developer's Guide.

Overview of Specific JAAS Programming Steps

The major steps for writing a secure application client using JAAS are as follows.

Step 1: Authenticate the User

The steps to authenticate the user are as follows:

  1. Instantiate a LoginContext
  2. Invoke the login by calling the LoginContext's login method.

These steps are executed using the JAAS classes and are described in Sun's JAAS Authentication Tutorial available at http://java.sun.com/j2se/1.4/docs/guide/security/jaas/tutorials/GeneralAcnOnly.html.

Step 2: Retrieve Subject and Associate it with the Client Actions

If authentication is successful, the LoginModule populates the Subject with a Principal representing the user. The Principal the LoginModule places in the Subject is an instance of Principal, which is a class implementing the java.security.Principal interface. The calling application can subsequently retrieve the authenticated Subject by calling the LoginContext's getSubject method. The weblogic.security.Security class runAs() method is then used to associate the Subject identity with the PrivilegedAction or PrivilegedExceptionAction to be executed on behalf of the user identity.

Note: Use of the JAAS javax.security.auth.Subject.doAs methods in WebLogic Server applications do not associate the Subject with the client actions. You may use the doAs methods to implement J2SE security in WebLogic Server applications, but such usage is independent of the need to use the Security.runAs() method.

Step 3: Implement the CallbackHandler Interface

The LoginModule uses the CallbackHandler to communicate with the user and obtain the requested information, such as the username and password.

This step is executed using the JAAS CallbackHandler interface and is described in Sun's JAAS Authentication Tutorial available at http://java.sun.com/j2se/1.4/docs/guide/security/jaas/tutorials/GeneralAcnOnly.html.

 


Writing a Client Application Using JAAS Authentication

To use JAAS in a Java client to authenticate a Subject, perform the following procedure:

  1. Implement a LoginModule class for the authentication mechanism you want to use with WebLogic Server (see Listing 3-1). You need a LoginModule class for each type of authentication mechanism. You can have multiple LoginModule classes for a single WebLogic Server deployment.

    The weblogic.security.auth.Authenticate class uses a JNDI Environment object for initial context as described in Table 3-1.

  2. Implement the CallbackHandler class that the LoginModule will use to communicate with the user and obtain the requested information, such as the username and password. See Listing 3-2 for the sample CallbackHandler used in the JAAS client sample provided in the WebLogic Server distribution.
  3. Implement a configuration file that specifies which LoginModule classes should be used for your WebLogic Server and in which order the LoginModule classes should be invoked. See Listing 3-3 for the sample configuration file used in the JAAS client sample provided in the WebLogic Server distribution.
  4. In the Java client, instantiate a LoginContext. The LoginContext consults the configuration file, sample_jaas.config, to load the default LoginModule configured for WebLogic Server. See Listing 3-4 for an example LoginContext instantiation.

Note: If you use another means to authenticate the user such as a perimeter authenticator or a remote WebLogic Server, the default LoginModule is determined by the remote resource.

  1. Invoke the login() method of LoginContext. The login() method invokes all the loaded LoginModules. Each LoginModule attempts to authenticate the Subject. The LoginContext throws a LoginException if the configured login conditions are not met. See Listing 3-5 for an example of the login() method.
  2. Retrieve the authenticated Subject from the LoginContext and call the action as the Subject. Upon successful authentication of a Subject, access controls can be placed upon that Subject by invoking the runAs() method of the weblogic.security.Security class. The runAs() method associates the specified Subject with the current thread's access permissions and then executes the action. See Listing 3-6 for an example implementation of the getSubject() and runAs() methods.
  3. Write code to execute an action if the Subject has the required privileges. See Listing 3-7 for a sample implementation of the javax.security.PrivilegedAction class that executes an EJB to trade stocks.
  4. Logout the user. The LoginModule logs out the user. See Listing 3-1 for a sample LoginModule.

Note: Before compiling your JAAS applications on WebLogic Server, add the following paths to your system's CLASSPATH to obtain required JAR files:

Theexamples.security.jaas example in the SAMPLES_HOME\server\src\examples\security\jaas directory provided with WebLogic Server shows how to implement JAAS authentication in a Java client.

The following sections show how each of these steps is implemented in the JAAS sample.

Sample LoginModule Implementation

Listing 3-1 shows the UsernamePasswordLoginModule that performs password authentication in the examples.security.jaas example. This login module class is provided in the WebLogic Server distribution in the weblogic.jar file located in the WL_HOME\server\lib directory.

Listing 3-1 Example of LoginModule for Password Authentication

package weblogic.security.auth.login;
import java.util.Map;
import java.util.Hashtable;
import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.RemoteException;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.spi.LoginModule;
import weblogic.security.auth.Authenticate;
import weblogic.security.auth.callback.URLCallback;
import weblogic.jndi.Environment;
/**
* <code>UsernamePasswordLoginModule</code> is used in a WLS client
* to authenticate to the WLS server. This LoginModule
* authenticates users with a username/password.
* It can be used for both the T3 and IIOP clients.
*
* Callers of this module must implement callbacks to pass the
* username, password, and optional URL for the WLS server. Caller
* must implement a NameCallback for the username, a
* PasswordCallback for the username, and a URLCallback for
* the URL. If no URL is available, then the callback handler should
* return null for the URLCallback.
*
* @author Copyright (c) 2002 by BEA. All Rights Reserved.
*/
public class UsernamePasswordLoginModule implements LoginModule
{
private Subject subject = null;
private CallbackHandler callbackHandler = null;
private Map sharedState = null;
private Map options = null;
private boolean debug = true;
private String url = null;
  // Authentication status
private boolean succeeded = false;
private boolean commitSucceeded = false;
  // Username and password
private String username = null;
private String password = null;
  /**
* Initialize the login module.
*
* @param subject Subject to contain principals returned from
* WLS server.
* @param callbackHandler CallbackHandler containing Name,
* Password, and URL callbacks.
* @param sharedState Map used to share state among different
* login modules.
* This is not used by this login module.
* @param options Map used to specify options to this login module.
* Supported options
* debug and URL. The debug option can be used to display
* additional debugging information. The URL option can be used
* instead of the URL callback.
*/
public void initialize(Subject subject,
CallbackHandler callbackHandler,
Map sharedState,
Map options)
{
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;
    // Check options for login module debug enabled
if(options != null)
{
Object val = options.get("debug");
if((val != null) && ((String) val).equalsIgnoreCase("true"))
{
debug = true;
System.out.println("UsernamePasswordLoginModule.initialize(),
debug enabled");
}
val = options.get("URL");
if(val != null)
{
url = (String) val;
if(debug)
System.out.println("UsernamePasswordLoginModule.initialize(),
URL " + url);
}
}
}
/**
* Authenticate the user by username and password passed in
*
* @return true in all cases
*
* @exception FailedLoginException if the authentication fails.
*
* @exception LoginException if this LoginModule is unable to
* perform the authentication.
*/
public boolean login() throws LoginException
{
// Verify that the client supplied a callback handler
if(callbackHandler == null)
{
if(debug)
System.out.println("UsernamePasswordLoginModule.login(), no
callback handler specified");
throw new LoginException("No CallbackHandler Specified");
}
// Populate callback list
Callback[] callbacks = new Callback[3];
callbacks[0] = new NameCallback("username: ");
callbacks[1] = new PasswordCallback("password: ", false);
callbacks[2] = new URLCallback("URL: ");
try
{
// Prompt for username and password and URL
callbackHandler.handle(callbacks);
      // Retrieve username
username = ((NameCallback) callbacks[0]).getName();
if(debug)
System.out.println("UsernamePasswordLoginModule.login(),
username " + username);
      // Retrieve password, converting from char[] to String
char[] charPassword = ((PasswordCallback)
callbacks[1]).getPassword();
if(charPassword == null)
{
// Treat a NULL password as an empty password, not NULL
charPassword = new char[0];
}
password = new String(charPassword);
if(debug)
System.out.println("UsernamePasswordLoginModule.login(),
password " + password);
      // Retrieve URL 
String callbackURL = ((URLCallback) callbacks[2]).getURL();
if(callbackURL != null)
{
url = callbackURL;
}
}
catch(IOException ioe)
{
throw new LoginException(ioe.toString());
}
catch(UnsupportedCallbackException uce)
{
throw new LoginException("Error: Callback " +
uce.getCallback().toString() + " Not Available");
}
// Populate weblogic environment and authenticate
if (url != null) {
Environment env = new Environment();
env.setProviderUrl(url);
env.setSecurityPrincipal(username);
env.setSecurityCredentials(password);
try
{
// Authenticate user credentials, populating Subject
Authenticate.authenticate(env, subject);
}
catch(RemoteException re)
{
if(debug)
System.out.println("Error: Remote Exception on
authenticate, " + re.getMessage());
throw new LoginException(re.toString());
}
catch(IOException ioe)
{
if(debug)
System.out.println("Error: IO Exception on authenticate,
" + ioe.getMessage());
throw new LoginException(ioe.toString());
}
catch(LoginException le)
{
if(debug)
System.out.println("Error: Login Exception on authenticate,
" + le.getMessage());
throw new LoginException(le.toString());
}
}
// Successfully authenticated subject with supplied info
succeeded = true;
return succeeded;
}
/**
* This method is called if the LoginContext's overall
* authentication succeeded (the relevant REQUIRED, REQUISITE,
* SUFFICIENT and OPTIONAL LoginModules succeeded).
*
* If this LoginModule's own authentication attempted failed, then
* this method removes any state that was originally saved.
*
* @exception LoginException if the commit fails.
*
* @return true if this LoginModule's own login and commit
* attempts succeeded, or false otherwise.
*/
public boolean commit() throws LoginException
{
if(succeeded)
{
// Just put the username and password into the private
//credentials.
final PasswordCredential passwordCred = new
PasswordCredential(username, password);
AccessController.doPrivileged(new PrivilegedAction() {
public java.lang.Object run() {
subject.getPrivateCredentials().add(passwordCred);
return null;
}
});

url = null;
commitSucceeded = true;
return true;
}
else
{
username = null;
password = null;
url = null;
return false;
}
}

/**
* This method is called if the LoginContext's
* overall authentication failed.
* If this LoginModule's own authentication attempt
* succeeded (checked by retrieving the private state saved by the
* login and commit methods), then this method cleans up any state
* that was originally saved.
*
* @exception LoginException if the abort fails.
*
* @return false if this LoginModule's own login and/or commit
* attempts failed, and true otherwise.
   */
public boolean abort() throws LoginException
{
if(succeeded == false)
{
return false;
}
else if((succeeded == true) && (commitSucceeded == false))
{
// Login succeeded but overall authentication failed
succeeded = false;
username = null;
password = null;
url = null;
}
else
{
// Overall authentication succeeded and commit succeeded,
// but a commit further down the authentication chain failed
logout();
}
return true;
}
/**
* Logout the user.
*
* @exception LoginException if the logout fails.
*
* @return true in all cases since this LoginModule
* should not be ignored.
*/
public boolean logout() throws LoginException
{
succeeded = false;
commitSucceeded = false;
username = null;
password = null;
url = null;
return true;
}
}

Note: WebLogic Server supports all callback types defined by JAAS as well as all callback types that extend the JAAS specification.

The UsernamePasswordLoginModule that is part of the WebLogic Server product checks for existing system user authentication definitions prior to execution and does nothing if they are already defined.

Sample Implementation of the CallbackHandler Interface

Listing 3-2 contains an example of a CallbackHandler interface used in JAAS authentication. The code in Listing 3-2 is taken from the SampleCallbackHandler.java file in the SAMPLES_HOME\server\src\examples\security\jaas directory.

Listing 3-2 Implementation of the CallbackHandler Interface

package examples.security.jaas;
import java.io.*;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.callback.NameCallback;
import weblogic.security.auth.callback.URLCallback;
import examples.utils.common.ExampleUtils;
/**
* SampleCallbackHandler.java
* Implementation of the CallbackHandler Interface
*
* @author Copyright (c) 2000-2002 by BEA Systems, Inc. All Rights
* Reserved.
*/
class SampleCallbackHandler implements CallbackHandler
{
private String username = null;
private String password = null;
private String url = null;
  public SampleCallbackHandler() { }
  public SampleCallbackHandler(String pUsername, String pPassword,
String pUrl)
{
username = pUsername;
password = pPassword;
url = pUrl;
}
  public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException
{
for(int i = 0; i < callbacks.length; i++)
{
if(callbacks[i] instanceof TextOutputCallback)
{
// Display the message according to the specified type
TextOutputCallback toc = (TextOutputCallback)callbacks[i];
switch(toc.getMessageType())
{
case TextOutputCallback.INFORMATION:
ExampleUtils.log(toc.getMessage());
break;
case TextOutputCallback.ERROR:
ExampleUtils.log("ERROR: " + toc.getMessage());
break;
case TextOutputCallback.WARNING:
ExampleUtils.log("WARNING: " + toc.getMessage());
break;
default:
throw new IOException("Unsupported message type: " +
toc.getMessageType());
}
}
else if(callbacks[i] instanceof NameCallback)
{
// If username not supplied on cmd line, prompt the user
// for the username.
NameCallback nc = (NameCallback)callbacks[i];
if (ExampleUtils.isEmpty(username)) {
System.err.print(nc.getPrompt());
System.err.flush();
nc.setName((new BufferedReader(new
InputStreamReader(System.in))).readLine());
}
else {
ExampleUtils.log("username: "+username);
nc.setName(username);
}
}
else if(callbacks[i] instanceof URLCallback)
{
// If url not supplied on cmd line, prompt the user for the
// url.
// This example requires the url.
URLCallback uc = (URLCallback)callbacks[i];
if (ExampleUtils.isEmpty(url)) {
System.err.print(uc.getPrompt());\
System.err.flush();
uc.setURL((new BufferedReader(new
InputStreamReader(System.in))).readLine());\
}
else {
ExampleUtils.log("URL: "+url);
uc.setURL(url);
}
}
else if(callbacks[i] instanceof PasswordCallback)
{
PasswordCallback pc = (PasswordCallback)callbacks[i];
        // If password not supplied on cmd line, prompt the user
// for the password.
if (ExampleUtils.isEmpty(password)) {
System.err.print(pc.getPrompt());
System.err.flush();
          // Note: JAAS specifies that the password is a char[]
// rather than a String.
String tmpPassword = (new BufferedReader(new
InputStreamReader(System.in))).readLine();
int passLen = tmpPassword.length();
char[] passwordArray = new char[passLen];
for(int passIdx = 0; passIdx < passLen; passIdx++)
passwordArray[passIdx] = tmpPassword.charAt(passIdx);
pc.setPassword(passwordArray);
}
else {
String tPass = new String();
for(int p = 0; p < password.length(); p++)
tPass += "*";
ExampleUtils.log("password: "+tPass);
pc.setPassword(password.toCharArray());
}
}
else if(callbacks[i] instanceof TextInputCallback)
{
// Prompt the user for the username
TextInputCallback callback =
(TextInputCallback)callbacks[i];
System.err.print(callback.getPrompt());
System.err.flush();
callback.setText((new BufferedReader(new
InputStreamReader(System.in))).readLine());
}
else
{
throw new UnsupportedCallbackException(callbacks[i],
"Unrecognized Callback");
}
}
}
}

Sample LoginModule Configuration File

Listing 3-3 shows an implementation of the LoginModule configuration file that you use with a Java client to define the out-of-the-box UsernamePasswordLoginModule. This code is excerpted from the sample_jaas.config file located in the SAMPLES_HOME\server\src\examples\security\jaas directory.

Listing 3-3 Sample_jaas.config Code Example

/** Login Configuration for the JAAS Sample Application **/
Sample {
weblogic.security.auth.login.UsernamePasswordLoginModule
required debug=false;
};

Sample LoginContext Implementation

Listing 3-4 shows an implementation of the LoginContext class. This code is excerpted from the JavaClient.java located in the SAMPLES_HOME\server\src\examples\security\jaas directory.

Listing 3-4 LoginContext Code Fragment

...
import javax.security.auth.login.LoginContext;
...
    LoginContext loginContext = null;
    try
{
// Create LoginContext; specify username/password login module
loginContext = new LoginContext("Sample",
new SampleCallbackHandler(username, password, url));
}

Sample Login Method Implementation

Listing 3-5 shows an implementation of the login() method. This code is excerpted from the JavaClient.java file located in the SAMPLES_HOME\server\src\examples\security\jaas directory.

Listing 3-5 Login Method Code Fragment

...
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.AccountExpiredException;
import javax.security.auth.login.CredentialExpiredException;
...
/**
* Attempt authentication
*/
try
{
// If we return without an exception, authentication succeeded
loginContext.login();
    }
    catch(FailedLoginException fle)
    {
System.out.println("Authentication Failed, " +
fle.getMessage());
System.exit(-1);
}
catch(AccountExpiredException aee)
{
System.out.println("Authentication Failed: Account Expired");
System.exit(-1);
}
catch(CredentialExpiredException cee)
{
System.out.println("Authentication Failed: Credentials
Expired");
System.exit(-1);
}
catch(Exception e)
{
System.out.println("Authentication Failed: Unexpected
Exception, " + e.getMessage());
e.printStackTrace();
System.exit(-1);
}

Sample Implementation of the getSubject and runAS Methods

Listing 3-6 shows an implementation of the getSubject() and runAs() methods. This code is excerpted from the JavaClient.java file located in the SAMPLES_HOME\server\src\examples\security\jaas directory.

Listing 3-6 GetSubject and RunAs Methods Code Fragment

...
/**
* Retrieve authenticated subject, perform SampleAction as Subject
*/
Subject subject = loginContext.getSubject();
SampleAction sampleAction = new SampleAction(url);
Security.runAs(subject, sampleAction);
System.exit(0);
...

Sample PrivilegedAction Implementation

Listing 3-7 contains an implementation of the javax.security.PrivilegedAction class.

If the user is valid, has provided the correct password, and has sufficient permissions to execute the EJB, then the SampleClient uses the weblogic.security.Security.runAs() method to call SampleAction to execute of the Trader EJB.

The code in Listing 3-7 is excerpted from the SampleAction.java file in the SAMPLES_HOME\server\src\examples\security\jaas directory.

Listing 3-7 Example of a PrivilegedAction Implementation

package examples.security.jaas;
import java.security.PrivilegedAction;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.util.Hashtable;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.FinderException;
import javax.ejb.ObjectNotFoundException;
import javax.ejb.RemoveException;
import java.rmi.RemoteException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import examples.ejb20.basic.statelessSession.TraderHome;
import examples.ejb20.basic.statelessSession.Trader;
import examples.utils.common.ExampleUtils;
/**
* SampleAction.java
*
* JAAS sample PrivilegedAction Implementation
*
* @author Copyright (c) 2000-2002 by BEA Systems, Inc. All Rights
* Reserved.
*/
public class SampleAction implements PrivilegedAction
{
private static final String JNDI_NAME =
"ejb20-statelessSession-TraderHome";
private String url;

public SampleAction(String url)
{
this.url = url;
}
  public Object run()
{
Object obj = null;
    try {
callTraderEJB();
}
catch(Exception e) {
e.printStackTrace();
}
return obj;
}
  /**
* Call Trader EJB.
*/
public void callTraderEJB()
throws NamingException, CreateException, RemoteException,
RemoveException
{
TraderHome home = lookupTraderHome();
    // create a Trader
ExampleUtils.log("Creating a trader");
Trader trader = (Trader)ExampleUtils.narrow(home.create(),
Trader.class);
    String [] stocks = {"BEAS", "MSFT", "AMZN", "HWP" };
      // execute some buys
for (int i=0; i<stocks.length; i++) {
int shares = (i+1) * 100;
ExampleUtils.log("Buying "+shares+" shares of
"+stocks[i]+".");
trader.buy(stocks[i], shares);
}
    // execute some sells
for (int i=0; i<stocks.length; i++) {
int shares = (i+1) * 100;
ExampleUtils.log("Selling "+shares+" shares of
"+stocks[i]+".");
trader.sell(stocks[i], shares);
}
    // remove the Trader
ExampleUtils.log("Removing the trader");
trader.remove();
}
  /**
* Look up the bean's home interface using JNDI.
*/
private TraderHome lookupTraderHome()
throws NamingException
{
Context ctx = ExampleUtils.getInitialContext(url);
Object home = (TraderHome)ctx.lookup(JNDI_NAME);
return (TraderHome)ExampleUtils.narrow(home, TraderHome.class);
}
}

Sample Implementation of a Java Client That Uses JAAS

Listing 3-8 contains an example of a Java client that uses JAAS authentication. The code in Listing 3-8 is excerpted from the SampleClient.java file in the SAMPLES_HOME\server\src\examples\security\jaas directory.

Listing 3-8 Example of Java Client That Uses JAAS Authentication

package examples.security.jaas;
import java.util.*;
import javax.security.auth.Subject;
import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.AccountExpiredException;
import javax.security.auth.login.CredentialExpiredException;
import weblogic.security.acl.internal.AuthenticatedSubject;
import weblogic.security.Security;
import examples.utils.common.ExampleUtils;
/**
* SampleClient.java
* Sample client for JAAS user authentication.
* Usage: java examples.security.jaas.SampleClient [url]
* [username(optional)] [password(optional)]
* Example: java examples.security.jaas.SampleClient
* t3://localhost:7001 weblogic weblogic
*
* @author Copyright (c) 2000-2002 by BEA Systems, Inc. All Rights
* Reserved.
*/
public class SampleClient
{
/**
* Attempt to authenticate the user.
*/
public static void main(String[] args)
{
String username = null;
String password = null;
String url = null;

// Url is required
if(args.length < 1) {
System.out.println("Usage:
java examples.security.jaas.SampleClient [url]
[username(optional)] [password(optional)]");
System.out.println("Example:
java examples.security.jaas.SampleClient
t3://localhost:7001");
System.exit(0);
}

// Parse the argument list
switch(args.length) {
case 3:
password = args[2];
// fall through
case 2:
username = args[1];
// fall through
case 1:
url = args[0];
break;
}

LoginContext loginContext = null;

/**
* Set JAAS server url system property and create a LoginContext
*/
try
{
// Create LoginContext; specify username/password login module
loginContext = new LoginContext("Sample",
new SampleCallbackHandler(username, password, url));
}
catch(SecurityException se)
{
se.printStackTrace();
System.exit(-1);
}
catch(LoginException le)
{
le.printStackTrace();
System.exit(-1);
}
    /**
* Attempt authentication
*/
try
{
// If we return without an exception, authentication succeeded
loginContext.login();
    }
    catch(FailedLoginException fle)
    {
System.out.println("Authentication Failed, " +
fle.getMessage());
System.exit(-1);
}
catch(AccountExpiredException aee)
{
System.out.println("Authentication Failed: Account Expired");
System.exit(-1);
}
catch(CredentialExpiredException cee)
{
System.out.println("Authentication Failed: Credentials
Expired");
System.exit(-1);
}
catch(Exception e)
{
System.out.println("Authentication Failed: Unexpected
Exception, " + e.getMessage());
e.printStackTrace();
System.exit(-1);
}

/**
* Retrieve authenticated subject, perform SampleAction as
* Subject
*/
Subject subject = loginContext.getSubject();
SampleAction sampleAction = new SampleAction(url);
Security.runAs(subject, sampleAction);
System.exit(0);
}
}

For more information about using JAAS, see the Java Authentication and Authorization Service Developer's Guide.

 


Using JNDI Authentication

Java clients use the Java Naming and Directory Interface (JNDI) to pass credentials to WebLogic Server. A Java client establishes a connection with WebLogic Server by getting a JNDI InitialContext. The Java client then uses the InitialContext to look up the resources it needs in the WebLogic Server JNDI tree.

Note: JAAS is the preferred method of authentication, however, the WebLogic-supplied LoginModule only supports username and password authentication. Thus, for client certificate authentication (also referred to as two-way SSL authentication), you should use JNDI. To use JAAS for client authentication, you must write a custom LoginModule that does certificate authentication.

To specify a user and the user's credentials set the JNDI properties listed in the following table.

Table 3-1 JNDI Properties Used for Authentication 

Property

Meaning

INITIAL_CONTEXT_FACTORY

Provides an entry point into the WebLogic Server environment. The class weblogic.jndi.WLInitialContextFactory is the JNDI SPI for WebLogic Server.

PROVIDER_URL

Specifies the host and port of the WebLogic Server. For example: t3://weblogic:7001.

SECURITY_AUTHENTICATION

Indicates the types of authentication to be used. The following values are valid:

  • None indicates that no authentication is performed.

  • Simple indicates that password authentication is performed.

  • Strong indicates that certificate authentication is performed.

Note: If you try to access a secure component on WebLogic Server, user authentication will be required by WebLogic Server regardless of the type of authentication indicated by the SECURITY_AUTHENTICATION setting. For example, if you set SECURITY_AUTHENTICATION to None, you will still be required to supply the correct password to access a secure component.

SECURITY_PRINCIPAL

Specifies the identity of the User when that User authenticates to the WebLogic Server security realm.

SECURITY_CREDENTIALS

Specifies the credentials of the User when that User authenticates to the WebLogic Server security realm.

  • For password authentication enabled via SECURITY_AUTHENTICATION="simple", this property specifies a string that is either the User's password or a User object used by WebLogic Server to verify credentials.

  • For certificate authentication enabled via SECURITY_AUTHENTICATION="strong", this property specifies the name of the X509 object that contains the digital certificate and private key for the WebLogic Server.


 

These properties are stored in a hash table that is passed to the InitialContext constructor.

Listing 3-9 demonstrates how to use certificate authentication in a Java client. Notice the use of T3S, which is a WebLogic Server proprietary version of SSL. T3S uses encryption to protect the connection and communication between WebLogic Server and the Java client.

Listing 3-9 Example of Certificate Authentication

...
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
env.put(WLContext.PROVIDER_URL, "t3s://weblogic:7001");
env.put(WLContext.SECURITY_AUTHENTICATION "strong");
env.put(Context.SECURITY_PRINCIPAL, "javaclient");
env.put(Context.SECURITY_CREDENTIALS, "certforclient");

ctx = new InitialContext(env);

The code in Listing 3-9 generates a call to the Identity Asserter class that implements the weblogic.security.providers.authentication.UserNameMapperinterface. The class that implements the UserNameMapper interface returns a User object if the digital certificate is valid. WebLogic Server stores this authenticated User object on the Java client's thread in WebLogic Server and uses it for subsequent authorization requests when the thread attempts to use resources protected by the security realm.

Note: The implementation of the weblogic.security.providers.authentication.UserNameMapper interface must be specified in your CLASSPATH.

 


Writing Applications that Use SSL

This section covers the following topics:

Communicating Securely with SSL-Enabled Web Browsers

You can use a URL object to make an outbound SSL connection from a WebLogic Server acting as a client to another WebLogic Server. The weblogic.net.http.HttpsURLConnection class provides a way to specify the security context information for a client including the digital certificate and private key of the client.

The weblogic.net.http.HttpsURLConnection class provides methods for determining the negotiated cipher suite, getting/setting a HostName Verifier, getting the server's certificate chain, and getting/setting an SSLSocketFactory in order to create new SSL sockets.

The SSLClient code example demonstrates using the weblogic.net.http.HttpsURLConnection class to make an outbound SSL connection. In addition, the SSL client code example demonstrates using the Java Secure Socket Extension (JSSE) application programming interface (API) to make the outbound SSL connection. The SSLclient code example is available in the examples.security.sslclient package in the SAMPLES_HOME\server\src\examples\security\sslclient directory.

Writing SSL Clients

This section describes, by way of example, how to write various types of SSL clients. Examples of the following types of SSL clients are provided:

SSL Client Sample

The SSLClient sample demonstrates how to use the WebLogic SSL library to make outgoing SSL connections. It shows both how to do this from a stand-alone application as well as from within WebLogic Server, that is, in a servlet.

You can compile this sample using WebLogic's implementation or Sun's JSSE implementation. To use Sun's JSSE implementation, you must uncomment the following code in the SSLClient.java file and then recompile the sample:

Note: When making an outgoing SSL connection, WebLogic Server will use that instance of the server's certificate. When communicating to either the same or another WebLogic Server with two-way SSL, the originating server's certificate will be verified against the client root CA list in the receiving WebLogic Server.

Listing 3-10 shows a sample SSLClient. This code taken from the SSLClient.java file located at SAMPLES_HOME\server\src\examples\security\sslclient.

Listing 3-10 SSL Client Sample

package examples.security.sslclient;
import java.io.File;
import java.net.URL;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Hashtable;
import java.security.Provider;
import javax.naming.NamingException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletOutputStream;
//import com.sun.net.ssl.internal.www.protocol.https.*;
//import com.sun.net.ssl.*;
import weblogic.net.http.*;
import weblogic.jndi.Environment;
/** SSLClient is a short example of how to use the SSL library of 
* WebLogic to make outgoing SSL connections. It shows both how
* to do this from a stand-alone application as well as from within
* WebLogic (in a Servlet).
*
* If you are using the JSSE implementation from Sun Microsystems, you must
* uncomment three places in the SSLClient.java code and re-compile:
*
* 1. JSSE import statements at the top of this file.
* 2. The code in method jsseURLConnect.
* 3. The NullHostnameVerifier inner class.
*
* Be careful to notice that the WebLogic Server, when making an
* outgoing SSL connection, will use that instance of the server's
* certificate. When communicating to either the same or another
* WebLogic Server with two-way SSL, the originating server's
* certificate will be verified against the client root CA list in
* the receiving WebLogic Server.
*
* @author Copyright (c) 1999-2002 by BEA Systems, Inc. All Rights
* Reserved.
*/
public class SSLClient {
public void SSLClient() {}
public static void main (String [] argv)
throws IOException {
if ((!((argv.length == 4) || (argv.length == 5))) ||
(!((argv[0].equals("wls") || argv[0].equals("jsse"))))
) {
System.out.println("usage: java SSLClient jsse|wls host port
sslport <query>");
System.out.println("example: java SSLClient wls server2.weblogic.com 80 443
/examplesWebApp/SnoopServlet.jsp");
System.exit(-1);
}
try {
System.out.println("----");
if (argv.length == 5) {
if (argv[0].equals("wls"))
wlsURLConnect(argv[1], argv[2], argv[3], argv[4],
System.out);
else
jsseURLConnect(argv[1], argv[2], argv[3], argv[4],
System.out);
} else { // for null query, default page returned...
if (argv[0].equals("wls"))
wlsURLConnect(argv[1], argv[2], argv[3], null, System.out);
else
jsseURLConnect(argv[1], argv[2], argv[3], null,
System.out);
}
System.out.println("----");
} catch (Exception e) {
e.printStackTrace();
printSecurityProviders(System.out);
System.out.println("----");
}
}
private static void printOut(String outstr, OutputStream stream) {
if (stream instanceof PrintStream) {
((PrintStream)stream).print(outstr);
return;
} else if (stream instanceof ServletOutputStream) {
try {
((ServletOutputStream)stream).print(outstr);
return;
} catch (IOException ioe) {
System.out.println(" IOException: "+ioe.getMessage());
}
}
System.out.print(outstr);
}
private static void printSecurityProviders(OutputStream stream) {
StringBuffer outstr = new StringBuffer();
outstr.append(" JDK Protocol Handlers and Security
Providers:\n");
outstr.append(" java.protocol.handler.pkgs - ");
outstr.append(System.getProperties().getProperty(
"java.protocol.handler.pkgs"));
outstr.append("\n");
Provider[] provs = java.security.Security.getProviders();
for (int i=0; i<provs.length; i++)
outstr.append(" provider[" + i + "] - " + provs[i].getName()
+ " - " + provs[i].getInfo() + "\n");
outstr.append("\n");
printOut(outstr.toString(), stream);
}
  private static void tryConnection(java.net.HttpURLConnection
connection, OutputStream stream)
throws IOException {
connection.connect();
String responseStr = "\t\t" +
connection.getResponseCode() + " -- " +
connection.getResponseMessage() + "\n\t\t" +
connection.getContent().getClass().getName()
+ "\n";
connection.disconnect();
printOut(responseStr, stream);
}
/**
* This method contains an example of how to use the URL and
* URLConnection objects to create a new SSL connection, using
* JSSE SSL client classes.
*
* This method is commented out for compilation without the JSSE
* classes. To observe JSSE in action, see instructions in
* package.html.
*/
public static void jsseURLConnect(String host, String port,
String sport, String query,
OutputStream out) {
/*
try {
if (query == null)
query = "/examplesWebApp/index.jsp";
String handlers =
System.getProperty("java.protocol.handler.pkgs");
System.setProperty("java.protocol.handler.pkgs",
"com.sun.net.ssl.internal.www.protocol|" + handlers);
java.security.Security.addProvider(new
com.sun.net.ssl.internal.ssl.Provider());
printSecurityProviders(out);
printOut(" Trying a new HTTP connection using JDK client
classes - \n\thttp://" +
host + ":" + port + query + "\n", out);
URL url = null;
try {
url = new URL( "http", host,
Integer.valueOf(port).intValue(), query);
java.net.HttpURLConnection connection =
(java.net.HttpURLConnection) url.openConnection();
tryConnection(connection, out);
} catch (Exception e) {
printOut(e.getMessage(), out);
e.printStackTrace();
printSecurityProviders(System.out);
System.out.println("----");
}
printOut(" Trying a new HTTPS connection using JDK client
classes - \n\thttps://" +
host + ":" + sport + query +"\n", out);
URL jsseUrl = new URL( "https", host,
Integer.valueOf(sport).intValue(), query,
new com.sun.net.ssl.internal.www.protocol.https.Handler() );
java.net.HttpURLConnection sconnection =
(java.net.HttpURLConnection) jsseUrl.openConnection();
      /* Uncomment this and the inner class at the bottom of the
* file to observe how the HostnameVerifier works with JSSE.
*//*
if (sconnection instanceof com.sun.net.ssl.HttpsURLConnection)
((com.sun.net.ssl.HttpsURLConnection)sconnection)
.setHostnameVerifier(new NullHostnameVerifier());
*//*
printOut("\t using a "+sconnection.getClass().getName() +
"\n", out);
tryConnection(sconnection, out);
} catch (IOException ioe) {
ioe.printStackTrace();
printOut(ioe.getMessage(), out);
}
// end of jsseURLConnect method */
}
/*
* This method contains an example of how to use the URL and
* URLConnection objects to create a new SSL connection, using
* WebLogic SSL client classes.
*/
public static void wlsURLConnect(String host, String port,
String sport, String query,
OutputStream out) {
try {
if (query == null)
query = "/examplesWebApp/index.jsp";
// The following protocol registration is taken care of in the
// normal startup sequence of WebLogic. It can be turned off
// using the console SSL panel.
//
// We duplicate it here as a proof of concept in a stand alone
// java application. Using the URL object for a new connection
// inside of WebLogic would work as expected.
java.util.Properties p = System.getProperties();
String s = p.getProperty("java.protocol.handler.pkgs");
if (s == null) {
s = "weblogic.net";
} else if (s.indexOf("weblogic.net") == -1) {
s += "|weblogic.net";
}
p.put("java.protocol.handler.pkgs", s);
System.setProperties(p);
printSecurityProviders(out);
// end of protocol registration
printOut(" Trying a new HTTP connection using WLS client classes -
\n\thttp://" + host + ":" + port + query + "\n", out);
URL wlsUrl = null;
try {
wlsUrl = new URL("http", host, Integer.valueOf(port).intValue(), query);
weblogic.net.http.HttpURLConnection connection =
new weblogic.net.http.HttpURLConnection(wlsUrl);
tryConnection(connection, out);
} catch (Exception e) {
printOut(e.getMessage(), out);
e.printStackTrace();
printSecurityProviders(System.out);
System.out.println("----");
}
printOut(" Trying a new HTTPS connection using WLS client classes -
\n\thttps://" + host + ":" + sport + query + "\n", out);
wlsUrl = new URL("https", host, Integer.valueOf(sport).intValue(), query);
weblogic.net.http.HttpsURLConnection sconnection =
new weblogic.net.http.HttpsURLConnection(wlsUrl);
// Only when one has a two-way SSL connection, i.e. ClientCertificateEnforced
// is selected in the server under the SSL tab, the following private key
// and the client cert chain is used.
File ClientKeyFile = new File ("clientkey.pem");
File ClientCertsFile = new File ("client2certs.pem");
if (!ClientKeyFile.exists() || !ClientCertsFile.exists())
{
System.out.println("Error : clientkey.pem/client2certs.pem is not present
in this directory.");
System.out.println("To create it run - ant createmycerts.");
System.exit(0);
}
InputStream [] ins = new InputStream[2];
ins[0] = new FileInputStream("client2certs.pem");
ins[1] = new FileInputStream("clientkey.pem");
String pwd = "clientkey";
sconnection.loadLocalIdentity(ins[0], ins[1], pwd.toCharArray());
      tryConnection(sconnection, out);
} catch (Exception ioe) {
printOut(ioe.getMessage(), out);
ioe.printStackTrace();
}
}
/* This inner class is a null version which always returns true when checking
the server certificate SubectDN CommonName against the hostname of the
server we're connecting to. Uncomment this class and the registration of
this class in JSSE (also commented out above in the jsseURLConnect method)
to observe how it works.
*//*
static class NullHostnameVerifier implements com.sun.net.ssl.HostnameVerifier {
public boolean verify(String urlHostname, String certHostname) {
return true;
}
}*/
}

SSLSocketClient Sample

The SSLSocketClient sample demonstrates how to use the secure port to connect to a JSP served by WebLogic Server and display the results of that connection. It shows how to implement the following functions:

Listing 3-11 shows a sample SSLSocketClient. This code taken from the SSLSocketClient.java file located at SAMPLES_HOME\server\src\examples\security\sslclient.

Listing 3-11 SSLSocketClient Sample

package examples.security.sslclient;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.util.Hashtable;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import weblogic.security.SSL.HostnameVerifierJSSE;
import weblogic.security.SSL.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSession;
import weblogic.security.SSL.SSLSocketFactory;
import weblogic.security.SSL.TrustManagerJSSE;
  /**
* A Java client demonstrates connecting to a JSP served by WebLogic Server
* using the secure port and displays the results of the connection.
*
* @author Copyright (c) 1999-2002 by BEA Systems, Inc. All Rights Reserved.
*/
public class SSLSocketClient {
public void SSLSocketClient() {}
public static void main (String [] argv)
throws IOException {
if ((argv.length < 2) || (argv.length > 3)) {
System.out.println("usage: java SSLSocketClient host sslport
<HostnameVerifierJSSE>");
System.out.println("example: java SSLSocketClient server2.weblogic.com 443
MyHVClassName");
System.exit(-1);
}
/////////////////////////////////
    try {
System.out.println("\nhttps://" + argv[0] + ":" + argv[1]);
System.out.println(" Creating the SSLContext");
SSLContext sslCtx = SSLContext.getInstance("https");
      File KeyStoreFile  = new File ("mykeystore");
if (!KeyStoreFile.exists())
{
System.out.println("Keystore Error : mykeystore is not present in this
directory.");
System.out.println("To create it run - ant createmykeystore.");
System.exit(0);
}
      System.out.println(" Initializing the SSLContext with client\n" +
" identity (certificates and private key),\n" +
" HostnameVerifierJSSE, AND NulledTrustManager");
     // Open the keystore, retrieve the private key, and certificate chain
KeyStore ks = KeyStore.getInstance("jks");
ks.load(new FileInputStream("mykeystore"), null);
PrivateKey key = (PrivateKey)ks.getKey("mykey", "testkey".toCharArray());
Certificate [] certChain = ks.getCertificateChain("mykey");
sslCtx.loadLocalIdentity(certChain, key);
      HostnameVerifierJSSE hVerifier = null;
if (argv.length < 3)
hVerifier = new NulledHostnameVerifier();
else
hVerifier = (HostnameVerifierJSSE) Class.forName(argv[2]).newInstance();
sslCtx.setHostnameVerifierJSSE(hVerifier);
TrustManagerJSSE tManager = new NulledTrustManager();
sslCtx.setTrustManagerJSSE(tManager);
System.out.println(" Creating new SSLSocketFactory with SSLContext");
SSLSocketFactory sslSF = (SSLSocketFactory) sslCtx.getSocketFactoryJSSE();
System.out.println(" Creating and opening new SSLSocket with
SSLSocketFactory");
      // using createSocket(String hostname, int port)
SSLSocket sslSock = (SSLSocket) sslSF.createSocket(argv[0],
new Integer(argv[1]).intValue());
System.out.println(" SSLSocket created");
sslSock.addHandshakeCompletedListener(new MyListener());
OutputStream out = sslSock.getOutputStream();
      // Send a simple HTTP request
String req = "GET /examplesWebApp/ShowDate.jsp HTTP/1.0\r\n\r\n";
out.write(req.getBytes());
      // Retrieve the InputStream and read the HTTP result, displaying
// it on the console
InputStream in = sslSock.getInputStream();
byte buf[] = new byte[1024];
try
{
while (true)
{
int amt = in.read(buf);
if (amt == -1) break;
System.out.write(buf, 0, amt);
}
}
catch (IOException e)
{
return;
}
sslSock.close();
System.out.println(" SSLSocket closed");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* MyListener implements the interface
* <code>javax.net.ssl.HandshakeCompletedListener</code> and shows the user how
* to receive notifications about the completion of an SSL protocol handshake
* on a given SSL connection.
*
* It also shows the user the number of times an SSL handshake takes
* place on a given SSL connection.
*
*/
static class MyListener implements javax.net.ssl.HandshakeCompletedListener
{
public void handshakeCompleted(javax.net.ssl.HandshakeCompletedEvent event)
{
SSLSession session = event.getSession();
System.out.println("Handshake Completed with peer " +
session.getPeerHost());
System.out.println("cipher: " + session.getCipherSuite());
javax.security.cert.X509Certificate[] certs = null;
try
{
certs = session.getPeerCertificateChain();
}
catch (javax.net.ssl.SSLPeerUnverifiedException puv)
{
certs = null;
}
if (certs != null)
{
System.out.println("peer certificates:");
for (int z=0; z<certs.length; z++) System.out.println(
"certs["+z+"]: " + certs[z]);
}
else
{
System.out.println("No peer certificates presented");
}
}
}
}

SSLClientServlet Sample

SSLClientServlet is a simple servlet wrapper of SSLClient sample.

Listing 3-12 shows a sample SSLClientServlet. This code taken from the SSLSClientServlet.java file located at SAMPLES_HOME\server\src\examples\security\sslclient.

Listing 3-12 SSLClientServlet Sample Code

package examples.security.sslclient;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import javax.servlet.*;
import javax.servlet.http.*;
/**
* SSLClientServlet is a simple servlet wrapper of
* examples.security.sslclient.SSLClient.
*
* @see SSLClient
* @author Copyright (c) 1999-2002 by BEA Systems, Inc. All Rights Reserved.
*/
public class SSLClientServlet extends HttpServlet {
public void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
response.setHeader("Pragma", "no-cache"); // HTTP 1.0
response.setHeader("Cache-Control", "no-cache"); // HTTP 1.1
ServletOutputStream out = response.getOutputStream();
    out.println("<br><h2>ssl client test</h2><br><hr>");
String[] target = request.getParameterValues("url");
try {
      out.println("<h3>wls ssl client classes</h3><br>");
out.println("java SSLClient wls localhost 7001 7002
/examplesWebApp/SnoopServlet.jsp<br>");
out.println("<pre>");
SSLClient.wlsURLConnect("localhost", "7001", "7002",
"/examplesWebApp/SnoopServlet.jsp", out);
out.println("</pre><br><hr><br>");
    } catch (IOException ioe) {
out.println("<br><pre> "+ioe.getMessage()+"</pre>");
ioe.printStackTrace();
}
}
}

Using Two-Way SSL Authentication

When using certificate authentication, WebLogic Server sends a digital certificate to the requesting client. The client examines the digital certificate to ensure that it is authentic, has not expired, and matches the WebLogic Server instance that presented it.

With two-way SSL authentication (a form of mutual authentication), the requesting client also presents a digital certificate to WebLogic Server. When WebLogic Server is configured for two-way SSL authentication, requesting clients are required to present digital certificates from a specified set of certificate authorities. WebLogic Server accepts only digital certificates that are signed by root certificates from the specified certificate authorities.

For information on how to configure WebLogic Server for two-way SSL authentication, see the "Configuring the SSL Protocol" section in Managing WebLogic Security

The following sections describe the different ways two-way SSL authentication can be implemented in WebLogic Server.

Two-Way SSL Authentication with JNDI

When using JNDI for two-way SSL authentication in a Java client, use the setSSLClientCertificate() method of the WebLogic JNDI Environment class. This method sets a private key and chain of X.509 digital certificates for client authentication. To supply the Java client's digital certificate and private key read the Definite Encoding Rules (DER) files that contain the digital certificate and private key into an X509 object, and then set the X509 object in a JNDI hash table. Use the JNDI properties described in Using JNDI Authentication to specify the information required for authentication.

To pass digital certificates to JNDI, create an array of InputStreams opened on files containing DER-encoded digital certificates and set the array in the JNDI hash table. The first element in the array must contain an InputStream opened on the Java client's private key file. The second element must contain an InputStream opened on the Java client's digital certificate file. (This file contains the public key for the Java client.) Additional elements may contain the digital certificates of the root certificate authority and the signer of any digital certificates in a certificate chain. A certificate chain allows WebLogic Server to authenticate the digital certificate of the Java client if that digital certificate was not directly issued by a certificate authority registered for the Java client in the WebLogic Server keystore file.

You can use the weblogic.security.PEMInputStream class to read digital certificates stored in Privacy Enhanced Mail (PEM) files. This class provides a filter that decodes the base 64-encoded DER certificate into a PEM file.

Listing 3-13 demonstrates how to use mutual authentication in a Java client.

Listing 3-13 Example of Mutual Authentication

import java.io.FileInputStream;
import java.io.InputStream;
import javax.naming.Context;
import weblogic.jndi.Environment;
import weblogic.security.PEMInputStream;


public class JavaClient
{

public static void main(String[] args)
{
Context ctx = null;

String url = args[0];
try
{
Environment env = new Environment();

env.setProviderUrl(url);

// The second and third args are username and password
if (args.length >= 3)
{
env.setSecurityPrincipal(args[1]);
env.setSecurityCredentials(args[2]);
}

// Fourth and fifths arguments are private key and
// public key.
if (url.startsWith("t3s") && args.length >= 5)
{
InputStream[] certs = new InputStream[args.length - 3];
for (int q = 3; q < args.length; q++)
{
String file = args[q];
InputStream is = new FileInputStream(file);

if (file.toLowerCase().endsWith(".pem"))
{
is = new PEMInputStream(is);
}
certs[q - 3] = is;
}
env.setSSLClientCertificate(certs);
}
ctx = env.getInitialContext();
...

When the JNDI getInitialContext() method is called, the Java client and WebLogic Server execute mutual authentication in the same way that a Web browser performs mutual authentication to get a secure Web server connection. An exception is thrown if the digital certificates cannot be validated or if the Java client's digital certificate cannot be authenticated in the security realm. The authenticated User object is stored on the Java client's server thread and is used for checking the permissions governing the Java client's access to any protected WebLogic Server resources.

When you use the WebLogic JNDI Environment class, you must create a new Environment object for each call to the getInitialContext() method. Once you specify a User and security credentials, both the user and their associated credentials remain set in the Environment object. If you try to reset them and then call the JNDI getInitialContext() method, the original User and credentials are used.

When you use mutual authentication from a Java client, WebLogic Server gets a unique Java Virtual Machine (JVM) ID for each client JVM so that the connection between the Java client and WebLogic Server is constant. Unless the connection times out from lack of activity, it persists as long as the JVM for the Java client continues to execute. The only way a Java client can negotiate a new SSL connection reliably is by stopping its JVM and running another instance of the JVM.

If you have not configured an Identity Asserter to do certificate-based authentication, a Java client running in a JVM with an SSL connection can change the WebLogic Server User identity by creating a new JNDI InitialContext and supplying a new username and password in the JNDI SECURITY_PRINCIPAL and SECURITY_CREDENTIALS properties. Any digital certificates passed by the Java client after the SSL connection is made are not used. The new WebLogic Server User continues to use the SSL connection negotiated with the initial User's digital certificate.

If you have configured an Identity Asserter to do certificate-based authentication, WebLogic Server passes the digital certificate from the Java client to the implementation of the Identity Asserter class that implements the UserNameMapper interface and the user-name-mapper class maps the digital certificate to a WebLogic Server user name. Therefore, if you want to set a new user identity when you use the certificate-based identity assertion, the Java client must create a new Environment because the original Environment processed the digital certificate and cached the first set of credentials. This is because the digital certificate is processed only at the time of the first connection request from the JVM for each Environment.

Using Two-Way SSL Authentication Between WebLogic Server Instances

You can use two-way SSL authentication in server-to-server communication in which one WebLogic Server instance is acting as the client of another WebLogic Server instance. Using two-way SSL authentication in server-to-server communication enables you to have dependable, highly-secure connections, even without the more familiar client/server environment.

Listing 3-14 shows an example of how to establish a secure connection from a servlet running in one instance of WebLogic Server to a second WebLogic Server instance called server2.weblogic.com.

Listing 3-14 Establishing a Secure Connection to Another WebLogic Server

FileInputStream [] f = new FileInputStream[3]; 
f[0]= new FileInputStream("demokey.pem");
f[1]= new FileInputStream("democert.pem");
f[2]= new FileInputStream("ca.pem");
Environment e = new Environment ();
e.setProviderURL("t3s://server2.weblogic.com:443");
e.setSSLClientCertificate(f);
e.setSSLServerName("server2.weblogic.com");
e.setSSLRootCAFingerprints("ac45e2d1ce492252acc27ee5c345ef26");

e.setInitialContextFactory
("weblogic.jndi.WLInitialContextFactory");
Context ctx = new InitialContext(e.getProperties())

In Listing 3-14, the WebLogic JNDI Environment class creates a hash table to store the following parameters:

Using Two-Way SSL Authentication with Servlets

To authenticate Java clients in a servlet (or any other server-side Java class), you must check whether the client presented a digital certificate and if so, whether the certificate was issued by a trusted certificate authority. The servlet writer is responsible for asking whether the Java client has a valid digital certificate. When writing servlets with the WebLogic Servlet API, you must access information about the SSL connection through the getAttribute() method of the HTTPServletRequest object.

The following attributes are supported in WebLogic Server servlets:

You have access to the user information defined in the digital certificates. When you get the javax.servlet.request.X509Certificate attribute, it is an array of java.security.cert X509Certificate. You simply cast it to that and look at the certificates.

A digital certificate includes information, such as the following:

Using a Custom Host Name Verifier

A Host Name Verifier validates that the host to which an SSL connection is made is the intended or authorized party. A Host Name Verifier is useful when a WebLogic client or a WebLogic Server is acting as an SSL client to another application server. It prevents man-in-the-middle attacks.

By default, WebLogic Server, as a function of the SSL handshake, compares the common name in the SubjectDN of the SSL server's digital certificate with the host name of the SSL server used to initiate the SSL connection. If these names do not match, the SSL connection is dropped.

The dropping of the SSL connection is caused by the SSL client which validates the host name of the server against the digital certificate of the server. If anything but the default behavior is desired, you can either turn off host name verification or register a custom host name verifier. Turning off host name verification leaves WebLogic Server vulnerable to man-in-the-middle attacks.

Note: Turn off host name verification when using the demonstration digital certificates shipped with WebLogic Server.

You can turn off host name verification in the following ways:

You can write a custom Host Name Verifier. The weblogic.security.SSL.HostnameVerifierJSSE interface provides a callback mechanism so that you can define a policy for handling the case where the server name that is being connected to does not match the server name in the SubjectDN of the server's digital certificate.

To use a custom Host Name Verifier, create a class that implements the weblogic.security.SSL.HostnameVerifierJSSE interface and define the methods that capture information about the server's security identity.

Note: This interface takes new style certificates and replaces the weblogic.security.SSL.HostnameVerifier interface, which is being deprecated in this release of WebLogic Server.

Before you can use a custom Host Name Verifier, you need to define the class for your implementation in the following ways:

An example of a custom Host Name Verifier (See Listing 3-15) is available in the SSLclient code example in the examples.security.sslclient package in the SAMPLES_HOME\server\src\examples\security\sslclient directory. This code example contains a NulledHostnameVerifier class which always returns true for the comparison. This sample allows the WebLogic SSL client to connect to any SSL server regardless of the server's host name and digital certificate SubjectDN comparison.

Listing 3-15 HostNameVerifierJSSE Sample Code

package examples.security.sslclient;
/**
* HostnameVerifierJSSE provides a callback mechanism so that
* implementers of this interface can supply a policy for handling
* the case where the host that's being connected to and the server
* name from the certificate SubjectDN must match.
*
* This is a null version of that class to show the WebLogic SSL
* client classes without major problems. For example, in this case,
* the client code connects to a server at 'localhost' but the
* democertificate's SubjectDN CommonName is 'bea.com' and the
* default WebLogic HostnameVerifierJSSE does a String.equals() on
* those two hostnames.
*
* @see HostnameVerifier#verify
* @author Copyright (c) 1999-2002 by BEA Systems, Inc. All Rights
* Reserved.
*/
public class NulledHostnameVerifier implements
weblogic.security.SSL.HostnameVerifierJSSE {
public boolean verify(String urlHostname, String certHostname) {
return true;
}
}

Using a Trust Manager

The weblogic.security.SSL.TrustManagerJSSE interface allows you to override validation errors in a peer's digital certificate and continue the SSL handshake. You can also use the interface to discontinue an SSL handshake by performing additional validation on a server's digital certificate chain.

Note: This interface takes new style certificates and replaces the weblogic.security.SSL.TrustManager interface, which is being deprecated in this release of WebLogic Server.

When an SSL client connects to an SSL server, the SSL server presents its digital certificate chain to the client for authentication. That chain could contain an invalid digital certificate. The SSL specification says that the client should drop the SSL connection upon discovery of an invalid certificate. Web browsers, however, ask the user whether to ignore the invalid certificate and continue up the chain to determine if it is possible to authenticate the SSL server with any of the remaining certificates in the certificate chain. The Trust Manager eliminates this inconsistent practice by enabling you to control when to continue or discontinue an SSL connection. Using a Trust Manager you can perform custom checks before continuing an SSL connection. For example, you can use the Trust Manager to specify that only users from specific localities, such as towns, states, or countries, or users with other special attributes, to gain access via the SSL connection.

Use the weblogic.security.SSL.TrustManagerJSSE interface to create a Trust Manager. The interface contains a set of error codes for certificate verification. You can also perform additional validation on the peer certificate and interrupt the SSL handshake if need be. After a digital certificate has been verified, the weblogic.security.SSL.TrustManagerJSSE interface uses a callback function to override the result of verifying the digital certificate. You can associate an instance of a Trust Manager with an SSL Context through the setTrustManager() method. The weblogic.security.SSL.TrustManagerJSSE interface conforms to the JSSE specification. Note that the use of a Trust Manager may potentially impact performance depending on the checks performed. You can only set up a Trust Manger programmatically; its use cannot be defined through the Administration Console or on the command-line.

An example of a NulledTrustManager (See Listing 3-16) is available in the SSLclient code example in the examples.security.sslclient package in the SAMPLES_HOME\server\src\examples\security\sslclient directory.

Listing 3-16 TrustManager Code Example

package examples.security.sslclient;

import weblogic.security.SSL.TrustManagerJSSE;
import javax.security.cert.X509Certificate;
public class NulledTrustManagerJSSE implements TrustManagerJSSE{ 
  public boolean certificateCallback(X509Certificate[] o, int validateErr) {
System.out.println(" --- Do Not Use In Production ---\n" + " By using this " +
"NulledTrustManager, the trust in the server's identity "+
"is completely lost.\n --------------------------------");
for (int i=0; i<o.length; i++)
System.out.println(" certificate " + i + " -- " + o[i].toString());
return true;
}
}

The SSLSocketClient example uses the custom Trust Manager show above. The SSLSocketClient shows how to set up a new SSL connection by using an SSL Context with the Trust Manager. This example is at this location on WebLogic Server:

SAMPLES_HOME\server\src\examples\security\sslclient

Using an SSLContext

SSLContext is used to implement a secure socket protocol that holds information such as Host Name Verifier and Trust Manager for a given set of SSL connections. An instance of the SSLContext class is used as a factory for SSL sockets. For example, all sockets that are created by socket factories provided by the SSLContext can agree on session state by using the handshake protocol associated with the SSLContext. Each instance can be configured with the keys, certificate chains, and trusted root CAs that it needs to perform authentication. These sessions are cached so that other sockets created under the same SSLContext can reuse them later. See Modifying Parameters for Session Caching in the Administration Guide for more information on session caching. To associate an instance of a Trust Manager class with its SSLContext, use the setTrustManagerJSSE method.

You can only set up an SSL context programmatically; not by using the Administration Console or the command line. A Java new expression or the getInstance() method of the SSLContext class can create an SSLContext object. The getInstance() method is static and it generates a new SSLContext object that implements the specified secure socket protocol. An example of using SSLContext is provided in the SSLSocketClient sample in the SAMPLES_HOME\server\src\examples\security\sslclient directory. This example shows how to create a new SSL Socket Factory that will create a new SSL Socket using SSLContext: SSLContext conforms to Sun Microsystems's Java Secure Socket Extension (JSSE), so it is forward-compatible code.

Listing 3-17 shows a sample instantiation.

Listing 3-17 SSL Context Code Example

import weblogic.security.SSL.SSLContext;
SSLcontext sslctx = SSLContext.getInstance ("https")

Using an SSLServerSocketFactory

Instances of this class create and return SSL sockets. This class extends javax.net.SocketFactory.

Listing 3-18 shows a sample instantiation.

Listing 3-18 SSLServerSocketFactory Code Example

import weblogic.security.SSL.SSLSocketFactory;
  SSLSocketFactory sslSF = (SSLSocketFactory) sslCtx.getSocketFactoryJSSE();

Using URLs to Make Outbound SSL Connections

You can use a URL object to make an outbound SSL connection from a WebLogic Server acting as a client to another WebLogic Server. The weblogic.net.http.HttpsURLConnection class provides a way to specify the security context information for a client including the digital certificate and private key of the client. Instances of this class represent an HTTPS connection to a remote object.

The SSLClient code example demonstrates using the WebLogic URL object to make an outbound SSL connection (see Listing 3-19). The code example shown in Listing 3-19 is excerpted from the SSLClient.java file in the SAMPLES_HOME\server\src\examples\security\sslclient directory.

Listing 3-19 WebLogic URL Outbound SSL Connection Code Example

wlsUrl = new URL("https", host, Integer.valueOf(sport).intValue(),
query);
weblogic.net.http.HttpsURLConnection sconnection =
new weblogic.net.http.HttpsURLConnection(wlsUrl);

In addition, the SSLClient code example demonstrates using the Java Secure Socket Extension (JSSE) application programming interface (API) to make an outbound SSL connection (see Listing 3-20).

Listing 3-20 JSSE URL Outbound SSL Connection Code Example

URL jsseUrl = new URL( "https", host,
Integer.valueOf(sport).intValue(), query,
new com.sun.net.ssl.internal.www.protocol.https.Handler() );
java.net.HttpURLConnection sconnection =
(java.net.HttpURLConnection) jsseUrl.openConnection();

 

Back to Top Previous Next