Question

I have a spring context that initializes CXF web service and wraps it with signature checking:

<bean id="myProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
    <property name="ignoreResourceNotFound" value="true"/>
    <property name="locations">
        <list>
            <value>classpath:my.properties</value>
        </list>
    </property>
</bean>

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="ignoreResourceNotFound" value="true"/>
    <property name="properties" ref="myProperties"/>
    <property name="placeholderPrefix" value="!sp{"/>
    <property name="placeholderSuffix" value="}"/>
</bean>

<bean id="inbound-security" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
    <constructor-arg>
        <map>
            <entry key="action" value="Signature"/>
            <entry key="signaturePropFile" value="!sp{acq.signature.properties.location}"/>
        </map>
    </constructor-arg>
</bean>

I realized that signaturePropFile must be on classpath, it cannot be read from filesystem :-(

Caused by: org.apache.ws.security.WSSecurityException: General security error (Cannot load the resource D:\Dev\Projekty\smartpos-backend-parent\smartpos-backend-acquirer\src\main\resources\signature.properties)
at org.apache.ws.security.components.crypto.CryptoFactory.getProperties(CryptoFactory.java:261) ~[wss4j-1.6.11.jar:1.6.11]
at org.apache.ws.security.components.crypto.CryptoFactory.getInstance(CryptoFactory.java:186) ~[wss4j-1.6.11.jar:1.6.11]
at org.apache.cxf.ws.security.wss4j.AbstractWSS4JInterceptor.loadCryptoFromPropertiesFile(AbstractWSS4JInterceptor.java:224) ~[cxf-rt-ws-security-2.7.5.jar:2.7.5]
at org.apache.ws.security.handler.WSHandler.loadCrypto(WSHandler.java:911) ~[wss4j-1.6.11.jar:1.6.11]

Nevermind, I let it be part of deployment, but I do want to externalize keystore, which is defined with following property:

org.apache.ws.security.crypto.merlin.keystore.file=server-keystore.jks

I tried to replace the path with some configuration property !sp{keystore.location} and ${keystore.location}, but it does not work. In fact it fails with same exception like property file does not exist:

Caused by: org.apache.ws.security.WSSecurityException: General security error (Cannot load the resource classpath:signature.properties)
at org.apache.ws.security.components.crypto.CryptoFactory.getProperties(CryptoFactory.java:261) ~[wss4j-1.6.11.jar:1.6.11]
at org.apache.ws.security.components.crypto.CryptoFactory.getInstance(CryptoFactory.java:186) ~[wss4j-1.6.11.jar:1.6.11]
at org.apache.cxf.ws.security.wss4j.AbstractWSS4JInterceptor.loadCryptoFromPropertiesFile(AbstractWSS4JInterceptor.java:224) ~[cxf-rt-ws-security-2.7.5.jar:2.7.5]
at org.apache.ws.security.handler.WSHandler.loadCrypto(WSHandler.java:911) ~[wss4j-1.6.11.jar:1.6.11]

What is a proper way to configure WSS4J keystore location? I do not like editing war before deployment. (I use maven to build it).

Was it helpful?

Solution

This issue has been solved lately.

When using the older version of the wss4j (v2.1.1 and older) you can pass the need of the String reference for the property file (e.g. WSHandlerConstants.SIG_PROP_REF_ID) by overriding WSHandler.loadCrypto() and injecting a created Property object by Spring util, e.g.:

<util:properties id="wss4jCryptoProperties">
    <prop key="org.apache.ws.security.crypto.merlin.keystore.file">!sp{keystore.file}</prop>
    <prop key="org.apache.ws.security.crypto.merlin.keystore.type">!sp{keystore.type}</prop>
    <prop key="org.apache.ws.security.crypto.merlin.keystore.password">!sp{keystore.password}</prop>
</util:properties>

Overriding the WSHandler.loadCrypto() by an example:

public class PropertiesWSS4JInInterceptor extends WSS4JInInterceptor {

    private Properties cryptoProperties;

    public PropertiesWSS4JInInterceptor(Map<String, Object> inProps,
                                        Properties cryptoProperties) {
        super(inProps);
        this.cryptoProperties = cryptoProperties;
    }

    @Override
    protected Crypto loadCrypto(String cryptoPropertyFile, String cryptoPropertyRefId,
                                RequestData requestData) throws WSSecurityException {
        return CryptoFactory.getInstance(cryptoProperties);
    }
}

In addition, you can inject the wss4jCryptoProperties in your customBean (don't forget to create the field named cryptoProperties in the referenced class and a setter):

<bean id="customBean" class="cz.company.CustomBean">
    <property name="cryptoProperties" ref="wss4jCryptoProperties"/>
</bean>

Finally, you can add the interceptor to your endpoint:

endpoint.getInInterceptors().add(new PropertiesWSS4JInInterceptor(inProps, cryptoProperties));

OTHER TIPS

Not sure that it will help but I had a similar goal: I wanted to point to a keystore outside my jar because it depends on the final user certificate.

Providing that your Manifest.mf has this classpath setup:

Class-Path: .

You can simply target an external folder in the same directory than your jar. In my case, I do the following:

org.apache.ws.security.crypto.merlin.file=config-folder/mycert.jks

Being config-folder in the same directory that my application.jar file.

I was using this page to resolve my problem of referring it from external folder. actually while converting the path of property *org.apache.ws.security.crypto.merlin.keystore.file * , It is being fed to FileSystem. File(path) -

Merlin.java:333

could actually decrypt the path of the file from external folder. No need to do extra work.

org.apache.ws.security.crypto.merlin.keystore.file=/usr/local/path/server-keystore.jks works!!!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top