keytool -genkey -alias tomcat -keyalg RSA -keystore producerhttps.keystore -dname "cn=localhost" -keypass changeme -storepass changeme
There are two main ways to secure the communication between a producer and consumer:
Securing the Transport Layer
This requires using SSL and a HTTPS endpoint. By using this, the communication between the consumer and producer will be encrypted.
Securing the Contents of the SOAP message
This option requires using WS-Security to handle parts of the SOAP message. With this option you can specify things like encryption, signing, timestamps, etc as well as passing across user credentials to perform a login on the producer side. WS-Security is more powerful and has more options, but requires more complex configurations.
Depending on requirements, an HTTPs endpoint or/and WS-Security can be used.
It is possible to use WSRP over SSL for a secure exchange of data. Since GateIn Portal does not come initially configured for HTTPS connectors, we will need to configure the producer's server for this first. This is a global configuration change to JBoss AS and will affect more than just GateIn Portal and WSRP. Please see the JBoss AS documentation for how to configure HTTPS connectors for the server.
Once the producer is configured for HTTPS connections, on the consumer you will just need to modify the URL for the WSRP endpoint to point to the new HTTPS-based url. This will require either manually updating the value in the WSRP admin application, or by specifying it using the wsrp-consumers-config.xml configuration file before the server is first started.
This is just a simple, test configuration to be used as an example as to how its possible to setup the HTTPS/SSL with WSRP. It is not meant to show best practices for configuring HTTPS with JBoss AS and does things which should not be used in a production server (such as self-signed certificates). Please see the JBoss AS documentation for full configuration options.
First we will need to configure the producer's server to use HTTPS. This is handled in the same manner that you would configure any JBoss AS server for HTTPS.
Generate the keystore for the producer
keytool -genkey -alias tomcat -keyalg RSA -keystore producerhttps.keystore -dname "cn=localhost" -keypass changeme -storepass changeme
Configure the server to add an https connection. This requires modifying the standalone/configuration/standalone.xml file with the following content in bold:
...
<subsystem xmlns="urn:jboss:domain:web:1.1" default-virtual-server="default-host" native="false">
<connector name="http" protocol="HTTP/1.1" scheme="http" socket-binding="http"/>
<connector name="https" protocol="HTTP/1.1" scheme="https" socket-binding="https" secure="true">
<ssl certificate-key-file="/path/to/producerhttps.keystore" password="changeme"/>
</connector>
<virtual-server name="default-host" enable-welcome-root="true">
<alias name="localhost"/>
<alias name="example.com"/>
</virtual-server>
...
Start the server and verify that https://localhost:8443/portal is accessible. Note that since you are using a self-signed certificate that your browser will give a warning that the certificate cannot be trusted.
In this example case we are accessing the portal using 'localhost' hence why we are using "cn=localhost" in the keytool command. If you are using this across another domain, you will need to make the necessary change.
Ideally we should be able to just change the consumer configuration URL for the producer in the WSRP administration interface to use HTTPS, but we need to tell the consumer's server to trust our self-signed certificate first.
Export the producer's public key from the producer's keystore
keytool -export -alias tomcat -file producerkey.rsa -keystore producerhttps.keystore -storepass changeme
Import the producer's public key into a new keystore for the consumer
keytool -import -alias tomcat -file producerkey.rsa -keystore consumerhttps.keystore -storepass changeme -noprompt
Configure the bin/standalone.conf file to add the following line at the end of the file:
JAVA_OPTS="$JAVA_OPTS -Djavax.net.ssl.trustStore=/path/to/consumerhttps.keystore -Djavax.net.ssl.trustStorePassword=changeme"
Start the consumer and change the selfv2 producer URL to https://localhost:8443/wsrp-producer/v2/MarkupService?wsdl and verify that the consumer can access the producer.
It is also possible to modify the wsrp-consumers-config.xml configuration file to change the URL instead of modifying it in the administration interface
Portlets may present different data or options depending on the currently authenticated user. For remote portlets, this means having to propagate the user credentials from the consumer back to the producer in a safe and secure manner. The WSRP specification does not directly specify how this should be accomplished, but delegates this work to the existing WS-Security standards. The WS-Security standards can also be used to secure the SOAP message, such as encryption and signing the message.
WSRP and WS-Security is currently only supported on GateIn Portal when running on top of JBoss AS 7.
You will want to encrypt the credentials, and potentially the content and data, being sent between the consumer and producer, otherwise they will be sent in plain text and could be easily intercepted. You can either configure WS-Security to encrypt and sign SOAP messages being sent or secure the transport layer by using an HTTPS endpoint. Failure to encrypt SOAP messages or transport layer will result in the username and password being sent in plain text. Use of encryption is strongly recommended.
When the consumer sends the user credentials to the producer, it is sending the credentials for the currently authenticated user in the consumer. This makes signing in to remote portlets transparent to end users, but also requires that the producer and consumer use the same credentials. This means that the username and password must be the same and valid on both servers.
The recommended approach for this situation would be to use a common LDAP configuration. Please see the user guide on how to configure LDAP for use with GateIn Portal.
JBoss AS7 uses a different web service implementation than the previous versions: it is now uses the JBossWS CXF Stack instead of the JBossWS Native Stack. Due to these changes, the way we configure WS-Security for WSRP with GateIn Portal on JBossAS 7 has changed.
We only support one WS-Security configuration option for the producer. All consumers accessing the producer will have to conform to this security constraint. This means if the producer requires encryption, all consumers will be required to encrypt their messages when accessing the producer.
We also only support one WS-Security configuration option to be used by all the consumers. A consumer has the option to enable or disable WS-Security, which allows for one or more consumers to use WS-Security while the others do not.
CXF uses interceptors to extend and configure its behaviour. There are two main types of interceptors: InInterceptors and OutInterceptors. InInterceptors are invoked for communication coming into the client or server, while OutInterceptors are invoked when the client or server sends a message.
So for the WSRP case, the communication from the consumer to the producer is governed by the consumer's OutInterceptor and the producer's InIntereceptor. The communication from the producer to the consumer is governed by the producer's OutInterceptor and the consumer's InInterceptor. This may mean having to configure 4 Interceptors.
When dealing with WS-Security, there are some things to consider here:
When dealing with user propagation, only the consumer sends the user credentials to the producer. So Username Tokens only need to be configured for the consumer's OutInterceptor and the producer's InInterceptor.
When dealing with things like encryption, you will most likely want to encrypt the message from the consumer to the producer and also the message from the producer to the consumer. This means that encryption properties must be configured for all 4 interceptors.
Please see the CXF Documentation for more details on interceptors and their types: http://cxf.apache.org/docs/interceptors.html
To support WS-Security, GateIn Portal uses CXF's WSS4J Interceptors which handle all WS-Security related tasks. Please see the CXF Documentation for more details: http://cxf.apache.org/docs/ws-security.html
The WSS4J Interceptors are configured using properties, and as such can be configured using simple property files.
WSRP looks for specific property files to know whether or not in/out interceptors must be added and configured for either consumers or producer. Theses files are located in the standalone/configuration/gatein/wsrp/cxf/ws-security directory of your the JBoss AS 7 home directory. Consumer-specific files are in the consumer subdirectory while producer-specific files should be located in the producer subdirectory. To add and configure a WSS4J interceptor, you just need to add the proper configuration file in the proper directory. If no configuration file is found for a specific interceptor type, then no such interceptor will be added. "In" interceptors are configured using WSS4JInInterceptor.properties files while "out" interceptors are configured using WSS4JOutInterceptor.properties files.
Putting things together, here are the files you need to add to configure each interceptor types for each WSRP side:
Side |
Interceptor type |
Configuration file |
Consumer |
IN |
standalone/configuration/gatein/wsrp/cxf/ws-security/consumer/WSS4JInInterceptor.properties |
|
OUT |
standalone/configuration/gatein/wsrp/cxf/ws-security/consumer/WSS4JOutInterceptor.properties |
Producer |
IN |
standalone/configuration/gatein/wsrp/cxf/ws-security/producer/WSS4JInInterceptor.properties |
|
OUT |
standalone/configuration/gatein/wsrp/cxf/ws-security/producer/WSS4JOutInterceptor.properties |
Please refer to the CXF or WSS4J documentation for instructions and options available for each type of interceptors.
User propagation can be configured to be used over WSRP with ws-security. What this means is that a user logged into a consumer can have their credentials propagated over to the producer. This allows the producer to authenticate the user and any portlet on the producer (a remote portlet from the consumer's perspective) will view the user as being properly authenticated. This allows for remote portlets to access things like user information.
This only works if the user's credentials on the producer and consumer are the same. This may require using a common authentication mechanism, such as LDAP.
This requires some special options when configuring the producer and server.
In order to configure ws-security on the consumer side, you will have to configure the WSS4J Interceptors as seen above. This will require having to configure the WSS4JInInterceptor and/or WSS4JOutInterceptor.
You will also need to check the 'Enable WS-Security' checkbox on the WSRP Admin Portlet for the consumer configuration to take effect.
In order to handle user propagation in GateIn Portal across ws-security, a couple of special configuration options have been created which should be applied to the consumer's WSS4JOutInterceptor.
user=gtn.current.user
What this option will do is it will set the 'user' property to the currently authenticated user on the consumer.
action=gtn.UsernameToken.ifCurrentUserAuthenticated
If a user is currently authenticated, it will replace the 'gtn.UsernameToken.ifCurrentUserAuthenticated' with 'UsernameToken'. If the current user is an unauthenticated user, 'gtn.UsernameToken.ifCurrentUserAuthenticated' will be removed from the action list. If no other actions are specified, then the WSS4J interceptor will not be added to the consumer. This allows you to only use ws-security when dealing with authenticated users, and not for anonymous users.
This requires that the user option is set to 'gtn.current.user'
To set the password for the username token, we need to specify the password in a callback class. See the cxf ws-security documentation for more details (http://cxf.apache.org/docs/ws-security.html).
A special callback class has already been created which handles this for you: CurrentUserPasswordCallback. This class will retrieve the currently authenticated user's password and set this as the password in the callback object.
passwordCallbackClass=org.gatein.wsrp.wss.cxf.consumer.CurrentUserPasswordCallback
The configuration of the producer is similar to that of the consumer. It also requires having to configure the WSS4JInInterceptor and/or WSS4JOutInterceptor.
To properly propagate user information on the producer-side, you will need to use GTNSubjectCreatingInterceptor instead of a regular WSS4JInInterceptor. This GateIn Portal specific "in" interceptor is an extension of the traditional WSS4JInInterceptor and therefore can be configured similarly and accept the same configuration properties. To specify that you want to use the GTNSubjectCreatingInterceptor, please create a property file at standalone/configuration/gatein/wsrp/cxf/ws-security/producer/GTNSubjectCreatingInterceptor.properties instead of the regular WSS4JInInterceptor.properties file.
This Interceptor will handle the ws-security headers and retrieve the users credentials. It will then use these credentials to perform a login on the producer site, thus authenticating the user on the producer and makes the user available to remote portlets.
This class also extends org.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingInterceptor and can accept the same properties this class normally accepts. See the JBossWS documentation for options and more information.
action=gtn.UsernameToken.ifAvailable
When this option is activated, the interceptor will set the action to 'UsernameToken' when the received SOAP message contains ws-security headers. If no ws-security header is included in the message, then no action is taken and the interceptor is not run. This is useful for dealing with authenticated and unauthentcated users trying to access the producer.
This example configuration does not encrypt the message. This means the username and password will be sent between the producer and consumer in plain text. This is a security concern and is only being shown as a simple example. It is up to administrators to properly configure the WSS4J Interceptors to encrypt messages or to only use https communication between the producer and consumer.
create the following file: standalone/configuration/gatein/wsrp/cxf/ws-security/producer/GTNSubjectCreatingInterceptor.properties
set the content of GTNSubjectCreatingInterceptor.properties created in step 1 to:
action=gtn.UsernameToken.ifAvailable
start the producer server
create the following file: standalone/configuration/gatein/wsrp/cxf/ws-security/consumer/WSS4JOutInterceptor.properties
set the content of the WSS4JOutInterceptor.properties created in step 1 to:
passwordType=PasswordText user=gtn.current.user action=gtn.UsernameToken.ifCurrentUserAuthenticated passwordCallbackClass=org.gatein.wsrp.wss.cxf.consumer.CurrentUserPasswordCallback
start the consumer server
in the WSRP admin portlet, click the 'enable ws-security' checkbox
access a remote portlet (for example, the user identity portlet included as an example portlet in GateIn Portal) and verify that the authenticated user is the same as the one on the consumer
The following steps outline how to configure the producer and consumer to encrypt and sign SOAP messages passed between the producer and consumer. This example only deals with SOAP messages being sent between the producer and consumer, and not with user propagation.
Some of the configuration options specified here are based on the content at http://cxf.apache.org/docs/ws-security.html and http://www.jroller.com/gmazza/entry/cxf_x509_profile More information may be available at these sites.
WSS4J uses a Java class to specify the password when performing any security related actions. For the purpose of these encryption and signing examples, we will use the same password for the producer's and consumer's keystore (wsrpAliasPassword). This simplifies things a bit as it means we can use just one password callback class for both the producer and consumer.
Example test.TestCallbackHandler class:
package test; import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.ws.security.WSPasswordCallback; import org.gatein.wsrp.wss.cxf.consumer.CurrentUserPasswordCallback; public class TestCallbackHandler implements CallbackHandler { @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { //First check if we have any user name token call backs to add. //NOTE: only needed if using username tokens, and you want the currently authenticated users password added CurrentUserPasswordCallback currentUserPasswordCallback = new CurrentUserPasswordCallback(); currentUserPasswordCallback.handle(callbacks); for (Callback callback: callbacks) { if (callback instanceof WSPasswordCallback) { WSPasswordCallback wsPWCallback = (WSPasswordCallback)callback; // since the CurrentUserPasswordCallback already handles the USERNAME_TOKEN case, we don't want to set it in this case if (wsPWCallback.getUsage() != WSPasswordCallback.USERNAME_TOKEN) { wsPWCallback.setPassword("wsrpAliasPassword"); } } } } }
CallbackHandler implementations are provided to GateIn Portal using the standard Java ServiceLoader infrastructure. As such, CallbackHandler implementations need to be bundled in a jar containing a file META-INF/services/javax.security.auth.callback.CallbackHandler specifying the fully qualified name of the CallbackHandler implementation class. This jar then needs to be put in the gatein/extensions directory of your GateIn Portal installation.
You can see a working example of a CallbackHandler implentation at https://github.com/gatein/gatein-wsrp/tree/master/examples/wss-callback
In this example we are making it a bit easier by specifying the same keystore password for both the producer and consumer, as they can use the same password callback class.
Generate the producer's private encryption keys
keytool -genkey -alias producerAlias -keypass wsrpAliasPassword -keystore producer.jks -storepass keyStorePassword -dname "cn=producerAlias" -keyalg RSA
Export the producer's public key
keytool -export -alias producerAlias -file producerkey.rsa -keystore producer.jks -storepass keyStorePassword
Generate the consumer's private encryption keys
keytool -genkey -alias consumerAlias -keypass wsrpAliasPassword -keystore consumer.jks -storepass keyStorePassword -dname "cn=consumerAlias" -keyalg RSA
Export the consumer's public key
keytool -export -alias consumerAlias -file consumerkey.rsa -keystore consumer.jks -storepass keyStorePassword
Import the consumer's public key into the producer's keystore
keytool -import -alias consumerAlias -file consumerkey.rsa -keystore producer.jks -storepass keyStorePassword -noprompt
Import the producer's public key into the consumer's keystore
keytool -import -alias producerAlias -file producerkey.rsa -keystore consumer.jks -storepass keyStorePassword -noprompt
Copy the producer.jks file to the standalone/configuration/gatein/wsrp/cxf/ws-security/producer directory on the producer
Copy the consumer.jks file to the standalone/configuration/gatein/wsrp/cxf/ws-security/consumer directory on the consumer
Create standalone/configuration/gatein/wsrp/cxf/ws-security/producer/WSS4JInInterceptor.properties with the following content. This will configure the incoming message between the producer and the consumer
action=Signature Encrypt Timestamp signaturePropFile=producer-security.properties decryptionPropFile=producer-security.properties passwordCallbackClass=test.TestCallbackHandler
Create standalone/configuration/gatein/wsrp/cxf/ws-security/producer/WSS4JOutInterceptor.properties with the following content. This will configure the outgoing message between the producer and the consumer
action=Signature Encrypt Timestamp signaturePropFile=producer-security.properties encryptionPropFile=producer-security.properties passwordCallbackClass=test.TestCallbackHandler user=producerAlias encryptionUser=consumerAlias signatureUser=producerAlias
Create standalone/configuration/gatein/wsrp/cxf/ws-security/producer/producer-security.properties with the following content:
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=keyStorePassword org.apache.ws.security.crypto.merlin.file=producer.jks
The passwordCallbackClass property in these configuration files needs to match the fully qualified name of your CallbackHandler implementation class. In our case, it is test.TestCallbackHandler.
Create standalone/configuration/gatein/wsrp/cxf/ws-security/consumer/WSS4JOutInterceptor.properties with the following content. This will configure the outgoing message between the consumer and the producer
action=Signature Encrypt Timestamp signaturePropFile=consumer-security.properties encryptionPropFile=consumer-security.properties passwordCallbackClass=test.TestCallbackHandler user=consumerAlias encryptionUser=producerAlias signatureUser=consumerAlias
Create standalone/configuration/gatein/wsrp/cxf/ws-security/consumer/WSS4JInInterceptor.properties with the following content. This will configure the incoming message between the consumer and the producer
action=Signature Encrypt Timestamp signaturePropFile=consumer-security.properties decryptionPropFile=consumer-security.properties passwordCallbackClass=test.TestCallbackHandler
Create standalone/configuration/gatein/wsrp/cxf/ws-security/consumer/consumer-security.properties with the following content:
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=keyStorePassword org.apache.ws.security.crypto.merlin.file=consumer.jks
The passwordCallbackClass property in these configuration files needs to match the fully qualified name of your CallbackHandler implementation class. In our case, it is test.TestCallbackHandler.
The following setps outline how to configure the producer and consumer to encrypt and sign the soap message as well as use user propagation between the producer and consumer.
Follow the steps outlined in the Sample Configuration Securing the Endpoints using Encryption and Signing section but make the following changes:
rename the WSS4JInInterceptor.properties file to GTNSubjectCreatingInterceptor.properties
set the action property in GTNSubjectCreatingInterceptor.properties as:
action= gtn.UsernameToken.ifAvailable Signature Encrypt Timestamp
set the passwordType in GTNSubjectCreatingInterceptor.properties as:
passwordType=PasswordText
Follow the steps outlined in the Sample Configuration Securing the Endpoints using Encryption and Signing section but make the following changes:
set the action property in WSS4JOutInterceptor.properties as:
action=gtn.UsernameToken.ifCurrentUserAuthenticated Signature Encrypt Timestamp
set the user in the WSS4JOutInterceptor.properties as:
user=gtn.current.user
set the passwordType in the WSS4JOutInterceptor.properties as:
passwordType=PasswordText