BEA Logo BEA WebLogic Server Release 6.1

  BEA Home  |  Events  |  Solutions  |  Partners  |  Products  |  Services  |  Download  |  Developer Center  |  WebSUPPORT

 

  |  

  WebLogic Server Doc Home   |     Security   |   Previous Topic   |   Next Topic   |   Contents   |   View as PDF

Programming with the WebLogic Security SPI

 

The following sections describe how to program with the WebLogic Security SPI including:

 


Before You Begin

This section describes programming with application programming interfaces (APIs) in the Security service provider interface (SPI) supplied by WebLogic Server. Before you perform the programming tasks described in this section, the following configuration tasks must be completed:

  1. Specify a security realm (the default, an alternate, or a custom security realm).

  2. Add Users and Groups to the security realm.

  3. Assign ACLs and permissions to the resources in the security realm.

  4. Configure the SSL protocol (an optional step to provide additional protection for network connections or when using certificate authentication).

  5. Configure two-way SSL (optional).

  6. Configure certificate authentication (optional)

For more information about these configuration tasks, see Managing Security in the Administration Guide.

For information about security in WebLogic EJBs, see Programming WebLogic Enterprise JavaBeans.

 


WebLogic Security SPI

The WebLogic Security SPI builds upon the Java Developer's Kit (JDK) security SPI: it provides implementations and extensions where needed and a realm interface that collects the security APIs into an authentication and authorization service for WebLogic Server. The authentication scheme in WebLogic Server is based on the Java Authentication and Authorization Service (JAAS). This standard provides the support needed to submit a username and credential (password or digital certificate) when initiating a secure connection to WebLogic Server.

Table 4-1 lists the packages that are used when security is used in the WebLogic Server environment.

Table 4-1 WebLogic Security Packages

This Package. . .

Is Used for. . .

javax.security.auth

Performing JAAS-style LoginContext and Subject based authentication.

weblogic.security

Mapping digital certificates sent from Web browsers and Java clients to WebLogic Server. This class makes it unnecessary for a user with a valid digital certificate to enter a username and password when accessing resources in WebLogic Server.

weblogic.security.acl

Creating custom security realms to access WebLogic Server users, groups, or ACLs from an external store. In addition, this package is used to test custom ACLs in server-side programs.

weblogic.security.audit

Auditing security events. WebLogic Server calls the Audit class with information about authentication and authorization requests. The package can be used to filter the authorization and authentication requests and direct them to a log file or other administrative facility.

weblogic.security.net

Examining connections to WebLogic Server and allowing or denying the connections based on attributes such as the IP address, domain, or protocol of the initiator of the network connection.

weblogic.net.http.
HTTPsURLConnection

Makes an outbound SSL connection from a WebLogic Server acting as a client to another WebLogic Server.

weblogic.security.SSL.HostNameVerifier

Provides support for client authentication. it includes the Host Name Verifier, Trust Manager, and SSL Context classes.


 

 


Using 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 who runs the code. JAAS is provided in WebLogic Server as an alternative to the JNDI authentication mechanism. It is the preferred method of authentication. In order to use JAAS, you need to have the Java SDK version 1.3 installed.

Note: The authorization component of JAAS is not provided in WebLogic Server.

Table 4-2 lists the JAAS classes supported in WebLogic Server.

Table 4-2 JAAS Classes

Class

Description

javax.security.auth.Subject

Represents the source of the request and can be any entity (for example, a person or a client). A Subject object is created at the completion of a successful user authentication or login.

javax.security.auth.login.LoginContext

Through the LoginContext object, an application initiates login, logout, and acquires the authenticated Subject for the purpose of authorization checking.

javax.security.auth.login.Configuration

Provides the getConfiguration() method for the purpose of obtaining a list of LoginModules provided in a particular implementation of WebLogic Server.

javax.security.auth.spi.LoginModule

Provides the ability to implement different kinds of authentication technologies into WebLogic Server. For example, one LoginModule object may perform password authentication while another LoginModule object performs certificate authentication.

javax.security.auth.callback.Callback

Gathers input from users (such as a password or the name of a digital certificate file) and passes it to the Java client.

javax.security.auth.callback.Callback.
CallbackHandler

Provides a way for the LoginModule to communicate with a Subject to obtain authentication information. Implements the CallbackHandler interface and passes it to the LoginContext which forwards it directly to the underlying LoginModules. The LoginModules use the CallbackHandler both to gather input from users (such as a password) or to supply information to users (such as status information). By using CallbackHandlers, LoginModules can remain independent of the different ways WebLogic Server communicates with users.


 

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

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

    WebLogic Server provides a helper class weblogic.security.auth.Authenticate that facilitates the writing of a LoginModule class. The weblogic.security.auth.Authenticate class uses a JNDI Environment object and returns an authenticated Subject. The JNDI Environment object should include the properties listed in Table 4-3.

  2. Implement a Configuration class that specifies which LoginModule classes should be used for your WebLogic Server and in which order the LoginModule classes should be invoked.

  3. In the Java client, instantiate a LoginContext.

    The LoginContext consults the Configuration to load all of the LoginModules configured for WebLogic Server.

  4. 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.

  5. Retrieve the authenticated Subject from the LoginContext.

  6. Upon successful authentication of a Subject, access controls can be placed upon that Subject by invoking the doAs() method of the javax.security.auth.Subject class. The doAs() method associates the specified Subject with the current thread's ACL and then executes the action. If the Subject has the necessary access controls the action is completed; however, if the Subject does not have the necessary access controls, a security exception is raised.

Theexamples.security.jaas example in the samples/examples/security directory provided with WebLogic Server shows how to use JAAS authentication in a Java client.

Listing 4-1 contains an implementation of the javax.security.auth.spi.LoginModule class that performs password authentication. The code in Listing 4-1 is excerpted from the SampleLoginModule in the examples.security.jaas package.

Listing 4-1 Example of LoginModule for Password Authentication

...

//Import the relevant classes.//
import java.util.Map;
import java.io.IOException;
import java.net.MalformedURLException;
import java.rmi.RemoteException;
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.jndi.Environment;
public class SampleLoginModule implements LoginModule 
{
	private Subject subject = null;
	private CallbackHandler callbackHandler = null;
	private Map sharedState = null;
	private Map options = null;
	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 
  public void initialize(Subject subject,
			CallbackHandler callbackHandler,
			Map sharedState,
			Map options) 
  {
    		this.subject = subject;
		this.callbackHandler = callbackHandler;
		this.sharedState = sharedState;
		this.options = options;
// Retrieve WebLogic Server URL string

		url = System.getProperty
			("weblogic.security.jaas.ServerURL");
//Authenticate the user using the username and password passed in.
//Return true if successful. 
//Raise FailedLoginException if the authentication fails.
//Raise 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)
	throw new LoginException("No CallbackHandler Specified");
	// Populate callback list//
	Callback[] callbacks = new Callback[2];
	callbacks[0] = new NameCallback("username: ");
	callbacks[1] = new PasswordCallback("password: ", false);
	// Prompt for username and password
	callbackHandler.handle(callbacks);
	// Retrieve username
	username = ((NameCallback) callbacks[0]).getName();
	// 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);
	}
// Populate weblogic environment and authenticate
	Environment env = new Environment();
	env.setProviderUrl(url);
	env.setSecurityPrincipal(username);
	env.setSecurityCredentials(password);
// Authenticate user credentials, populating Subject
	Authenticate.authenticate(env, subject);
// Successfully authenticated subject with supplied info
	succeeded = true;
	return succeeded;
...

WebLogic Server uses the default LoginModule (weblogic.security.internal.ServerLoginModule) to gather authentication information during server initialization. To replace the default Login module, edit the Server.policy file and replace the name of the default Login module with the name of a custom Login module.

The JAAS implementation in WebLogic Server allows the use of NameCallback, PasswordCallback, and TextInputCallback callbacks in the provided LoginModules.

Optionally, custom Login modules can be specified in the server.policy file ahead of the default LoginModule. The JAAS implementation in WebLogic Server uses Login modules in the order in which they are defined in the server.policy file. The default Login module checks for existing system user authentication definitions prior to execution and does nothing if they are already defined.

The default Login Module is required to define JVM properties for both the system username and password. These properties are specified as weblogic.management.username and weblogic.management.password respectively. In order to use a custom Login modules, these properties must be set accordingly.

Listing 4-2 contains an implementation of the javax.security.auth.login.Configuration class. The code in Listing 4-2 is excerpted from the SampleConfig in the examples.security.jaas package.

Listing 4-2 Example of a Configuration Implementation

...
import java.util.Hashtable;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.AppConfigurationEntry;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class SampleConfig extends Configuration
{
	String configFileName = null;
//Create a new Configuration object.
	public SampleConfig()
//Retrieve an entry from the Configuration using an application name 
//as an index. This code specified a single Login Module.
	public AppConfigurationEntry[] 
		getAppConfigurationEntry(String applicationName)
	{

		AppConfigurationEntry[] list = 
				new AppConfigurationEntry[1];
		AppConfigurationEntry entry = null;
// Get the specified configuration file
	configFileName =
		System.getProperty("weblogic.security.jaas.Policy");
	System.out.println("Using Configuration File: " + 
		configFileName);
	try
	{
		FileReader fr = new FileReader(configFileName);
		BufferedReader reader = new BufferedReader(fr);
		String line;
	line = reader.readLine();
	while(line != null)
	{
// Skip lines until the line starting with a '{'
	if(line.length() == 0 || line.charAt(0) != '{')
		{
			line = reader.readLine();
			continue;
	}
// Read following line which contains the LoginModule configured
	line = reader.readLine();
	int i;
	for(i = 0; i < line.length(); i++)
	{
		char c = line.charAt(i);
		if(c != ' ')
	break;
	}
		int sep = line.indexOf(' ', i);

	String LMName = line.substring(0, sep).trim();
	String LMFlag = line.substring(sep + 1)line.indexOf
						(' ', sep + 1));

	System.out.println("Login Module Name: " + LMName);
	System.out.println("Login Module Flag: " + LMFlag);

	if(LMFlag.equalsIgnoreCase("OPTIONAL"))
	{
		entry = new AppConfigurationEntry(LMName,
                       AppConfigurationEntry.LoginModuleControlFlag.
			OPTIONAL,new Hashtable());
	list[0] = entry;
	}

	else if(LMFlag.equalsIgnoreCase("REQUIRED"))
	{
		entry = new AppConfigurationEntry(LMName,
     			AppConfigurationEntry.LoginModuleControlFlag.
			REQUIRED, new Hashtable());
	list[0] = entry;
	}

	else if(LMFlag.equalsIgnoreCase("REQUISITE"))
	{
		entry = new AppConfigurationEntry(LMName,
			AppConfigurationEntry.LoginModuleControlFlag.
			REQUISITE, new Hashtable());
	list[0] = entry;
	}

	else if(LMFlag.equalsIgnoreCase("SUFFICIENT"))
        {
		entry = new AppConfigurationEntry(LMName,
			AppConfigurationEntry.LoginModuleControlFlag.
			SUFFICIENT,new Hashtable());
	list[0] = entry;
...
//Refresh and reload all of the Login configurations.
	public void refresh()
...

Listing 4-3 contains an example of a Java client that uses JAAS authentication. The code in Listing 4-3 is excerpted from the SampleClient in the examples.security.jaas package. Note that the Java client includes an implementation of the javax.security.auth.callback.Callback.CallbackHandler class.

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

//Import the required classes.
import java.io.*;
import java.util.*;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
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;
public class SampleClient
{
//Attempt to authenticate the user.
	LoginContext loginContext = null;
//Set JAAS server url system property and create a LoginContext.
{

//Set Server url for SampleLoginModule, the LoginModule for
//the JAAS code example
       Properties property = new Properties();
       property = System.getProperties();
       property.put("weblogic.security.jaas.ServerURL", args[0]);
       System.setProperties(property);
// Set configuration class name to load SampleConfiguration, the
//Configuration for the JAAS code example
       Properties property = new Properties();
       property = System.getProperties();
       property.put("weblogic.security.jaas.Configuration", 
                      "examples.security.jaas.SampleConfig");
       System.setProperties(property);
// Set Configuration file name to load sample configuration policy 
//file.
       Properties property = new Properties();
       property = System.getProperties();
       property.put("weblogic.security.jaas.Policy", 
                        "Sample.policy");
       System.setProperties(property);
// Create LoginContext
       loginContext = new LoginContext("SampleLoginModule", new 
       MyCallbackHandler());
}
//Attempt authentication
	loginContext.login();
//Retrieve authenticated Subject and perform SampleAction as 
//Subject.
	Subject subject = loginContext.getSubject();
	SampleAction sampleAction = new SampleAction();
	Subject.doAs(subject, sampleAction);
//Implementation of the CallbackHandler Interface
class MyCallbackHandler implements CallbackHandler
{
	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:
		System.out.println(toc.getMessage());
	break;
		case TextOutputCallback.ERROR:
		System.out.println("ERROR: " + toc.getMessage());
	break;
		case TextOutputCallback.WARNING:
		System.out.println("WARNING: " + toc.getMessage());
	break;
		default:
		throw new IOException("Unsupported message type: " + 
		toc.getMessageType());
		}
	}
	else if(callbacks[i] instanceof NameCallback)
      {
// Prompt the user for the username
	NameCallback nc = (NameCallback) callbacks[i];
	System.err.print(nc.getPrompt());
	System.err.flush();
	nc.setName((new BufferedReader(new 
	InputStreamReader(System.in))).readLine());
	}else if(callbacks[i] instanceof PasswordCallback)
      {
// Prompt the user for the password
	PasswordCallback pc = (PasswordCallback) callbacks[i];
	System.err.print(pc.getPrompt());
	System.err.flush();
//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[] password = new char[passLen];
		for(int passIdx = 0; passIdx < passLen; passIdx++)
		password[passIdx] = tmpPassword.charAt(passIdx);
		pc.setPassword(password);
	}
	else
	{
	throw new UnsupportedCallbackException(callbacks[i], 
	"Unrecognized Callback");
	}
...

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

 


Using JNDI Authentication

Java clients can also use JNDI to pass credentials. 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.

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

Table 4-3 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 4-4 demonstrates how to use password authentication in a Java client. The code in Listing 4-4 is excerpted from the Client in the examples.security.aclexample provided with WebLogic Server in the samples/examples/security directory.

Listing 4-4 Example of Password Authentication

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

      ctx = new InitialContext(env);

Listing 4-5 demonstrates how to use certificate authentication in a Java client. Notice the use of the T3S protocol, which is a WebLogic Server proprietary protocol over the SSL protocol. The SSL protocol protects the connection and communication between WebLogic Server and the Java client.

Listing 4-5 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 4-4 and Listing 4-5 generates a call to weblogic.security.acl.Security.getUser() which returns a User object if the username and password are correct or 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 ACLs.

Note: For information on JNDI contexts and threads and how to avoid potential JNDI context problems, see JNDI Contexts and Threads and How to Avoid JNDI Context Problems in the Programming WebLogic JNDI.

 


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/examples/security/sslclient directory.

 


Using Mutual 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 that presented it.

With mutual authentication, the requesting client also presents a digital certificate to WebLogic Server. By setting fields in the Administration Console, you can configure WebLogic Server to require requesting clients 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 more information, see the "Configuring the SSL Protocol" section in Managing Security.

The following sections describes the different ways mutual authentication can be implemented in WebLogic Server.

Note: When using JAAS for authentication in a Java client, you write a LoginModule class that performs mutual authentication.

Mutual Authentication with JNDI

When using JNDI for 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 fileRealm.properties 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 4-6 demonstrates how to use mutual authentication in a Java client. The code in Listing 4-6 is excerpted from the AltClient in the examples.security.aclexample in the samples/examples/security directory provided with WebLogic Server.

Listing 4-6 Example of Mutual Authentication

package examples.security.acl;

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


public class AltClient
{

  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 ACL-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.

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 implement the CertAuthenticator interface, WebLogic Server passes the digital certificate for the Java client to the implementation of the CertAuthenticator class. The CertAuthenticator class maps the digital certificate to a WebLogic Server User. Because the digital certificate is processed only at the time of the first connection request from the JVM, it is not possible to set a new user identity when you use the CertAuthenticator class.

Caution: Restriction: Multiple, concurrent, user logins to WebLogic Server from a single client JVM when using mutual authentication and JNDI is not supported. If multiple logins are executed on different threads, the results are undeterminable and might result in one user's requests being executed on another user's login, thereby allowing one user to access another user's data. WebLogic Server does not support multiple, concurrent, certificate-based logins from a single client JVM. For information on JNDI contexts and threads and how to avoid potential JNDI context problems, see JNDI Contexts and Threads and How to Avoid JNDI Context Problems in Programming WebLogic JNDI.

Mapping a Digital Certificate to a WebLogic Server User

When you perform mutual authentication, WebLogic Server authenticates the digital certificate of the Web browser or Java client in order to establish an SSL connection. However, the digital certificate does not identify the Web browser or Java client as a User in the WebLogic Server security realm. If the Web browser or Java client requests a WebLogic Server resource protected by an ACL, WebLogic Server requires the Web browser or Java client to provide a username and password.

To map a Web browser or Java client to a User in the WebLogic Server security realm, implement the weblogic.security.acl.CertAuthenticator interface. The CertAuthenticator class is called after an SSL connection has been established. The class can extract data from a digital certificate to determine which User owns the digital certificate. The CertAuthenticator class then calls the weblogic.security.acl.getUser() method to retrieve the authenticated User object from the WebLogic Server security realm.

When the CertAuthenticator class is installed, it is unnecessary for Web browsers to prompt for WebLogic Server usernames and for Java applications to set a password in the JNDI SECURITY_CREDENTIALS property. For more information, see the "Configuring the SSL Protocol" section in Managing Security.

If you use the CertAuthenticator class with a Java client application, note that the Java client cannot change the User identity once the SSL connection is established. To supply a new digital certificate, you must stop the JVM for the Java client and restart the client in a new JVM instance so that a new SSL connection can be negotiated.

You can use any of the several methods to map a digital certificate to a User. One technique is to set the password of a User to the fingerprint of the User's digital certificate. Then you can extract the username from the digital certificate, calculate the fingerprint, and call the weblogic.security.acl.getUser() method in the same way WebLogic Server does when a User submits a username and a password to access a resource.

Note: The fingerprint of a digital certificate is not part of the certificate but it can be computed from the certificate. A fingerprint is the MD5 digest of the DER-encoded CertificateInfo which is an ANS.1 type included in the X.509 specification.

The CertAuthenticator class has a public no-arg constructor and invokes the authenticate() method. WebLogic Server calls the authenticate() method with a username, which may be null, a Certificate array containing the digital certificate of the Java client or a certificate chain, and a Boolean that is true if an SSL handshake succeeds. You can call methods on the Certificate array to retrieve data from the digital certificate.

Listing 4-7 shows how to implement the CertAuthenticator interface. It extracts the username from the e-mail address in the digital certificate and calls the weblogic.security.acl.getUser() method to retrieve an authenticated User object from the WebLogic Server security realm. Because the code example examines only a portion of the e-mail address, this example is not very secure. Digital certificates with the same e-mail address in different domains can be mapped to the same User and no additional authentication is performed. If you want to implement this feature, you may want to add code that fully establishes the identity of the client.

Listing 4-7 also shows how to map a digital certificate to a User in a WebLogic Server security realm. The code in Listing 4-7 is excerpted from SimpleCertAuthenticator in theexamples.security.cert example in the samples/examples/security directory provided with WebLogic Server.

Listing 4-7 Example of Mapping a Digital Certificate to a WebLogic Server User


package examples.security.cert;


import weblogic.security.Certificate;
import weblogic.security.Entity;
import weblogic.security.X500Name;
import weblogic.security.acl.CertAuthenticator;
import weblogic.security.acl.BasicRealm;
import weblogic.security.acl.Realm;
import weblogic.security.acl.User;

public class SimpleCertAuthenticator
  implements CertAuthenticator
{
  private BasicRealm realm;

  public SimpleCertAuthenticator()
  {
    realm = Realm.getRealm("weblogic");
  }


  /**
   * Attempt to authenticate a remote user.
   *
   * @param userName ignored by this example
   * @param certs used to attempt to map from email address to 
   * @a WebLogic user.
   * @param ssl if false, this example returns null
   * @return authenticated user, or null if authentication failed
   */
  public User authenticate(String userName, Certificate[] certs, 
boolean ssl)
  {
    // This implementation only trusts certificates that originate
    // from a successful two-way SSL handshake.
    if (ssl == false)
    {
      return null;
    }

    User result = null;
    Certificate cert = certs[0];
    Entity holder = cert.getHolder();

    if (holder instanceof X500Name)
    {
      X500Name x500holder = (X500Name) holder;
      String email = x500holder.getEmail();

      if (email != null)
      {
int at = email.indexOf("@");

if (at > 0)
{
  String name = email.substring(0, at);

  // Make sure that the user we've pulled out of the email
  // address really exists.
  result = realm.getUser(name);
}
      }
    }

    return result;
  }
}

The constructor in Listing 4-7 shows how to get access to the WebLogic Server security realm in a server-side class. The getRealm("weblogic") method of the weblogic.security.acl.realm class returns the realm being used by WebLogic Server, whether it is the File realm or an alternative security realm, such as the LDAP Security realm.

The weblogic.security.X500Name class includes accessor methods to retrieve fields from the weblogic.security.Certificate class. Listing 4-7 casts the Certificate object to an X500Name object, calls the getEmail() method of the X500Name object, and takes the initial substring of the e-mail address. The getUser(String) method in the weblogic.security.acl.AbstractableRealm class retrieves the WebLogic Server user with the computed username. If the user does not exist, the authenticate() method of the weblogic.security.acl.AbstractableRealm class returns null.

Using Mutual Authentication with Other WebLogic Servers

You can use mutual authentication in server-to-server communication in which one WebLogic Server is acting as the client of another WebLogic Server. Using mutual authentication in server-to-server communication allows you to depend on high-security connections, even without the more familiar client/server environment.

Listing 4-8 establishes a secure connection to a second WebLogic Server called server2.weblogic.com from a servlet running in WebLogic Server.

Listing 4-8 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");
T3Client t3c = e.createProviderClient();
t3c.connect();
e.setInitialContextFactory
      ("weblogic.jndi.WLInitialContextFactory");
Context ctx = new InitialContext(e.getProperties())
...

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

Note: For information on JNDI contexts and threads and how to avoid potential JNDI context problems, see JNDI Contexts and Threads and How to Avoid JNDI Context Problems in the Programming WebLogic JNDI.

Using Mutual 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. A digital certificate includes information, such as the following:

You can see the available information in each digital certificate by calling the to_string()method on the X509 object that represents the digital certificate.

You can create a weblogic.security.JDK11Certificate object for a digital certificate by passing an X509 object to its constructor. The weblogic.security.JDK11Certificate class implements the java.security.Certificate interface.

Listing 4-9 converts the first X509 object in an array of X509 objects to a JDK111Certificate object.

Listing 4-9 Converting an X509 Object to a JDK111Certificate Object

weblogic.security.JDK11Certificate jdk11cert =
	new weblogic.security.JDK111Certificate(X509certs [0]);
print(out, "jdk11cert.getPrincipal().getName() -",
                        jdk11cert.getPrincipal().getName() );
print(out, "jdk11cert.getGuarantor().getName() -",
                        jdk11cert.getGuarantor().getName() );

The weblogic.security.JDK11Certificate class has the following member functions that provide additional information about the digital certificate:

 


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 Server or a WebLogic client is acting as an SSL client to another application server. It prevents man-in-the-middle attacks.

The default behavior of 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.HostnameVerifier 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.HostnameVerifier interface and define the methods that capture information about the server's security identity.

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

To make a connection that uses a non-default JDK protocol handler, make sure to initialize the handler by calling these two functions:

weblogic.net.http.Handler.init();
weblogic.management.application.Handler.init();

An example of a custom Host Name Verifier is available in the SSLclient code example in the examples.security.sslclient package in the /samples/examples/security/sslclient directory. This code example contains a NullHostnameVerifier 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.

You can associate an instance of a Host Name verifier with an SSL Context through the setHostnameVerifier method. For example:

public void setHostnameVerifier (HostnameVerifier hv)

See the following information on Using an SSL Context.

 


Using a Trust Manager

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

When an SSL client connects to an SSL server, the SSL server presents its digital certificate chain to the client for authentication. That chain can sometimes contain an invalid digital certificate. The SSL specification says that the client should drop the SSL connection upon discovery of an invalid certificate. Web browers, however, attempt 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.

Use the weblogic.security.SSL.TrustManager class to create a Trust Manager. The class 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.TrustManager class uses a callback function to override the result of verifying the digital certificate. You can associate an instance of a Trust Manager wtih an SSL Context through the setTrustManager() method. The weblogic.security.SSL.TrustManager class conforms to the JSSE specification. Note that the use of a Trust Manager does not impact performance. You can only set up a Trust Manger programmatically; its use is not defined through the Administration Console or on the command-line.

Examples of using Trust Manager are available in the /samples/examples/security/sslclient directory:

 


Using an SSL Context

SSL Context 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 SSL Context class is used as a factory for SSL sockets. For example, all sockets that are created by socket factories provided by the SSL Context can agree on session state by using the handshake protocol associated with the SSL Context. 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 SSL Context can resuse 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 SSL Context, use the setTrustManager method.

You can only set up SSL Context programatically; not by using the Administration Console or the command line. A Java new expression or the getInstance () factory method of the SSL Context class can create an SSL Context object. The getInstance() factory method is static and it returns an instance to implement a secure socket protocol. An example of using SSL Context is available in the /samples/examples/security/sslclient directory:

SSL Context conforms to Sun Microsystems's Java Secure Socket Extension (JSEE), so is forward-compatible code.

 


Using Custom ACLs

WebLogic Server defines a standard set of access control lists (ACL)s to protect resources. If you create an ACL for a resource, WebLogic Server automatically checks the permissions for that resource before allowing anyone to access it. Most resources in WebLogic Server can be fully protected with these standard ACLs.

Some resources, however, require more protection than is offered by the standard set of ACLs. WebLogic Server allows you to augment the security for such resources. You may, for example, create a servlet that checks user permissions before writing certain data to a Web page. The weblogic.security.acl.Security class provides access to realm operations, such as checking an ACL. This class is available only to server-side code.

TheSecurity.hasPermission() and Security.checkPermission() methods in the weblogic.security.acl.Security class test whether a user has the required permission necessary to access a resource. The two methods are similar except that the Security.hasPermission() method returns a Boolean (true if the user has the relevant permission) and the Security.checkPermission()method throws java.lang.SecurityException if the user does not have the permission.

Theexamples.security.acl example provided with WebLogic Server (in the samples/examples/security directory) shows how to create your own ACLs and test them in a server-side class. The custom ACL protects an RMI class, FrobImpl, with a custom ACL named aclexample that has the permission of frob.

Note: Before a custom ACL can be used, the ACL must be installed in the security realm being used by WebLogic Server.

To use the custom ACL, the Java client application must:

  1. Get a JNDI InitialContext from WebLogic Server.

  2. Look up the protected resource in the WebLogic Server JNDI tree using the permission name. For example, the Java client looks up FrobImpl using the permission name, frob.

  3. Execute the frob() method on the RMI stub.

The FrobImpl class contains the server-side code that tests the ACL. Listing 4-10 shows how to use the static checkPermission() method in the weblogic.security.acl.realm class to test the custom ACL.

Listing 4-10 Testing Custom ACLs in the Default Security Realm

Security.checkPermission(Security.getCurrentUser(),
          "aclexample", 
          Security.getRealm().getPermission("frob"), 
          '.');

You can also test custom ACLs in alternate security realms:

  1. Use the getRealm(realm_name) method of the weblogic.security.acl.realm class to get the alternate security realm.

  2. Retrieve the custom ACL and permission using the getACL()and the getPermission()methods of the weblogic.security.acl.realm class.

  3. Test the permission by calling the acl.checkPermission()method.

Listing 4-11 illustrates this technique for testing custom ACLs.

Listing 4-11 Testing Custom ACLs in an Alternate Security Realm

    User p = Security.getCurrentUser();
    BasicRealm realm = Realm.getRealm(realm_name);
    Acl acl = realm.getAcl(acl_name);
    Permission perm = realm.getPermission(permission_name);
    boolean result = acl == null || !acl.checkPermission(p, perm);

The last line in Listing 4-11 tests whether the custom ACL was found and whether the user p has the frob permission. The sense of the test is reversed; if the ACL exists and the user has frob permission, the result is false.

If a security realm does not implement the getACL() functionality, it should throw a java.lang.UnsupportedOperationException exception. ACL lookups will then fall back to the secondary realm. If no secondary realm is configured, a runtime error will occur.

If you audit security events and you use the technique in Listing 4-12 for testing permissions, you must explicitly call the static Audit class of the weblogic.security.audit package if you want to audit your permission tests. The call to the Audit class generates a notification of the permission-checking event to the AuditProvider class in WebLogic Server.

Listing 4-12 Testing Permission

    Audit.checkPermission("Frob", acl, p, perm, !result);

For a complete code example that uses custom ACLs, see the examples.security.acl package in the samples/examples/security directory provided by WebLogic Server.

 


Writing a Custom Security Realm

You may need to create your own security realm to draw from an existing security store in your environment such as a directory server on the network. To write a custom security realm that supports authentication you need to write code that:

  1. Defines a User class for the custom security realm.

  2. Defines a Group class for the custom security realm.

  3. Defines an enumeration class that return all Users and Groups in a security store and releases the resources of the security store when finished.

  4. Defines a class for the custom security realm.

  5. Obtains configuration data about the security store.

  6. Authenticates a User.

  7. Returns the members of a Group and creates a hash table that contains the members of a Group.

  8. Returns a User object given a User name.

  9. Returns a Group object given a Group name.

  10. Uses an enumeration for Users to return User objects for all the Users in the security store.

  11. Uses an enumeration for Groups to return Group objects for all the Groups in the security store.

You can also write a custom security realm that supports authorization. For more information, see Using Authorization in a Custom Security Realm.

Note: WebLogic Server also provides the capability to create a custom security realm that can be managed through the WebLogic Server Administration Console. For more information, see the Javadoc for the weblogic.security.acl package or contact BEA Professional Services.

Do not execute RMI calls to another server from a custom security realm. Using RMI calls may cause the server to run out of socket reader threads.

Table 4-4 lists the WebLogic classes used to create a custom security realm.

Table 4-4 WebLogic Classes Used to Create Custom Security Realms

Class

Definition

weblogic.security.acl.User

Defines a User that is retrieved from a local security store.

weblogic.security.acl.
FlatGroup

Defines a Group whose membership is updated when the local security store is updated.

weblogic.security.acl.
CloseableEnumeration

Defines an enumeration that can be closed thus releasing resources.

weblogic.security.acl.
AbstractListableRealm

Allows you to create a security realm whose Users, Groups, ACLs, and permissions can be viewed through the Administration Console. However, you need to use the facilities provided by the security store you are using to add and delete Users, Groups, and ACLs and assign permissions to Users and Group.

weblogic.security.acl.
RefreshableRealm

Synchronizes the information about Users, Groups, ACLs, and permissions displayed in the Administration Console with the information in the local security store.

weblogic.management.
configuration.
CustomRealmMbean

Obtains configuration information about the security store accessed from the custom security realm.


 

Figure 4-1 illustrates how these classes work together to create a custom security realm.

Figure 4-1 WebLogic Classes Used to Create Custom Security Realms


 

The following sections describe the programming tasks required to write and custom security realm.

Define a Class for Users

Extend the weblogic.security.acl.User class to create a User class for the custom security realm.

Listing 4-13 contains code that defines a User class.

Listing 4-13 Defining a User Class

// Import the required classes
import weblogic.security.acl.user;
...
// Create a custom User class for the custom realm
      /*package */class CustomRealmUser 
          extends User
    {
        // Keep track of the User's custom realm
        CustomRealm realm = null;

        // Implement a constructor
        /*package*/ CustomRealmUser(String name, CustomRealm realm);

            {
                // Call base constructor class passing in the name of the
                // User 
                super(name);

                // Keep track of this User's realm
                this.realm = realm;
            }
                // Return the User's custom realm      	
                public BasicRealm getRealm()
                  {
                        return realm;
                    }
        }
...

Define a Class for Groups

Groups make it easier to manage security. Internally, a custom security realm represents a Group as a hash table containing a list of members which can be Users or Groups.

To implement a Group in a custom security realm, extend the weblogic.security.acl.Flatgroup class to create a new Group with no membership information. The constructor for the class takes as input the name of the desired Group and the realm object corresponding to the custom security realm.

The Flatgroup class is especially designed to work with custom security realms. A custom security realm needs to periodically update Group membership. The FlatGroup maintains its group membership in a cache instead of in a static set. When the cache expires, the Group implementation queries the security store to obtain the most recent membership information. The default time for the Group cache is five minutes which means any changes you make in the underlying store will be recognized in the custom security realm within five minutes. You can tune this value by setting the GroupMembershipCacheTTL field in the Administration Console to the number of seconds a cached group remains valid.

Listing 4-14 contains code that defines a Group class.

Listing 4-14 Defining a Group Class

// Import the required classes
import weblogic.security.acl.FlatGroup;
...
/*package*/ class CustomRealmGroup
extends FlatGroup
{
        // Implement a constructor
        /*package*/ CustomRealmGroup(String name, 
                CustomRealm realm);
            {
                // Call the base class constructor passing in the name
	 // of the Group and custom security realm
            	  super(name, source);
            }
                // Implement a method that returns the user class for 
	 // custom security realm
	 protected Class getUserClass()
      {
        return CustomRealmUser.class;
            }
}
...

Define Enumeration Classes for Users and Groups

Write enumeration classes for Users and Groups. If the enumeration holds resources that must be released when the enumeration is done (for example, release a database cursor), then implement the weblogic.security.acl.CloseableEnumeration class. Otherwise, implement the java.util.Enumeration interface. In the enumerator constructor, pass the arguments needed to access the security store.

Do not create a User or Group object for every User or Group in the custom security realm and put them in a hash table that enumerates over them. A custom security realm in a deployed WebLogic Server can have more Users and Groups than can fit into memory. Instead, use a database cursor which can be used to incrementally create User or Groups objects as they are needed.

Listing 4-15 contains code that defines enumeration classes for users and groups.

Listing 4-15 Defining Enumeration Classes for Users and Groups

// Import the required classes
import weblogic.security.acl.FlatGroup;
import weblogic.security.acl.ClosableEnumeration;
...
// Define an enumeration class for users.
/*package*/ class CustomRealmUsersEnumeration
  implements CloseableEnumeration
{
        // Keep data members here (for example, a database cursor)

            // Keep track of the enumeration's security realm (for
            // use with the User constructor)
            private CustomRealm realm            = null;
	
            // Implement a constructor
            /*package */ CustomRealmUsersEnumeration(...,
				CustomRealm realm)
            {
                this.realm          = realm;
            }
	
            // Implement a method to determine if there are more Users.
            public boolean hasMoreElements()
            {
                //For example, use a database cursor to see if there are
                // more users
                return (there are more users ...) ? true : false;
            }

	 // Implement a method to return the next user.
	  // The method must return users objects that use the
	  // User class for the custom security realm
                public Object nextElement()
                {
                    // For example, use the database cursor to get the name
                    // of the next user.
      	      return new CustomRealmUser(next user name ..., realm);
    	  }
	
	  // Implement a method to terminate the enumeration. 
	  // This step is optional
	  public void close()

    	{
                    // If this enumeration is delegating to an iterator that
	      // needs to be closed (e.g., a database cursor), release
                    // the resources here.
    	}
}

// Create a Group class for the custom security realm
/*package*/class CustomRealmGroupsEnumeration 
      implements CloseableEnumeration
{
        // Keep data members here (for example, a database cursor

            // Keep track of the enumeration's security realm (for
            // use with the Groups constructor)
            private CustomRealm realm            = null;
	
                // Implement a constructor
                /*package */ CustomRealmUsersEnumeration(...,
				CustomRealm realm)
                {
                    this.realm          = realm;
                }
	
                // Implement a method to determine if there are more Groups.
                public boolean hasMoreElements()
                {
                    // For example, use a database cursor to see if there are
                    // more groups
  	      return (there are more groups ...) ? true : false;
	  }
                // Implement a method to return the next group.
	  // The method must return group objects that use the
	  // Group class for the custom security realm
	  public Object nextElement()
	  {
                    // For example, use the database cursor to get the name 
	   // of the next group.
                    return new CustomRealmGroup(next group name ..., realm);
                }
	
	// Implement a method to terminate the enumeration. 
	  // This step is optional
	  public void close()

    	{
                    // If this enumeration is delegating to an iterator that
	      // needs to be closed (e.g., a database cursor), release
                    // the resources here.
    	}
}
...

Define a Class for the Custom Security Realm

Extend the weblogic.security.acl.AbstractListableRealmclass to define a new class for the custom security realm and implement a constructor that creates the custom security realm. This class needs to:

  1. Obtain configuration data for the security store.

  2. Authenticate Users.

  3. Determine the members of a Group

  4. Get Users and Groups from the security store.

Weblogic Server caches User and Group information in memory. Therefore, BEA recommends having a custom security realm go to disk to whenever it needs User or Group information. The WebLogic Server system administrator can only change Users or Groups for a custom security realm by editing the information in the security store with tools provided for the security store. After the system administrator changes User or Group information in the security store, the system administrator must update the information in the Administration Console by clicking on the Reset button. This action automatically flushes the User and Group information kept in memory.

If the custom security realm caches User or Group information in memory, implement the weblogic.security.acl.RefreshableRealm class and the refresh() method of the class. When the system administrator updates resets the realm, the refresh() method is called. Use the refresh() method to discard any cached User or Group information.

Listing 4-16 contains code that defines a class for a custom security realm.

Listing 4-16 Defining a Class for a Custom Security Realm

...
// Import the necessary classes
import weblogic.security.acl.AbstractListableRealm;
import weblogic.security.acl.BasicRealm;
import weblogic.security.acl.RefreshableRealm;
import weblogic.server.Server;

// Create a class for the custom security realm
public class CustomRealm 
      extends AbstractListableRealm // Required
      implements Refreshable Realm // Optional

// Implement a constructor that creates the custom security realm
public CustomRealm()
{
	super("Custom Realm");
...
public void refresh()
{
	// Discard User and Group information in-memory
}

Obtain Configuration Data for the Security Store

In order to connect to the security store, configuration properties (for example, a property that contains a URL or a property that specifies a directory path) that specify how to access the security store used by the custom security realm must be defined. Once these properties are defined, the system administrator for WebLogic Server must set the properties in the Configuration Data section of the Custom Security Realm Create window in the Administration Console. The properties for the security store must be defined in the WebLogic Server administration environment before the custom security realm can be used.

For example, define two properties userInfoFileName and groupInforFileName that specify directory paths to files that contain User and Group information. The system administrator enters those properties in the Configuration Data section of the Custom Security Realm Create window in the Administration Console.

In the code for the custom security realm, use the weblogic.management.Helper and weblogic.management.configuration.DomainMbean classes to retrieve configuration properties for the custom security and use those properties to connect to the security store.

Listing 4-17 contains code that retrieves configuration properties for the security store and then connects to the security store.

Listing 4-17 Accessing the Security Store

...
// Import the necessary classes
import weblogic.management.Helper;
import weblogic.management.configuration.DomainMBean;
MBeanHome mHome = Helper.getMBeanHome(user, password, url,
                                         servername);
DomainMBean domainMBean = mHome.getActiveDomain();
SecurityMBean secMBean = domainMBean.getSecurity();
BasicRealmMBean basicRealmMBean =
               secMBean.getRealm().getCachingRealm.getBasicRealm();
CustomRealmMBean customRealmMBean =
    (CustomRealmMBean)basicRealmMBean;

// Get configuration data from the CustomRealmMBean
Properties configData = customRealmMBean.getConfigurationData();

// Get the properties for the custom security store.
String userInfoFileName =
	configData.getProperty("UserInfoFileName);
String groupInfoFileName =
	configData.getProperty("GroupInfoFileName);

Authenticate Users

If the custom security realm uses passwords to authenticate Users, you must explicitly implement the authUserPassword() method to authenticate Users. Write code that checks the security store to ensure that the user specified by the authUserPassword() method exists with the supplied password.

Listing 4-18 contains code that authenticates a User.

Listing 4-18 Authenticating Users

...
//Implement a method which authenticates a user
protected User authUserPassword(String name, String password)
{
        // Check the security store to see if there is a user
       // named name with password password

        if (...) {
		return new CustomRealmUser(name, this);
        } 	else     {
	return null;
      }
}

Determine the Members of a Group

Use the getGroupMembersInternal() method of the weblogic.security.acl.AbstractListableRealm class to get the members of a group and build a hash table with the members of a group.

Listing 4-19 contains code that obtains the members of a Group.

Listing 4-19 Obtaining the Members of a Group

...
// Implement a method to return members of a group.
protected Hashtable getGroupMembersInternal(String name)
{
	// Check the security store to see if there is a group with
	// the specified name
	if (!...)  {
	
	// If the group does not exist, throw an exception
              throw new CustomRealmException("No such group : " + name);
        }

	// Create a hash table which is filled with group members
	// and is then returned.

          	Hashtable members = new Hashtable();

	// Get the group members from the security store
	for (...) {// loop over the members
	      if (...) {
	      // If this member is a user, create a user object
	      // for this user and add it to the list of members.
	      // Use the User class created for the custom 
	      // security realm.
                   members.put(memberName, new CustomRealmUser
                                        (memberName, this));
                    }    else if
                   // If this member is a group, create a group object
                    // for this group and add it to the list of members.
	      // Use the Group class created for the custom
	      // security realm.
                    members.put(memberName, new CustomRealmGroup
                                        (memberName, this));
                    }
        }
        // Return the hash table containing the members of the group.
        return members;
    }
...

Get Users and Groups from Security Store

Implement the getUser() and getGroup() methods of the weblogic.security.acl.AbstractListableRealm class to retrieve Users and Groups from the security store into the custom security realm. Use the getUsers() and getGroups() methods of the weblogic.security.acl.AbstractListableRealm class to return enumeration objects that return User objects and Group objects for the User and Groups in the security store. The Users and Groups in the security store can now be viewed through the Administration Console.

Listing 4-20 contains code that retrieves Users and Groups from the security store.

Listing 4-20 Retrieving Users and Groups from the Security Store

...
// Implement a method to return a User object given the name of 
// the user.
public User getUser(String name)
{
    // Check the security store to see if there is a user with the
    // the specified name.
   if (...) {
    // If the user exists, return a User object for the
    // the user. Use the User class created for the custom
    // security realm.
    return new CustomRealmUser(name, this);
    } else {
    // Return a null if the user does not exist
    return null;
    }
}
// Implement a method to return a Group object given the name of 
// the Group.
public Group getGroup(String name)
{
    // Check the security store to see if there is a group with the
    // the specified name.
    if (...) {
    // If the group exists, return a Group object for the
    // the Group. Use the Group class created for the
    // custom security realm
    return new CustomRealmGroup(name, this
    } else {
    // Return a null if the group does not exist
    return null;
    }
}
// Implement a method to return an enumeration object that can 
// can be used to iterate over all the users in the custom security
// realm.
public Enumeration getUsers()
{
    // Return an enumeration object that returns User object for
    // the users in the security store. Use the user enumeration 
    // class created for the custom security realm. Remember to 
    // give the enumeration access to the security store so that
    // it can iterate over the users.
    return new CustomRealmUsersEnumeration(..., this);
}
// Implement a method to return an enumeration object that can 
// can be used to iterate over all the groups in the custom security
// realm.
public Enumeration getGroups()
{
    // Return an enumeration object that returns Group object for
    // the Groups in the security store. Use the group
    // enumeration class created for the custom security realm. 
    // Remember to give the enumeration access to the security 	
    // store so that it can iterate over the Groups.
    return new CustomRealmGroupsEnumeration(..., this);
}
...

Note: To improve performance, use the isMember() method of the weblogic.security.acl.FlatGroup class. This method checks to see if an individual user is a member of a group rather than fetching all group members.

Using Authorization in a Custom Security Realm

To construct an ACL in a custom security realm, create a new AclImpl object, set its name, and then add an AclEntryImpl object for each User or Group. Each AclEntry object contains a set of permissions associated with a particular principal which represents a User or a Group.

The AclImpl interface imposes an ordering constraint on constructing ACLs. You must add all permissions to an AclEntryImpl object before you add the AclEntryImpl object to the AclImpl object.

In the class for the custom security realm, use the getAcl() method to retrieve ACLs from a security store. The getAclInternal() method steps through the result set creating an AclImpl object, creating an AclEntryImpl object for each User or Group, and then adding permissions to the AclEntryImpl object. When an AclEntryImpl object is finished, it is added to the AclImpl object. When the AclImpl object is finished, the getAclInternal() method returns the finished ACL.

 


Auditing Security Events

The weblogic.security.audit package allows you to use an audit SPI for events that occur in the WebLogic Server security realm. The package includes an interface, AuditProvider, and a static class, Audit, to which WebLogic Server sends auditable security events.

To enable auditing, you create a class that implements the AuditProvider interface and the methods that represent the security events you want to audit. WebLogic Server calls the methods on your class when a user attempts to authenticate, when a permission is tested, or when an invalid digital certificate or root digital certificate is presented. Your AuditProvider class receives the information for each event type and processes the event in whatever way you choose. For example, it could log only unsuccessful authentication requests in the WebLogic Server log file, or record all auditable events in a database table.

Before you can use an AuditProvider class, you need to install the class through the Administration Console. For more information, see the "Installing an Audit Provider" section in Managing Security.

The LogAuditProvider example is available in the examples.security.audit package in the /samples/examples/security/audit directory provided by WebLogic Server. The example writes all events it receives in the WebLogic Server log file. It also defines filter methods for each event type, and calls those filters to decide whether to log a particular event. In the example code, the filter methods always return true so that all events are logged. If you extend this example, you can override the filter methods with methods that select the events you want to log. If you want to take some action other than logging, you can use the LogAuditProvider example as a starting point for creating your own provider.

 


Filtering Network Connections

Passwords, ACLs, and digital certificates allow you to secure WebLogic Server resources using some characteristic of a user. You can add an additional layer of security by filtering network connections. For example, you can deny any non-SSL connections originating outside of your corporate network.

To filter network connections, create a class that implements the weblogic.security.net.ConnectionFilter interface and install the class in WebLogic Server so that you can examine requests as they occur and then accept or deny them.

Before you can use a Connection Filter, you need to install the class through the Administration Console. For more information, see the "Installing a Connection Filter" section in Managing Security.

When a Java client or Web browser client tries to connect to WebLogic Server, WebLogic Server constructs a ConnectionEvent object and passes it to the accept() method of your ConnectionFilter class. The ConnectionEvent object includes the remote IP address (in the form of java.net.InetAddress), the remote port number, the port number of the local WebLogic Server, and a string specifying the protocol (HTTP, HTTPS, T3, T3S, or IIOP).

Your ConnectionFilter class can examine the ConnectionEvent object and accept the connection by returning, or deny the connection by throwing a FilterException.

The examples.security.net.SimpleConnectionFilter example included in the samples/examples/security/net directory provided by WebLogic Server filters connections using a rules file. The SimpleConnectionFilter example parses the rules file and sets up a rule-matching algorithm so that connection filtering adds minimal overhead to a WebLogic Server connection. The SimpleConnectionFilter example is an efficient, generalized connection filter. If necessary, you can modify this code. You may, for example, want to accommodate the local or remote port number in your filter or a more site-specific algorithm that will reduce filtering overhead.

In Listing 4-21, WebLogic Server calls the SimpleConnectionFilter.accept() method with a ConnectionEvent. The SimpleConnectionFilter.accept() method gets the remote address and protocol and converts the protocol to a bitmask to avoid string comparisons in rule-matching. Then the SimpleConnectionFilter.accept() method compares the remote address and protocol against each rule until it finds a match.

Listing 4-21 Example of Filtering Network Connections

  public void accept(ConnectionEvent evt)
    throws FilterException
  {
    InetAddress remoteAddress = evt.getRemoteAddress();
    String protocol = evt.getProtocol().toLowerCase();
    int bit = protocolToMaskBit(protocol);

    // this special bitmask indicates that the
    // connection does not use one of the recognized 
    // protocols
    if (bit == 0xdeadbeef)
    {
      bit = 0;
    }

    // Check rules in the order in which they were written.

    for (int i = 0; i < rules.length; i++)
    {
      switch (rules[i].check(remoteAddress, bit))
      {
      case FilterEntry.ALLOW:
return;
      case FilterEntry.DENY:
throw new FilterException("rule " + (i + 1));
      case FilterEntry.IGNORE:
break;
      default:
throw new RuntimeException("connection filter internal error!");
      }
    }

    // If no rule matched, we allow the connection to succeed.

    return;
  }

 


Using RMI over IIOP over SSL

The SSL protocol can be used to protect IIOP connections to RMI or EJB remote objects. The SSL protocol secures connections through authentication and encrypts the data exchanged between objects. You can use RMI over IIOP over SSL in WebLogic Server in the following ways:

In either case, you need to configure WebLogic Server to use the SSL protocol. For more information, see Configuring the SSL Protocol.

To use RMI over IIOP over SSL with a CORBA client ORB, do the following:

  1. Configure the CORBA client ORB to use the SSL protocol. Refer to the product documentation for your client ORB for information about configuring the SSL protocol.

  2. Use the host2ior utility to print the WebLogic Server IOR to the console. The host2ior utility prints two versions of the IOR, one for SSL connections and one for non-SSL connections.

  3. Use the SSL IOR when obtaining the initial reference to the CosNaming service that accesses the WebLogic Server JNDI tree.

For more information about using RMI over IIOP, see Programming WebLogic RMI Over IIOP.

To use RMI over IIOP over SSL with a Java client, do the following:

  1. If you want to use callbacks, obtain a private key and digital certificate for the Java client.

  2. Extend the java.rmi.server.RMISocketFactory class to handle SSL socket connections. Be sure to specify the port on which WebLogic Server listens for SSL connections. For an example of a class that extends the java.rmi.server.RMISocketFactory class, see Listing 4-22.

  3. Run the ejbc compiler with the -d option.

  4. Add your extension of the java.rmi.server.RMISocketFactory class to the CLASSPATH of the Java client.

  5. Use the following command options when starting the Java client:

    -xbootclasspath/a:%CLASSPATH%
    -Dorg.omg.CORBA.ORBSocketFactoryClass=
    implementation of java.rmi.server.RMISocketFactory
    -Dssl.certs= directory location of digital certificate for Java client
    -Dssl.key=directory location of private key for Java client

The Java client needs to have the classes that WebLogic Server uses for the SSL protocol included in its CLASSPATH.

For incoming connections (from WebLogic Server to the Java client for the purpose of callbacks), you need to specify a digital certificate and private key for the Java client on the command line. Use the ssl.certs and ssl.key command-line options to provide this information. The Java client in Listing 4-22 uses the SSL libraries in WebLogic Server to provide the SSL socket. Alternatively, you can use SSL provider such as Sun Microsystem Inc.'s JSSE as the SSL socket.

Listing 4-22 Example of java.rmi.server.RMISocketFactory

package examples.rmi_iiop.ejb.rmi_iiop;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.server.RMISocketFactory;
import java.util.StringTokenizer;
import java.util.Vector;
import weblogic.security.PEMInputStream;
import weblogic.security.RSAPrivateKey;
import weblogic.security.SSL.SSLCertificate;
import weblogic.security.SSL.SSLParams;
import weblogic.security.SSL.SSLServerSocket;
import weblogic.security.SSL.SSLSocket;
import weblogic.security.X509;
/**
*To use the SSL protocol , set the 
*org.omg.CORBA.ORBSocketFactoryClass system property to 
*examples.rmi_iiop.ejb.rmi_iiop.SSLSocketFactory.
*Since WebLogic Server may need to talk to the Java client
*(for example, when the Java client exports remote objects that
*WebLogic Server must call), it may be necessary to provide an SSL
*private key and digital certificate so that WebLogic Server can
*establish an SSL connection with the Java client*/
public class SSLSocketFactory extends RMISocketFactory
{
        static int sslPort = 7002;
        SSLCertificate cert;
        RSAPrivateKey key;
        private static InputStream getDERStream(String fileName)
        throws IOException
        {
            InputStream is = new FileInputStream(fileName);
            if (fileName.toLowerCase().endsWith(".pem")) {
                  is = new PEMInputStream(is);
        }
        return is;
        }
        public SSLSocketFactory()
        {
            String certFiles = System.getProperty("ssl.certs");
            String keyFile = System.getProperty("ssl.key");

            if (certFiles == null) {
            System.err.println("Warning: no server certs (ssl.certs)
            provided!");
            System.err.println("Warning: incoming server connections 
            may fail!");
            return;
        }
            if (keyFile == null) {
            System.err.println("Warning: no server private key (ssl.key)
            provided!");
            System.err.println("Warning: incoming server connections 
            may fail!");
            }
            StringTokenizer toks = new StringTokenizer(certFiles,
                                                        System.getProperty("path.separator",  
                                                          ","));
            cert = new SSLCertificate();
            cert.certificateList = new Vector();
            try {
                if (keyFile != null) {
                cert.privateKey = new
                                                    RSAPrivateKey(getDERStream(keyFile));
            }
                  while (toks.hasMoreTokens()) {
                  InputStream is = getDERStream(toks.nextToken());
                  cert.certificateList.addElement(new X509(is));
                  is.close();
                  }
            }
            catch (Exception e) {
                e.printStackTrace();
                System.exit(1);
            }
        }
        public Socket createSocket(String host, int port)
            throws IOException
        {
            Socket sock = null;
            System.out.println("*** connecting to " + host + ":" + port);
            if (port == sslPort) {
                  try {
                  SSLParams p = new SSLParams();
                  sock = new SSLSocket(host, port, p);
                  }
                  catch (Exception e) {
                  e.printStackTrace();
                  }
            } 
            else {
                  sock = new Socket(host, port);
            }
              return sock;
        }
            public ServerSocket createServerSocket(int i)
                  throws IOException
            {
                ServerSocket sock = null;
                  if (true) {
                      try {
                      SSLParams p = new SSLParams();
                  if (cert != null) {
                      p.setServerCert(cert);
                    } 
                    else {
                    System.err.println
                    ("**** Listening for SSL connections without server 
                      private key or certs!");
                    System.err.println
                    ("**** THIS MAY CAUSE FAILURES IF THE SERVER 
                    CONNECTS TO US!");
                    }
                  sock = new SSLServerSocket(i, p);
                  }
                  catch (Exception e) {
                  e.printStackTrace();
                  }
            } 
            else {
                sock = new ServerSocket(i);
            }
            int lp = sock.getLocalPort();
            if (i != lp) {
                  System.out.println("*** listening on any port - 
                  got " + lp);
            } 
            else {
                System.out.println("*** listening on port " + lp);
            }

            return sock;
        }
}

 

back to top previous page