Java Dynamic Management Kit 5.0 Tutorial

Chapter 13 Access Control and Security

Whenever considering a distributed architecture, security issues are often an added factor in the complexity of the design. This is not the case with the Java Dynamic Management Kit (DMK), whose security features are built into the modularity of the components.

Management solutions can evolve from basic password-based protection all the way to secure connections using cryptography simply by switching protocol connectors or by adding filter components. The rest of the architecture is unchanged because it relies on the interface that is common to all connectors.

There are two categories of access-control: connection-level control through a password and request-level control through a context object. Context checkers work as filters between the connector server and the MBean server. The filter logic can be determined dynamically, based on the nature of the request and on a context object provided by the client.

Security in the communication layer is achieved through the cryptography of a Secure Socket Layer (SSL) and the HTTPS connector. Using other components of the Java platform, connectors can effectively make all open communication undecipherable.

The code samples in this chapter are taken from the files in the Context example directory located in the main examplesDir (see “Directories and Classpath” in the Preface).

This chapter covers the following topics:

Password-Based Authentication

The simplest form of agent security is to accept management requests only if they contain a valid login identity and password. Agents recognize a given list of login-password pairs, and managers must provide a matching login and password when they try to establish a connection.

In the , only the HTTP–based connectors support password–based authentication. The SNMP protocol adaptor also supports access control, but it is based on a different mechanism (see IP-Based Access Control Lists).

By default, no authentication is enabled in the HTTP-based connectors and any manager can establish a connection. The password-checking behavior is enabled by defining the list of authorized login-password pairs.

You can define this authentication information in one of the following ways:

In both cases, only the agent application has access to these methods, meaning that the agent controls the authentication mechanism. As soon as an AuthInfo object is added to the connector server through either method, all incoming requests must provide a recognized name and password. In our example, we read the authentication information from the command line and call the addUserAuthenticationInfo.

Example 13–1 Implementing Password Authentication in the HTTP Connector Server

// Here we show the code for reading the
// id-password pairs from the command line
int firstarg = 0;
boolean doAuthentication = (args.length > firstarg);

AuthInfo[] authInfoList;

if (doAuthentication) {
    authInfoList = new AuthInfo[(args.length - firstarg) / 2];
    for (int i = firstarg, j = 0; i < args.length; i += 2, j++)

        authInfoList[j] = new AuthInfo(args[i], args[i + 1]);

} else

    authInfoList = null;

[...] // instantiate and register an HTTP connector server

// Define the authentication list
if (doAuthentication) {
    for (int i = 0; i < authInfoList.length; i++)

On the manager-side, identifiers and passwords are given in the address object, because authentication applies when the connection is established.

Example 13–2 Specifying the Login and Password in the HTTP Connector Server

// login and password were read from the command line
AuthInfo authInfo = null;

if (login != null) {
    authInfo = new AuthInfo( login, password );

// agentHost and agentPort are read from the command
// line or take on default values
HttpConnectorAddress addr =
    new HttpConnectorAddress(
        agentHost, agentPort, authInfo );

final RemoteMBeanServer connector =
    (RemoteMBeanServer) new HttpConnectorClient();

connector.connect( addr );

The connector is identified by the one AuthInfo object it uses to instantiate the connector address. If the agent has authentication enabled, both the login and the password must match one of the AuthInfo objects in the agent. If the agent does not perform authentication, providing a login and password has no effect because all connections are accepted.

If the authentication fails, the call to the connect method returns an exception. Normally, the client's code should catch this exception to handle this error case.

As demonstrated by the code examples, the authentication mechanism is very simple to configure. It prevents unauthorized access with very little overhead.

Note –

The HTML adaptor provides a similar authentication mechanism, where the list of accepted identities is given to the server object. In the case of the HTML protocol, the web browser is the management application that must provide a login and password. The behavior is browser-dependent, but the browser usually requests that the user type this login and password in a dialog box.

Running the Example With Authentication

The examplesDir/Context directory contains the applications that demonstrate the use of password authentication through the HTTP connector.

To Run the Example With Authentification
  1. Compile all files in this directory with the javac command.

    For example, on the Solaris platform with the Korn shell, type:

    $ cd examplesDir/Context/
    $ javac -classpath classpath *.java
  2. Start the agent in a terminal window and specify a list of login-password pairs, as in the following command:

    $ java -classpath classpath ContextAgent  jack jill  billy bob
  3. Wait for the agent to be completely initialized, then start the manager in another window with the following command:

    $ java -classpath classpath ContextClient -ident andy bob

    The client application tries to establish a connection with the login andy and the password bob. The authentication mechanism refuses the connection, and the com.sun.jdmk.comm.UnauthorizedSecurityException is raised by the connector server.

  4. Start the manager again, this time with a valid identity:

    $ java -classpath classpath ContextClient -ident jack jill

    The connection is established and the output from management operation is displayed in both windows.

  5. Leave both applications running for the next example.

Context Checking

Context checking is a more advanced security mechanism that can perform selective filtering of incoming requests. The context is an arbitrary object provided by the client and used by the server to decide whether or not to allow the request.

Filtering and context checking are performed in between the communicator server and the MBean server. The mechanism relies on two objects called the MBeanServerForwarder and the MBeanServerChecker.

Filter Mechanism

The MBeanServerForwarder allows for the principle of stackable MBean servers. An MBeanServerForwarder implements the MBeanServer interface and one extra method called setMBeanServer. Its function is to receive requests and forward them to the designated MBean server.

The setMBeanServer method of a communicator server object enables you to specify the MBean server that fulfills its requests. By chaining one or more MBeanServerForwarder objects between a communicator server and the actual MBean server, the agent application creates a stack of objects that can process the requests before they reach the MBean server.

The MBeanServerChecker is an extension of the forwarder that forces each request to call a –checker method. By extending the MBeanServerChecker class and providing an implementation of the checker methods, you can define a policy for filtering requests before they reach the MBean server. Table 13–1 shows the checker methods that apply to groups of MBeanServer methods.

Table 13–1 Filter Method Granularity for Context Checking

Filter Method 

MBean Server Operations Filtered 


Every method of the MBeanServer interface


All forms of the create and registerMBean methods


The unregisterMBean method


All forms of the instantiate method


The invoke method that handles all operation invocations


Both addNotificationListener and removeNotificationListener


Both queryMBeans and queryNames


All methods that access but do not change the state of the agent: getAttribute, getAttributes, getObjectInstance, isRegistered, getMBeanCount, getDefaultDomain, getMBeanInfo, and isInstanceOf


The setAttribute and setAttributes methods

As a request passes through a stack of MBean servers, the checker methods are called to determine if the request is allowed. In order to identify the manager that issued a request, the checker can access the operation context of the request.

The operation context, or just context, is an object defined by the manager which seeks access through a context checker. It usually contains some description of the manager's identity. The only restriction on the context object is that it must implement the OperationContext interface. The context object is passed from the connector client to the connector server and is then associated with the execution of a request. Conceptually, this object is stored in the user accessible context of the thread that executes the request.

All methods in the MBeanServerChecker class can access the context object by calling the protected getOperationContext method. The methods of the context checker then implement some policy to filter requests based on the context object, the nature of the request, and the data provided in the request, such as the attribute or operation name.

Figure 13–1 shows the paths of two requests through a stack of MBean server implementations, one of which is stopped by the context checker because it does not provide the correct context.

Figure 13–1 Context Checking in Stackable MBean Servers

Using a context checker in stackable MBean servers

Only connectors fully support the context mechanism. Their connector clients expose the methods that allow the manager to specify the context object. Existing protocol adaptors have no way to specify a context. Their requests can be filtered and checked, but their context object will always be null.

This functionality can still be used to implement a filtering policy, but without a context object, straightforward manager identification is not possible. However, a proprietary protocol adaptor could define some mapping to determine a context object that could be accepted by the filters.

Context Implementation

An agent wanting to implement context checking first needs to extend the MBeanServerChecker class. This class retrieves the context object and dtermines whether any given operation is allowed.

Example 13–3 Implementation of the Context Checker


import com.sun.jdmk.MBeanServerChecker;
import com.sun.jdmk.OperationContext;

public class ContextChecker extends MBeanServerChecker {

    // Constructor
    public ContextChecker(MBeanServer mbs) {

    // Implementation of the abstract methods of the
    // MBeanServerChecker class: for each of the specific
    // checks, we just print out a trace of being called.

    protected void checkWrite( String methodName,
                               ObjectName objectName) {
        System.out.println("checkWrite(\"" + methodName +
                           "\", " + objectName + ")");

    protected void checkQuery( String methodName,
                               ObjectName name,
                               QueryExp query) {
        System.out.println("checkQuery(\"" + methodName +
                           "\", " + name + ", " + query + ")");


     * This is where we implement the check that requires every
     * operation to be called with an OperationContext whose
     * toString() method returns the string "nice".
    protected void checkAny( String methodName,
                             ObjectName objectName ) {

        System.out.println("checkAny(\"" + methodName + "\", " +
        OperationContext context = getOperationContext();
        System.out.println("  OperationContext: " + context);

        if (context == null || !context.toString().equals("nice")) {
            RuntimeException ex =
                new SecurityException("  Bad context: " + context);
            throw ex;


The agent application then instantiates its context checkers and stacks them between the communicator servers and the MBean server. Each communicator server has its own stack, although filters and context checkers can be shared. The agent performs the stacking inside a synchronized block because other threads can try to do stacking simultaneously.

Example 13–4 Stacking MBean Server and Context Checkers

// Create MBeanServer
MBeanServer server = MBeanServerFactory.createMBeanServer();

/* Create context checker.  The argument to the constructor is
 * our MBean server to which all requests will be forwarded
ContextChecker contextChecker = new ContextChecker( server );

[...] // Create HTTP connector server

/* Add the context checker to this HTTP connector server.
 * We point it at the context checker which already points
 * to the actual MBean server.
 * It is good policy to check that we are not sidetracking
 * an existing stack of MBean servers before setting ours.
synchronized (http) {
    if (http.getMBeanServer() != server) {
        System.err.println("After registering connector MBean, " +
           "http.getMBeanServer() != " + "our MBeanServer");

Finally, the manager operation defines a context object class and then provides a context object instance through its connector client.

Example 13–5 Setting the Context in the Connector Client

/* In this example, the agent checks the OperationContext of
   each operation by examining its toString() method, so we
   define a simple implementation of OperationContext whose
   toString() is a constant string supplied in the constructor
class StringOperationContext
          implements OperationContext, Cloneable {

    private String s;

    StringOperationContext(String s) {
        this.s = s;

    public String toString() {
        return s;

    public Object clone() throws CloneNotSupportedException {
        return super.clone();


// the contextName must be provided on the command line
OperationContext context =
    new StringOperationContext(contextName);


// The context is set for all requests issued through
// the connector client; it can be changed at any time

Running the Example With Context Checking

The ContextClient and ContextAgent applications in the examplesDir/Context directory also demonstrate the use of stackable MBean servers and context checking through the HTTP connector.

If you have not done so already, compile all files in this directory with the javac command. For example, on the Solaris platform with the Korn shell, type:

$ cd examplesDir/Context/
$ javac -classpath classpath *.java
To Run the Example With Context Checking
  1. If the agent and client applications are not already running from the previous example, type the following commands in separate windows:

    $ java -classpath classpath ContextAgent

    $ java -classpath classpath ContextClient

    The classpath should include the current directory (.) for both applications because they rely on classes that were compiled in this directory.

  2. Press Enter in the client application to trigger another set of requests.

    The agent window displays the output of the ContextChecker class. We can see that the checkAny method verifies the “nice” context of every request and that the other checkers just print out their names, providing a trace of the request.

  3. Stop both applications by pressing Control-C in each of the windows.

  4. Restart both applications, but specify a different context string for the client:

    $ java -classpath classpath ContextAgent

    $ java -classpath classpath ContextClient -context BadToTheBone

    This time the context is not recognized. The agent raises a java.lang.SecurityException that is propagated to the client, which then exits.

  5. Press Control-C in the agent window to stop the ContextAgent application.

HTTPS Connector

The HTTPS connector provides data encryption and certificate-based security through a Secure Socket Layer (SSL). An implementation of secure sockets is only available for the Java 2 platform. However, secure sockets are not part of the Java 2 Software Development Kit (SDK), and their libraries must be installed separately.

The Java Secure Socket Extension (JSSE) provides a compatible implementation of secure sockets for the Java 2 platform.

The web site for the JSSE is This site provides links for downloading the software and the documentation. For further information and details regarding the use of the secure sockets, refer to the JSSE documentation.

The HTTPS connector exposes the same interfaces as all other connectors and has exactly the same behavior. The development of a management application that relies on the HTTPS connector is no different from that of any other Java dynamic manager. See Connector Clients for details about programming with the RemoteMBeanServer API.

Where the HTTPS connector differs is that it relies on the security mechanisms built into the Java language and extended by JSSE. In order to use these libraries and communicate securely, you must configure your application environment to meet all security requirements. The cost of security is establishing all of the structures that guarantee the trust between two communicating parties.

This section covers the steps that are required to establish a secure connection between your agent and manager applications. These instructions do not guarantee total security. They just explain the programmatic steps needed to ensure data security between two remote Java applications.

Before performing these steps, run each of your manager and agent applications on a separate host, and ensure that each host has its own installation of the Java SDK (not a shared network installation).

To Establish a Secure HTTPS Connection
  1. Install the software.

    You should install both the Java 2 SDK, Standard Edition, v1.2.2, and the JSSE 1.01 products on all hosts that will use the HTTPS connector client or connector server components.

    In this procedure, the directories where you have installed these products are named JAVAhome and JSSEhome, respectively. These names are used on all hosts where the products are installed, even though their values are specific to each host.

  2. Extend your Java runtime libraries.

    For each of your SDK/JSSE installations, copy the three JAR files (jsse.jar, jcert.jar, and jnet.jar) of the JSSE reference implementation into the extensions directory of your Java runtime environment.

    • For example, on the Solaris platform, type:

      $ cp JSSEhome/lib/jsse.jar JAVAhome/jre/lib/ext/
      $ cp JSSEhome/lib/jcert.jar JAVAhome/jre/lib/ext/
      $ cp JSSEhome/lib/jnet.jar JAVAhome/jre/lib/ext/
    • Or add these JAR files to your environment's classpath, as follows (for the Korn shell):

      $ export CLASSPATH=${CLASSPATH}:JSSEhome/lib/jsse.jar:\ 
  3. Designate your security provider

    The JSSE follows the same provider architecture found in the Java Cryptography Architecture (JCA) which is provided in the Java 2 platform as the Java Cryptography Extension (JCE). In order to use JSSE, you must install this provider either statically or dynamically. Again, you must do this for all of your SDK/JSSE installations.

    • To install the provider statically, edit the security properties file (JAVAhome/lib/security/ The boldface text is the part you must add:

      The first line of this file depends upon your SDK platform and should not be changed.

    • To install the provider dynamically in your Java application, call the addProvider method of the class as follows:

      Security.addProvider( new );

  4. Generate public and private keys.

    Repeat this step on all agent and manager hosts.

    Generate a key pair (a public key and associated private key).

    Wrap the public key into an X.509 v1 self-signed certificate, which is stored as a single-element certificate chain. This certificate chain and the private key are stored in a new keystore entry identified by alias.

    In the following command, the –dname parameters designates the X.500 Distinguished Name for the host where you are generating the certificates. The commonName field must be the host name.

    $ keytool -genkey -alias alias -keyalg RSA -keysize 1024 -sigalg MD5withRSA
              -dname "CN=commonName, OU=orgUnit, O=org, L=location, 
    S=state, C=country"
              -keypass passPhrase -storetype jks -keystore yourHome/.keystore
              -storepass passPhrase
  5. Export a local certificate

    Repeat this step on all agent and manager hosts.

    Read the certificate that is associated with your alias from the keystore and store it in a hostCertFile:

    $ keytool -export -alias alias -file hostCertFile -storetype jks
              -keystore yourHome/.keystore -storepass passPhrase -rfc

    When you are done with this step, you will have a certificate for each of your hosts.

  6. Import all remote certificates

    Repeat this step on both the agent and manager hosts for all pairs of agent-managers in your management architecture.

    In this step, agent and manager pairs must exchange their certificates. The manager imports the agent's hostCertFile and the agent imports the manager's hostCertFile. If a manager has two agents, it will import two certificates and each agent will import a copy of the manager's certificate.

    Import the certificate into the file containing the trusted Certificate Authorities (CA) certificates. This will add our self-signed certificate as a trusted CA certificate to the cacerts file so that the server and the client will be able to authenticate each other.

    $ keytool -import -alias alias -file hostCertFile -noprompt -trustcacerts
              -storetype jks -keystore JAVAhome/jre/lib/security/cacerts
              -storepass changeit

    This command modifies the JAVAhome/jre/lib/security/cacerts that will affect all applications running on that installation. If you do not want to modify this file, you can create a file named jssecacerts and use it instead. The default location of this file is either JAVAhome/lib/security/jssecacerts or if that does not exist, then JAVAhome/lib/security/cacerts.

  7. Run your Java dynamic management agent

    Start your agent applications with the following properties:

    $ java

    If you are using the notification push mechanism, add the following property definition to the above command line:
  8. Run your management application

    Start your management applications with the following properties:

    $ java