Writing Security-Aware Java Clients


Writing Security-Aware Java Clients

In this section, we will show you how to write security-aware Java clients that interact with WebLogic Server. The focus will be primarily around authentication and the use of SSL. First, we talk about how your Java application client can use JAAS to authenticate to WebLogic Server. Then, we show you how to set up an SSL connection between your application client and WebLogic Server. In Chapter 15, we show you how to write security-aware Web Services clients that use both SSL and Web Services Security to encrypt the message itself.

Writing Java Clients That Use JAAS

Before we jump into the details of how to use JAAS with your WebLogic Server Java client application, let s briefly look at the theory behind it. JAAS provides a standard way to authenticate specific users and authorize those users for specific sets of code and resources.

JAAS authentication is designed to be a pluggable framework that removes authentication methods and decisions from business logic entirely. This framework allows for a new method of authentication to be added to an application to either replace or augment the current authentication modules without requiring the application code to change. We will spend the bulk of this section looking at how JAAS authentication works.

JAAS authorization is built using the existing Java 2 Security model, which uses a security policy to restrict the rights of executing code. JAAS extends this model by allowing the policy to be defined for specific users and groups. Typically and by default, this policy is defined in a text file that uses a special syntax; this file is java.policy . Through this mechanism, JAAS allows the Java run time to restrict access based on where the code came from, who the code might be digitally signed by, and what authenticated principal the code is running on behalf of. The granularity of restriction is still limited to the same low-level, system resources of the Java run time. For example, you could restrict access to reading and/or writing specific Java system properties, files, or network ports. JAAS authorization does not address the problems associated with protecting a server s application-level resources such as an EJB method or JMS queue. Such access control restrictions are left to the application server itself and do not use JAAS.

You can turn on JAAS authorization by adding the following two Java command-line options to set the required Java system properties:

 Djava.security.manager Djava.security.policy=weblogic.policy 

The RecordingSecurityManager is a useful utility when dealing with some of the policy requirements your application will need, plus dealing with the policy file syntax. This utility runs during development time as a replacement for the Java run time s default Security Manager and records the permissions your application will require. It can be found in the Alpha Code section for WebLogic Server on the BEA developer s Web site at http://dev2dev.bea.com/ .

JAAS authorization might be useful to restrict the Java run-time permissions for untrusted code running on your server. Most production environments, though, typically are not running untrusted code on the server, so using JAAS authorization to control access on your application server is probably not worth pursuing. A typical J2EE environment has at least minimal audits of the business applications and code running on the server. The only real-world situation where the extra performance hit of using JAAS authorization might be useful would be for application service providers (ASPs). JAAS authorization makes sense only if you host applications that you don t directly control or have the ability to audit. In most cases, the benefits do not justify the run-time performance overhead.

Best Practice  

JAAS authorization only addresses authorization only for low-level system resources or capabilities like reading or writing to a file or creating a new class loader. All application-level resources are left as an exercise for the application server and/or application security vendors . Most production server environments are not typically running untrusted code, and the performance overhead of JAAS authorization can be substantial. Therefore, we do not recommend JAAS authorization for most application authorization needs.

JAAS authentication occurs in a few basic steps. A JAAS client application begins the authentication process by instantiating a LoginContext object with the client type and a new custom CallbackHandler , as shown in the following code fragment:

 CallbackHandler callback =      new MasteringWebLogicCallbackHandler(username, password, url)); LoginContext loginContext =      new LoginContext(MasteringWebLogic, callback); 

In this case, the client type is MasteringWebLogic and the custom CallbackHandler is the MasteringWeblogicCallbackHandler .

When this client code executes, the LoginContext looks up its configuration to determine the required authentication types, or LoginModule s, to be used in performing the authentication. While JAAS supports a pluggable configuration model, the Java run time ships only with the file-based implementation, so the configuration typically comes from a file. The LoginContext matches the client type with the LoginModule and its associated flags. In our example, the configuration information is stored in the masteringweblogic.config file, whose contents are as follows :

 MasteringWebLogic {    weblogic.security.auth.login.UsernamePasswordLoginModule              required debug=false; }; 

When the LoginContext reads this file, it finds the entry for its client type, MasteringWebLogic , and determines that the correct LoginModule to use is weblogic.security.auth.login.UsernamePasswordLoginModule , whose control flag is set to Required and debug flag is set to false . This control flag value has the exact same semantics as we previously discussed when covering LoginModule settings in the Authentication section.

So, the next question that should come to mind is how does the LoginContext know where to find its configuration? Java provides two mechanisms for telling the LoginContext where to find its configuration information. One way to specify the location of the JAAS login configuration is through the Java system property java.security.auth.login.config , which can be set to point to the config-_uration file the Java program should use. If this system property is not set, the Java run time will search through the list of entries like the one shown next in the $JAVA_HOME/jre/lib/security/java.security file looking for a configuration file that contains an entry that matches our client type:

 login.config.url.1=file:/c:/book/ch10_examples/masteringweblogic.policy 

The next step in the application is to call the LoginContext s login() method, which starts the whole authentication process. The LoginModule s are called with the original CallbackHandler . A basic LoginModule might simply use the CallbackHandler to prompt for a username and password on the command line. More complex ones might require an X.509 certificate, a Kerberos token, or even some biometric information. Each new authentication method requires a new LoginModule , and possibly a new CallbackHandler to deal with any new Callback types the LoginModule might need. Each LoginModule uses the CallbackHandler to decide whether to authenticate this subject.

In our example, we use a MasteringWebLogicCallbackHandler to hold the username and password. We chose this approach so that you could see the inner workings of the CallbackHandler . In this case, it would be just as easy to use the WebLogic Server s built-in weblogic.security.SimpleCallbackHandler or weblogic.security.URLCallbackHandler . The SimpleCallbackHandler supports only prompting for the username and password; the URLCallbackHandler supports prompting for the username, password, and server URL.

Upon success, LoginModule s associate various Principal s with the newly created Subject . If the LoginContext is successful, the application can then retrieve the Subject from it. This new Subject has a list of Principal objects that represent the identity of the currently authenticated user . To invoke business logic, you must create a class that implements PriviledgedAction and contains the business logic, and you must pass it to the WebLogic Security subsystem along with the newly created Subject :

 loginContext.login(); Subject subject = loginContext.getSubject(); JAASExampleAction clientAction = new JAASExampleAction(url); Security.runAs(subject, clientAction); 

You should notice right away that the business logic is now encapsulated inside a PriviledgedAction object, which is just a wrapper of a Runnable . We pass the PriviledgedAction object to the WebLogic Security subsystem, along with the authenticated Subject , to be run. This is different from standard JAAS, which uses the Subject itself to run the PriviledgedAction . Although covering the rationale for this is beyond the scope of this book, suffice it to say that there are problems with losing the user identity when combining the JAAS-specified Subject.doAs() with AccessController.doPriviledged() . Because J2EE requires the use of AccessController.doPriviledged() in certain conditions, WebLogic Server provides a Security.runAs() method that is similar in spirit to Subject.doAs() and works with all of your JAAS code, but does not suffer from the user identity problem.

Another important point is that the LoginModule being used by the client is not the same as the one being used by the WebLogic Server Security Service on the server. The client-side LoginModule calls to the server to try to authenticate the client. As a result of this request, the server will call into the Security Framework, which will end up invoking the server-side LoginModule (s) to make the actual authentication decision. Your application client should either use the WebLogic Server-provided LoginModule , called the UsernamePasswordLoginModule , or provide its own custom LoginModule . If you use a custom LoginModule , always call weblogic.security.auth.Authenticate.authenticate() from within its login method, as shown here:

 Subject subject = new Subject(); weblogic.jndi.Environment env = new weblogic.jndi.Environment(); env.setProviderUrl(url); env.setSecurityPrincipal(username); env.setSecurityCredentials(password); weblogic.security.auth.Authenticate.authenticate(env, subject); 

The basic idea here is that the client application running the custom LoginModule must contact the server to allow the server to do the authentication. As a part of this server-side authentication process, the server will populate and digitally sign the Subject before returning. You can place any Serializable object in the Environment to act as the credential instead of a password as long as the server s configured authentication provider knows how to use it to authenticate the user. This authenticate() method is only for authentication to a remote server. To authenticate to the server from code running inside that server, you need to use the weblogic.security .service.Authenticate.login() method.

Best Practice  

Use only the weblogic.security.auth.Authenticate.authenticate() method to authenticate to a remote server process. Always use the weblogic.security.services.Authenticate.login() method for code running inside the server to which you want to authenticate.

Writing Java Clients That Use SSL

When writing a Java client that uses SSL, there are three main types of application clients to consider: RMI clients, programmatic HTTP clients, and Web Services clients. We discuss RMI clients and HTTP clients in this section, but we defer the discussion of Web Services clients to Chapter 15. We end this section with a discussion of application authentication, hostname verification, and trust managers.

RMI Clients

The first type of client to discuss is an RMI client using SSL. For one-way SSL, it is as simple as specifying an SSL protocol and port in the PROVIDER_URL that you use to create the JNDI InitialContext object:

 Hashtable ht = new Hashtable(); ht.put(Context.INITIAL_CONTEXT_FACTORY,        "weblogic.jndi.WLInitialContextFactory"); ht.put(Context.PROVIDER_URL, "  t3s  ://localhost:  7002  "); InitialContext ctx = new InitialContext(ht); 

Making a two-way SSL connection from a stand-alone client is a little bit more work. When running inside the server, there is no need to supply the credentials because the server will automatically provide them for any outgoing SSL connection in which the remote SSL server requests the credentials. In the stand-alone client case, you simply need to get the certificate chain and private key from the key store and pass it into the InitialContext constructor:

 weblogic.jndi.Environment env = new weblogic.jndi.Environment(); env.setProviderUrl(url); env.loadLocalIdentity(certificateChain, privateKey); Initial Context ctx = env.getInitialContext(); 

Another example available for downloading shows how to accomplish the same thing using the new thin RMI client that also supports using a key store but does it by using JSSE under the covers. This example will work only if the client is using the new thin client jar file, wlclient.jar , and not running inside a WebLogic Server. As mentioned previously, you don t need to worry about outgoing SSL connections from the WebLogic Server as long as you have the server configured with the proper information to find its certificate and private key.

JSSE uses the KeyManagerFactory to handle all interaction with the KeyStore . It queries the KeyStore to determine which certificate and private key to use when asked to provide credentials. One important thing to point out is that KeyStoreManager uses the same password for accessing every key in the key store. This means that you should set your key store password and all of the private key passphrases to be the same. Given this, you should always try to use one identity key store per client certificate to protect the integrity of the private key.

Warning  

When using JSSE s KeyManagerFactory , you must use the same password for the key store password and the passphrase for all private keys in the key store. We strongly recommend that you do not store more than one user s certificate in each identity key store to prevent compromising the integrity of the private keys.

When using JSSE, you can either configure a TrustManagerFactory to use your trust key store or tell the default TrustManagerFactory which trust key store to use by setting the Java system property javax.net.ssl.trustStore . To have the TrustManagerFactory verify the integrity of the data it retrieves from the key store, specify the key store password using the javax.net.ssl.trustStorePassword Java system property. You simply need to package up the KeyManagerFactory and the TrustManager in an SSLContext object and supply it to the first InitialContext creation:

 import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; ... KeyManagerFactory kmf =     KeyManagerFactory.getInstance(SunX509, SunJSSE); kmf.init(identityKeyStore, keyStorePassword); TrustManagerFactory tmf =     TrustManagerFactory.getInstance(SunX509, SunJSSE); tmf.init(trustKeyStore); SSLContext sslCtx = SSLContext.getInstance(SSL); sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); Hashtable props = new Hashtable(); props.put(Context.INITIAL_CONTEXT_FACTORY,           weblogic.jndi.WLInitialContextFactory); props.put(Context.PROVIDER_URL, iiops://127.0.0.1:7002); props.put(Context.SECURITY_CREDENTIALS, sslCtx); InitialContext ctx = new InitialContext(props); 

Programmatic HTTP Clients

The next type of client is a Java application client making HTTP requests over SSL using URLConnection objects. You might wonder how common it is to need a Java client that makes HTTP requests. The answer is that while it may not be very common for a true client application, it is a relatively common thing to do when writing server-side code that may need to call out to some other site to get information.

When writing these types of clients, there are two main ways of establishing the SSL connection: WebLogic SSL APIs or JSSE APIs. First, let s look at using the WebLogic SSL APIs. For one-way SSL (or two-way SSL from inside the server) you simply create your HttpsURLConnection object:

 URL url = new URL(https, hostname, sslPortNumber, page); weblogic.net.http.HttpsURLConnection sslConn =     new weblogic.net.http.HttpsURLConnection(url); sslConn.connect(); 

If you want to make a two-way SSL connection from outside the server, you need to do a little bit more work. You need to get the X.509 certificate chain and the private key and use these to store the client s identity in WebLogic Server s version of the SSLContext . Then, you get the WebLogic Server SSLSocketFactory from the SSLContext and use it to call setSSLSocketFactory() on the weblogic.het.http.HttpsURLConnection object before you call connect() . The downloadable examples contain a complete working example, the highlights of which are shown here.

 import weblogic.net.http.HttpsURLConnection; import weblogic.security.SSL.SSLContext; import weblogic.security.SSL.SSLSocketFactory; ... SSLContext ctx = SSLContext.getInstance(https); ctx.loadLocalIdentity(certChain, privateKey); SSLSocketFactory sslSocketFactory = ctx.getSocketFactoryJSSE(); URL url = new URL(https, hostName, sslPortNumber, page); HttpsURLConnection sslConn = new HttpsURLConnection(url); sslConn.setSSLSocketFactory(sslSocketFactory); sslConn.connect(); 

For JSSE clients making a one-way SSL connection, it is simply a matter of changing the type of HttpsURLConnection object:

 URL jsseUrl = new URL(https, hostname, sslPortNumber, page); javax.net.ssl.HttpsURLConnection sslConn =     (javax.net.ssl.HttpsURLConnection)url.openConnection(); sslConn.connect(); 

For a two-way SSL connection, it is almost the same as we saw with the RMI thin client except that you use the JSSE SSLContext object to get a JSSE SSLSocketFactory and use it to call setSSLSocketFactory() on the javax.net.ssl.HttpsURLConnection object before you call connect() , as highlighted here.

See the downloadable examples on the companion Web site for a complete working example:

 import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; ... KeyManagerFactory kmf =     KeyManagerFactory.getInstance("SunX509", "SunJSSE"); kmf.init(identityKeyStore, args[1].toCharArray()); TrustManagerFactory tmf =     TrustManagerFactory.getInstance("SunX509", "SunJSSE"); tmf.init(trustKeyStore); SSLContext ctx = SSLContext.getInstance("SSL"); ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);  SSLSocketFactory sslSocketFactory = ctx.getSocketFactory();  URL url = new URL("https", serverName, sslPortNumber, page); HttpsURLConnection sslConn =     (HttpsURLConnection)url.openConnection();  sslConn.setSSLSocketFactory(sslSocketFactory);  sslConn.connect(); 

Web Services Clients

The last type of SSL client we need to discuss is a Web Services client. Because you may be new to Web Services, we have decided to delay this discussion to Chapter 15, where we will cover both connection- and data-level security. Before we leave the topic of SSL, we need to discuss a few miscellaneous options related to SSL: application authentication, hostname verification, and trust managers.

Application Authentication

You can use two-way SSL to authenticate a client to your WebLogic Server application. To do this, you simply need to map the client certificate presented to the server to a WebLogic Server username. You can do this using the identity assertion capabilities of the WebLogic Server Security Framework, as discussed earlier in the Identity Assertion section. All of the two-way SSL examples we just discussed use this capability to allow the server-side application to determine the username. Once the WebLogic Server identity is established, you can use any standard J2EE or WebLogic Server authorization capabilities to restrict access to protected resources.

Hostname Verification

Both WebLogic Server and JSSE try to verify that the certificate a remote process presents matches its hostname. They do this by invoking a default set of rules to try to match the common name (CN) field of the X.509 certificate s distinguished name (DN) to the host from which it came. If they are unable to do that, they will invoke the registered hostname verifier class to verify that the certificate is, in fact, from the host. In most situations, the hostname verification process will not need the hostname verifier. By default, both WebLogic Server and JSSE are configured to fail all verification calls that need to call the hostname verifier. This means that if your certificates are failing the default verification rules, you will need to get new certificates or provide your own implementation of the weblogic.security.ssl.HostnameVerifierJSSE or javax.net.ssl.HostnameVerifier interface, depending on which SSL implementation you are using.

There are three ways to register your custom hostname verifier class with WebLogic SSL. You can call the setHostnameVerifierJSSE() method on either the WebLogic HttpsURLConnection or SSLContext objects. If your code is running in the server, you can use the Advanced Options section of the server s Keystores & SSL tab in the WebLogic Console. Finally, you can use the Java system property weblogic.security.SSL.HostnameVerifierJSSE to point to the fully qualified class name of your hostname verifier class.

Trust Managers

Our last topic is the TrustManager interface. A TrustManager is a secondary means of verifying the certification chain supplied by the other party participating in the SSL handshake. It is a simple interface that is called only if the default certification chain verification failed. A common example of this occurs when an SSL client connects to an SSL server that has a single expired certificate somewhere in its certificate signing chain and the expired certificate has been replaced with a new certificate, next to it in the chain. Using the new certificate in the server certificate chain and simply ignoring the expired certificate will verify correctly. The problem is that standard SSL certificate verification will not accept a certificate chain like this. It is considered untrusted, and the SSL handshake will fail.

If the SSL client has been set up with a TrustManager , the client s TrustManager will be called with the unverified server certificate chain. The TrustManager can then decide whether to allow the certificate chain verification and thus the SSL handshake to continue. Use of a TrustManager is not required if the default SSL certificate chain verification is all that is needed. Both WebLogic SSL and JSSE support registering a custom TrustManager . With WebLogic SSL, you need to provide an implementation of the weblogic.security.SSL.TrustManagerJSSE interface and register it with the weblogic.security.SSL.SSLContext object. For JSSE, you need to create and register your own javax.net.ssl.TrustManagerFactory or implement the javax.net.ssl.X509TrustManager interface directly. Both of these are then used in conjunction with the javax.net.ssl.SSLContext object s init() method.




Mastering BEA WebLogic Server. Best Practices for Building and Deploying J2EE Applications
Mastering BEA WebLogic Server: Best Practices for Building and Deploying J2EE Applications
ISBN: 047128128X
EAN: 2147483647
Year: 2003
Pages: 125

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net