Chapter 7. Securing SOAP Web-Services using XWSS Library

In this chapter, we will cover:

  • Authenticating a Web-Service call using the username token with a plain/digested password

  • Authenticating a Web-Service call using Spring security to authenticate a username token with a plain/digested password

  • Authenticating a Web-Service call using the JAAS service to authenticate a username token

  • Preparing pair and symmetric keystores

  • Securing SOAP messages using a digital signature

  • Authenticating a Web-Service call using X509 certificate

  • Encrypting/decrypting SOAP messages

Introduction

WS-Security (WSS), published by OASIS, is an extension to SOAP to provide security-standard features to a Web-Service. XML and Web-Services Security (XWSS) is SUN's implementation of WSS, which is included in the Java Web-Services Developer Pack (WSDP).

XWSS is a form of message-level security in which security data is included within a SOAP message/attachment and allows security information to be transmitted with messages or attachments. For instance, while signing a message, a security token will be added to the message that is generated from the encryption of a part of the message for a specific receiver. When a sender sends this message, this token remains in the encrypted form and travels along with the message. When a receiver gets this message, the token can be decrypted only if he/she has the specific key for decryption. So if within transmission of this message, any non-authorized receiver (who doesn't have the specific key) gets this message, he/she cannot decrypt the token (this token will be used to check if the original message is altered). The originality of the message verification can be done by the regeneration of the token at the receiver's end (from the incoming message) and by comparing it with the incoming token that came along with the message.

An EndpointInterceptor, as the name suggests, intercepts the request and performs some action prior to invoking the endpoint. EndpointInterceptors are called before calling the appropriate endpoint to perform several processing aspects such as logging, validating, security, and so on. In earlier chapters, SoapEnvelopeLoggingInterceptor, PayloadLoggingInterceptor, and PayloadValidatingInterceptor were explained for logging and validation purposes.

In this chapter, and the next one, SecurityInterceptors will be explained.

Spring-WS XwsSecurityInterceptor is an EndpointInterceptor for performing security operations on a request message before calling the endpoint. This interceptor, which is based on XWSS, requires a policy configuration file to operate. Here is a sample of the policy configuration file that can include several security requirements:

<xwss:SecurityConfiguration ...>
<xwss:RequireTimestamp .../>
<xwss:RequireUsernameToken ...../>
........
</xwss:SecurityConfiguration>

The security interceptor uses this configuration to find what security information to expect from incoming SOAP messages (on the receiver side), and what information is to be added to outgoing messages (on the sender side).

In addition, this interceptor needs one or more callBackHandlers for security operations such as authentication, signing outgoing messages, verifying the signature of incoming messages, decryption, and encryption. These callBackHandlers need to be registered in the application context file:

<sws:interceptors>
<bean
class="...XwsSecurityInterceptor">
<property name="policyConfiguration" value="/WEB-INF/securityPolicy.xml" />
<property name="policyConfiguration" value="/WEB-INF/securityPolicy.xml" />
<property name="callbackHandlers">
<list>
<ref bean="callbackHandler1" />
<ref bean="callbackHandler2" />
..............
</list>
</property>
</bean>
</sws:interceptors>
<bean id="callbackHandler1"
class=".....SimplePasswordValidationCallbackHandler">
<property name="users">
<props>
<prop key="admin">secret</prop>
<prop key="clinetUser">pass</prop>
</props>
</property>
</bean>
.........

This chapter presents how to apply Spring-WS XWSS to different security operations. In every recipe's project, the client applies a security operation by adding or modifying data in the outgoing message and sends it to the server. The server receives the message, extracts security information, and proceeds with the message if the security information matches the expected requirement; otherwise it returns a fault message back to the client.

For simplification, most of the recipes in this chapter use the projects used in the Integration testing using Spring-JUnit support recipe, discussed in Chapter 3, Testing and Monitoring Web Services, to set up a server and send and receive messages by client. However, in the last recipe, projects from the Creating Web-Service client for WS-Addressing endpoint recipe, discussed in Chapter 2, Building Clients for SOAP Web Services, are used for the server and client side.

Authenticating a Web-Service call using plain/digested username token

Authentication simply means checking whether callers of a service are who they claim to be. One way of checking the authentication of a caller is to check the password.

XWSS provides APIs to get the usernames and passwords from incoming SOAP messages and compare them with what is defined in the configuration file. This goal will be accomplished by defining policy files for the sender and the receiver of the messages that on the sender side, client includes a username token in outgoing messages, and on the receiver side, the server expects to receive this username token along with the incoming messages for authentication.

Transmitting a plain password makes a SOAP message unsecured. XWSS provides the configuration setting in the policy file to include a digest of passwords (a hash generated from the password text by a specific algorithm) inside the sender message. On the server side, the server compares the digested password included in the incoming message with the digested password calculated from what is set in the configuration file (see the property users within the callbackHandler bean inside spring-ws-servlet.xml) using the same algorithms on the sender side. This recipe shows how to authenticate a Web-Service call using the username token with a plain/digest password. This recipe contains two cases. In the first case, the password will be transmitted in plain text format. However, in the second case, by changing the policy file configuration, the password will be transmitted in the digest format.

Getting ready

In this recipe, the project's name is LiveRestaurant_R-7.1 (for the server-side Web-Service) and has the following Maven dependencies:

  • spring-ws-security-2.0.1.RELEASE.jar

  • spring-expression-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

LiveRestaurant_R-7.1-Client (for the client-side Web-Service) has the following Maven dependencies:

  • spring-ws-security-2.0.1.RELEASE.jar

  • spring-ws-test-2.0.0.RELEASE.jar

  • spring-expression-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

  • junit-4.7.jar

How to do it...

The following steps implement authentication using a username token with a plain password:

  1. Register the security interceptor (XwsSecurityInterceptor) and callbackHandler (SimplePasswordValidationCallbackHandler) in the application context file (applicationContext.xml) of LiveRestaurant_R-7.1-Client.

  2. Add the security policy file (securityPolicy.xml) for LiveRestaurant_R-7.1-Client.

  3. Register the security interceptor (XwsSecurityInterceptor) and callbackHandler (SimplePasswordValidationCallbackHandler) in the application context file (spring-ws-servlet.xml) of LiveRestaurant_R-7.1.

  4. Add the security policy file (securityPolicy.xml) for LiveRestaurant_R-7.1.

  5. Run the following command from Liverestaurant_R-7.1:

    mvn clean package tomcat:run
    
    
  6. Run the following command from Liverestaurant_R-7.1-Client:

mvn clean package

  • The following is the client-side output (note the password's tag wsse:Password ...#PasswordText) within the underlined section:

INFO: ==== Sending Message Start ====
<SOAP-ENV:Envelope ...">
<SOAP-ENV:Header>
<wsse:Security ..>
<wsu:Timestamp ...>
<wsu:Created>2011-11-06T07:19:16.225Z</wsu:Created>
<wsu:Expires>2011-11-06T07:24:16.225Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken .....>
<wsse:Username>clinetUser</wsse:Username>
<wsse:Password ...#PasswordText">****</wsse:Password>
<wsse:Nonce ..#Base64Binary">...</wsse:Nonce>
<wsu:Created>2011-11-06T07:19:16.272Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<tns:placeOrderRequest xmlns:tns="...">
.....
.......
</tns:placeOrderRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Sending Message End ====
.....
INFO: ==== Received Message Start ====
......
<SOAP-ENV:Envelope....">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<tns:placeOrderResponse .....>
<tns:refNumber>order-John_Smith_1234</tns:refNumber>
</tns:placeOrderResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Received Message End ====

The following steps implement authentication using the username token with the digest password:

  1. Modify the security policy file (securityPolicy.xml) of Liverestaurant_R-7.1 to get the digest password from the incoming message.

  2. Modify the security policy file (securityPolicy.xml) of Liverestaurant_R-7.1-Client to send the digest password.

  3. Run the following command from Liverestaurant_R-7.1:

    mvn clean package tomcat:run
    
    
  4. Run the following command from Liverestaurant_R-7.1-Client:

mvn clean package

  • The following is the client-side output (note the password's tag wsse:Password ...#PasswordDigest) within the underlined section:

Nov 6, 2011 12:19:25 PM com.sun.xml.wss.impl.filter.DumpFilter process
INFO: ==== Sending Message Start ====
..
<SOAP-ENV:Envelope .../">
<SOAP-ENV:Header>
<wsse:Security ...>
<wsu:Timestamp ..>
<wsu:Created>2011-11-06T08:19:25.515Z</wsu:Created>
<wsu:Expires>2011-11-06T08:24:25.515Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken...>
<wsse:Username>clinetUser</wsse:Username>
<wsse:Password ...#PasswordDigest">****</wsse:Password>
<wsse:Nonce ...#Base64Binary">...</wsse:Nonce>
<wsu:Created>2011-11-06T08:19:25.562Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<tns:placeOrderRequest..">
......
</tns:placeOrderRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Sending Message End ====
........
INFO: ==== Received Message Start ====
<?xml version="1.0" ...>
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<tns:placeOrderResponse ...>
<tns:refNumber>order-John_Smith_1234</tns:refNumber>
</tns:placeOrderResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Received Message End ====

How it works...

The Liverestaurant_R-7.1 project is a server-side Web-Service that requires its client to send a message along with the username token and password. The Liverestaurant_R-7.1-Client project is a client-side test project that sends a message to the server along with the username token and password.

On the server side, XwsSecurityInterceptor forces the server to apply the policy inside securityPolicy.xml for all incoming messages and uses SimplePasswordValidationCallbackHandler to compare incoming messages username/password with includes username/password in the server configuration file (see the property users within the callbackHandler bean):

<sws:interceptors>
...
<bean class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="/WEB-INF/securityPolicy.xml" />
<property name="callbackHandlers">
<list>
<ref bean="callbackHandler" />
</list>
</property>
</bean>
</sws:interceptors>
<bean id="callbackHandler"
class="org.springframework.ws.soap.security.xwss.callback.SimplePasswordValidationCallbackHandler">
<property name="users">
<props>
<prop key="admin">secret</prop>
<prop key="clinetUser">pass</prop>
</props>
</property>

</bean>

In the securityPolicy.xml file,<xwss:RequireUsernameToken passwordDigestRequired="false" nonceRequired="true"/> requires that the incoming messages have username tokens with non-encrypted passwords. useNonce="true" indicates that each incoming message will have a random number that is not equal to the previous message:

<xwss:SecurityConfiguration dumpMessages="true" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:RequireTimestamp maxClockSkew="60" timestampFreshnessLimit="300"/>
<xwss:RequireUsernameToken passwordDigestRequired="false" nonceRequired="true"/>
</xwss:SecurityConfiguration>

On the client side, XwsSecurityInterceptor forces the client to apply the policy inside securityPolicy.xml for all outgoing messages:

<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
....
<property name="interceptors">
<list>
<ref local="xwsSecurityInterceptor" />
</list>
</property>
</bean>
<bean id="xwsSecurityInterceptor"
class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="/securityPolicy.xml"/>
<property name="callbackHandlers">
<list>
<ref bean="callbackHandler"/>
</list>
</property>
</bean>
<bean id="callbackHandler" class="org.springframework.ws.soap.security.xwss.callback.SimplePasswordValidationCallbackHandler"/>

In the securityPolicy.xml file,<xwss:UsernameToken name="clinetUser" password="pass" digestPassword="false" useNonce="true"/> includes the username token with the password for all outgoing messages:

<xwss:SecurityConfiguration dumpMessages="true" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
<xwss:Timestamp />
<xwss:UsernameToken name="clinetUser" password="pass" digestPassword="false" useNonce="true"/> ...
</xwss:SecurityConfiguration>

Here, useNonce="true" indicates that each request will be sent out with a new random number for each message (Nonce helps to protect against hijacking of the username token).

In the case of authentication using a username token with a plain password, since digestPassword="false" is in both the client- and server-side policy files, you see in the output result that the message sent by the client has a username and a plain text password included in the username token:

<wsse:UsernameToken ....>
<wsse:Username>clinetUser</wsse:Username>
<wsse:Password ..>****</wsse:Password>
...
</wsse:UsernameToken>

However, in the second case of authenticating using the digest username token with the digest password, since digestPassword="true" is in both the client- and server-side policy files, the digest of the password is included in the username token:

<wsse:UsernameToken ....>
<wsse:Username>clinetUser</wsse:Username>
<wsse:Password ...#PasswordDigest">****</wsse:Password>
...
</wsse:UsernameToken>

In this case, the server compares the incoming SOAP message digest password with the calculated digested password from inside spring-ws-servlet.xml. In this way, communication will be more secure by comparison with the first case in which the password was transmitted in plain text (the plain text password could be easily extracted from the SOAP messages. However, using an SSL connection can secure such a communication).

See also...

The recipes Authenticating a Web-Service call using Spring security to authenticate a username token with plain/digested password, Authenticating a Web-Service call using JAAS service to authenticate a username token, and Authenticating a Web-Service call using X509 certificate, discussed in this chapter.

Authenticating a Web-Service call using Spring security to authenticate a username token with a plain/digested password

Here we make use of the same authentication method used in the first recipe. The only difference here is that the Spring Security framework is used for authentication. Since the Spring Security framework is beyond the scope of this book, it is not described here. However, you can read more about it in the Spring Security reference documentation (http://www.springsource.org/security).

Same as the first recipe of this chapter, this recipe also contains two cases. In the first case, the password will be transmitted in plain text format. In the second case, by changing the policy file's configuration, the password will be transmitted in digest format.

Getting ready

In this recipe, the project's name is LiveRestaurant_R-7.2 (for the server-side Web-Service) and has the following Maven dependencies:

  • spring-ws-security-2.0.1.RELEASE.jar

  • spring-expression-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

LiveRestaurant_R-7.2-Client (for the client-side Web-Service) has the following Maven dependencies:

  • spring-ws-security-2.0.1.RELEASE.jar

  • spring-ws-test-2.0.0.RELEASE.jar

  • spring-expression-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

  • junit-4.7.jar

How to do it...

In this recipe, all the steps are the same as in the previous recipe, Authenticating a Web-Service call using username token with plain/digested password, except the server-side application context file (spring-ws.servlet.xml) callback handler changes and uses the DAO layer to fetch data:

The following steps implement authentication of a Web-Service call using Spring Security to authenticate a username token with a plain password:

  1. Register the security interceptor (XwsSecurityInterceptor) and callbackHandler (SpringPlainTextPasswordValidationCallbackHandler) in the application context file (spring-ws-servlet.xml) of LiveRestaurant_R-7.2.

  2. Add the DAO layer classes to fetch data.

  3. Run the following command from Liverestaurant_R-7.2:

    mvn clean package tomcat:run
    
    
  4. Run the following command from Liverestaurant_R-7.2-Client:

mvn clean package

  • The following is the client-side output:

Nov 6, 2011 1:42:37 PM com.sun.xml.wss.impl.filter.DumpFilter process
INFO: ==== Sending Message Start ====
...
<SOAP-ENV:Envelope ....>
<SOAP-ENV:Header>
<wsse:Security ...>
<wsu:Timestamp....>
<wsu:Created>2011-11-06T09:42:37.391Z</wsu:Created>
<wsu:Expires>2011-11-06T09:47:37.391Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken ...>
<wsse:Username>clinetUser</wsse:Username>
<wsse:Password ...#PasswordText">****</wsse:Password>
<wsse:Nonce ...#Base64Binary">...</wsse:Nonce>
<wsu:Created>2011-11-06T09:42:37.442Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<tns:placeOrderRequest ...>
......
</tns:placeOrderRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Sending Message End ====
INFO: ==== Received Message Start ====
<SOAP-ENV:Envelope ...">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<tns:placeOrderResponse ....">
<tns:refNumber>order-John_Smith_1234</tns:refNumber>
</tns:placeOrderResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Received Message End ====

The following steps implement authentication of a Web-Service call using Spring Security to authenticate a digested username token:

  1. Modify springSecurityHandler to SpringDigestPasswordValidationCallbackHandler in the server application context file (spring-ws-servlet.xml).

  2. Modify the security policy file (securityPolicy.xml) in both the server side and client side to digest the password.

  3. Run the following command from Liverestaurant_R-7.2:

    mvn clean package tomcat:run
    
    
  4. Run the following command from Liverestaurant_R-7.2-Client:

mvn clean package

  • The following is the client-side output:

Nov 6, 2011 2:04:37 PM com.sun.xml.wss.impl.filter.DumpFilter process
INFO: ==== Sending Message Start ====
...
<SOAP-ENV:Envelope ...>
<SOAP-ENV:Header>
<wsse:Security ...>
<wsu:Timestamp ...>
<wsu:Created>2011-11-06T10:04:36.622Z</wsu:Created>
<wsu:Expires>2011-11-06T10:09:36.622Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken...>
<wsse:Username>clinetUser</wsse:Username>
<wsse:Password #PasswordDigest">****</wsse:Password>
<wsse:Nonce #Base64Binary">...</wsse:Nonce>
<wsu:Created>2011-11-06T10:04:36.683Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<tns:placeOrderRequest xmlns:tns="http://www.packtpub.com/liverestaurant/OrderService/schema">
......
</tns:placeOrderRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Sending Message End ====
Nov 6, 2011 2:04:37 PM com.sun.xml.wss.impl.filter.DumpFilter process
INFO: ==== Received Message Start ====
<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<tns:placeOrderResponse...">
<tns:refNumber>order-John_Smith_1234</tns:refNumber>
</tns:placeOrderResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Received Message End ====

How it works...

In the Liverestaurant_R-7.2 project, every aspect of security for the client and server is almost the same as Liverestaurant_R-7.1 that we made use of in the recipe Authenticating a Web-Service call using username with plain/digested password token, except for validating the user on the server side. A Spring Security class is responsible for validating the user and password by comparison with the incoming message's username/password with fetched data from a DAO layer (instead of hardcoding the username/password in spring-ws-servlet.xml). In addition, other data (such as permissions, isAccountBlocked, isAccountExpired, and so on) related to the successfully authenticated user (that matches the username and password) can be fetched from the DAO layer and returned for the authorization task or for any validation about the expiry date of the account and to check if the account is blocked or not.

In the first case, CallbackHandler SpringPlainTextPasswordValidationCallbackHandler compares the plain password included in the incoming SOAP message with the plain password that is fetched from the DAO layer.

<sws:interceptors>
<bean
....
<bean class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="/WEB-INF/securityPolicy.xml"/>
<property name="callbackHandlers">
<list>
<ref bean="springSecurityHandler"/>
</list>
</property>
</bean>
</sws:interceptors>
<bean id="springSecurityHandler"
class="org.springframework.ws.soap.security.xwss.callback.SpringPlainTextPasswordValidationCallbackHandler">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
....

In the second test, however, CallbackHandler is SpringDigestPasswordValidationCallbackHandler that compares the digest password included in the incoming SOAP message with the digest of the password that is fetched from the DAO layer.

<bean id="springSecurityHandler"
class="org.springframework.ws.soap.security.xwss.callback.SpringDigestPasswordValidationCallbackHandler">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>

springSecurityHandler uses MyUserDetailService.java, which should implement Spring's UserDetailService to get the username from the provider and internally fetch all information for that user from a DAO layer (for example, password, roles, is expired, and so on).

public class MyUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
return getUserDataFromDao(username);
}
private MyUserDetail getUserDataFromDao(String username) {
/**
*Real scenario: find user data from a DAO layer by userName,
* if this user name found, populate MyUserDetail with its data(username, password,Role, ....).
*/
MyUserDetail mydetail=new MyUserDetail(username,"pass",true,true,true,true);
mydetail.getAuthorities().add(new GrantedAuthorityImpl("ROLE_GENERAL_OPERATOR"));
return mydetail;
}

This service finally returns the populated data in MyUserDetails.java, which should implement Spring's UserDetails.

public class MyUserDetail implements UserDetails {
private String password;
private String userName;
private boolean isAccountNonExpired;
private boolean isAccountNonLocked;
private boolean isCredentialsNonExpired;
private boolean isEnabled;
public static Collection<GrantedAuthority> authority =
new ArrayList<GrantedAuthority>();
public MyUserDetail( String userName, String password,boolean isAccountNonExpired, boolean isAccountNonlocked,boolean isCredentialsNonExpired, boolean isEnabled){
this.userName=userName;
this.password=password;
this.isAccountNonExpired=isAccountNonExpired;
this.isAccountNonLocked=isAccountNonlocked;
this.isCredentialsNonExpired=isCredentialsNonExpired;
this.isEnabled=isEnabled;
}
@Override
public Collection<GrantedAuthority> getAuthorities() {
return authority;
}
.....
}

Now, if the UserDetails data matches the incoming message's username/password, it returns a response; otherwise, it returns a SOAP fault message.

Same as the 7.1 project, setting digestPassword to true/false in securityPolicy.xml on the server/client-side causes the password to be transmitted in plain text or in the digested format.

Note

In real time, we never configure the plain password option. This is a good option for hackers to enable and disable. We never need such an option in real time. Passwords are always transmitted in encrypted format, irrespective of any type of system or application configuration.

See also...

The recipes Authenticating a Web-Service call using Spring security to authenticate a username token with plain/digested password, Authenticating a Web-Service call using JAAS service to authenticate a username token, and Authenticating a Web-Service call using X509 certificate, discussed in this chapter.

Authenticating a Web-Service call using a JAAS service to authenticate a username token

We make use of the same authentication task with a plain username token, as used in the first recipe. The only difference here is that Java Authentication and Authorization Service (JAAS) is used here for authentication and authorization. Since the JAAS framework is beyond the scope of this book, it is not described here. However, you can read more about JAAS in the reference documentation ( http://download.oracle.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html).

JaasPlainTextPasswordValidationCallbackHandler from the xwss package is the API that calls the Login module that is configured inside the JAAS configuration file.

Getting ready

In this recipe, the project's name is LiveRestaurant_R-7.3 (for the server-side Web-Service) and has the following Maven dependencies:

  • spring-ws-security-2.0.1.RELEASE.jar

  • spring-expression-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

LiveRestaurant_R-7.3-Client (for the client-side Web-Service) has the following Maven dependencies:

  • spring-ws-security-2.0.1.RELEASE.jar

  • spring-ws-test-2.0.0.RELEASE.jar

  • spring-expression-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

  • junit-4.7.jar

How to do it...

In this recipe, all the steps are the same as in the previous recipe, Authenticating a Web-Service call using username token with plain/digested password, except that the server-side application context file (spring-ws.servlet.xml) callback handler changes and uses the JAAS framework as an authentication and authorization service:

  1. Register the JAAS callbackHandler (JaasPlainTextPasswordValidationCallbackHandler) in the server-side application context file (spring-ws.servlet.xml).

  2. Add the JAAS framework's required classes (RdbmsPrincipal, RdbmsCredential, and RdbmsPlainTextLoginModule) and the configuration file (jaas.config).

  3. Run the following command from Liverestaurant_R-7.3:

    mvn clean package tomcat:run -Djava.security.auth.login.config="src/main/resources/jaas.config"
    
    
  4. Run the following command from Liverestaurant_R-7.3-Client:

mvn clean package

  • The following is the client-side output:

INFO: ==== Sending Message Start ====
....
<SOAP-ENV:Envelope ....">
<SOAP-ENV:Header>
<wsse:Security ....>
<wsu:Timestamp ...>
<wsu:Created>2011-11-06T11:59:09.712Z</wsu:Created>
<wsu:Expires>2011-11-06T12:04:09.712Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken ...>
<wsse:Username>clinetUser</wsse:Username>
<wsse:Password ....#PasswordText">****</wsse:Password>
<wsse:Nonce ...0#Base64Binary">...</wsse:Nonce>
<wsu:Created>2011-11-06T11:59:09.774Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<tns:placeOrderRequest...>
.....
</tns:placeOrderRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Sending Message End ====
...
INFO: ==== Received Message Start ====
...
<SOAP-ENV:Envelope ...>
<SOAP-ENV:Header>
<wsse:Security ...>
<wsu:Timestamp ....>
<wsu:Created>2011-11-06T11:59:11.630Z</wsu:Created>
<wsu:Expires>2011-11-06T12:04:11.630Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<tns:placeOrderResponse ...>
<tns:refNumber>order-John_Smith_1234</tns:refNumber>
</tns:placeOrderResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Received Message End ====

How it works...

In the Liverestaurant_R-7.3 project, everything about security for the client and server is almost the same as the Liverestaurant_R-7.1 project that we used in the recipe Authenticating a Web-Service call using a username with plain/digested password token except for validating a user on the server side. A JAAS framework is responsible for validating the user and password by comparison of incoming message's username/password with fetched data from a data source (database here).

The client sends a request SOAP message that contains the username token in plain text. The server receives this message and uses the JAAS framework to compare an incoming message username/password with what is fetched from the DAO layer by JAAS. If it matches, it returns a normal response; otherwise, it returns a failure message.

In spring-ws-servlet.xml, JaasPlainTextPasswordValidationCallbackHandler is registered as a callback handler that uses RdbmsPlainText as a pluggable JAAS login module for the username/password authentication:

<sws:interceptors>
.......
<bean class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="/WEB-INF/securityPolicy.xml" />
<property name="callbackHandlers">
<list>
<ref bean="jaasValidationHandler" />
</list>
</property>
</bean>
</sws:interceptors>
<bean id="jaasValidationHandler" class="org.springframework.ws.soap.security.xwss.callback.jaas.JaasPlainTextPasswordValidationCallbackHandler">
<property name="loginContextName" value="RdbmsPlainText" />
</bean>

When the server side is being run using mvn -Djava.security.auth.login.config="src/main/resources/jaas.config", it uses the jaas.config file to locate the JAAS login module (RdbmsPlainTextLoginModule) that is registered in the server-side application context as RdbmsPlainText:

RdbmsPlainText {
com.packtpub.liverestaurant.service.security.RdbmsPlainTextLoginModule Required;
};

The login method from RdbmsPlainTextLoginModule.java will be called to fetch the user password and credentials from the DAO layer. If the fetched password matches the incoming message's password, then it sets credential and returns true; otherwise, it throws an exception that leads the server to send back a fault message to the client:

public class RdbmsPlainTextLoginModule implements LoginModule {
private Subject subject;
private CallbackHandler callbackHandler;
private boolean success;
private List<RdbmsPrincipal> principals = new ArrayList<RdbmsPrincipal>();
private List<RdbmsCredential> credentials = new ArrayList<RdbmsCredential>();
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
.....
}
@Override
public boolean login() throws LoginException {
......
}
private List<String> getAllPermission(String username) {
......
}
private boolean authenticate(String username,String password)
{
....
}
public boolean commit() throws LoginException {
.....
}
@Override
public boolean logout() throws LoginException {
.....
}
}

Note

In important applications, even the username is encrypted. This provides more security and competitors can't guess which users are coming from which location using ISP-level filtering. Hackers guess or track a username and send duplicate requests to load servers with unnecessary data. In this recipe, since the password is being transmitted in plain-text format, using an SSL connection is recommended. Spring-WS also supports JaasCertificateValidationCallbackHandler, which uses a certificate for authentication. This handler is not used here. However, you can find out more about it at the following URL:

http://static.springsource.org/spring-ws/site/apidocs/org/springframework/ws/soap/security/xwss/callback/jaas/JaasCertificateValidationCallbackHandler.html.

See also...

The recipes Authenticating a Web-Service call using username token with plain/digested password, Authenticating a Web-Service call using Spring Security to authenticate a username token with plain/digested password, and Authenticating a Web-Service call using X509 certificate, discussed in this chapter.

Preparing pair and symmetric keystores

In order to add more security measures for a Web-Service call, we do need some extra operations such as signing and verifying the signature of Web-Service messages, encryption/decryption, and authentication using certificates. XWSS provides these operations using keystores. The java.security.KeyStore class provides a memory container for the cryptographic keys and certificates. This class can include three types of entries:

  • Private key entry, which contains a private key and a public key certificate (note that the public key here is wrapped within the X.509 certificate a combination of a private key and a public key certificate is known as a key pair)

  • Secret key entry, which contains a symmetric key

  • Trusted certificate entry, which contains a trusted certificate (this certificate is the other party certificate, imported as a trusted certificate, which means the owner keys store the public key within the other party's certificate that belongs to the third party)

A keystore may contain one to many entries. Aliases in a keystore are for distinguishing entries from one another. The private key and certificate are referred to by one alias while any other trusted certificates or secret key entries are referred to by different individual aliases within a keystore.

Earlier in this chapter, authentication of a Web-Service call using the username token was presented. A Web-Service call can be authenticated by using a certificate. Later in this chapter, in the recipe Authenticating a Web-Service call using X509 certificate, authentication using a certificate will be presented. In addition, these certificates can be used for certificate validation, signature verification, and encryption.

Java keytool is a tool that generates and stores the keys and certificates in a keystore file. This keystore is protected by a keystore password. In addition, there is another password that protects the private key.

In this recipe, using the keytool to generate keystores with symmetric key entries, private key entries (private keys and public key certificates), and trusted certificate entries is presented. These keys will be used later in this chapter and in Chapter 8, Securing SOAP Web-Services using WSS4J Library, for signing and verifying the signature of Web-Service messages, encryption/decryption, and authentication using certificates.

Getting ready

Installation of Java, as described in the first recipe.

How to do it...

To generate a keystore with a secret key entry with the alias symmetric, run the following command (this keystore is to be used later for symmetric encryption/decryption):

keytool -genseckey -alias 'symmetric' -keyalg 'DESede' -keystore symmetricStore.jks -storepass 'symmetricPassword' -keypass 'keyPassword' -storetype "JCEKS"

To generate a keystore with a private key entry or a key pair (that contains private key and public certificate pairs) follow next steps:

  1. To generate a receiver (server side here) keystore, run the following command and follow the command prompt:

    keytool -genkey -alias server -keyalg RSA -keystore serverStore.jks -validity 3653
    Enter keystore password:serverPassword
    Re-enter new password:serverPassword
    What is your first and last name?
    [Unknown]: MyFirstName MyLastName
    What is the name of your organizational unit?
    [Unknown]: Software
    What is the name of your organization?
    [Unknown]: MyCompany
    What is the name of your City or Locality?
    [Unknown]: MyCity
    What is the name of your State or Province?
    [Unknown]: MyProvince
    What is the two-letter country code for this unit?
    [Unknown]: ME
    Is CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME correct?
    [no]: yes
    Enter key password for <server>
    (RETURN if same as keystore password):serPkPassword
    Re-enter new password:serPkPassword
    
    
  2. To generate a sender (client side here) keystore, run the following command and follow the command prompt:

    keytool -genkey -alias client -keyalg RSA -keystore clientStore.jks -validity 3653
    Enter keystore password:clientPassword
    Re-enter new password:clientPassword
    What is your first and last name?
    [Unknown]: MyFirstName MyLastName
    What is the name of your organizational unit?
    [Unknown]: Software
    What is the name of your organization?
    [Unknown]: MyCompany
    What is the name of your City or Locality?
    [Unknown]: MyCity
    What is the name of your State or Province?
    [Unknown]: MyProvince
    What is the two-letter country code for this unit?
    [Unknown]: ME
    Is CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME correct?
    [no]: yes
    Enter key password for <server>
    (RETURN if same as keystore password):cliPkPassword
    Re-enter new password:cliPkPassword
    
    
  3. To see the generated private key entry in a keystore, run the following command (please note privateKeyEntry within the underlined text):

    keytool -list -v -keystore serverStore.jks -storepass serverPassword
    Keystore type: JKS
    Keystore provider: SUN
    Your keystore contains 1 entry
    Alias name: server
    Creation date: 26-Jul-2011
    Entry type: PrivateKeyEntry
    Certificate chain length: 1
    Certificate[1]:
    Owner: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
    Issuer: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
    Serial number: 4e2ebd0c
    Valid from: Tue Jul 26 17:11:40 GST 2011 until: Mon Jul 26 17:11:40 GST 2021
    Certificate fingerprints:
    MD5: 9E:DF:5E:18:F5:F6:52:4A:B6:9F:67:04:39:C9:57:66
    SHA1: C5:0B:8C:E6:B6:02:BD:38:56:CD:BB:50:CC:C6:BA:74:86:27:6C:C7
    Signature algorithm name: SHA1withRSA
    Version: 3
    
    
  4. To generate a certificate (public key) from a keystore with a private key entry, run the following command for the client/server-side keystore:

    keytool -export -file clientStore.cert -keystore clientStore.jks -storepass clientPassword -alias client
    keytool -export -file serverStore.cert -keystore serverStore.jks -storepass serverPassword -alias server
    
    
  5. To import the sender (client) public key certificate into the receiver (server) keystore, run the following command for the server-side keystore (this certificate will be stored as a trusted certificate entry in the keystore with the alias client):

    keytool -import -file clientStore.cert -keystore serverStore.jks -storepass serverPassword -alias client
    Owner: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
    Issuer: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
    Serial number: 4e2ebf1e
    Valid from: Tue Jul 26 17:20:30 GST 2011 until: Mon Jul 26 17:20:30 GST 2021
    Certificate fingerprints:
    MD5: FD:BE:98:72:F0:C8:50:D5:4B:10:B0:80:3F:D4:43:E8
    SHA1: 91:FB:9D:1B:69:E9:5F:0B:97:8C:E2:FE:49:0E:D8:CD:25:FB:D8:18
    Signature algorithm name: SHA1withRSA
    Version: 3
    Trust this certificate? [no]: yes
    Certificate was added to keystore
    
    
  6. To import the receiver (server) public key certificate into the sender (client) keystore, run the following command for the sender (client side) keystore (this certificate will be stored as a trusted certificate entry in the keystore with the alias server):

    keytool -import -file serverStore.cert -keystore clientStore.jks -storepass clientPassword -alias server
    Owner: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
    Issuer: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
    Serial number: 4e2ebf1e
    Valid from: Tue Jul 26 17:20:30 GST 2011 until: Mon Jul 26 17:20:30 GST 2021
    Certificate fingerprints:
    MD5: FD:BE:98:72:F0:C8:50:D5:4B:10:B0:80:3F:D4:43:E8
    SHA1: 91:FB:9D:1B:69:E9:5F:0B:97:8C:E2:FE:49:0E:D8:CD:25:FB:D8:18
    Signature algorithm name: SHA1withRSA
    Version: 3
    Trust this certificate? [no]: yes
    Certificate was added to keystore
    
    
  7. To see the server's private key entry and trusted certificate entry in the keystore, run the following command (please note trustedCertEntry and privateKeyEntry within the underlined text):

    keytool -list -v -keystore serverStore.jks -storepass serverPassword
    Keystore type: JKS
    Keystore provider: SUN
    Your keystore contains 2 entries
    Alias name: client
    Creation date: 26-Jul-2011
    Entry type: trustedCertEntry
    Owner: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
    Issuer: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
    Serial number: 4e2ebf1e
    Valid from: Tue Jul 26 17:20:30 GST 2011 until: Mon Jul 26 17:20:30 GST 2021
    Certificate fingerprints:
    MD5: FD:BE:98:72:F0:C8:50:D5:4B:10:B0:80:3F:D4:43:E8
    SHA1: 91:FB:9D:1B:69:E9:5F:0B:97:8C:E2:FE:49:0E:D8:CD:25:FB:D8:18
    Signature algorithm name: SHA1withRSA
    Version: 3
    *******************************************
    *******************************************
    Alias name: server
    Creation date: 26-Jul-2011
    Entry type: PrivateKeyEntry
    Certificate chain length: 1
    Certificate[1]:
    Owner: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
    Issuer: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
    Serial number: 4e2ebd0c
    Valid from: Tue Jul 26 17:11:40 GST 2011 until: Mon Jul 26 17:11:40 GST 2021
    Certificate fingerprints:
    MD5: 9E:DF:5E:18:F5:F6:52:4A:B6:9F:67:04:39:C9:57:66
    SHA1: C5:0B:8C:E6:B6:02:BD:38:56:CD:BB:50:CC:C6:BA:74:86:27:6C:C7
    Signature algorithm name: SHA1withRSA
    Version: 3
    *******************************************
    *******************************************
    
    
  8. To see the client's private key entry and trusted certificate entry in the keystore, run the following command:

keytool -list -v -keystore clientStore.jks -storepass clientPassword
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 2 entries
Alias name: client
Creation date: 26-Jul-2011
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
Issuer: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
Serial number: 4e2ebf1e
Valid from: Tue Jul 26 17:20:30 GST 2011 until: Mon Jul 26 17:20:30 GST 2021
Certificate fingerprints:
MD5: FD:BE:98:72:F0:C8:50:D5:4B:10:B0:80:3F:D4:43:E8
SHA1: 91:FB:9D:1B:69:E9:5F:0B:97:8C:E2:FE:49:0E:D8:CD:25:FB:D8:18
Signature algorithm name: SHA1withRSA
Version: 3
*******************************************
******************************************
Alias name: server
Creation date: 26-Jul-2011
Entry type: trustedCertEntry
Owner: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
Issuer: CN=MyFirstName MyLastName, OU=Software, O=MyCompany, L=MyCity, ST=MyProvince, C=ME
Serial number: 4e2ebd0c
Valid from: Tue Jul 26 17:11:40 GST 2011 until: Mon Jul 26 17:11:40 GST 2021
Certificate fingerprints:
MD5: 9E:DF:5E:18:F5:F6:52:4A:B6:9F:67:04:39:C9:57:66
SHA1: C5:0B:8C:E6:B6:02:BD:38:56:CD:BB:50:CC:C6:BA:74:86:27:6C:C7
Signature algorithm name: SHA1withRSA
Version: 3
*******************************************
*******************************************

How it works...

In the beginning, a symmetric key store is generated that can be shared by a client and a server for encryption and decryption. This command generates the symmetric key store:

keytool -genseckey -alias 'symmetric' -keyalg 'DESede' -keystore symmetricStore.jks -storepass 'symmetricPassword' -keypass 'keyPassword' -storetype "JCEKS"

To generate a keystore with a private key entry and a trusted certificate entry, first a key pair (private key and public certificate) keystore for both the client and server side should be generated.

Then the public key certificate should be exported from the client/server keystore. Finally, the client certificate should be imported into the server keystore and the server certificate should be imported into the client keystore (this imported certificate will be called trusted certificate).

keytool -genkey -alias aliasName -keyalg RSA -keystore keyStoreFileName.jks -validity 3653

The preceding command generates a keystore with a private key entry for which aliasName is an identifier of the keystore. Validity is the number of days that this key is valid.

keytool -export -file clientStore.cert -keystore clientStore.jks -storepass clientPassword -alias client

The preceding command exports the public key certificate that is embedded inside the private key entry in a keystore.

keytool -import -file clientStore.cert -keystore serverStore.jks -storepass serverPassword -alias client

The preceding command imports the generated public key certificate from the client keystore into the server keystore (this imported certificate will be called trusted certificate).

More information about cryptography and keystores can be found at the following URLs:

http://docs.oracle.com/javase/1.5.0/docs/api/java/security/KeyStore.html.

http://en.wikipedia.org/wiki/Category:Public-key_cryptography.

See also...

The recipes Securing SOAP messages using digital signature, Authenticating a Web-Service call using X509 certificate, and Encrypting/Decrypting of SOAP messages, discussed in this chapter.

Securing SOAP messages using digital signature

The purpose of digital signature is to verify whether a received message is altered to prove the sender is who he/she claims to be (authentication) and to prove the action from a specific sender. Digital signing of a message means adding hash data, that is, a piece of information (token) added to the SOAP envelop. The receiver needs to regenerate its own hash from the incoming message and compare it with the sender's one. If the receiver's hash matches the sender's one, the data integrity is achieved and the receiver will proceed; otherwise it returns a SOAP fault message to the sender.

In order to authenticate the sender, the sender should encrypt the signature token using his/her own private key. The receiver should have the sender's public-key certificate in the receiver keystore (the certificate is called a trusted certificate and comes under the trusted certificate entry) to decrypt the sender's signature token and repeat the already-explained step to check the message integrity. Now if the message integrity is achieved, the authentication of the sender is proved (since only the sender's certificate embedded in the receiver keystore could decrypt the encrypted signature of the sender). In addition, the action of sending the message by the sender also is proved (since successful decryption of the signature on the receiver's side shows that the sender has encrypted it by its own private key).

In this recipe, the sender (client) signs a message and uses its own private key (within the client keystore) for encryption of signature. On the receiver side (server), the client public key certificate in the server keystore (the certificate is called trusted certificate and comes under the trusted certificate entry within the keystore) will be used for decryption of the signature of the token; then the server verifies the signature token.

Getting ready

In this recipe, the project's name is LiveRestaurant_R-7.4 (for the server-side Web-Service) with the following Maven dependencies:

  • spring-ws-security-2.0.1.RELEASE.jar

  • spring-expression-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

LiveRestaurant_R-7.4-Client (for the client-side Web-Service) has the following Maven dependencies:

  • spring-ws-security-2.0.1.RELEASE.jar

  • spring-ws-test-2.0.0.RELEASE.jar

  • spring-expression-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

  • junit-4.7.jar

How to do it...

  1. Copy serverStore.jks to the server and clientStore.jks to the client (these keystores are already generated in the recipe Preparing pair and symmetric keystores discussed in this chapter.

  2. Configure the security policy file (securityPolicy.xml) on the server side to expect a signature token along with the incoming message on the client side to sign outgoing messages.

  3. Register keyStoreHAndler (KeyStoreCallbackHandler) and trustStore (KeyStoreFactoryBean) in the server-side application context file.

  4. Register keyStoreHAndler (KeyStoreCallbackHandler) and keyStore (KeyStoreFactoryBean) in the client-side application context file.

  5. Run the following command from Liverestaurant_R-7.4:

    mvn clean package tomcat:run
    
    
  6. Run the following command from Liverestaurant_R-7.4-Client:

mvn clean package

  • The following is the client-side output (note the tag ds:Signature) within the underlined text:

INFO: ==== Sending Message Start ====
....
<SOAP-ENV:Envelope.....>
<SOAP-ENV:Header>
<wsse:Security ....>
...
<ds:Signature ....>
<ds:SignedInfo>
.....
</ds:SignedInfo>
<ds:SignatureValue>....</ds:SignatureValue>
<ds:KeyInfo>
<wsse:SecurityTokenReference .........>
<wsse:Reference ..../>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body....>
<tns:placeOrderRequest ...>
......
</tns:placeOrderRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Sending Message End ====....
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<tns:placeOrderResponse ....>
<tns:refNumber>order-John_Smith_1234</tns:refNumber>
</tns:placeOrderResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Received Message End ====

How it works...

Security policy on the server side requires the client to include a binary signature token in the message. Settings in the client-side policy file include the signature token in the outgoing messages. A client uses its own private key included in the client-side keystore to encrypt the signature token of the message. On the server side, the client public key certificate, included in the server keystore (the certificate is called trusted certificate and comes under the trusted certificate entry within the keystore), will be used for decrypting the incoming signature token. Then the server proceeds towards the verification of the signature.

The following server-side security configuration in the policy files causes the server to expect a security token from the incoming message (for verification of incoming messages):

<xwss:RequireSignature requireTimestamp="false" />
</xwss:SecurityConfiguration>

On the client side, however, this security configuration in the policy files causes the client to include a security token inside the SOAP message in the outgoing message:

<xwss:Sign includeTimestamp="false">
</xwss:Sign>

The following setting in the client-side application context causes the client to use the private key inside clientStore.jks to encrypt the signature token of the message. The private key's password is cliPkPassword, the alias of the private key entry is client, and the keystore bean is generated by reading the keystore clientStore.jks with the keystore password clientPassword:

<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="password" value="clientPassword" />
<property name="location" value="/clientStore.jks" />
</bean>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore" />
<property name="privateKeyPassword" value="cliPkPassword" />
<property name="defaultAlias" value="client" />
</bean>

On the server side, the following setting in the server configuration file causes the server to first decrypt the signature token using a client certificate in the server keystore (the certificate is called a trusted certificate). It then verifies the signature of the incoming messages (to see whether the original message is altered):

<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="/WEB-INF/serverStore.jks" />
<property name="password" value="serverPassword" />
</bean>

See also...

The recipes Preparing pair and symmetric key stores and Authenticating a Web-Service call using X509 certificate, discussed in this chapter.

Authenticating a Web-Service call using X509 certificate

In the previous recipe, Securing SOAP messages using digital signature, by changing the sender (client) security policy file, the sender can include the client's certificate along with the outgoing messages. Then on the receiver side (server), before the verification of signatures, the server tries to authenticate the sender by comparing the client's certificate along with incoming message with client certificate embedded in the server keystore (trusted certificate). Additionally in this recipe, the client certificate is included in the sender's outgoing message and to extract data included in the certificate for authentication and authorization purposes, on the receiver side.

SpringCertificateValidationCallbackHandler, from the XWSS package, can extract the certificate data (such as CN=MyFirstName MyLastName) and this data could be for authentication as well as authorization.

In this recipe, we make use of the Securing SOAP messages using digital signature recipe for the signing and verification of signatures. Then SpringCertificateValidationCallbackHandler is used for authentication, using data fetching from the DAO layer as well as authorization for that Web-Service call.

Getting ready

In this recipe, the project's name is LiveRestaurant_R-7.5 (for the server-side Web-Service) and it has the following Maven dependencies:

  • spring-ws-security-2.0.1.RELEASE.jar

  • spring-expression-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

LiveRestaurant_R-7.5-Client (for the client-side Web-Service) has the following Maven dependencies:

  • spring-ws-security-2.0.1.RELEASE.jar

  • spring-ws-test-2.0.0.RELEASE.jar

  • spring-expression-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

  • junit-4.7.jar

How to do it...

In this recipe, all the steps are the same as in the previous recipe, Securing SOAP messages using a digital signature, except for modifying the client's policy file, as that changes to include the client certificate along with the outgoing message and the server-side application context file (spring-ws.servlet.xml) changes, and it uses the DAO layer to fetch data:

  1. Register springSecurityCertificateHandler in the server-side application context file (spring-ws-servlet.xml).

  2. Modify the client-side security policy file to include the client certificate along with the outgoing messages.

  3. Add the DAO layer classes to fetch data.

    The following is the client-side output (note the X509 client certification) within the underlined text:

INFO: ==== Sending Message Start ====
<?xml...>
<SOAP-ENV:Header>
<wsse:Security ...>
<wsse:BinarySecurityToken...wss-x509-token-..>.....</wsse:BinarySecurityToken>
<ds:Signature .....>
<ds:SignedInfo>
......
</ds:SignedInfo>
<ds:SignatureValue>.....</ds:SignatureValue>
<ds:KeyInfo>
<wsse:SecurityTokenReference...>
<wsse:Reference ...wss-x509-token-profile-1.0.../>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body ....>
<tns:placeOrderRequest ...>
.....
</tns:placeOrderRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Sending Message End ====
INFO: ==== Received Message Start ====
<?xml version="1.0" ....>
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<tns:placeOrderResponse xmlns:tns="http://www.packtpub.com/liverestaurant/OrderService/schema">
<tns:refNumber>order-John_Smith_1234</tns:refNumber>
</tns:placeOrderResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Received Message End ====

How it works...

Everything about signatures is the same as described in the recipe Securing SOAP messages using a digital signature. In addition, the client-side certificate is included in the outgoing messages and extracting a client's certificate data on the server side for some processing operations.

Once the client's certificate is extracted (that is, embedded within the incoming message), authentication can be done by retrieving the username or other information.

Including the following section in the client-side policy file causes the client to include its own public key certificate in the outgoing messages:

<xwss:X509Token certificateAlias="client" />

Embedding a client certificate in a caller message while signing the message causes the server to validate this certificate with the one included in the server keystore (sender trusted certificate entry) before signature validation. This validation confirms that the caller is the person he/she claims to be. However, if activation/locking of account needs to be checked or authorization of the caller to access specific resources is required, then springSecurityCertificateHandler, configured in the server configuration file, handles these tasks:

<bean class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="/WEB-INF/securityPolicy.xml"/>
<property name="secureResponse" value="false" />
<property name="callbackHandlers">
<list>
<ref bean="keyStoreHandler"/>
<ref bean="springSecurityCertificateHandler"/>
</list>
</property>
</bean>
</sws:interceptors>
<bean id="springSecurityCertificateHandler"
class="org.springframework.ws.soap.security.xwss.callback.SpringCertificateValidationCallbackHandler">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<bean class="org.springframework.ws.soap.security.x509.X509AuthenticationProvider">
<property name="x509AuthoritiesPopulator">
<bean class="org.springframework.ws.soap.security.x509.populator.DaoX509AuthoritiesPopulator">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="userDetailsService" class="com.packtpub.liverestaurant.service.dao.MyUserDetailService" />

This handler uses the authentication manager that calls DaoX509AuthoritiesPopulator, which applies the customized service class MyUserDetailService for authentication and extracts the user credentials for authorization purposes:

public class MyUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
return findUserDetailFromDAO(username);
}
private UserDetails findUserDetailFromDAO(String userName)throws UsernameNotFoundException{
MyUserDetail mydetail=null;
/**
*Real scenario: Find user-name from DAO layer, if user found, get data from the DAO and set MyUserDetail otherwise throw UsernameNotFoundException.
*/
if(! userName.equals("MyFirstName MyLastName")){
throw new UsernameNotFoundException("User name not found");
}
mydetail=new MyUserDetail(userName,"fetchedPassword",true,true,true,true,new GrantedAuthorityImpl("ROLE_GENERAL_OPERATOR"));
return mydetail;
}
}

See also...

The recipes Securing SOAP messages using a digital signature and Preparing pair and symmetric keystores, discussed in this chapter.

Encrypting/decrypting of SOAP messages

Encryption is the process of converting readable or plain text data format into an un-readable encrypted format or cipher text using specific algorithms. These algorithms, known as encryption algorithms, require an encryption key. Decryption is just the reverse operation of encryption; it converts back the cipher text into readable or plain text data format using a decryption key. The encryption and decryption keys could be the same or different. If encryption and decryption keys are the same and the sender and receiver share the key, then this key is known as symmetric or secret key. The encryption and decryption keys could be different, and in this case, the key is called asymmetric or public key.

The following diagram presents the usage of a symmetric key for encryption/decryption. The sender and receiver can share the same key, which is known as symmetric key. Those having this key can decrypt/encrypt messages. For example, a symmetric key is used for encryption by the sender and decryption by the receiver:

Encrypting/decrypting of SOAP messages

The following diagram presents the usage of the public/private key for encryption/decryption. Bob, as a sender, gets Alice's public key, encrypts a message, and sends it to Alice. Since only she is the holder of her own private key, she can decrypt the message:

Encrypting/decrypting of SOAP messages

In this recipe, the sender (client here) encrypts a message and sends it to a receiver (server here) in three different cases. In the first case, a symmetric key (which is in a store with the secret key entry that is the same for the client and server) is used for encryption on the client side and for decryption on the server side. Then, in the second case, the receiver's (server) public key certificate on the sender's (client) keystore (within the receiver trusted certificate entry) is used for data encryption and the receiver's (server) private key on the server-side keystore is used for decryption.

Since encryption of the whole payload in the annotation endpoint mappings (PayloadRootAnnotationMethodEndpointMapping) makes routing information (for example, localPart = "placeOrderRequest", namespace = "http://www.packtpub.com/liverestaurant/OrderService/schema", which is included in payload) encrypted along with whole payload, and the annotation endpoint mapping cannot be used. Instead, the SoapActionAnnotationMethodEndpointMapping addressing style is used for endpoint mapping. In this case, routing data is included in the SOAP header whereas it is included in payload in the annotation endpoint mapping. Although encryption of a part of the payload can work with the payload annotation endpoint mapping, however for consistency, SoapActionAnnotationMethodEndpointMapping addressing style is used for whole of the recipe.

For more information about endpoint mapping, refer to the recipes Setting up an endpoint by annotating the payload-root and Setting up a transport-neutral WS-Addressing endpoint, discussed in Chapter 1,Building SOAP Web-Services.

In the first two cases, the whole payload is used for encryption/decryption. The XWSS policy configuration file makes it possible to encrypt/decrypt the payload part. In the third case, only a part of the payload is set as the target for encryption/decryption.

Getting ready

In this recipe, the project's name is LiveRestaurant_R-7.6 (for the server-side Web-Service) and has the following Maven dependencies:

  • spring-ws-security-2.0.1.RELEASE.jar

  • spring-expression-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

  • mail-1.4.1.jar

  • saaj-api-1.3.jar

  • saaj-impl-1.3.2.jar

LiveRestaurant_R-7.6-Client (for the client-side Web-Service) has the following Maven dependencies:

  • spring-ws-security-2.0.1.RELEASE.jar

  • spring-ws-test-2.0.0.RELEASE.jar

  • spring-expression-3.0.5.RELEASE.jar

  • log4j-1.2.9.jar

  • junit-4.7.jar

How to do it...

The following steps implement encryption/decryption using a shared symmetric key (symmetricStore.jks):

  1. Register keyStoreHandler and symmetricStore in the server/client application context. Copy the symmetric keystore (symmetricStore.jks) to the server/client folder (this keystore is already generated in the recipe Preparing pair and symmetric keystores discussed in this chapter).

  2. Configure the security policy file (securityPolicy.xml) on the server side to expect encryption of messages from its client and on the client side to encrypt the outgoing messages.

  3. Run the following command from Liverestaurant_R-7.6:

    mvn clean package tomcat:run
    
    
  4. Run the following command from Liverestaurant_R-7.6-Client:

mvn clean package

  • The following is the client-side output (note the underlined part in the output):

INFO: ==== Received Message Start ====
....
<SOAP-ENV:Envelope ....>
<SOAP-ENV:Header>
<wsse:Security ....>
.......
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<xenc:EncryptedData.....">
<xenc:EncryptionMethod ....>
<ds:KeyInfo ...xmldsig#">
<ds:KeyName>symmetric</ds:KeyName>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>
3esI76ANNDEIZ5RWJt.....
</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Received Message End ====
Nov 7, 2011 11:48:46 PM com.sun.xml.wss.impl.filter.DumpFilter process
INFO: ==== Sending Message Start ====
<?xml version="1.0" ...
><SOAP-ENV:Envelope ...>
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<tns:placeOrderResponse xmlns:tns="http://www.packtpub.com/liverestaurant/OrderService/schema">
<tns:refNumber>order-John_Smith_1234</tns:refNumber>
</tns:placeOrderResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Sending Message End ====

The following steps implement encryption using a server-trusted certificate (or public key) on the client-side keystore (clientStore.jks) and decryption on the server private key on the server-side keystore (serverStore.jks):

  1. Modify securityPolicy.xml for encryption of messages using a server-trusted certificate on the client side (included in clientStore.jks) and decryption on the server side by the server private key (included in serverStore.jks).

  2. Register keyStoreHandler and keyStore on the server side and keyStoreHandler and trustStore on the client-side application context. Copy clientStore.jks to the client and serverStore.jks to the server folder (this keystore is already generated in the recipe Preparing pair and symmetric Keystores discussed in this chapter).

  3. Configure the security policy file (securityPolicy.xml) on the server side to expect encryption of messages from its client and on the client side to encrypt the outgoing messages.

  4. Run the following command from Liverestaurant_R-7.6:

    mvn clean package tomcat:run
    
    
  5. Run the following command from Liverestaurant_R-7.6-Client:

mvn clean package

  • The following is the client-side output (note the underlined part in the output):

INFO: ==== Sending Message Start ====
...
<SOAP-ENV:Envelope ....>
<SOAP-ENV:Header>
<wsse:Security ...>
........
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<xenc:EncryptedData....>
<xenc:EncryptionMethod .../>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<wsse:SecurityTokenReference...>
<wsse:Reference ..../>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
<xenc:CipherData>
...
</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Sending Message End ====
Nov 8, 2011 12:12:11 AM com.sun.xml.wss.impl.filter.DumpFilter process
INFO: ==== Received Message Start ====
<SOAP-ENV:Envelope ....>
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<tns:placeOrderResponse xmlns:tns="http://www.packtpub.com/liverestaurant/OrderService/schema">
<tns:refNumber>order-John_Smith_1234</tns:refNumber>
</tns:placeOrderResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Received Message End ====

The following steps implement encryption/decryption for a part of the payload:

  1. Modify securityPolicy.xml on client side/server side to set the target of the encryption.

  2. Run the following command from Liverestaurant_R-7.6:

    mvn clean package tomcat:run
    
    
  3. Run the following command from Liverestaurant_R-7.6-Client:

mvn clean package

  • The following is the client-side output (note underlined part in the output):

INFO: ==== Sending Message Start ====
...
<SOAP-ENV:Envelope ....>
<SOAP-ENV:Header>
<wsse:Security ....>
........
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<tns:placeOrderRequest ...>
<xenc:EncryptedData .....>
..........
<xenc:CipherData>
<xenc:CipherValue>NEeTuduV....
..........
</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</tns:placeOrderRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Sending Message End ====
Nov 8, 2011 12:18:39 AM com.sun.xml.wss.impl.filter.DumpFilter process
INFO: ==== Received Message Start ====
....
<SOAP-ENV:Envelope ...>
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<tns:placeOrderResponse ....>
<tns:refNumber>order-John_Smith_1234</tns:refNumber>
</tns:placeOrderResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
==== Received Message End ====

How it works...

In the first case, both the client and the server share the symmetric key. The client encrypts the whole payload using a symmetric key and sends it to the server. On the server side, the same key will be used to decrypt the payload. However, in the second and third cases, the server certificate embedded in the client store is used for encryption of the payload and the server-side private key of the server store will be used for decryption.

The RequireEncryption/Encrypt tag in the server/client policy files causes the client to encrypt a message and the server to decrypt it. The keyAlias is the alias name that is set at the time of symmetric keystore generation. The following sections in the client- and server-side policy files target the part of a message envelop that is to be encrypted/decrypted. qname: {http://schemas.xmlsoap.org/soap/envelope/}Body causes only the body part of a SOAP envelop to be used for encryption/decryption.

---server policy file
<xwss:RequireEncryption>
<xwss:SymmetricKey keyAlias="symmetric" />
<xwss:EncryptionTarget type="qname" value="{http://schemas.xmlsoap.org/soap/envelope/}Body" enforce="true"
contentOnly="true" />
</xwss:RequireEncryption>
---client policy file
<xwss:Encrypt>
<xwss:SymmetricKey keyAlias="symmetric" />
<xwss:Target type="qname">{http://schemas.xmlsoap.org/soap/envelope/}Body
</xwss:Target>
</xwss:Encrypt>

This part in the server and client configuration files causes a symmetric store to be used for cryptography. The callbackHandler (keyStoreHandlerBean) uses a symmetric keystore (symmetricStore bean) with the key password as keyPassword. The KeyStore bean will be generated by reading from a keystore location (symmetricStore.jks) with the keystore password as symmetricPassword and the type set to JCEKS (passwords and the type are set at the time of symmetric keystore generation).

<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="symmetricStore" ref="symmetricStore" />
<property name="symmetricKeyPassword" value="keyPassword" />
</bean>
<bean id="symmetricStore"
class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="password" value="symmetricPassword" />
<property name="location" value="/WEB-INF/symmetricStore.jks" />
<property name="type" value="JCEKS" />
</bean>

In the second case, almost all the settings are the same, except that the client is using the server public key for encrypting and the server is using the server store private key for decryption. The following section in the server-side configuration file causes the server to use a server private key in the server-side keystore for decryption. The private key password is serPkPasswords and the alias of the private key entry in the keystore is server. The KeyStore bean will be generated by reading from the keystore file (serverStore.jks) with the password serverPassword (passwords and the alias are set at the time of keystore generation).

---server configuration
<bean id="keyStoreHandler"
class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore" />
<property name="privateKeyPassword" value="serPkPassword" />
<property name="defaultAlias" value="server" />
</bean>
<bean id="keyStore"
class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="/WEB-INF/serverStore.jks" />
<property name="password" value="serverPassword" />
</bean>

This section in the client-side configuration file causes the client to use the server certificate (public key) in the client-side trust store for encryption. The KeyStore (trust store here) bean will be generated by reading from clientStore.jks with the password clientPAssword.

---client configuration
<bean id="keyStoreHandler"
class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="trustStore" ref="trustStore"/>
</bean>
<bean id="trustStore"
class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="/clientStore.jks" />
<property name="password" value="clientPassword" />
</bean>

In the policy file for the client and server side, the following line causes the server public key to be used for encrypting in the client and the private key in the server store to be used for decryption.

<xwss:X509Token certificateAlias="server"/>

In the third case, the following section in the policy files for the server and client causes only a part of the payload to be encrypted:

<xwss:Target type="qname">{http://www.packtpub.com/LiveRestaurant/placeOrderService/schema}OrderRequest</xwss:Target>

Note

When encrypting the whole of the payload, use WS-Addressing because routing information will be included in the header.

Note

Keystore, key management, frequent updates to keys, and certificates are separate areas and are not a part of this book. Choosing the best option needs more study, and this is part of architecture-related work.

See also...

The recipes Securing SOAP messages using a digital signature and Preparing pair and symmetric keystores, discussed in this chapter.

The recipe Creating Web-Service client for WS-Addressing endpoint, discussed in Chapter 2,Building Clients for SOAP Web Services.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset