2014-01-31 4 views
2

Я создаю клиент CXF (версия 2.7.4), который вызывает веб-службу с поддержкой WS-Security. Он использует BinarySecurityToken, шифрование и подпись. Я получил CXF работает на «обычные» SOAP вызовов, но когда ответ возвращается как MTOM, я в конечном итоге получить странные ошибки в клиенте:CXF Client + WS-Security + MTOM = проблема?

 
org.apache.cxf.binding.soap.SoapFault: The signature or decryption was invalid 
    at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.createSoapFault(WSS4JInInterceptor.java:778) 
    at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:334) 
    at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:96) 
... 
Caused by: org.apache.xml.security.encryption.XMLEncryptionException: Could not find a resolver for URI cid:urn%3Auuid%[email protected] and Base null 
Original Exception was org.apache.xml.security.utils.resolver.ResourceResolverException: Could not find a resolver for URI cid:urn%3Auuid%[email protected] and Base null 
    at org.apache.xml.security.encryption.XMLCipherInput.getDecryptBytes(XMLCipherInput.java:134) 
    at org.apache.xml.security.encryption.XMLCipherInput.getBytes(XMLCipherInput.java:103) 
    ... 46 more 
Caused by: org.apache.xml.security.utils.resolver.ResourceResolverException: Could not find a resolver for URI cid:urn%3Auuid%[email protected] and Base null 
    at org.apache.xml.security.utils.resolver.ResourceResolver.internalGetInstance(ResourceResolver.java:130) 
    at org.apache.xml.security.utils.resolver.ResourceResolver.getInstance(ResourceResolver.java:87) 
    at org.apache.xml.security.encryption.XMLCipherInput.getDecryptBytes(XMLCipherInput.java:130) 
    ... 51 more 

Я пытался дозвониться setMTOMEnabled(true) на связывающем поставщике, установке SAAjInInterceptor, AttachmentInInterceptor, но все равно не повезло. Любые идеи о том, как исправить это?

+0

Предполагаю, что другим вариантом будет написать пользовательский ResourceResolver, а затем каким-то образом предоставить вложения через этот класс. Похож на перебор ... –

+0

Каковы правила безопасности XML в такой ситуации? Я пробовал читать их один раз (много лет назад), но они были очень сонными ... –

+3

А, мне жаль, что я не знал. Это очень странная настройка. Нам нужно настроить пользовательские заголовки, добавить токен двоичной безопасности, токен имени пользователя и отметку времени, а затем подписать все. Ответ возвращается и зашифрован. CXF отлично справляется с большинством вызовов, за исключением того, что с приложениями MTOM ... –

ответ

1

Все восходит к тому, что CXF не работает с MTOM когда WSS включена :(

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 не обновляются. Чтобы исправить это и, наконец, создать рабочее решение, мне пришлось взломать DocumentSerializer (разрешает проблему 1 с помощью base64, кодируя содержимое, если он не может быть разобран) и XMLCipher (разрешает проблему 2, заменяя экземпляры Attachment).

Смежные вопросы