简体   繁体   中英

Apache CXF Policy Exception (WS Security)- Security configuration could not be detected

Could really do with some help with this one as I've been banging my head against a brick wall for several days now.

I've implemented a very simple JAX-WS service using Apache CXF, I'm trying to add WS-SecurityPolicies to encrypt and sign the message body however client side I'm getting the following exception:

org.apache.cxf.ws.policy.PolicyException: Security configuration could not be detected. Potential cause: Make sure jaxws:client element with name attribute value matching endpoint port is defined as well as a security.signature.properties element within it.
    at org.apache.cxf.ws.security.wss4j.policyhandlers.AbstractCommonBindingHandler.unassertPolicy(AbstractCommonBindingHandler.java:92)
    at org.apache.cxf.ws.security.wss4j.policyhandlers.AbstractBindingBuilder.getSignatureBuilder(AbstractBindingBuilder.java:1797)
    at org.apache.cxf.ws.security.wss4j.policyhandlers.AsymmetricBindingHandler.doSignature(AsymmetricBindingHandler.java:693)
    at org.apache.cxf.ws.security.wss4j.policyhandlers.AsymmetricBindingHandler.doSignBeforeEncrypt(AsymmetricBindingHandler.java:171)
    at org.apache.cxf.ws.security.wss4j.policyhandlers.AsymmetricBindingHandler.handleBinding(AsymmetricBindingHandler.java:110)
    at org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JOutInterceptor$PolicyBasedWSS4JOutInterceptorInternal.handleMessageInternal(PolicyBasedWSS4JOutInterceptor.java:190)
    at org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JOutInterceptor$PolicyBasedWSS4JOutInterceptorInternal.handleMessage(PolicyBasedWSS4JOutInterceptor.java:109)
    at org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JOutInterceptor$PolicyBasedWSS4JOutInterceptorInternal.handleMessage(PolicyBasedWSS4JOutInterceptor.java:96)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
    at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:514)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:423)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:324)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:277)
    at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
    at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:139)

This is my applicationContext.xml client side:

<bean id="passwordCallback"
        class="com.example.payment.engine.soap.service.ClientPasswordCallback"></bean>

<jaxws:client
    name="{http://service.soap.engine.payment.example.com/}HelloWorldImplService"
    createdFromAPI="true" id="helloClient"
    serviceClass="com.example.payment.engine.soap.service.HelloWorld"
    address="http://localhost:8080/payment-engine/api/soap/hello">
    <jaxws:properties>
        <entry key="ws-security.callback-handler" value-ref="passwordCallback" />
        <entry key="ws-security.encryption.properties" value="crypto.properties" />
        <entry key="ws-security.signature.properties" value="crypto.properties" />
        <entry key="ws-security.encryption.username" value="server" />
    </jaxws:properties>
</jaxws:client>

This is the client crypto.properties

org.apache.ws.security.crypto.merlin.keystore.file=client-keystore.jks
org.apache.ws.security.crypto.merlin.keystore.password=client-pass
org.apache.ws.security.crypto.merlin.keystore.private.password=key-pass
org.apache.ws.security.crypto.merlin.keystore.alias=client

And finally this is the WSDL:

<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:tns="http://service.soap.engine.payment.example.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" name="HelloWorldImplService"
    targetNamespace="http://service.soap.engine.payment.example.com/">
    <wsdl:types>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://service.soap.engine.payment.example.com/"
            elementFormDefault="unqualified" targetNamespace="http://service.soap.engine.payment.example.com/" version="1.0">
            <xs:element name="sayHello" type="tns:sayHello" />
            <xs:element name="sayHelloResponse" type="tns:sayHelloResponse" />
            <xs:complexType name="sayHello">
                <xs:sequence />
            </xs:complexType>
            <xs:complexType name="sayHelloResponse">
                <xs:sequence>
                    <xs:element minOccurs="0" name="return" type="xs:string" />
                </xs:sequence>
            </xs:complexType>
        </xs:schema>
    </wsdl:types>
    <wsdl:message name="sayHelloResponse">
        <wsdl:part element="tns:sayHelloResponse" name="parameters"></wsdl:part>
    </wsdl:message>
    <wsdl:message name="sayHello">
        <wsdl:part element="tns:sayHello" name="parameters"></wsdl:part>
    </wsdl:message>
    <wsdl:portType name="HelloWorld">
        <wsdl:operation name="sayHello">
            <wsdl:input message="tns:sayHello" name="sayHello"></wsdl:input>
            <wsdl:output message="tns:sayHelloResponse" name="sayHelloResponse"></wsdl:output>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="HelloWorldImplServiceSoapBinding" type="tns:HelloWorld">
    <wsp:PolicyReference URI="#HelloWorldBindingPolicy"/>
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="sayHello">
            <soap:operation soapAction="" style="document" />
            <wsdl:input name="sayHello">
                <wsp:PolicyReference URI="#HelloWorldInputBindingPolicy" />
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output name="sayHelloResponse">
                <wsp:PolicyReference URI="#HelloWorldOutputBindingPolicy" />
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="HelloWorldImplService">
        <wsdl:port binding="tns:HelloWorldImplServiceSoapBinding" name="HelloWorldImplPort">
            <soap:address location="http://localhost:8080/payment-engine/api/soap/hello" />
        </wsdl:port>
    </wsdl:service>
    <wsp:Policy wsu:Id="HelloWorldBindingPolicy">
        <wsp:ExactlyOne>
            <wsp:All>
                <sp:AsymmetricBinding>
                    <wsp:Policy>
                        <sp:InitiatorToken>
                            <wsp:Policy>
                                <sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
                                    <wsp:Policy>
                                        <sp:WssX509V3Token11 />
                                    </wsp:Policy>
                                </sp:X509Token>
                            </wsp:Policy>
                        </sp:InitiatorToken>
                        <sp:RecipientToken>
                            <wsp:Policy>
                                <sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Never">
                                    <wsp:Policy>
                                        <sp:WssX509V3Token11 />
                                        <sp:RequireIssuerSerialReference />
                                    </wsp:Policy>
                                </sp:X509Token>
                            </wsp:Policy>
                        </sp:RecipientToken>
                        <sp:Layout>
                            <wsp:Policy>
                                <sp:Strict />
                            </wsp:Policy>
                        </sp:Layout>
                        <sp:IncludeTimestamp />
                        <sp:OnlySignEntireHeadersAndBody />
                        <sp:AlgorithmSuite>
                            <wsp:Policy>
                                <sp:Basic128 />
                            </wsp:Policy>
                        </sp:AlgorithmSuite>
                        <sp:EncryptSignature />
                    </wsp:Policy>
                </sp:AsymmetricBinding>
                <sp:Wss11>
                    <wsp:Policy>
                        <sp:MustSupportRefIssuerSerial />
                    </wsp:Policy>
                </sp:Wss11>
            </wsp:All>
        </wsp:ExactlyOne>
    </wsp:Policy>
    <wsp:Policy wsu:Id="HelloWorldInputBindingPolicy">
        <wsp:ExactlyOne>
            <wsp:All>
                <sp:EncryptedParts>
                    <sp:Body />
                </sp:EncryptedParts>
                <sp:SignedParts>
                    <sp:Body />
                </sp:SignedParts>
            </wsp:All>
        </wsp:ExactlyOne>
    </wsp:Policy>
    <wsp:Policy wsu:Id="HelloWorldOutputBindingPolicy">
        <wsp:ExactlyOne>
            <wsp:All>
                <sp:EncryptedParts>
                    <sp:Body />
                </sp:EncryptedParts>
                <sp:SignedParts>
                    <sp:Body />
                </sp:SignedParts>
            </wsp:All>
        </wsp:ExactlyOne>
    </wsp:Policy>
</wsdl:definitions>

Any help would be really appreciated!

I spent a few days solving this and additional issues with Apache CXF 3.1.4. Fact is, the Error messages in CXF seldom provide the true clue about what to fix. The one at stake is quite typical: it tells about a name not found but does not provide the actual value that is looked up, and second, that was not the issue in my context.

I was starting from an XML/Spring-based CXF bus/policy configuration working in test and prod for over a year, and like:

SpringBusFactory bf = new SpringBusFactory();
URL busFile = MyClient.class.getResource("/wssec_policy.xml");
Bus bus = bf.createBus(busFile);
BusFactory.setDefaultBus(bus);

where the wssec_policy.xml contained the jaxws:client definition as follows:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:cxf="http://cxf.apache.org/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xsi:schemaLocation="...etc...">
<bean id="logOutbound" class="MyLoggingOutInterceptor"/>
<bean id="logInbound" class="MyLoggingInInterceptor"/>
<cxf:bus>
    <cxf:outInterceptors><ref bean="logOutbound"/></cxf:outInterceptors>
    <cxf:inInterceptors><ref bean="dumpMessageInterceptor"/><ref bean="logInbound"/></cxf:inInterceptors>
    <cxf:features><cxf:logging/></cxf:features>
</cxf:bus>
<jaxws:client name="{http://some.domain/webservices}servicioSoap" createdFromAPI="true">
        <jaxws:properties>
            <entry key="security.encryption.properties" value="etc/Client_Encrypt.properties"/>
            <entry key="security.signature.properties" value="etc/Client_Sign.properties"/>
        </jaxws:properties>
    </jaxws:client>
</beans>

and where encryption and signature.properties contain WSS4J properties .

My challenge was just to get this working with CXF 3.1.4, BouncyCastle 1.52, Spring 4.2.4, on JRE 8; which did not, banging first on the error you cite.

I assembled the code in a POJO client and started drilling down in CXF source code to find the true cause, and discovered that the default bus setup as above (which did work previously) no longer supplied the jaxws:client meta-data to WSS4J layers. No data, hence the error. Apache CXF supplies three ways of configuring your WS-* stack (Explicit Interceptors, policy-based with XML, and policy-based via Java API). To solve your issue, I switched to policy-based config via the java API as follows:

serviceGDS = new WSDL2Java_generated_Service();
portGDS = serviceGDS.getServiceGdsSoap();
Map<String, Object> ctx = ((javax.xml.ws.BindingProvider)portGDS).getRequestContext();
Properties encProps = new Properties();
encProps.load(new FileInputStream("etc/Client_Encrypt.properties"));
ctx.put("security.encryption.properties", encProps);
Properties signProps = new Properties();
signProps.load(new FileInputStream("etc/Client_Sign.properties"));
ctx.put("security.signature.properties", signProps);
// setting the props below once helped solving the error at stake
ctx.put("security.signature.username","test_keypair");
ctx.put("security.encryption.username","gds_test_cert");

The last two properties were once needed to progress in debugging, solving wrong key length errors due to missing key alias!!! But at the very end, I noted that it worked back without them! (they actually duplicate keystore alias in the Client_Encrypt and Client_Sign properties).

I then solved no certificate found for encryption errors by suppressing "org.apache.ws.security.crypto.merlin.keystore.provider=BC" from Client_Encrypt and Client_Sign properties (yet worked in the past version).

I solved missing certificate errors by switching from pkcs12 keystore type to jks . I discovered that keystore.load() does not fetch all the contained certificates... with the same p12 keystore I get varying outcome according to the context in which it is called... buggy indeed.

I solved AsymmetricBinding is not supported by observing that the re-deployment in a live server (JBoss EAP7 in my case) never worked, but when the deployed app starts along with the server (hence after server reboot), it works. I strongly suspect class loader issues.

Let me add that I once switched back to JRE 7 (where legacy runs OK) and the errors were significantly different (I suspect policy config files in the JRE).

Another major difficulty is that all errors, be it client side exceptions on handling the request or even the response, as well as server side faults, are all wrapped by CXF as soap faults with terse error messages making it very difficult to understand what's really happening. The only way left that allowed me to progress was assembling needed client code in a POJO and debugging it with help of CXF and WSS4J source code libraries( Eclipse/Maven fetches the source code for you while you use the debugger and drill down... efficient!)

Managed to get this half working by using the following

    HelloWorldImplService service = new HelloWorldImplService();
    HelloWorld port = service.getHelloWorldImplPort();

    Map<String, Object> ctx = ((BindingProvider) port).getRequestContext();
    ctx.put("ws-security.callback-handler", "com.example.payment.engine.soap.service.ClientPasswordCallback");
    ctx.put("ws-security.encryption.properties", "config/crypto.properties");
    ctx.put("ws-security.signature.properties", "config/crypto.properties");
    ctx.put("ws-security.encryption.username", "server");

    System.out.println(port.sayHello());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM