如何在 WSS4J 2.2.3 中启用 RSA15 传输算法?

How can I enable RSA15 transport algorithm in WSS4J 2.2.3?

我正在将 SOAP Web 服务客户端升级到 JDK 11 和 WSS4J 2.2.3 (w/o CXF/Spring)。 java 代码使用 'stand-alone' 方法来处理加密并且无法解密传入的响应,因为它是使用 RSA15 加密的。该算法从 2.0.0 版开始默认禁用,但迁移指南建议可以通过将 WSHandlerConstants.ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM 属性 设置为 "true" 来启用该算法。我该怎么做?

我查看了 WSS4J 代码,可以看到 RequestData 对象是从 WSS4J 框架中的 WSSecurityEngine class 实例化的,但我没有看到允许我设置 allowRSA15KeyTransportAlgorithm 属性 到 'true'。我已经能够在 eclipse 中设置断点并更改值以确保它按预期工作并且确实如此。我在网上搜索过,我发现的最接近的是一个类似的问题,这里没有明显的答案 Allow the RSA v1.5 Key Transport Algorithm for WildFly / JBossWS / CXF / WSS4J stack。我试图将 WSS4J 恢复到允许该算法的 1.6 版,但随后它因不同的错误 "WRONG_DOCUMENT_ERR: A node is used in a different document than the one that created it" 而失败。这似乎发生在将 WSS4J 1.6 与任何版本的 jaxws(JDK 外部)一起使用时。我能想到解决这个问题的唯一方法是侵入 WSS4J 2.2.3 依赖项并在 RequestData class 中将 属性 默认设置为 "true" 但我真的不想这样做。

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

返回的错误信息是...

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.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue.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.run(EventQueue.java:745)
    at java.desktop/java.awt.EventQueue.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)

经过一番追踪..我设法为你找到了它。

长话短说,您应该为自己创建一个 RequestData 对象,如下所示:

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

下面是我通过堆栈跟踪和查看源代码发现这个解决方案的方法:

从抛出异常的地方开始

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
    }
    // ...
}

它死了,因为 requestData 没有 allowRSA15KeyTransportAlgorithm 设置为 true。 但为什么不设置为真呢?请求数据来自哪里?

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)

所有只是传递在这个方法中创建的requestData

// 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
}

调用者:

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

您在 inboundWSSConfig 方法中调用。

因此,因为您正在调用 processSecurityHeader(Document doc, String actor, CallbackHandler cb, Crypto crypto),它正在初始化一个新的 RequestData 并在其上设置 CryptoCallbackHandler,但从未设置 allowRSA15KeyTransportAlgorithm