Java's creators have often highlighted the JVM's three biggest benefits: automatic garbage collection, built-in security, and the ability to load classes from any network location. Automatic garbage collection is now taken for granted in most modern programming environments. However, network-mobile classloading and the Java security infrastructure have evolved on separate, parallel paths. As a result, completely satisfying the security needs of distributed systems based on mobile Java code has thus far been impossible. Jini's security model extends both Java's mobile code and security features, and combines their strengths, enabling secure distributed computing with mobile code.
The Jini security infrastructure has been in the works for almost two years. Starting in 2001, Sun's Jini team took the unusual step of releasing periodic snapshots of the early specifications and implementation to elicit feedback from the Jini community. Since then, the security project has also been a Jini community effort under the project name "Davis." At the time of this writing, the specifications resulting from Davis are winding their way through the Jini Decision Process. By the time you read this, most likely, both Jini community houses have approved the specifications, and they have become official Jini standards.
While no special contract or license prescribes the use of the new security model, many in the Jini community believe that security-conscious Jini projects will use these new specifications and APIs. The new specifications form the key innovations in Sun's Jini Starter Kit (JSK) 2.0. Thus, I refer to the security framework in Sun Microsystems' JSK 2.0 as "the" Jini security framework, even though other security-related Jini projects may spring up in the future. In this article, I describe the Jini security model and give you a taste of the new security-related APIs.
An overview of Jini security
Perhaps the best news about the Jini security framework is that it requires few changes to existing Jini services. Instead, it defines security as a deployment-time option. If you have an existing Jini service, you can take advantage of utilities in the new JSK to deploy that service securely. Jini security, therefore, shares that similarity with J2EE (Java 2 Platform, Enterprise Edition) security—it primarily belongs to the domains of system architecture and administration.
The basic unit of a Jini service remains the service proxy you register in lookup services. When clients discover that proxy—based on the interfaces the proxy implements—code for the proxy object not already available at the client downloads into the client following RMI (Remote Method Invocation) mobile code semantics. Following that, the client invokes methods on the proxy. As a client, you don't care or know how the proxy implements those methods: some proxy methods may involve network communication with a remote service implementation, whereas others might execute locally to the proxy.
The Jini security model adds three steps to that simple programming model: First, as a client, it allows you to decide whether to trust a proxy object at all. If a service proxy is supposed to represent your bank account, how do you know the proxy you just downloaded is in fact your account's proxy? The Jini security framework helps you decide that.
Next, once you place some trust in a downloaded proxy, you must ensure the proxy offers some guarantees in performing its work. If you want to deposit money in your account by invoking a method on that proxy, you may want to perform that action only if the proxy can guarantee the integrity and confidentiality of the information traversing the network. The Jini security model lets you place such constraints on a proxy.
Finally, you want to grant privileges to a proxy based on the trust you placed in that proxy. If your bank account requires a network connection to a utility company's server to help pay your bills, you must grant that connection permission to the proxy. On the other hand, if that proxy wishes to reformat your hard disk, you want to deny it that permission. The Jini security model's final aspect is its ability to dynamically grant permissions to a downloaded service proxy.
Rather than making programming changes to your proxy, you inject security-related information to that proxy at the time you make your service available on the network. Making a service available for remote invocation is broadly referred to as exporting that service. The Jini security framework defines a new exporting mechanism that supports security-conscious exporting of a Jini service.
That exporting mechanism, in turn, relies on a set of configuration options you must specify for a securely deployed Jini service. The security-related APIs define what configuration you may specify and provide static methods that rely on that configuration information to perform security-related tasks. JSK 2.0 employs configuration files when specifying service-deployment information. In the future, Jini-aware application servers will likely provide graphical user interfaces to allow an administrator to set deployment-time service options. Figure 1 shows an overview of the Jini security model, and its exporting and configuration infrastructure.
To choose your services' configuration options, you must understand how those options relate to the Jini security model's various aspects.
Constrain your proxy
To appreciate the problems the Jini security infrastructure solves, consider the communication patterns of a system built on network-mobile code. Figure 2 below illustrates the movement of objects and data in the presence of a distributed Jini system. First, the client downloads into its address space a proxy object for the service, including that proxy's code. It then invokes methods on that proxy, passing in method parameters, some of which may refer to other remote objects. While a Jini proxy may perform all of its computations locally to the client's address space, this example illustrates a remote service implementation. Code for any third-party objects that interact with the proxy also downloads into the client's address space.
As a Jini service operator, when a client downloads your service's proxy, you may want to restrict how a client—or what client—can invoke your proxy's methods. You may require, for instance, that a client authenticate itself before it invokes a method, and you may refuse access to clients you either don't recognize or don't want to interact with.
Similarly, a client may also place constraints on your service's proxy: it may require server authentication before invoking the service, or it may insist the proxy guarantee the integrity of data it sends back to a remote service implementation.
Observe that all interaction between client and server occurs through proxy objects. Therefore, to impose security restrictions on either the client or the server, those restrictions must be placed on their respective proxies.
In the Jini security model, to constrain a proxy, that proxy must implement the
net.jini.core.constraint.RemoteMethodControl interface in addition to the other application-specific interfaces it implements. In a typical deployment scenario, you use a smart proxy that implements that interface and embed your original service proxy into that constraint-aware wrapper. That allows you to leave the original proxy intact. This article's conclusion illustrates such a utility.
RemoteMethodControl defines a method to set an entire collection of constraints on a proxy:
public RemoteMethodControl setConstraints(MethodConstraints constraints);
When invoked, that method returns a copy of the proxy, with the set of constraints injected into it. Both the server and client can inject constraints on a service proxy: the server, before exporting the proxy, and the client, after retrieving the proxy from the network.
As its name suggests,
RemoteMethodControl constrains a proxy on the granularity of method calls.
MethodConstraints is but an immutable map of methods and a set of constraints on those methods. That map contains
java.lang.reflect.Methods as keys, each with an instance of
InvocationConstraints as value.
InvocationConstraints, in turn, is a collection of required and preferred constraints. When making decisions based on a proxy's constraints, all required constraints must be satisfied. In addition, all preferred constraints should be satisfied, if possible. If all required constraints cannot be satisfied, the method to which the constraint applies will not be invoked, and a
java.rmi.ConnectIOException will be thrown instead.
The constraints themselves are expressed as implementations of the
net.jini.core.constraint.InvocationConstraint marker interface. You can create any constraint you'd like. Note, however, that the client and server must both understand a proxy's constraints. If either encounters an unknown constraint type, it won't perform the method call since it can't possibly satisfy that unknown constraint.
Some constraints express requirements, whereas others might convey restrictions. For instance, a constraint might say, "I require you to authenticate," whereas another might say, "I'd like to stay anonymous and do not want to be authenticated." Conflicting constraints indicate a discord between the client's and server's requirements; the method call then won't proceed.
The JSK 2.0 defines four trust-related constraints:
Integrity:Ensures the integrity of network objects. That includes both data and classfile integrity (more on the latter below).
ServerAuthentication:Proves the subject on whose behalf the server executes. If the client does not trust that subject, the call terminates.
ClientAuthentication:Proves the subject on whose behalf the client executes. The server does not perform method invocation for an untrusted client.
Delegation:If a server needs to invoke third parties in the client's name, delegation allows the server to authenticate as the client.
If your Jini service wanted each client to authenticate, you would inject
ClientAuthentication.YES into your server's proxy when exporting that proxy. Likewise, if the client wanted to ensure your proxy guards a service's data and code integrity, it would enter
Integrity.YES into the service proxy once it downloaded that proxy from the network.
Another set of
InvocationConstraints define the principals a client or server must authenticate as:
ClientMinPrincipal:The server uses this constraint to tell a client it must authenticate at least as the specified set of principals. If your Jini service specifies the
ClientAuthenticationconstraint on the client, then it can specify this additional constraint to require, for example, that the client authenticate as
ClientMaxPrincipal:A client uses this constraint to limit how much information it reveals about itself to the server. While a client may authenticate as several principals, that client may want to limit which of those principals it reveals to the server.
ClientMinPrincipalType:When authenticating a client, the server can require that the client authenticate at least as one of the types of principals in the specified set. For instance, suppose you want to only expose your Jini service to clients that belong to your own company. Thus, you may require that one of the client's authenticated principals be a
com.mycompany.GeneralPrincipaltype. Each organization's department might create subtypes of that principal class, and all those subtypes would be acceptable to your Jini service.
ClientMaxPrincipalType:A client uses this constraint to limit how much information it reveals about itself to the server. For instance, if your client authenticates as both
com.goodcompany.CompanyPrincipal, the client may decide to reveal only its former identity to the service.
ServerMinPrincipal:If the client stipulates the
ServerAuthenticationconstraint, this additional constraint requires that the server authenticate at least as the specified set of principals.
The invocation constraint mechanism is not limited to specifying security-related requirements: the client or server may specify any kind of constraint on a remote invocation. The
net.jini.core.constraint package contains a few additional constraints related to quality of service:
ConnectionRelativeTime:Controls the maximum amount of time to wait for a proxy to establish a network connection to the remote service. For instance, your client might specify to your service's proxy that it wants to wait no longer than five seconds to establish a connection back to a remote implementation. If your service is too busy serving other clients, the time-conscious client might move on to another instance of the equivalent Jini service.
ConnectionAbsoluteTime:Controls the absolute point in time by which a network connection must be established.