I am using JBoss AS 6.1 with CXF 2.3.1 bundled and I believe the version of WSS4J included in that is 1.5.8. My version of the JDK is 1.6.
I have been struggling for several days now to get a simple test going using SOAPUi 4.5.1 as the client. I have reviewed the WS-Security spec, looked at numerous tutorials, including the documentation on the CXF website and Mr. Mazza's blog describing the process. I cannot be sure whether the problem is SOAPUi or my service. In any case, I am sort of constrained to use this old version of JBoss AS along with it's old versions of CXF and WSS4j, so many of the features described on the CXF tutorial are not applicable, I believe, for example the DefaultCryptoCoverageChecker. I think I would prefer to use the interceptors rather than the WS-Policy as I am not using the WSDL first method. I have not tried the WS-Policy annotations. I have a server.keystore which contains the public/private key pair for the server. Into this I have also imported the client certs for two test clients, client1 and client2. So, I have the inverse, a client1.keystore with client1 public/private keys and the server public key certificate is imported into this file. Likewise for client2. The aliases for these are server, client1, and client2, respectively. The passwords for all the keystores and aliases is currently just "password". First, here is an excerpt of my service implementation: @WebService(serviceName = "My_Web_Service", name = "MyWebService", targetNamespace = "http://mycompany.com") @SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED) //@EndpointConfig(configFile = "WEB-INF/jaxws-endpoint-config.xml", configName = "Standard WSSecurity Endpoint") public class MyWebService { private static Log log = LogFactory.getLog(MyWebService.class); /** * Returns the list of Area of Operation codes. * * @return List<AreaOfOperationsBean> */ @WebMethod() @WebResult(name = "payLoad") public PayLoad getAreaOfOperationsData() { log.debug("Req method: getCipAreaOfOperationsData!"); [... setup the payload object deleted] return p; } // getAreaOfOperationsData() [...] } Configuration of the WSS4J interceptors is done using the jbossws-cxf.xml file which is in the WEB-INF directory of my service WAR: <beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:beans='http://www.springframework.org/schema/beans' xmlns:jaxws='http://cxf.apache.org/jaxws' xsi:schemaLocation='http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd'> <bean id="Sign_Request" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry key="action" value="Timestamp Signature"/> <entry key="signaturePropFile" value="security.properties"/> <entry key="decryptionPropFile" value="security.properties"/> <entry key="passwordCallbackClass" value="com.mycompany.ws.common.KeystorePasswordCallback"/> </map> </constructor-arg> </bean> <bean id="Sign_Response" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <entry key="action" value="Timestamp Signature Encrypt"/> <entry key="user" value="server "/> <entry key="signaturePropFile" value="security.properties"/> <entry key="encryptionPropFile" value="security.properties"/> <!-- <entry key="encryptionUser" value="useReqSigCert"/> --> <entry key="encryptionUser" value="client1 " /> <entry key="signatureKeyIdentifier" value="DirectReference"/> <entry key="encryptionKeyIdentifier" value="DirectReference" /> <entry key="passwordCallbackClass" value="com.mycompany.ws.common.KeystorePasswordCallback"/> <entry key="signatureParts" value="{Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss -wssecurity-utility-1.0.xsd}Timestamp;{Element}{http://schemas.xmlsoap.o rg/soap/envelope/}Body"/> <entry key="encryptionParts" value="{Element}{http://www.w3.org/2000/09/xmldsig#}Signature;{Content}{ http://schemas.xmlsoap.org/soap/envelope/}Body"/> <entry key="encryptionKeyTransportAlgorithm" value="http://www.w3.org/2001/04/xmlenc#rsa-1_5"/> <entry key="encryptionSymAlgorithm" value="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/> </map> </constructor-arg> </bean> <jaxws:endpoint id='CipService' address='http://@jboss.bind.address@:8080/ws-cip' implementor='com.mycompany.ws.MyService'> <jaxws:invoker> <bean class='org.jboss.wsf.stack.cxf.InvokerJSE'/> </jaxws:invoker> <jaxws:outInterceptors> <bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor"/> <ref bean="Sign_Response"/> </jaxws:outInterceptors> <jaxws:inInterceptors> <ref bean="Sign_Request"/> <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/> </jaxws:inInterceptors> </jaxws:endpoint> </beans> You'll note that I have encryptionUser = useReqSigCert commented out at the moment because I haven't been able to get it working, so I'm trying to just hardcode the client alias, but alas, that isn't working either as you will see. My password callback handler is simple: package com.mycompany.ws.common; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.log4j.Logger; import org.apache.ws.security.WSPasswordCallback; public class KeystorePasswordCallback implements CallbackHandler { private static Logger log = Logger .getLogger(KeystorePasswordCallback.class); private Map<String, String> passwords = new HashMap<String, String>(); public KeystorePasswordCallback() { log.info("Creating ws security callback handler..."); passwords.put("server ", "password"); passwords.put("client1 ", "password"); passwords.put("client2 ", "password"); } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { log.info("KeystorePasswordCallback invoked handle"); for (int i = 0; i < callbacks.length; i++) { WSPasswordCallback pc = (WSPasswordCallback) callbacks[i]; String id = pc.getIdentifier(); log.info("Looking up password for identifier: " + id); String pass = passwords.get(pc.getIdentifier()); if (pass != null) { pc.setPassword(pass); return; } } } public void setAliasPassword(String alias, String password) { passwords.put(alias, password); } } My security.properties file is located in the WEB-INF directory (since my service is a POJO): org.apache.ws.security.crypto.provider=org.apache.ws.security.components .crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=password org.apache.ws.security.crypto.merlin.keystore.alias=server org.apache.ws.security.crypto.merlin.file=server.keystore Since I have the client certs with their public keys in there, I don't think I need a separate truststore. OK, so that's all of the server side. My goal is to test this using soapUi version 4.5.1. This version of soapUi is using an embedded Java JRE 1.7 if that makes a difference. I hope someone is familiar with it, but basically, under my project configuration I have defined the two client keystores, client1.keystore and client2.keystore and entered their password. My "incoming" WS-Security configuration is simple; there isn't much to configure there: I have specified the decrypt and signature keystores as client1.keystore and included the password. For the "outgoing" WS-Security configuration, I currently have a default Username/alias of "client1", the default password of "password", no actor specified, and "Must Understand" is not checked. For the actions: "Timestamp" with a TTL of 1800000 and millisecond precision. "Signature" with Keystore = client1.keystore Alias = client1 Password = password Key Identifier Type: [options are "Issurer Name and Serial Number", "Binary Security Token", "X509 Certificate", and "Subject Key Identifier"]. I have tried all of them and my current setting is "Binary Security Token". (In the WS-Security spec it said an X.509 cert was an example of such) Signature Algorithm is: http://www.w3.org/2000/09/xmldsig#rsa-sha1 Signature Canonicalization is: http://www.w3.org/2001/10/xml-exc-c14n# Digest Algorithm is: http://www.w3.org/2000/09/xmldsig#sha1 Use Single Certificate: true I currently do not have any Parts specified. I think this may be incorrect, or at least not recommended. OK, so when I create a request in SoapUi and attach the "outgoing" ws-security stuff and send it to the server, I see this in the console: 17:44:10,348 INFO [com.mycompany.ws.common.KeystorePasswordCallback] Creating ws security callback handler... 17:44:10,348 INFO [com. mycompany.ws.common.KeystorePasswordCallback] KeystorePasswordCallback invoked handle 17:44:10,348 INFO [com. mycompany.ws.common.KeystorePasswordCallback] Looking up password for identifier: server And, then, in the soapUi error log, I see this stack trace: Wed Apr 17 17:44:10 CDT 2013:ERROR:org.apache.ws.security.WSSecurityException: The signature or decryption was invalid org.apache.ws.security.WSSecurityException: The signature or decryption was invalid at org.apache.ws.security.processor.EncryptedKeyProcessor.handleToken(Encry ptedKeyProcessor.java:106) at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurity Engine.java:396) at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurity Engine.java:304) at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurity Engine.java:249) at com.eviware.soapui.impl.wsdl.support.wss.IncomingWss.processIncoming(Inc omingWss.java:121) at com.eviware.soapui.impl.wsdl.submit.transports.http.support.attachments. WsdlSinglePartHttpResponse.processIncomingWss(WsdlSinglePartHttpResponse .java:49) at com.eviware.soapui.impl.wsdl.submit.transports.http.support.attachments. WsdlSinglePartHttpResponse.<init>(WsdlSinglePartHttpResponse.java:38) at com.eviware.soapui.impl.wsdl.submit.filters.HttpPackagingResponseFilter. wsdlRequest(HttpPackagingResponseFilter.java:71) at com.eviware.soapui.impl.wsdl.submit.filters.HttpPackagingResponseFilter. afterAbstractHttpResponse(HttpPackagingResponseFilter.java:48) at com.eviware.soapui.impl.wsdl.submit.filters.AbstractRequestFilter.afterR equest(AbstractRequestFilter.java:64) at com.eviware.soapui.impl.wsdl.submit.transports.http.HttpClientRequestTra nsport.sendRequest(HttpClientRequestTransport.java:297) at com.eviware.soapui.impl.wsdl.WsdlSubmit.run(WsdlSubmit.java:123) at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) Caused by: org.apache.ws.security.WSSecurityException: General security error (The private key for the supplied alias does not exist in the keystore) at org.apache.ws.security.components.crypto.Merlin.getPrivateKey(Merlin.jav a:661) at org.apache.ws.security.processor.EncryptedKeyProcessor.handleToken(Encry ptedKeyProcessor.java:103) ... 17 more Caused by: java.security.UnrecoverableKeyException: Cannot recover key at sun.security.provider.KeyProtector.recover(Unknown Source) at sun.security.provider.JavaKeyStore.engineGetKey(Unknown Source) at sun.security.provider.JavaKeyStore$JKS.engineGetKey(Unknown Source) at java.security.KeyStore.getKey(Unknown Source) at org.apache.ws.security.components.crypto.Merlin.getPrivateKey(Merlin.jav a:647) ... 18 more But, I know that the alias DOES exist in the keystore. I was confused by the fact that the PasswordCallbackhandler is looking up the password for "server", so I tried putting "client1" in there instead of " useReqSigCert" for that reason, but the result is the same. I read somewhere on a JBoss forum something about useReqSigCert not working correctly unless the request was also encrypted, so I have tried also encrypting the request from SoapUi. When encrypting from the client, I add a third action "Encryption" using the following settings: Keystore = client1.keystore Alias = server Password = "password" Key Identifier Type = "Binary Security Token" [ in addition to the options for signing, it also adds "Embedded KeyInfo", "Embed Security Token Reference", and "Thumbprint SHA1 Identifier". Again, I've tried them all with the exception of Embedded Key Info because I am unclear what to enter for "Embedded KeyName" and "Embedded Key Password" Embedded Keyname = blank Embedded Key Password = blank Symmetric Encoding Algorithm = http://www.w3.org/2001/04/xmlenc#tripledes-cbc Key Encryption Algorithm = http://www.w2.org/2001/04/xmlenc#rsa-1_5 Create Encrypted Key = checked. (I've tried it both ways) Parts = blank. (My understanding is that when parts are blank, the entire SOAP message is signed or encrypted. Again, perhaps not recommended?) When I do that, I get an error on the server: org.apache.cxf.interceptor.Fault: Message part {http://www.w3.org/2001/04/xmlenc#}EncryptedData was not recognized. (Does it exist in service WSDL?) Ok, so I uncheck the "Create Encrypted Key" and try again and I get: 19:06:57,547 INFO [com.mycompany.ws.common.KeystorePasswordCallback] Creating Akimeka ws security callback handler... 19:06:57,547 INFO [com.mycompany.ws.common.KeystorePasswordCallback] KeystorePasswordCallback invoked handle 19:06:57,547 INFO [com.mycompany.ws.common.KeystorePasswordCallback] Looking up password for identifier: null 19:06:57,547 WARN [org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor] : org.apache.ws.security.WSSecurityException: The signature or decryption was invalid (Unsupported key identification) I switch to Key Identifier = "X509 Certificate" I get the same error. I switch to "Issuer Name and Serial", same error. "Embedded KeyInfo" gets me an "actions mismatch" error on the server. And, so you get the drift: I'm basically a chimp with a keyboard here, trying to stumble upon a Shakespearean sonnet. I have no idea what the correct settings should be. (Although I swear I have some dim recollection of getting client to server decrypting working at some point over the past several days.) So... I feel like what I want to do should be very simple, but I'm not sure how to proceed at this point. I may be dealing with problems with SoapUi, with this old version of CXF/WSS4j/Jboss, or just plain ignorance, but I sure could use some help. Sorry for the long mail, thanks in advance. NOTICE: This transmission (including all attachments) is company confidential, is intended only for the individual or entity named above, and is likely to contain privileged, proprietary and confidential information that is exempt from disclosure requests under applicable law. If you are not the intended recipient, you are hereby notified that any disclosure, copying, distribution, use of or reliance upon any of the information contained in this transmission is strictly prohibited. Any inadvertent or unauthorized disclosure shall not compromise or waive the confidentiality of this transmission. If you have received this transmission in error, please forward this message immediately to [email protected] <mailto:[email protected]> and delete or otherwise remove this email from your system. Thank you
