Encrypting username token with apache cxf - cxf

Greetings good people.
I have a soap web service that I want to consume.I have created a small project to simulate what is required in the actual project especially on the username token encryption.
There are steps provided on how to encrypt the password on the client side as listed below:
Write the unencrypted password value.
Then, encrypt the block of data created in step 1 with the public portion of the password key certificate. Use the RSA algorithm, and use PKCS #1.5 padding (not OAEP), and add the result to the encrypted stream – this becomes the encrypted password which is submitted via the API.
Convert the resulting encrypted byte array into a string using base64 encoding. Present this base64 encoded string in the API request as the initiator SecurityCredential value.
Password to be encrypted with a public key from an X509 certificate issued to the Initiator specifically for this purpose.
So far I have been able to create a client and the server and I'm able to send a request and get a response.
I'm also able to secure the web service by passing a username token with password as plain text in the ClientPasswordCallback class and checking these credentails in the ServerPasswordCallback class.
I have gone further and in a seperate request encrypted the body part of the message using wss4j, RSA, X509 whereby I have public key stored in the clientKey.jks and a private key stored in privateKey.jks and by providing appropriate passwords in client and server password call back handlers I have been able to encrypt the body part at the client and decrypt it at the server.
The Challenge: The main challenge I'm experiencing is combining the two steps above in a single request such that using the public key, I'm able to encrypt the password in the username token and decrypt the same at the server side using the private key.
NB I have generated the keys for testing using keygen tool that comes with the jdk.
I'm imagining that there will be two passwords in the ClientPasswordCallback class, one for the clientKey.jks keystore and the other other password that needs to be encrypted.
This is what I have been able to archive so far:
Client side
TestMathUtility class
public static void main(String[] args) {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
// Use the URL defined in the soap address portion of the WSDL
factory.setAddress("http://localhost:8080/MathUtility/services/MathUtilityPort");
// Utilize the class which was auto-generated by Apache CXF wsdl2java
factory.setServiceClass(MathUtility.class);
Object client = factory.create();
// Adding Logging Interceptors
LoggingOutInterceptor loggingOutInterceptor = new LoggingOutInterceptor();
loggingOutInterceptor.setPrettyLogging(true);
ClientProxy.getClient(client).getOutInterceptors().add(loggingOutInterceptor);
LoggingInInterceptor loggingInInterceptor = new LoggingInInterceptor();
loggingInInterceptor.setPrettyLogging(true);
ClientProxy.getClient(client).getInInterceptors().add(loggingInInterceptor);
// Set up WS-Security Encryption
// Reference: https://ws.apache.org/wss4j/using.html
Map<String, Object> props = new HashMap<String, Object>();
props.put(WSHandlerConstants.USER, "testkey");
props.put(WSHandlerConstants.ACTION, WSHandlerConstants.ENCRYPT);
props.put(WSHandlerConstants.PASSWORD_TYPE, "PasswordText");
props.put(WSHandlerConstants.ENC_PROP_FILE, "clientKeystore.properties");
props.put(WSHandlerConstants.ENCRYPTION_PARTS, "{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body");
props.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordCallback.class.getName());
WSS4JOutInterceptor wss4jOut = new WSS4JOutInterceptor(props);
ClientProxy.getClient(client).getOutInterceptors().add(wss4jOut);
try {
// Call the Web Service to perform an operation
int response = ((MathUtility)client).addIntegers(5, 10);
System.out.println("Response we've got ========= "+response);
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
ClientPasswordCallback class
public class ClientPasswordCallback implements CallbackHandler {
#Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
// set the password for our message.
pc.setPassword("clientstorepass");
}
}
Server side
MathUtility class
#WebService(targetNamespace = "http://utility.math.com/", portName = "MathUtilityPort", serviceName = "MathUtilityService")
public class MathUtility {
public int addIntegers(int firstNum, int secondNum) {
return firstNum + secondNum;
}
public int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; i++) {
result = result * i;
}
return result;
}
}
ServerPasswordCallback class
public class ServerPasswordCallback implements CallbackHandler {
#Override
public void handle(Callback[] arg0) throws IOException,
UnsupportedCallbackException {
WSPasswordCallback pc = (WSPasswordCallback) arg0[0];
// set the password for our message.
pc.setPassword("storepass");
}
}
cxf-beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<bean id="myPasswordCallback" class="com.math.utility.security.ServerPasswordCallback"/>
<jaxws:endpoint xmlns:tns="http://utility.math.com/" id="mathutility"
implementor="com.math.utility.MathUtility" wsdlLocation="wsdl/mathutility.wsdl"
endpointName="tns:MathUtilityPort" serviceName="tns:MathUtilityService"
address="/MathUtilityPort">
<jaxws:features>
<bean class="org.apache.cxf.feature.LoggingFeature" />
</jaxws:features>
<jaxws:inInterceptors>
<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="user" value="testkey"/>
<entry key="action" value="Encrypt"/>
<entry key="passwordType" value="PasswordText"/>
<entry key="decryptionParts" value="{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body"/>
<entry key="decryptionPropFile" value="serverKeystore.properties"/>
<entry key="passwordCallbackRef">
<ref bean="myPasswordCallback"/>
</entry>
</map>
</constructor-arg>
</bean>
</jaxws:inInterceptors>
</jaxws:endpoint>
clientKeyStore.properties file the same structure is used on the server side
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.file=clientkeystore.jks
org.apache.ws.security.crypto.merlin.keystore.password=clientstorepass
org.apache.ws.security.crypto.merlin.keystore.type=jks
The .jks files used have not been provided
NB I'm not using spring.

If you want to have a custom digest you could override the method verifyCustomPassword(UsernameToken usernameToken,
RequestData data) in UsernameTokenValidator
To hook it up to your webservice have a look at my answer to another SO-question. The essentials of this answers are:
<property name="wssConfig">
<ref bean="usernameTokenWssConfig"/>
</property>
And add the referenced class to your codebase:
#Component("usernameTokenWssConfig")
public class UsernameTokenWssConfig extends WSSConfig {
public UsernameTokenWssConfig() {
setValidator(WSSecurityEngine.USERNAME_TOKEN, new CustomUsernameTokenValidator());
setRequiredPasswordType(WSConstants.CUSTOM_TOKEN );
}
}

This is how I did the encryption Of UT using encryption parts
outProps.put(
WSHandlerConstants.ENCRYPTION_PARTS,
"{Content}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}UsernameToken"
);
In order to encrypt the username token with the encryption parts . Add the SOAP header namespace inside the second curly braces
I got an encrypted UT like below(I only encrypted the content only you can do the element if you want)
<wsse:UsernameToken wsu:Id="UsernameToken-99bea96d-c6ef-444c-aa8a-ec807f58aa0c">
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="ED-59f84d2b-3195-436f-b8f4-513fea23c00a" Type="http://www.w3.org/2001/04/xmlenc#Content">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc" />
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey">
<wsse:Reference URI="#EK-748c3d27-f6be-4b81-b864-87bc6457e247" />
</wsse:SecurityTokenReference>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>wSZsu9LR6q9fpUPYYF5GSA7T/3iZWMd0cB/80Z33DThzCB0kqnupGETVmGfVQheGUc3O+/B4X7i70aMTyOo5u0fIqa4kwrlKZBe9he359mpgakKgC4wOb65sDThT1fH4PvY6TSBjIOJ0T5jIyt1pGwacRLzmvFxxHxr3qfAOf27LLGJ0P0eAKchE19nAkfP+Tc2GbAkcxi/4SDQ7bBWVaveRgSET0dpheooBGORtt4VJ/dyMwogupAyJKoiqe3RFRCvsmK/UtkVGQYh/W14ei/s7G3mVAch8fQZXCS8jcEaqzkDaNzrZo8+IjJFgrPQY23g3fp57QXIDB84NNUhsm7NHXMNfAq7x97kng+Qwke6uqHcMPjGI9boKw/wZmhipYstFzUpOpF86W9FwcJPyTFR58jvdnX5OGJ1wFbFdI9cAjWdncIEmnOTl69pKRmGmbJYj7Ie43q+eNH/1+2RawBRhZG43VLZL5C7ydFu0xJ2DsD4nacvDfH0i8tcMCHyHkWf2po9Y/dBtS2kWAxfNxWQNvI1BceumsMvpSzK7WjXPJ/vaKlMoSQJtsBxg9RhA
</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</wsse:UsernameToken>

Related

No consumers available on endpoint: Endpoint[direct://LookUpRoute]

I'm new to Apache Camel. I'm trying to send an exchange from a java method to a route but it gives me "Caused by: org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint" error. I want to understand what exactly this error is and when do we get this?
#EndpointInject(uri = "direct:reportRoute")
private ProducerTemplate templatereportRoute;
public void saveDataFromExchange(Map<String, Object> DataMap){
List<Map<String, Object>> paramList = new ArrayList<Map<String, Object>>();
List<Map<String, Object>> rows = templatereportRoute.requestBody("direct:reportReport", DataMap, List.class);
<from uri="direct:reportRoute"/>
<log message=" - ${body}" loggingLevel="INFO"/>
<setProperty propertyName="DataMap">
<simple>${body}</simple>
</setProperty>
The error you encounter means that you are sending to a direct endpoint that does not exist in the Camel Context.
Since you posted an XML fragment that defines the route in question there are two possible problems (as already commented by #claus-ibsen):
The XML you posted is not in use. You are starting a Camel Context but it does not use your XML code. Are you using Spring? Then you can define your Camel routes in Spring XML.
Your setup is fine but your Java code sends the message too early, i.e. before the direct endpoint is up and running. You can put this code in a Test class and run it after the Camel context is started and ready.
Try put in public class from routerBuilder implemention the anotation #Component from Spring context
Ex:
#Component //<<<<---- This
public class RouterClass extends RouteBuilder {
#Override
public void configure() throws Exception {
}
}//class closure

Camel JAXB marshalling return XML object type?

I am sending a Java object to producer endpoint and waiting for the marshalled XML object. I tried changing it to Node object/ File object but it is giving ClassCastException.
So took the xmlObj in an object class type. What should be the correct class to capture the response?
public class ClientEight {
#Produce(uri = "direct:invoice")
ProducerTemplate template;
public static void main(String args[]) throws InterruptedException {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("resources/camel-configTen.xml");
InvoiceXml invoice = new InvoiceXml("fdf3443", 3454, 435345.44 f, "hfhfddfdg"); //any java object we are passing
ClientEight client = (ClientEight) ctx.getBean("client");
Object xmlObj = client.template.requestBody(invoice);
System.out.println(xmlObj);
}
}
Above is a client code which you are using to send the Java object to a producer endpoint and since you are using template.requestBody, you are getting back the object returned.
<camel:camelContext>
<camel:dataFormats>
<!-- path to jaxb annotated class -->
<camel:jaxb id="invoiceJaxb" contextPath="com.java.bean"
prettyPrint="true" />
</camel:dataFormats>
<camel:route>
<camel:from uri="direct:invoice" />
<camel:marshal ref="invoiceJaxb" />
<camel:log message=" ${body}" />
<camel:to uri="file://src/resources?fileName=One.xml"/>
</camel:route>
</camel:camelContext>
the unmarshall processor return a stream, not a single object. In camel, more generally, if you want a specific type, you shouldn't get directly a body as an object, but use the various methods to convert the body into that type.
Try :
Document document = client.template.requestBody(invoice, org.w3c.dom.Document.class);

sending a response to client from camel cxf component

I am new to camel, i am trying to use camel cxf component to create a soap webservice. I started with a samples from camel in action. I have configured a route using cxf component and added a processor to process the request. I received the request in the bean i used to process the service but i cant able to send the response back to the client. Thanks in advance
This is the route i used :
<route>
<from uri="cxf:bean:orderEndpoint" />
<setExchangePattern pattern="InOut"/>
<to uri="bean:productService" />
</route>
This is the cxf endpoint i have configured,
<cxf:cxfEndpoint id="orderEndpoint"
address="/"
serviceClass="camelws.ws.ProductService"/>
This is the bean i used:
#Service("productService")
public class ProductServiceImpl {
public Product getProducts(){
System.out.println("Inside webservices method....");
Product product = new Product();
product.setName("test product");
product.setPrice("3242");
return product;
}
}
Sysout statement is printed on the console but i am getting a soap response with empty body.
below is my response when i hit http://localhost:9080// from browser:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body/>
</soap:Envelope>
You should implement a Processor, intercept and process your message using something like that :
public class MyProcessor implements Processor {
public void process(Exchange exchange) throws Exception {
...
// Set your response here
exchange.getOut().setBody(product);
}
}
Then reference your processor in your route.
Your response is what you have in your route body after your rote ends, so you must create your massege response object before route ends.

Apache CXFRS and CAMEL configuration

i want to consume the REST result by hitting a REST web Service available at http://someotherhost site. i have written a proxy client for it
I want to hit the above REST service using apache CXFRS client and the write the result to a file. for which i am doing the following,could any one review the below and comment the things i have done wrong .
a) My camel context configuration with apache cxf is as below
<jaxrs:client address="http://someotherhost/test/" id="cityServiceClient" username="test"
password="pwd"
serviceClass="com.santosh.proxy.service.city.CityService">
<jaxrs:features>
<ref bean="loggingFeature" />
</jaxrs:features>
</jaxrs:client>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<package>com.santosh.routes</package>
<routeBuilder ref="cityserviceroutebuilder" />
</camelContext>
b) MY Proxy service Interface
#Path(value="/getCities")
public interface CityService {
#POST
#Produces(value="text/xml")
public String getCities(#QueryParam("countrycode") String countryCode);
}
c) Call to service
CityService cityService = (CityService) context.getBean("cityServiceClient");
cityService.getCities("ae");
d) Camel Routes
public class CityRoutes extends RouteBuilder {
public void configure() throws Exception {
//ROUTES
from("cxfbean:cityServiceClient")
.to("file://data/xmls/cities?fileName=test.xml");
}
}
I got the solution, basically my camel-context configuration was not up to that mark,
The below configuration solved my problem.
<! -- 4 THE ACTUAL SERVER WHICH WILL GET HIT -->
<jaxrs:server id="restService" depends-on="camelContext"
address="http://REALSERVER.COM/REST/" createdFromAPI="true"
staticSubresourceResolution="true">
<jaxrs:serviceBeans>
<ref bean="servicecity" />
</jaxrs:serviceBeans>
</jaxrs:server>
<bean name="servicecity" id="servicecity" class="com.santosh.CityServiceImpl" />
<! -- 3 YOUR PROXY CLIENT -->
<cxf:rsClient id="rsClient" address="http://REALSERVER.COM/REST/"
serviceClass="com.santosh.CityServiceImpl"
username="santosh" password="pwd" />
<! -- 1 JAXRS PROXY CLIENT -->
<jaxrs:client id="cityServiceClient" address="http://localhost:8123/REST/"
serviceClass="com.santosh.CityService" username="santosh" password="pwd">
</jaxrs:client>
<! -- 2 YOUR LOCAL SERVER THAT YOU NEED TO HIT, YOUR LOCAL SERVER -->
<cxf:rsServer id="rsServer" address="http://localhost:8123/REST/" serviceClass="com.santosh.CityServiceImpl" />
THE STEPS ARE
1) create JAXRS PROXY CLIENT and get it in your have code
CityService cityService = (CityService) context.getBean("cityServiceClient");
cityService.getCities("INDIA");
2) the above code will call the SERVER (LOCAL)
3) the above step will call YOUR PROXY CLIENT
4) the PROXY CLIENT will call the ACTUAL REAL SERVER

Configuring WSS4J with CXF

I was doing pretty well with setting up a contract first set of web services using CXF until I started adding in the WSS4J piece.
I'm trying to debug sending a password and login in the soap header. I am getting null when I call getPassword() in the WSPasswordCallback class. I can see from the soap envelope that a password was sent.
This post, http://old.nabble.com/PasswordDigest-and-PasswordText-difference-td24475866.html, from 2009, made me wonder if I am missing (need to create) a UsernameTokenHandler.
And if that is true, can someone point me to how I would configure it in the spring/cxf bean xml file?
Any advice or suggestions would be appreciated.
Here's the Java file in question:
package com.netcentric.security.handlers;
import java.io.IOException;
import javax.annotation.Resource;
import javax.com.urity.auth.callback.Callback;
import javax.com.urity.auth.callback.CallbackHandler;
import javax.com.urity.auth.callback.UnsupportedCallbackException;
import org.apache.ws.com.urity.WSPasswordCallback;
public class ServicePWCallback implements CallbackHandler
{
#Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
try {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof WSPasswordCallback) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
sString login = pc.getIdentifier();
String password = pc.getPassword();
// password is null, not the expected myPASSWORD**1234
int n = pc.getUsage();
// this is 2 == WSPasswordCallback.USERNAME_TOKEN
//...
The CXF/Spring configuration file:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"
default-dependency-check="none" default-lazy-init="false">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<bean id="serverPasswordCallback" class="com.netcentric.security.handlers.ServicePWCallback"/>
<bean id="wss4jInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
<constructor-arg>
<map>
<entry key="action" value="UsernameToken"/>
<entry key="passwordType" value="PasswordText"/>
<entry key="passwordCallbackRef">
<ref bean="serverPasswordCallback"/>
</entry>
</map>
</constructor-arg>
</bean>
<jaxws:endpoint id="FederationImpl"
implementor="com.netcentric.services.federation.FederationImpl"
endpointName="e:federation"
serviceName="e:federation"
address="federation"
xmlns:e="urn:federation.services.netcentric.sec">
<jaxws:inInterceptors>
<bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/>
<ref bean="wss4jInInterceptor"/>
</jaxws:inInterceptors>
</jaxws:endpoint>
</beans
The soap message:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<wsse:comurity xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wscomurity-comext-1.0.xsd" soapenv:mustUnderstand="1">
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wscomurity-utility-1.0.xsd" wsu:Id="Timestamp-16757598">
<wsu:Created>2011-09-22T18:21:23.345Z</wsu:Created>
<wsu:Expires>2011-09-22T18:26:23.345Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wscomurity-utility-1.0.xsd" wsu:Id="UsernameToken-16649441">
<wsse:Username>pam</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">myPASSWORD**1234</wsse:Password>
</wsse:UsernameToken>
</wsse:comurity>
</soapenv:Header>
<soapenv:Body>
<getVersion xmlns="urn:federation.services.netcentric.com">
<getVersionRequest/>
</getVersion>
</soapenv:Body>
</soapenv:Envelope>
If using CXF 2.4.x, I would recommend reading:
http://coheigea.blogspot.com/2011/02/usernametoken-processing-changes-in.html
and seeing if that helps provide some extra information. Colm's blog is a treasure trove of useful info about recent WSS4J releases.
Thanks for all your help. I have made progress on this. I am using CXF 2.4.2 and WSS4J 1.6.2. The framework now takes care of checking the password for you. So the correct inside section is
if (callbacks[i] instanceof WSPasswordCallback) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
sString login = pc.getIdentifier();
String password = getPassword(login);
pc.getPassword(login);
//...
}
Instead of retrieving the password from the soap header to compare against the expected value, you lookup the expected value and pass it to the framework to do the comparison.
I add this :
// set the password on the callback.
This will be compared to the
// password which was sent from the client.
pc.setPassword("password");
==> the password between "" will be compared with password sended by client.
Client side: write login = bob ; Password = bobPassword (will be digested)
Server side: Catch user = bob and the function user.setPassword(bobPassword) verify if the password received is correct or no.

Resources