javawss4j

Configurable location of keystore in WSS4J


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).


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));