There are three aspects to securing WebLogic web services:
Access control security
You can secure the entire web service by restricting access to the URLs that invoke the web service (or its WSDL). This approach automatically secures any backend components used to implement the web service. Alternatively, you can secure the individual components that make up the web service: the web application that hosts the web-services.xml descriptor file, the stateless session EJBs, a subset of the methods of the EJB, and so on. You also can prevent access to the home page and WSDL, which is by default publicly accessible.
Connection level security
You can modify the web-services.xml descriptor file to indicate that clients can invoke the web services only over HTTPS. Moreover, if the client authenticates itself using SSL, you need to configure SSL security for WebLogic as well.
Message security
WebLogic 8.1 lets you use a mixture of digital signing, data encryption, and security token propagation to provide you with message integrity and confidentiality.
Like other J2EE components, WebLogic allows you to assign a security policy to a web service component. These policies allow WebLogic to enforce authorization checks on clients who invoke the web service. Because a web service relies on multiple backend components for its implementation, you can independently secure the web service backends as well. Configuring SSL security for a web service is equally easy most of the work lies in building clients that can invoke web services over SSL. WebLogic 8.1 provides SOAP message data encryption and signing, based on OASIS' draft Web Services Security Core Specification. As this is not yet an OASIS standard, WebLogic's implementation is subject to change.
19.8.1 Access Control
WebLogic's web services are packaged into standard J2EE enterprise applications. This means that you can secure a web service with access control settings on the various J2EE components that constitute the web service. WebLogic lets you control access to a web service in the following ways:
Let's take a closer look at these various mechanisms for securing access to a web service.
19.8.1.1 Assigning a security policy to the web service
You can use the Administration Console to assign a security policy to a deployed web service component i.e., a web application that hosts one or more web services. This policy determines the set of users, groups, and roles that are authorized to access the web service. Of course, this also means that the client needs to authenticate itself when interacting with the web service. Only then can WebLogic enforce these authorization checks on the web service.
In order to view or modify the security policy assigned to a web service, you need to right-click the web service component in the left pane of the Administration Console and select the Define Security Policy option from the pop-up menu. If you select "Define policies and roles for individual services" instead, you will be able to set a role or policy for each individual operation within a selected service. Chapter 17 provides more information on how to apply security policies to WebLogic resources.
19.8.1.2 Securing the web service URL
Clients need a URL to access the web service. For instance, our Simple web service is available via the URL http://10.0.10.10:8001/myWar/Simple. Similarly, the WSDL for the web service is available via http://10.0.10.10:8001/myWar/Simple?WSDL. This means that you could secure access to the entire web service and its operations by simply restricting access to the web service URL. To set up this access control, you need to configure a security constraint over the web service URL by modifying the deployment descriptors of the web application that hosts the web-services.xml descriptor file.
Chapter 2 provides more details on how to set up security constraints on a web resource collection. Because you need to enforce access control over the web service URL itself, you must restrict all GET and POST requests to URLs that match the /Simple/* pattern:
Simple Web Service Simple/* POST GET
This ensures that any client that attempts to invoke the protected web service or access the WSDL that describes the web service must authenticate itself. Later in this chapter, we examine how the client can authenticate itself when invoking an operation on a web service protected in this way.
19.8.1.3 Securing a stateless session EJB and its methods
If a stateless session EJB serves as the backend component for a web service, you can use the EJB's deployment descriptors to restrict access to the EJB methods. Chapter 17 explains how you can use the assembly-descriptor element in the ejb-jar.xml descriptor file to associate security roles with individual EJB methods, and the security-role-assignment element in the weblogic-ejb-jar.xml descriptor file to list WebLogic users and groups that belong to the role. You can use this to restrict access to individual operations of the web service by applying security constraints on the EJB methods that implement the operations. Other clients can continue to access the web application, the WSDL, and the home page for the web service.
Any unauthorized client that attempts to invoke an operation implemented by a method on a protected stateless session EJB will be denied access:
java.rmi.RemoteException: SOAP Fault:javax.xml.rpc.soap.SOAPFaultException: Security Violation: User: '' has insufficient permission to access EJB: type=, application=_appsdir_myEarEJB_dir, module=webserviceEJB.jar, ejb=Case, method=makeUpper, methodInterface=Remote, signature={java.lang.String}.;
19.8.1.4 Removing access to the home page and WSDL
You can prevent the home page and WSDL of a web service from being exposed by editing the web-services.xml descriptor file. Simply add an exposeWSDL or exposeHomePage attribute, as shown in the following example:
exposeWSDL="False" exposeHomePage="False">
19.8.1.5 Authenticating client access to a protected web service
Once you've restricted the access to a web service, the client can no longer anonymously invoke a web service operation over plain HTTP. The client now needs to authenticate itself as well. For instance, if you secure the URL for the Simple web service and then point your browser to the home page for the web service, you will be greeted with an HTTP 401 (Unauthorized) response. Instead, you need to specify a modified web service URL that includes the username and password of an authorized WebLogic user:
http://username:password@10.0.10.10:8001/myService/Simple
Similarly, if you've configured access restrictions over the URL for obtaining the WSDL for the web service, again you need to specify the login credentials of a WebLogic user authorized to view the WSDL:
http://joebloggs:pssst123@10.0.10.10:8001/myService/Simple?WSDL
For a static client that needs to authenticate itself when invoking an operation on a secure web service, the only change that's required is how the client creates an instance of the SimplePort stub implementation:
Simple ws = new Simple_Impl("http://10.0.10.10:8001/myService/Simple?WSDL"); SimplePort port = ws.getSimplePort(username, password); String returnVal = port.makeUpper("Hello There");
Here, we've supplied the username and password of an authorized WebLogic user to the web service-specific implementation of the getSimplePort( ) method. Remember, if you've restricted access to the web service URL, you must also modify the URL for the WSDL to include the user's login credentials. Once the client has authenticated successfully, WebLogic is able to enforce any authorization checks placed on the web service, the URLs, or even the backend stateless session EJBs.
In fact, the standard JAX-RPC approach for a client that invokes a secure web service and needs to authenticate itself is to specify values for two authentication properties:
The client JAR generated by WebLogic for a particular web service already contains the stub classes that automatically set these login credentials when the Java client invokes the getServicePort( ) method. The following example shows how a JAX-RPC client would submit its credentials before invoking a web service:
SimpleStub stub = // ... get the stub; stub._setProperty("javax.xml.rpc.security.auth.username", "juliet"); stub._setProperty("javax.xml.rpc.security.auth.password", "mypassword"); String returnVal = stub.makeUpper("lower case string!");
19.8.2 Using SSL
WebLogic lets you configure a web service so that it's accessible only through the HTTPS protocol, in which case plain HTTP clients will not be able to access the service. This connection-level security provides you with point-to-point security, which is securing communication between two endpoints. If your SOAP messages are going to pass through unsecured intermediaries, such as caches, you may want to also use the more advanced end-to-end security measures, such as SOAP message encryption and digital signing.
To force the use of HTTPS, modify the web-services.xml descriptor file by specifying a protocol attribute for the web-service element:
protocol="https" name="Simple" uri="/Simple" targetNamespace="http://www.oreilly.com/" style="rpc">
WebLogic's servicegen task also can let you adjust the protocol constraint for a web service, by simply adding a protocol attribute as follows:
protocol="https" serviceURI="/Simple" expandMethods="True">
When you configure a web service in this way, clients must create HTTPS connections when invoking a web service operation. Without the HTTPS protocol constraint, clients are free to create either HTTP or HTTPS connections when invoking the web service. Of course, HTTPS connections may be used only if you've properly configured SSL at the server's end. For a web service that accepts only HTTPS connections, a client must use SSL to access the web service operations.
19.8.2.1 Client access using WebLogic's SSL
WebLogic provides a client runtime JAR, webserviceclient+ssl.jar, which includes the standard JAX-RPC runtime classes and the SSL implementation classes. Thus, in order to configure a client application to use WebLogic's SSL implementation, you need to make a note of the following issues:
The following example summarizes these points and shows how you would run a client that needs to interact with HTTPS-protected web services:
java -classpath %WL_HOME%serverlibwebserviceclient+ssl.jar;%CLASSPATH% -Dbea.home=c:ea_home -Djava.protocol.handler.pkgs=com.certicom.net.ssl -Dweblogic.webservice.client.ssl.strictcertchecking=false oreilly.wlguide.webservices.secure.client.MyApp
19.8.2.2 Using a proxy server
If the client sits behind a firewall and must use a proxy server to invoke the web service, it can specify the host and port of the proxy server using the following two system properties:
java -Dweblogic.webservice.transport.https.proxy.host=10.0.0.1 -Dweblogic.webservice.transport.https.proxy.port=4567 ...
By specifying these two system properties, the client can make HTTPS connections to the web service via the configured proxy server.
19.8.2.3 Configuring SSL programmatically
While you can configure a client to use WebLogic's SSL implementation through the command-line options, you also can achieve the same results programmatically by using the weblogic.webservice.client.WLSSLAdapter class. The following code sample shows how to modify the client-side code so that it can use WebLogic's SSL implementation when invoking an SSL-protected web service:
System.setProperty("java.protocol.handler.pkgs", "com.certicom.net.ssl"); SSLAdapterFactory adapterFactory = SSLAdapterFactory.getDefaultFactory( ); WLSSLAdapter adapter = (WLSSLAdapter) adapterFactory.getSSLAdapter( ); adapter.setStrictChecking(false); //optional adapter.setTrustedCertificatesFile("trusted-ca.pem"); adapterFactory.setDefaultAdapter(adapter); adapterFactory.setUseDefaultAdapter(true); Simple ws = new Simple_Impl(argv[0]); SimplePort port = ws.getSimplePort("system", "12341234"); String returnVal = port.makeUpper("Hello There"); // ...
If the client uses the generic JAX-RPC interfaces, it also can choose WebLogic's SSL adapter for a particular web service invocation:
ServiceFactory factory = ServiceFactory.newInstance( ); Service service = factory.createService(serviceName); Call call = service.createCall( ); call.setProperty("weblogic.webservice.client.ssladapter", adapter); String result = (String) call.invoke( new Object[]{ "SOMEPARAM" } );
If the client statically invokes a web service using the Stub interface, it also needs to set the following property:
((javax.xml.rpc.Stub)stubClass)._setProperty( "weblogic.webservice.client.ssladapter", adapterInstance);
19.8.2.4 Using two-way SSL
If the WebLogic server hosting the web service is configured for two-way SSL, you will need to modify your client to load its identity, much like that described in Chapter 16. In this case, we need to modify our client code like this:
SSLAdapterFactory adapterFactory = SSLAdapterFactory.getDefaultFactory( ); WLSSLAdapter adapter = (WLSSLAdapter) adapterFactory.getSSLAdapter( ); adapter.setStrictChecking(false); adapter.setTrustedCertificatesFile("x:/server/lib/cacerts"); FileInputStream fs = new FileInputStream(CERT_CERTCHAINFILE); adapter.loadLocalIdentity(fs, CERT_KEYPASSWORD.toCharArray( )); adapterFactory.setDefaultAdapter(adapter); adapterFactory.setUseDefaultAdapter(true);
The loadLocalIdentity( ) method expects a FileInputStream that references an encoded certificate chain. You can create such a certificate chain by simply appending the mycertfile.pem and mykeyfile.pem (in that order) generated in Chapter 16.
19.8.2.5 Rolling your own SSL implementation
In the previous examples, we saw how the client uses an instance of WebLogic's SSLAdapterFactory to manufacture an object that implements the SSLAdapter interface in this case, a WLSSLAdapter class provided by WebLogic:
import weblogic.webservice.client.WLSSLAdapter; import weblogic.webservice.client.SSLAdapterFactory; //... SSLAdapterFactory adapterFactory = SSLAdapterFactory.getDefaultFactory( ); WLSSLAdapter adapter = (WLSSLAdapter) adapterFactory.getSSLAdapter( );
It is this adapter class that enables the client to interact with that SSL-protected web service. Thus, in order to use a custom SSL implementation, you need to first create your own SSL adapter class. Example 19-15 provides a sample adapter class that implements the SSLAdapter interface while relying on the standard JSSE implementation of SSL.
Example 19-15. Custom SSL adapter class
import java.net.URL; import java.net.Socket; import java.net.URLConnection; import java.io.IOException; public class JSSEAdapter implements weblogic.webservice.client.SSLAdapter { // Use Java's standard SSL socket factory javax.net.SocketFactory factory = javax.net.ssl.SSLSocketFactory.getDefault( ); // Use Java's implementation to return an SSL connection to the // server hosting the web service public Socket createSocket(String host, int port) throws IOException { return factory.createSocket(host, port); } // Assumes that you have set the java.protocol.handler.pkgs property public URLConnection openConnection(URL url) throws IOException { return url.openConnection( ); } public void setSocketFactory(javax.net.ssl.SSLSocketFactory factory) { this.factory = factory; } public javax.net.ssl.SSLSocketFactory getSocketFactory( ) { return (javax.net.ssl.SSLSocketFactory) factory; } }
A client then can create an instance of this custom SSL adapter in two ways:
java -Dweblogic.webservice.client.ssl.adapterclass= oreilly.wlguide.webservice.client.JSSEAdapter ... oreilly.wlguide.webservices.client.MyApp
weblogic.webservice.client.SSLAdapterFactor
In particular, you must override the method that creates a new SSL adapter instance:
public SSLAdapter createSSLAdapter( );
The client then needs to create an instance of the custom SSL adapter factory and set it as the default using the following method:
SSLAdapterFactory.setDefaultFactory(factory);
Subsequently, the client can use this default adapter factory to manufacture an instance of the custom SSL adapter:
SSLAdapterFactory myfactory = SSLAdapterFactory.getDefaultFactory( ); JSSEAdapter adapter = (JSSEAdapter) myfactory.getSSLAdapter( );
19.8.3 SOAP Message Security
WebLogic's implementation of SOAP message security is based on the OASIS draft specification WSS: SOAP Message Security, which is based on the WS-Security draft specification. These specifications aim to secure SOAP message exchanges through a flexible set of mechanisms based on security token propagation, message integrity, and message confidentiality.
19.8.3.1 Architecture
WebLogic augments three aspects of web services in order to implement SOAP message security:
WSDL
WebLogic augments the WSDL of a web service to indicate which operations should be secured and how they should be secured. As usual, you can either use the servicegen Ant task or modify the web-services.xml descriptor file to effect these changes. Because there is no standard specification, WebLogic's changes to the WSDL are necessarily proprietary.
Client runtime
The client runtime is augmented with WebLogic's implementation of SOAP message security. It also requires access to a key file and a certificate file, which are used to sign outgoing messages. The runtime performs any encryption and signature tasks just before the SOAP message is sent to the server, after all of the client handlers are executed.
Server runtime
The server runtime also is augmented with WebLogic's implementation of SOAP message security. The runtime performs any encryption and signature tasks just after receiving the SOAP message, before passing it on to the web service. It requires two key/certificate pairs one for encrypting and one for signing.
When a client invokes a web service operation, it reads the WSDL of the service. If the service has added SOAP message security, the WSDL will reflect this. For example, the WSDL will contain the server's public certificate for encrypting any messages that are sent to it! When such an operation is invoked, certificates, signatures, and tokens are sent back and forth many times. Don't be overawed by the following description because most of these actions occur transparently to the client and web service implementation. When a client invokes a web service operation that needs additional message security, the following actions occur before the SOAP message is actually sent:
When the server runtime receives a SOAP message and finds that additional security information has been included in the SOAP message, it performs the following actions:
Note that WebLogic asserts the identity of the client certificate so that it doesn't accept invocations from just any client. Only those clients with certificates that can be validated in this way may invoke the operations of the web service. So, for a client to use WebLogic's SOAP message security, it must possess a valid public certificate and WebLogic must have an Identity Assertion Provider installed.
|
When WebLogic sends a SOAP response, the same actions occur, but in reverse order:
When a client runtime receives a message from the web service, the following sequence of actions take place:
The following sections examine how to set up SOAP message security and then put it all into action. For the sake of the discussion, we assume that you have an existing web service say, the Simple web service described earlier in Example 19-1 that needs to be secured through username tokens, encryption, and signing. Although you also can choose which parts of the SOAP message ought to be encrypted instead of just the entire body, we will not consider this here because it involves lengthy changes to the web-services.xml descriptor file.
19.8.3.2 Configuring SOAP message security
To enable SOAP message security for a web service, you need to first add a security element to the servicegen task used to build the web service, as shown in Example 19-16.
Example 19-16. Using the servicegen task to enforce SOAP message security
By supplying the signKeyName and signKeyPass attributes, you enable the signing mechanism on outgoing SOAP messages. Likewise, by supplying the encryptKeyName and encryptKeyPass attributes, you enable the encryption of the body of the SOAP messages. The values of these attributes determine the aliases and passwords of the key and certificate pairs for signing and encryption. We also have set the enablePasswordAuth attribute to true, to force any client of the web service to supply a username token.
The security subelement here ensures that SOAP message security is enabled on all operations of the web service. In addition, it secures (encrypts and signs) the entire SOAP message body. If you want to secure only a subset of operations, or only parts of the message body, you must manually edit the web-services.xml descriptor file.
19.8.3.3 Creating the certificates
The server needs two key pairs. One is used for digitally signing a SOAP message, and another for encrypting the message. WebLogic's current SSL implementation for web services requires that the key length of certificates used for encrypting and signing be at least 1024. In this case, we will use the keytool to create the two keys referenced in Example 19-16. Refer to Chapter 16 to see how you can configure WebLogic to use this store as its identity store. We simply will add the two certificates to a keystore called myIdentityStore.jks developed in that chapter. The following commands create and store the key pairs:
keytool -genkey -keysize 1024 -keyalg RSA -dname "cn=system, ou=OR, o=UH, c=US" -alias encryptKeyAlias -keypass mypassword -keystore myidentitystore.jks -storepass mykeystorepass keytool -selfcert -keystore myidentitystore.jks -alias encryptKeyAlias -storepass mykeystorepass -keypass mypassword keytool -genkey -keysize 1024 -keyalg RSA -dname "cn=system, ou=OR, o=UH, c=US" -alias signKeyAlias -keypass mypassword -keystore myidentitystore.jks -storepass mykeystorepass keytool -selfcert -keystore myidentitystore.jks -alias signKeyAlias -storepass mykeystorepass -keypass mypassword
Notice how we have set the CN field to system. Later in this chapter, we shall configure the username mapper in the default Identity Assertion Provider to extract the WebLogic username from this field. A production environment would use something more robust.
Any client application that needs to interact with the web service must possess its own key pair too. Simply create a new keystore using similar commands the client then can extract the key pair from the keystore.
19.8.3.4 Setting up the Identity Assertion Provider
SOAP messages that are signed by the client will also have the client's public certificate embedded in the message. WebLogic uses the certificate to verify both the signature and the client's identity so as to prevent anonymous clients from invoking the operations of the web service. WebLogic does this by invoking the Identity Assertion Provider configured for the security realm. For our example, we simply will use WebLogic's Default Identity Asserter. (Chapter 18 explains how you configure this provider). The client-supplied token in this case is an X.509 certificate, so you must add this to the list of supported token types for the provider. Select the Default Identity Asserter from the left pane of the Administration Console, and in the Types option, move the X.509 to the Chosen column. This enables WebLogic to consider X.509 certificates as a form of identity assertion.
You also need to set up a username mapper that can extract some data from the certificate and map it to a WebLogic user. You can either write your own, similar to that in Example 18-2, or use WebLogic's default username mapper. For the running example, the latter approach will suffice. Select the Details tab of the Default Identity Asserter, and then the Use Default User Name Mapper option. Because the username can be extracted from the certificate's CN field, you should choose CN as the Default User Name Mapper Attribute Type and then blank out the Default User Name Mapper Attribute Delimiter. Finally, ensure that Base64Decoding Required is not selected.
19.8.3.5 Writing the client
A client that uses SOAP message security must be modified to support it. First, you need to include BEA's SOAP message security implementation in the client's classpath. In other words, you must add the WL_HOME/server/lib/wsse.jar library to the client's classpath. This library doesn't contain the web services JAX-RPC classes, so you still must keep the existing webserviceclient.jar in the classpath. Our Java client needs to load its security identity into the context and supply the username and password of a valid WebLogic user because we have forced the client to supply a username token. To do this, you must set the relevant attributes on the WebServiceSession object. Example 19-17 illustrates the code for a Java client that can invoke a web service operation securely.
Example 19-17. Client code to interact with secure SOAP messages
package com.oreilly.wlguide.webservices.secureSOAP.client; import java.io.FileInputStream; import java.net.URL; import java.rmi.RemoteException; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.X509Certificate; import javax.xml.namespace.QName; import javax.xml.rpc.Call; import javax.xml.rpc.Service; import javax.xml.rpc.ServiceFactory; import weblogic.webservice.context.WebServiceContext; import weblogic.webservice.context.WebServiceSession; import weblogic.webservice.core.handler.WSSEClientHandler; import weblogic.xml.security.UserInfo; public class Invoke { private static final String KEYSTORE = "myIdentityStore.jks"; private static final String KEYSTORE_PASS = "mystorepass"; private static final String KEY_ALIAS = "myalias"; private static final String KEY_PASS = "mypassword"; static void invoke(String where) throws Exception { // First get hold of the keystore that holds our key/cert pair KeyStore ks = KeyStore.getInstance("JKS"); ks.load(new FileInputStream(KEYSTORE), KEYSTORE_PASS.toCharArray( )); // Use the keystore to load the certificate and private key X509Certificate myCert = (X509Certificate) ks.getCertificate(KEY_ALIAS); PrivateKey myKey = (PrivateKey) ks.getKey(KEY_ALIAS, KEY_PASS.toCharArray( )); // Now retrieve the web service context, and its session, from the service Simple ws = new Simple_Impl(where); WebServiceContext wsCtx = ws.context( ); WebServiceSession session = wsCtx.getSession( ); // Finally, set the attributes session.setAttribute(WSSEClientHandler.CERT_ATTRIBUTE, myCert); session.setAttribute(WSSEClientHandler.KEY_ATTRIBUTE, myKey); // Since we set enablePasswordAuth, we have to supply token and define user UserInfo ui = new UserInfo("someWLUser", "somePassword"); session.setAttribute(WSSEClientHandler.REQUEST_USERINFO, ui); SimplePort port = ws.getSimplePort( ); System.out.println("The service returned: " + port.makeUpper("hello there")); } public static void main(String[] argv) throws Exception { invoke(argv[0]); } }
The first part of the code simply retrieves the client's private key and certificate from a keystore. After creating the service object, Simple, the code then retrieves WebLogic's context and session. These objects maintain any server-side state associated with the client. The session then is populated with the digital certificate, private key, and username token into predefined attributes. After this, you should be able to invoke a secured web service operation. Note that the user token information determines which WebLogic user is used to actually invoke the operation.
19.8.3.6 Running the client
Clients that use SOAP message security can be executed in the same way as ordinary web service clients, except that you should include the wsse.jar in the classpath. During development, you may find it useful to enable the debugging flags provided by WebLogic. Use the weblogic.xml.encryption.verbose and weblogic.xml.signature.verbose system properties to obtain debugging information about the encryption and signing processes. For example, you can use the following mouthful when running the client during development:
java -Dweblogic.xml.encryption.verbose=true -Dweblogic.xml.signature.verbose=true -Dweblogic.webservice.verbose=true -Dweblogic.webservice.client.ssl.strictcertchecking=false -cp mysecureSOAPclient.jar;classes;y:serverlibwsse.jar; y:serverlibwebserviceclient+ssl.jar com.oreilly.wlguide.webservices.secureSOAP.client.Invoke http://10.0.10.10:7001/secureSOAPService/Simple?WSDL
19.8.3.7 Encrypting passwords
The security element used to include the server's key, certificate, and password information, creates a number of additional elements in the web-services.xml descriptor file. By default, the key passwords are not encrypted in this file. You can encrypt them using the weblogic.webservice.encryptpass utility. This tool encrypts the passwords salted with the domain name. As a result, the EAR or WAR with the encrypted data can be deployed only to the same domain from which you encrypted the passwords in the first place.
The following command encrypts the secureSOAPService in the EAR file:
java weblogic.webservice.encryptpass -serviceName secureSOAPService out/secureSOAPService.ear
You must either run this command from the root of the domain so that it has access to the config.xml file, or specify the -domain argument to point to the root directory.
Introduction
Web Applications
Managing the Web Server
Using JNDI and RMI
JDBC
Transactions
J2EE Connectors
JMS
JavaMail
Using EJBs
Using CMP and EJB QL
Packaging and Deployment
Managing Domains
Clustering
Performance, Monitoring, and Tuning
SSL
Security
XML
Web Services
JMX
Logging and Internationalization
SNMP