【问题标题】:CXF Client + WS-Security + MTOM = Trouble?CXF 客户端 + WS-Security + MTOM = 麻烦?
【发布时间】:2014-01-31 23:53:58
【问题描述】:

我正在构建一个调用启用 WS-Security 的 Web 服务的 CXF(版本 2.7.4)客户端。它使用 BinarySecurityToken、加密和签名。我让 CXF 为“常规”SOAP 调用工作,但是当响应返回为 MTOM 时,我最终在客户端中遇到了奇怪的错误:

org.apache.cxf.binding.soap.SoapFault:签名或解密无效 在 org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.createSoapFault(WSS4JInInterceptor.java:778) 在 org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:334) 在 org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:96) ... 原因:org.apache.xml.security.encryption.XMLEncryptionException:找不到 URI cid:urn%3Auuid%3AD62B819A5C8E77D41B1391208838268@apache.org 和 Base null 的解析器 原始异常是 org.apache.xml.security.utils.resolver.ResourceResolverException:找不到 URI cid:urn%3Auuid%3AD62B819A5C8E77D41B1391208838268@apache.org 和 Base null 的解析器 在 org.apache.xml.security.encryption.XMLCipherInput.getDecryptBytes(XMLCipherInput.java:134) 在 org.apache.xml.security.encryption.XMLCipherInput.getBytes(XMLCipherInput.java:103) ... 46 更多 原因:org.apache.xml.security.utils.resolver.ResourceResolverException:找不到 URI cid:urn%3Auuid%3AD62B819A5C8E77D41B1391208838268@apache.org 和 Base null 的解析器 在 org.apache.xml.security.utils.resolver.ResourceResolver.internalGetInstance(ResourceResolver.java:130) 在 org.apache.xml.security.utils.resolver.ResourceResolver.getInstance(ResourceResolver.java:87) 在 org.apache.xml.security.encryption.XMLCipherInput.getDecryptBytes(XMLCipherInput.java:130) ... 51 更多

我尝试在绑定提供程序上调用 setMTOMEnabled(true),设置 SAAjInInterceptor、AttachmentInInterceptor 但仍然没有运气。关于如何解决它的任何想法?

【问题讨论】:

  • 我想另一种选择是编写一个自定义 ResourceResolver 然后以某种方式通过该类提供附件。看起来有点矫枉过正...
  • 在这种情况下,XML 安全的规则是什么?我尝试读过一次(几年前),但它们非常容易入睡……
  • 啊,我希望我知道。这是一个非常奇怪的设置。我们需要配置自定义标头,添加二进制安全令牌、用户名令牌和时间戳,然后对其进行签名。响应返回签名和加密。 CXF 可以很好地处理大多数呼叫,除了带有 MTOM 附件的呼叫...

标签: java web-services cxf ws-security


【解决方案1】:

这一切都可以追溯到启用 WSS 时 CXF 无法与 MTOM 一起使用的事实 :(

CXF Mailing List One

在解决了这个问题之后,它看起来像是一种解决方法,可能包括自定义资源解析器和转换。

所以我添加了一个自定义解析器,以便 WSS 知道在哪里可以找到附件内容:

package org.integration.client;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collection;

import org.apache.cxf.message.Attachment;
import org.apache.log4j.Logger;
import org.apache.xml.security.signature.XMLSignatureInput;
import org.apache.xml.security.utils.resolver.ResourceResolverContext;
import org.apache.xml.security.utils.resolver.ResourceResolverException;
import org.apache.xml.security.utils.resolver.ResourceResolverSpi;

public class AttachmentResolverSpi extends ResourceResolverSpi {

    private static final String SUPPORTED_URI_PREFIX = "cid:urn";

    private static final String ATTACHMENT_PREFIX = "cid:";

    private static Logger logger = Logger.getLogger(AttachmentResolverSpi.class);

    private Collection<Attachment> attachments;

    @Override
    public boolean engineIsThreadSafe() {
        return false;
    }

    @Override
    public boolean engineCanResolveURI(ResourceResolverContext context) {
        if (context.uriToResolve == null) {
            return false;
        }
        return context.uriToResolve.startsWith(SUPPORTED_URI_PREFIX);
    }

    @Override
    public XMLSignatureInput engineResolveURI(ResourceResolverContext context)
            throws ResourceResolverException {
        String resourceId = getResourceId(context);

        if (logger.isInfoEnabled()) {
            logger.info("Looking up: " + resourceId);
        }

        Attachment attachment = getAttachment(resourceId);

        if (attachment == null) {
            logger.error("Unable to resolve attachment for " + resourceId);

            throw new ResourceResolverException(context.uriToResolve, context.attr, context.baseUri);
        }

        if (logger.isInfoEnabled()) {
            logger.info("Found attachment: " + attachment);
        }

        XMLSignatureInput result;
        try {
            result = new XMLSignatureInput(attachment.getDataHandler().getInputStream());
        } catch (IOException e) {
            logger.error("Unable to create xml signature input", e);

            throw new ResourceResolverException(context.uriToResolve, context.attr, context.baseUri);
        }
        return result;
    }

    private String getResourceId(ResourceResolverContext context) {
        String resourceId = context.uriToResolve;
        try {
            resourceId = URLDecoder.decode(resourceId, "UTF-8");
        } catch (UnsupportedEncodingException e1) {
            throw new RuntimeException("Unable to decode", e1);
        }
        if (resourceId.startsWith(ATTACHMENT_PREFIX)) {
            resourceId = resourceId.substring(ATTACHMENT_PREFIX.length());
        }
        return resourceId;
    }

    private Attachment getAttachment(String resourceId) {
        Collection<Attachment> attachments = getAttachments();
        if (attachments == null) {
            return null;
        }

        for(Attachment a : attachments) {
            if (logger.isDebugEnabled()) {
                logger.debug("Comparing " + a.getId() + " with " + resourceId);
            }

            if (a.getId().equals(resourceId)) {
                return a;
            }
        }
        return null;
    }

    protected Collection<Attachment> getAttachments() {
        if (attachments == null) {
            attachments = AttachmentCachingSaajInInterceptor.getAttachments();
        }
        return attachments;
    }

}

现在,额外的调整是告诉 CXF 如何获取此内容的签名值:

package org.integration.client;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.InvalidCanonicalizerException;
import org.apache.xml.security.exceptions.Base64DecodingException;
import org.apache.xml.security.signature.XMLSignatureInput;
import org.apache.xml.security.transforms.Transform;
import org.apache.xml.security.transforms.TransformSpi;
import org.apache.xml.security.transforms.TransformationException;
import org.xml.sax.SAXException;

public class TransformAttachmentCiphertext extends TransformSpi {

    public static final String TRANSFORM_ATTACHMENT_CIPHERTEXT = 
            "http://docs.oasis-open.org/wss/oasis-wss-SwAProfile-1.1#Attachment-Ciphertext-Transform";

    /**
     * @see org.apache.xml.security.transforms.TransformSpi#engineGetURI()
     */
    @Override
    public String engineGetURI() {
        return TRANSFORM_ATTACHMENT_CIPHERTEXT;
    }

    /**
     * @see org.apache.xml.security.transforms.TransformSpi#enginePerformTransform(org.apache.xml.security.signature.XMLSignatureInput,
     *      java.io.OutputStream, org.apache.xml.security.transforms.Transform)
     */
    @Override
    protected XMLSignatureInput enginePerformTransform(XMLSignatureInput input,
            OutputStream os, Transform transformObject) throws IOException,
            CanonicalizationException, InvalidCanonicalizerException,
            TransformationException, ParserConfigurationException, SAXException {

        if (input.isOctetStream() || input.isNodeSet()) {
            if (os == null) {
                byte[] contentBytes = input.getBytes();
                XMLSignatureInput output = new XMLSignatureInput(contentBytes);
                return output;
            }

            if (input.isByteArray() || input.isNodeSet()) {
                os.write(input.getBytes());
            } else {
                try {
                    org.apache.xml.security.utils.Base64.decode(new BufferedInputStream(input.getOctetStreamReal()), os);
                } catch (Base64DecodingException e) {
                    throw new IOException("Unable to decode real octet stream", e);
                }
            }

            XMLSignatureInput output = new XMLSignatureInput(new byte[] {});
            output.setOutputStream(os);
            return output;
        }
        return input;
    }

}

最后,这两个元素需要在 CXF 中注册:

import org.apache.xml.security.utils.resolver.ResourceResolver;
...
ResourceResolver.register(AttachmentResolverSpi.class, true);
org.apache.xml.security.transforms.Transform.register(TransformAttachmentCiphertext.TRANSFORM_ATTACHMENT_CIPHERTEXT, TransformAttachmentCiphertext.class);

请不要问我是怎么做的,但是在这些操作之后,CXF 开始解密带有附件的 SOAP 消息。但这不是一个完整的解决方案,因为它仍然不适用于 MTOM 附件...发生的情况是 CXF 尝试使用附件内容更新 DOM 模型。这种方法不起作用有两个原因。首先,二进制附件通常不会生成格式良好的 XML 元素。其次,SOAP 消息上的 Attachment 实例没有更新。为了解决这个问题,并最终产生一个可行的解决方案,我不得不破解 DocumentSerializer(如果内容无法解析,则通过 base64 编码解决问题 1)和 XMLCipher(通过替换附件实例解决问题 2)。

【讨论】:

    猜你喜欢
    • 2014-03-02
    • 2013-01-12
    • 1970-01-01
    • 2012-02-29
    • 1970-01-01
    • 2016-01-31
    • 1970-01-01
    • 1970-01-01
    • 2011-05-15
    相关资源
    最近更新 更多