ghazouli:
Your code makes sense, now that I finally got around to looking at it. I had not realized the KeyStoreCallbackHandler could be passed to XWSSProcessorFactory.createProcessorForSecurityCon figuration() as the CallbackHandler parameter, but now I see it does implement that interface.
My securityConfig.xml is based on an example file "signv2.xml" from the XWS-Security samples.
I'm still having problems, though. Here's my relevant code, the exception and the description of the problem.
Code:
import LoginRequestDocument;
import LoginRequestType;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.xml.soap.SOAPMessage;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.springframework.core.io.Resource;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.springframework.ws.soap.security.xwss.XwsSecuritySecurementException;
import org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.StringSource;
import com.sun.xml.wss.ProcessingContext;
import com.sun.xml.wss.XWSSProcessor;
import com.sun.xml.wss.XWSSProcessorFactory;
import com.sun.xml.wss.XWSSecurityException;
public class TestClient {
private WebServiceTemplate webServiceTemplate;
private XmlOptions xmlOptions; // Injected by application context.
private XWSSProcessor cprocessor;
private ProcessingContext context;
public TestClient(Resource xwssConfig, KeyStoreCallbackHandler keystoreHandler) throws XWSSecurityException, IOException {
if (!xwssConfig.exists()) {
throw new FileNotFoundException();
}
XWSSProcessorFactory factory = XWSSProcessorFactory.newInstance();
cprocessor = factory.createProcessorForSecurityConfiguration(xwssConfig.getInputStream(), keystoreHandler);
context = new ProcessingContext();
}
public void login() {
// XMLBeans objects.
LoginRequestDocument loginDoc = LoginRequestDocument.Factory.newInstance();
LoginRequestType loginRequest = loginDoc.addNewLoginRequest();
sendAndReceive("http://localhost:8080/EchoServlet/", loginDoc, true);
}
private void sendAndReceive(
final String requestUri,
final XmlObject bodyDocument,
final boolean doSign) {
// Create the SOAP response message stream.
StringResult result = new StringResult();
// Create the SOAP body source; i.e. the service request.
StringSource source = new StringSource(bodyDocument.xmlText(xmlOptions));
webServiceTemplate.sendSourceAndReceiveToResult(requestUri, source,
new WebServiceMessageCallback() {
public void doWithMessage(WebServiceMessage message)
throws IOException {
SaajSoapMessage saajSoapMessage = (SaajSoapMessage) message;
SOAPMessage saajMessage = saajSoapMessage.getSaajMessage();
try {
context.setSOAPMessage(saajMessage);
SOAPMessage securedMessage = cprocessor.secureOutboundMessage(context);
saajSoapMessage.setSaajMessage(securedMessage);
} catch (XWSSecurityException e) {
throw new XwsSecuritySecurementException(e.getMessage());
}
}
},
result
);
System.out.println(result.getWriter().toString());
}
public void setWebServiceTemplate(WebServiceTemplate webServiceTemplate) {
this.webServiceTemplate = webServiceTemplate;
}
public XmlOptions getXmlOptions() {
return xmlOptions;
}
public void setXmlOptions(XmlOptions xmlOptions) {
this.xmlOptions = xmlOptions;
}
}
and my app context:
Code:
<?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:lang="http://www.springframework.org/schema/lang"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<bean id="webServiceClient"
class="TestClient">
<constructor-arg>
<bean class="org.springframework.core.io.ClassPathResource">
<constructor-arg value="securityPolicy.xml" />
</bean>
</constructor-arg>
<constructor-arg ref="keyStoreHandler" />
<property name="webServiceTemplate" ref="webServiceTemplate" />
<property name="xmlOptions" ref="xmlOptions" />
</bean>
<!-- Configuration options for XMLBeans unmarshalling. -->
<bean id="xmlOptions"
class="org.springframework.oxm.xmlbeans.XmlOptionsFactoryBean">
<property name="options">
<map>
<entry key="SAVE_SUGGESTED_PREFIXES">
<map>
<entry key="http://www.w3.org/2001/XMLSchema"
value="xs" />
<entry key="http://www.w3.org/2000/09/xmldsig#"
value="ds" />
<entry
key="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
value="wsse" />
</map>
</entry>
</map>
</property>
</bean>
<bean id="webServiceTemplate"
class="org.springframework.ws.client.core.WebServiceTemplate">
<property name="messageFactory" ref="soapMessageFactory" />
<property name="defaultUri"
value="http://localhost:8080/EchoServlet/" />
<property name="messageSender" ref="commonsMessageSender" />
</bean>
<!-- Handles the underlying HTTP transport. -->
<bean id="commonsMessageSender"
class="org.springframework.ws.transport.http.CommonsHttpMessageSender">
</bean>
<!-- Used to read and write SOAP messages. -->
<bean id="soapMessageFactory"
class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">
<!-- <property name="payloadCaching" value="true" /> -->
</bean>
<bean id="keyStoreHandler"
class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore" />
<property name="defaultAlias" value="bobo" />
<property name="privateKeyPassword" value="password" />
</bean>
<bean id="keyStore"
class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="classpath:mystore.jks" />
<property name="password" value="password" />
</bean>
</beans>
I generated my keystore like this:
Code:
keytool -genkey -alias bobo -keypass password -keystore .\mystore.jks
So I'm just trying to test with a self-signed certificate at the moment. I'll try to use a real one later.
When I run an application to load a TestClient from the app context and call login(), I get an exception saying it can't find the X509 certificate. I'm so new to all this, I don't understand what's going on.
Code:
Feb 7, 2008 10:41:07 AM com.sun.xml.wss.impl.filter.SignatureFilter process
SEVERE: WSS1417: Error while processing signature No X509Certificate was provided
Exception in thread "main" org.springframework.ws.soap.security.xwss.XwsSecuritySecurementException: com.sun.xml.wss.XWSSecurityException: com.sun.xml.wss.XWSSecurityException: No X509Certificate was provided
at TestClient$1.doWithMessage(TestClient.java:102)
at org.springframework.ws.client.core.WebServiceTemplate$4.doWithMessage(WebServiceTemplate.java:363)
at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:404)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:359)
at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceiveToResult(WebServiceTemplate.java:305)
at TestClient.sendAndReceive(TestClient.java:89)
at TestClient.login(TestClient.java:67)
at Application2.main(Application2.java:12)
If it couldn't find the keystore, I figure I'd get a FileNotFoundException. This sounds like it can't find the cert in the keystore, or it's not triggering the KeyStoreCallbackHandler at all.
Edit: I just wrapped the KeyStoreCallbackHandler in my own simple CallbackHandler implementation with some console logging added in the handle() method. KeyStoreCallbackHandler.handle() is being called.