javarsawss4j

How can I enable RSA15 transport algorithm in WSS4J 2.2.3?


I'm upgrading a SOAP web service client to JDK 11 and WSS4J 2.2.3 (w/o CXF/Spring). The java code uses the 'stand-alone' approach to handle encryption and fails to decrypt the incoming response because it is encrypted using RSA15. The algorithm was disabled by default as of release 2.0.0 but the migration guide suggests that it is possible to enable the algorithm by setting the WSHandlerConstants.ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM property to "true". How can I do this??

I've looked at the WSS4J code and can see that the RequestData object is instantiated from WSSecurityEngine class in the WSS4J framework and I don't see a hook that would allow me to set the allowRSA15KeyTransportAlgorithm property to 'true'. I've been able to put a break-point in eclipse and change the value to ensure it works as expected and it does. I've searched the web and the closest thing I found was a similar question with no apparent answer here Allow the RSA v1.5 Key Transport Algorithm for WildFly / JBossWS / CXF / WSS4J stack. I've tried to revert WSS4J back to version 1.6 which allows the algorithm but then it failed with a different error "WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it". This seems to occur when using WSS4J 1.6 with any version of jaxws (external to JDK). The only way I can think to solve this is by hacking into the WSS4J 2.2.3 dependency and setting the property default to "true" in the RequestData class but I really don't want to do that.

private void inboundWSSConfig(SOAPMessageContext context, Crypto crypto) throws WSSecurityException{

    log.info("Inbound Message...");

    KeystoreCallbackHandler keystoreCallbackHandler = new KeystoreCallbackHandler();
    keystoreCallbackHandler.addUsers((String)cryptoProperties.get("org.apache.ws.security.crypto.merlin.keystore.alias"), (String)cryptoProperties.get("org.apache.ws.security.crypto.merlin.keystore.password"));

    WSSecurityEngine newEngine = new WSSecurityEngine();
    WSSConfig config = WSSConfig.getNewInstance();
    config.setValidator(WSConstants.SIGNATURE, new SignatureTrustValidator());
    newEngine.setWssConfig(config);

    newEngine.processSecurityHeader(context.getMessage().getSOAPPart(), null, keystoreCallbackHandler, crypto);
}

error message returned is...

Jul 17, 2019 11:42:56 AM ensurebill.client.handler.HeaderHandler inboundWSSConfig
INFO: Inbound Message...
Jul 17, 2019 11:42:56 AM ensurebill.client.handler.HeaderHandler handleMessage
SEVERE: ERROR in handleMessage
org.apache.wss4j.common.ext.WSSecurityException: An error was discovered processing the <wsse:Security> header
    at org.apache.wss4j.dom.processor.EncryptedKeyProcessor.handleToken(EncryptedKeyProcessor.java:131)
    at org.apache.wss4j.dom.processor.EncryptedKeyProcessor.handleToken(EncryptedKeyProcessor.java:90)
    at org.apache.wss4j.dom.engine.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:340)
    at org.apache.wss4j.dom.engine.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:221)
    at org.apache.wss4j.dom.engine.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:168)
    at org.apache.wss4j.dom.engine.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:127)
    at ensurebill.client.handler.HeaderHandler.inboundWSSConfig(HeaderHandler.java:160)
    at ensurebill.client.handler.HeaderHandler.handleMessage(HeaderHandler.java:61)
    at ensurebill.client.handler.HeaderHandler.handleMessage(HeaderHandler.java:1)
    at com.sun.xml.ws.handler.HandlerProcessor.callHandleMessageReverse(HandlerProcessor.java:311)
    at com.sun.xml.ws.handler.HandlerProcessor.callHandlersResponse(HandlerProcessor.java:184)
    at com.sun.xml.ws.handler.ClientSOAPHandlerTube.callHandlersOnResponse(ClientSOAPHandlerTube.java:133)
    at com.sun.xml.ws.handler.HandlerTube.processResponse(HandlerTube.java:144)
    at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:1117)
    at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:1020)
    at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:989)
    at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:847)
    at com.sun.xml.ws.client.Stub.process(Stub.java:433)
    at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:161)
    at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:78)
    at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:62)
    at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:131)
    at com.sun.proxy.$Proxy33.callNotificationInquiry(Unknown Source)
    at ensurebill.client.swing.MenuItemListener.sendRequest(MenuItemListener.java:109)
    at ensurebill.client.swing.MenuItemListener.actionPerformed(MenuItemListener.java:92)
    at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1967)
    at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2308)
    at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
    at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
    at java.desktop/javax.swing.AbstractButton.doClick(AbstractButton.java:369)
    at java.desktop/javax.swing.plaf.basic.BasicMenuItemUI.doClick(BasicMenuItemUI.java:1020)
    at java.desktop/javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(BasicMenuItemUI.java:1064)
    at java.desktop/java.awt.Component.processMouseEvent(Component.java:6632)
    at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3342)
    at java.desktop/java.awt.Component.processEvent(Component.java:6397)
    at java.desktop/java.awt.Container.processEvent(Container.java:2263)
    at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5008)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4840)
    at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918)
    at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4547)
    at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307)
    at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2772)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4840)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:772)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
    at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:743)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

Solution

  • After a good bit of tracing.. I managed to find it for ya.

    Long story short, you should instead create a RequestData object for yourself, like this:

    private void inboundWSSConfig(SOAPMessageContext context, Crypto crypto) throws WSSecurityException{
    
        log.info("Inbound Message...");
    
        KeystoreCallbackHandler keystoreCallbackHandler = new KeystoreCallbackHandler();
        keystoreCallbackHandler.addUsers((String)cryptoProperties.get("org.apache.ws.security.crypto.merlin.keystore.alias"), 
        (String)cryptoProperties.get("org.apache.ws.security.crypto.merlin.keystore.password"));
    
        WSSecurityEngine newEngine = new WSSecurityEngine();
        WSSConfig config = WSSConfig.getNewInstance();
        config.setValidator(WSConstants.SIGNATURE, new SignatureTrustValidator());
        newEngine.setWssConfig(config);
    
        RequestData data = new RequestData();
        data.setWssConfig(config); // probably
        data.setDecCrypto(crypto);
        data.setSigVerCrypto(crypto);
        data.setCallbackHandler(keystoreCallbackHandler);
        data.setAllowRSA15KeyTransportAlgorithm(true);
    
        newEngine.processSecurityHeader(context.getMessage().getSOAPPart(), data);
    }
    

    Here's how I discovered this solution by working backward through the stack trace and looking at the source:

    Starting where the exception is thrown

    at org.apache.wss4j.dom.processor.EncryptedKeyProcessor.handleToken(EncryptedKeyProcessor.java:131)
    
    public List<WSSecurityEngineResult> handleToken(Element elem, RequestData data, AlgorithmSuite algorithmSuite) {
        // ...
        if (WSConstants.KEYTRANSPORT_RSA15.equals(encryptedKeyTransportMethod)
            && !data.isAllowRSA15KeyTransportAlgorithm() // <=== because this is false, etc.
            && (algorithmSuite == null
              || !algorithmSuite.getKeyWrapAlgorithms().contains(WSConstants.KEYTRANSPORT_RSA15))) {
            LOG.debug(
                "The Key transport method does not match the requirement"
            );
            throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY); // <=== this is where you die
        }
        // ...
    }
    

    It dies because the requestData does not have allowRSA15KeyTransportAlgorithm set to true. But why is it not set to true? where is request data coming from?

    at org.apache.wss4j.dom.processor.EncryptedKeyProcessor.handleToken(EncryptedKeyProcessor.java:90)
    at org.apache.wss4j.dom.engine.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:340)
    at org.apache.wss4j.dom.engine.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:221)
    

    All just pass along the requestData created in this method:

    // line 208
    public WSHandlerResult processSecurityHeader(
        Element securityHeader,
        String actor,
        CallbackHandler cb,
        Crypto sigVerCrypto,
        Crypto decCrypto
    ) throws WSSecurityException {
        RequestData data = new RequestData(); // creates RequestData but doesn't set `allowRSA15KeyTransportAlgorithm`
        data.setActor(actor);
        data.setWssConfig(getWssConfig());
        data.setDecCrypto(decCrypto);
        data.setSigVerCrypto(sigVerCrypto);
        data.setCallbackHandler(cb);
        return processSecurityHeader(securityHeader, data); // line 221
    }
    

    which is called by:

    public WSHandlerResult processSecurityHeader(
        Document doc,
        String actor,
        CallbackHandler cb,
        Crypto crypto
    ) throws WSSecurityException {
        return processSecurityHeader(doc, actor, cb, crypto, crypto);
    }
    

    which you call in your inboundWSSConfig method.

    So because you are calling processSecurityHeader(Document doc, String actor, CallbackHandler cb, Crypto crypto) it's initializing a new RequestData and setting the Crypto and CallbackHandler on it but never setting allowRSA15KeyTransportAlgorithm.