cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From serg...@apache.org
Subject cxf git commit: [CXF-7365] Support for multipart jws detached signatures
Date Mon, 08 May 2017 13:20:41 GMT
Repository: cxf
Updated Branches:
  refs/heads/master c91f436fa -> 17b0b5f44


[CXF-7365] Support for multipart jws detached signatures


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/17b0b5f4
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/17b0b5f4
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/17b0b5f4

Branch: refs/heads/master
Commit: 17b0b5f444e5eea9b1c553ba512dbde99ef2c92a
Parents: c91f436
Author: Sergey Beryozkin <sberyozkin@gmail.com>
Authored: Mon May 8 14:20:27 2017 +0100
Committer: Sergey Beryozkin <sberyozkin@gmail.com>
Committed: Mon May 8 14:20:27 2017 +0100

----------------------------------------------------------------------
 .../cxf/jaxrs/ext/multipart/Attachment.java     |  15 +-
 .../cxf/jaxrs/ext/multipart/MultipartBody.java  |   6 +-
 .../ext/multipart/MultipartInputFilter.java     |  25 ++++
 .../ext/multipart/MultipartOutputFilter.java    |  25 ++++
 .../cxf/jaxrs/provider/MultipartProvider.java   |  13 +-
 .../org/apache/cxf/jaxrs/utils/JAXRSUtils.java  |  15 +-
 .../jaxrs/utils/multipart/AttachmentUtils.java  |  38 ++++-
 .../jose/jaxrs/JwsWriterInterceptor.java        |   2 +-
 .../JwsMultipartClientRequestFilter.java        |  91 ++++++++++++
 .../JwsMultipartContainerRequestFilter.java     |  59 ++++++++
 .../JwsMultipartSignatureInFilter.java          |  96 +++++++++++++
 .../JwsMultipartSignatureOutFilter.java         |  69 +++++++++
 .../JwsMultipartSignatureProvider.java          |  63 +++++++++
 .../jose/jws/HmacJwsSignatureVerifier.java      |  31 ++++
 .../security/jose/jws/JwsDetachedSignature.java |  36 +++++
 .../rs/security/jose/jws/JwsInputStream.java    |  67 +++++++++
 .../rs/security/jose/jws/JwsOutputStream.java   |  14 +-
 .../security/jose/jws/JwsSignatureVerifier.java |   4 +
 .../jose/jws/JwsVerificationSignature.java      |  25 ++++
 .../jose/jws/NoneJwsSignatureVerifier.java      |  18 +++
 .../jose/jws/PublicKeyJwsSignatureVerifier.java |  35 +++++
 .../cxf/rt/security/crypto/CryptoUtils.java     |  20 ++-
 .../systest/jaxrs/security/jose/BookStore.java  |  20 ++-
 .../jose/jwejws/BookServerJwsMultipart.java     |  59 ++++++++
 .../security/jose/jwejws/JAXRSJweJwsTest.java   |   2 +-
 .../security/jose/jwejws/JAXRSJwsJsonTest.java  |   2 +-
 .../jose/jwejws/JAXRSJwsMultipartTest.java      | 140 +++++++++++++++++++
 .../jwejws/MultipartModificationFilter.java     |  52 +++++++
 .../security/jose/jwejws/serverMultipart.xml    |  82 +++++++++++
 29 files changed, 1099 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java
----------------------------------------------------------------------
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java
index ca095d0..1ae0019 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java
@@ -24,6 +24,7 @@ import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.util.Iterator;
 import java.util.List;
+import java.util.UUID;
 
 import javax.activation.DataHandler;
 import javax.activation.DataSource;
@@ -89,9 +90,15 @@ public class Attachment implements Transferable {
              headers);
     }
 
+    public Attachment(String mediaType, Object object) {
+        this(UUID.randomUUID().toString(), mediaType, object);
+    }
+    
     public Attachment(String id, String mediaType, Object object) {
         this.object = object;
-        headers.putSingle("Content-ID", id);
+        if (id != null) {
+            headers.putSingle("Content-ID", id);
+        }
         headers.putSingle("Content-Type", mediaType);
     }
 
@@ -121,13 +128,17 @@ public class Attachment implements Transferable {
     }
 
     public MediaType getContentType() {
-        String value = handler != null ? handler.getContentType() : headers.getFirst("Content-Type");
+        String value = handler != null && handler.getContentType() != null ? handler.getContentType() 
+            : headers.getFirst("Content-Type");
         return value == null ? MediaType.TEXT_PLAIN_TYPE : JAXRSUtils.toMediaType(value);
     }
 
     public DataHandler getDataHandler() {
         return handler;
     }
+    public void setDataHandler(DataHandler dataHandler) {
+        this.handler = dataHandler;
+    }
 
     public Object getObject() {
         return object;

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBody.java
----------------------------------------------------------------------
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBody.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBody.java
index b3e2ca3..01a51d7 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBody.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartBody.java
@@ -19,7 +19,6 @@
 package org.apache.cxf.jaxrs.ext.multipart;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 import javax.ws.rs.core.MediaType;
@@ -32,13 +31,11 @@ public class MultipartBody {
     public static final String OUTBOUND_MESSAGE_ATTACHMENTS = "org.apache.cxf.jaxrs.attachments.outbound";
 
     private static final MediaType MULTIPART_RELATED_TYPE = JAXRSUtils.toMediaType("multipart/related");
-    private boolean outbound;
     private List<Attachment> atts;
     private MediaType mt;
 
     public MultipartBody(List<Attachment> atts, MediaType mt, boolean outbound) {
         this.atts = atts;
-        this.outbound = outbound;
         this.mt = mt == null ? MULTIPART_RELATED_TYPE : mt;
     }
 
@@ -49,7 +46,6 @@ public class MultipartBody {
     public MultipartBody(Attachment att) {
         atts = new ArrayList<>();
         atts.add(att);
-        outbound = true;
         this.mt = MULTIPART_RELATED_TYPE;
     }
 
@@ -67,7 +63,7 @@ public class MultipartBody {
 
     public List<Attachment> getAllAttachments() {
 
-        return outbound ? atts : Collections.unmodifiableList(atts);
+        return atts;
     }
 
     public List<Attachment> getChildAttachments() {

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartInputFilter.java
----------------------------------------------------------------------
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartInputFilter.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartInputFilter.java
new file mode 100644
index 0000000..ed0fee7
--- /dev/null
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartInputFilter.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.jaxrs.ext.multipart;
+
+import java.util.List;
+
+public interface MultipartInputFilter {
+    void filter(List<Attachment> atts);
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartOutputFilter.java
----------------------------------------------------------------------
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartOutputFilter.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartOutputFilter.java
new file mode 100644
index 0000000..9c162d2
--- /dev/null
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/MultipartOutputFilter.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.jaxrs.ext.multipart;
+
+import java.util.List;
+
+public interface MultipartOutputFilter {
+    void filter(List<Attachment> parts);
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java
----------------------------------------------------------------------
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java
index 95ad0cb..a14958a 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java
@@ -62,6 +62,7 @@ import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
 import org.apache.cxf.jaxrs.ext.multipart.InputStreamDataSource;
 import org.apache.cxf.jaxrs.ext.multipart.Multipart;
 import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartOutputFilter;
 import org.apache.cxf.jaxrs.impl.MetadataMap;
 import org.apache.cxf.jaxrs.utils.AnnotationUtils;
 import org.apache.cxf.jaxrs.utils.ExceptionUtils;
@@ -262,6 +263,12 @@ public class MultipartProvider extends AbstractConfigurableProvider
         throws IOException, WebApplicationException {
 
         List<Attachment> handlers = convertToDataHandlers(obj, type, genericType, anns, mt);
+        if (mc.get(AttachmentUtils.OUT_FILTERS) != null) {
+            List<MultipartOutputFilter> filters = CastUtils.cast((List<?>)mc.get(AttachmentUtils.OUT_FILTERS));
+            for (MultipartOutputFilter filter : filters) {
+                filter.filter(handlers);
+            }
+        }
         mc.put(MultipartBody.OUTBOUND_MESSAGE_ATTACHMENTS, handlers);
         handlers.get(0).getDataHandler().writeTo(os);
     }
@@ -361,8 +368,10 @@ public class MultipartProvider extends AbstractConfigurableProvider
             dh = getHandlerForObject(obj, cls, genericType, anns, mimeType, id);
         }
         String contentId = getContentId(anns, id);
-
-        return new Attachment(contentId, dh, new MetadataMap<String, String>());
+        MultivaluedMap<String, String> headers = new MetadataMap<String, String>();
+        headers.putSingle("Content-Type", mimeType);
+        
+        return new Attachment(contentId, dh, headers);
     }
 
     private String getContentId(Annotation[] anns, int id) {

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
----------------------------------------------------------------------
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
index 8f10686..42f18a9 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/JAXRSUtils.java
@@ -1464,7 +1464,10 @@ public final class JAXRSUtils {
                     boolean parametersMatched = true;
                     for (Map.Entry<String, String> entry : userType.getParameters().entrySet()) {
                         String value = requiredType.getParameters().get(entry.getKey());
-                        if (value != null && !value.equals(entry.getValue())) {
+                        if (value != null && entry.getValue() != null
+                            && !(stripDoubleQuotesIfNeeded(value).equals(
+                                    stripDoubleQuotesIfNeeded(entry.getValue())))) {
+                            
                             if (HTTP_CHARSET_PARAM.equals(entry.getKey())
                                 && value.equalsIgnoreCase(entry.getValue())) {
                                 continue;
@@ -1509,10 +1512,18 @@ public final class JAXRSUtils {
         return new ArrayList<>(supportedMimeTypeList);
 
     }
+    
+    private static String stripDoubleQuotesIfNeeded(String value) {
+        if (value != null && value.startsWith("\"") 
+            && value.endsWith("\"") && value.length() > 1) {
+            value = value.substring(1,  value.length() - 1);
+        }
+        return value;
+    }
 
     private static boolean isMediaTypeCompatible(MediaType requiredType, MediaType userType) {
         boolean isCompatible = requiredType.isCompatible(userType);
-        if (!requiredType.isCompatible(userType) && requiredType.getType().equalsIgnoreCase(userType.getType())) {
+        if (!isCompatible && requiredType.getType().equalsIgnoreCase(userType.getType())) {
             isCompatible = compareCompositeSubtypes(requiredType, userType,
                                                     PhaseInterceptorChain.getCurrentMessage());
         }

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java
----------------------------------------------------------------------
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java
index ce71701..e2f9f6b 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/multipart/AttachmentUtils.java
@@ -20,6 +20,7 @@
 package org.apache.cxf.jaxrs.utils.multipart;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -33,24 +34,52 @@ import javax.ws.rs.core.MultivaluedMap;
 import org.apache.cxf.attachment.AttachmentDeserializer;
 import org.apache.cxf.common.i18n.BundleUtils;
 import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.helpers.CastUtils;
 import org.apache.cxf.jaxrs.ext.MessageContext;
 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
 import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition;
 import org.apache.cxf.jaxrs.ext.multipart.Multipart;
 import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartInputFilter;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartOutputFilter;
 import org.apache.cxf.jaxrs.impl.MetadataMap;
 import org.apache.cxf.jaxrs.utils.ExceptionUtils;
 import org.apache.cxf.jaxrs.utils.FormUtils;
 import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
 import org.apache.cxf.phase.PhaseInterceptorChain;
 
 public final class AttachmentUtils {
+    public static final String OUT_FILTERS = "multipart.output.filters";
+    public static final String IN_FILTERS = "multipart.input.filters";
     private static final Logger LOG = LogUtils.getL7dLogger(JAXRSUtils.class);
     private static final ResourceBundle BUNDLE = BundleUtils.getBundle(JAXRSUtils.class);
 
     private AttachmentUtils() {
     }
 
+    public static void addMultipartOutFilter(MultipartOutputFilter filter) {
+        Message m = JAXRSUtils.getCurrentMessage();
+        List<MultipartOutputFilter> outFilters = CastUtils.cast((List<?>)m.get(OUT_FILTERS));
+        if (outFilters == null) {
+            outFilters = new ArrayList<MultipartOutputFilter>();
+        }
+        outFilters.add(filter);
+        m.put(OUT_FILTERS, outFilters);
+    }
+    
+    public static void addMultipartInFilter(MultipartInputFilter filter) {
+        Message m = JAXRSUtils.getCurrentMessage();
+        List<MultipartInputFilter> inFilters = CastUtils.cast((List<?>)m.get(IN_FILTERS));
+        if (inFilters == null) {
+            inFilters = new ArrayList<MultipartInputFilter>();
+        }
+        inFilters.add(filter);
+        m.put(IN_FILTERS, inFilters);
+    }
+
+
+    
     public static MultipartBody getMultipartBody(MessageContext mc) {
         return (MultipartBody)mc.get(MultipartBody.INBOUND_MESSAGE_ATTACHMENTS);
     }
@@ -111,7 +140,14 @@ public final class AttachmentUtils {
         String propertyName = embeddedAttachment ? MultipartBody.INBOUND_MESSAGE_ATTACHMENTS + ".embedded"
             : MultipartBody.INBOUND_MESSAGE_ATTACHMENTS;
 
-        return (MultipartBody)mc.get(propertyName);
+        MultipartBody body = (MultipartBody)mc.get(propertyName);
+        if (!embeddedAttachment && mc.get(IN_FILTERS) != null) {
+            List<MultipartInputFilter> filters = CastUtils.cast((List<?>)mc.get(IN_FILTERS));
+            for (MultipartInputFilter filter : filters) {
+                filter.filter(body.getAllAttachments());
+            }
+        }
+        return body;
     }
 
     public static List<Attachment> getAttachments(MessageContext mc,

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsWriterInterceptor.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsWriterInterceptor.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsWriterInterceptor.java
index c0760fe..38ba470 100644
--- a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsWriterInterceptor.java
+++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwsWriterInterceptor.java
@@ -65,7 +65,7 @@ public class JwsWriterInterceptor extends AbstractJwsWriterProvider implements W
         if (useJwsOutputStream) {
             JwsSignature jwsSignature = sigProvider.createJwsSignature(headers);
             JoseUtils.traceHeaders(headers);
-            JwsOutputStream jwsStream = new JwsOutputStream(actualOs, jwsSignature);
+            JwsOutputStream jwsStream = new JwsOutputStream(actualOs, jwsSignature, true);
             byte[] headerBytes = StringUtils.toBytesUTF8(writer.toJson(headers));
             Base64UrlUtility.encodeAndStream(headerBytes, 0, headerBytes.length, jwsStream);
             jwsStream.write(new byte[]{'.'});

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartClientRequestFilter.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartClientRequestFilter.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartClientRequestFilter.java
new file mode 100644
index 0000000..c13d0a5
--- /dev/null
+++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartClientRequestFilter.java
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.rs.security.jose.jaxrs.multipart;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
+import org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils;
+import org.apache.cxf.rs.security.jose.common.JoseConstants;
+import org.apache.cxf.rs.security.jose.jws.JwsDetachedSignature;
+import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
+import org.apache.cxf.rs.security.jose.jws.JwsSignature;
+import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider;
+import org.apache.cxf.rs.security.jose.jws.JwsUtils;
+
+public class JwsMultipartClientRequestFilter implements ClientRequestFilter {
+
+    private JwsSignatureProvider sigProvider;
+    private boolean supportSinglePartOnly = true;
+
+    public void setSignatureProvider(JwsSignatureProvider signatureProvider) {
+        this.sigProvider = signatureProvider;
+    }
+    
+    @Override
+    public void filter(ClientRequestContext ctx) throws IOException {
+        MediaType contentType = ctx.getMediaType();
+        if (contentType != null && contentType.getType().equals("multipart")) {
+            Object rootEntity = ctx.getEntity();
+            List<Object> parts = null;
+            
+            if (rootEntity instanceof MultipartBody) {
+                parts = CastUtils.cast(((MultipartBody)rootEntity).getAllAttachments());
+            } else {
+                parts = new ArrayList<Object>();
+                if (rootEntity instanceof List) {
+                    List<Object> entityList = CastUtils.cast((List<?>)rootEntity);
+                    parts.addAll(entityList);
+                } else {
+                    parts.add(rootEntity);
+                }
+            }
+            if (supportSinglePartOnly && parts.size() > 1) {
+                throw new ProcessingException("Single part only is supported");
+            }
+            
+            JwsHeaders headers = new JwsHeaders();
+            JwsSignatureProvider theSigProvider = sigProvider != null ? sigProvider
+                : JwsUtils.loadSignatureProvider(headers, true);
+            JwsSignature jwsSignature = theSigProvider.createJwsSignature(headers);
+            AttachmentUtils.addMultipartOutFilter(new JwsMultipartSignatureOutFilter(jwsSignature));
+            
+            
+            JwsDetachedSignature jws = new JwsDetachedSignature(headers, jwsSignature);
+            
+            Attachment jwsPart = new Attachment("signature", JoseConstants.MEDIA_TYPE_JOSE, jws);
+            parts.add(jwsPart);
+            ctx.setEntity(parts);
+        }
+        
+    }
+
+    public void setSupportSinglePartOnly(boolean supportSinglePartOnly) {
+        this.supportSinglePartOnly = supportSinglePartOnly;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartContainerRequestFilter.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartContainerRequestFilter.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartContainerRequestFilter.java
new file mode 100644
index 0000000..eb69543
--- /dev/null
+++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartContainerRequestFilter.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.rs.security.jose.jaxrs.multipart;
+
+import java.io.IOException;
+
+import javax.annotation.Priority;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.cxf.jaxrs.ext.multipart.MultipartInputFilter;
+import org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils;
+import org.apache.cxf.rs.security.jose.jaxrs.Priorities;
+import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
+
+@PreMatching
+@Priority(Priorities.JWS_SERVER_READ_PRIORITY)
+public class JwsMultipartContainerRequestFilter implements ContainerRequestFilter {
+    
+    private JwsSignatureVerifier sigVerifier;
+    private boolean supportSinglePartOnly = true;
+    
+    @Override
+    public void filter(ContainerRequestContext ctx) throws IOException {
+        MediaType contentType = ctx.getMediaType();
+        if (contentType != null && contentType.getType().equals("multipart")) {
+            MultipartInputFilter jwsFilter = sigVerifier == null 
+                ? new JwsMultipartSignatureInFilter(supportSinglePartOnly) 
+                : new JwsMultipartSignatureInFilter(sigVerifier, supportSinglePartOnly); 
+            AttachmentUtils.addMultipartInFilter(jwsFilter); 
+        }
+        
+    }
+
+    public void setSigVerifier(JwsSignatureVerifier sigVerifier) {
+        this.sigVerifier = sigVerifier;
+    }
+    public void setSupportSinglePartOnly(boolean supportSinglePartOnly) {
+        this.supportSinglePartOnly = supportSinglePartOnly;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartSignatureInFilter.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartSignatureInFilter.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartSignatureInFilter.java
new file mode 100644
index 0000000..547fa58
--- /dev/null
+++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartSignatureInFilter.java
@@ -0,0 +1,96 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.rs.security.jose.jaxrs.multipart;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartInputFilter;
+import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+import org.apache.cxf.rs.security.jose.common.JoseUtils;
+import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
+import org.apache.cxf.rs.security.jose.jws.JwsInputStream;
+import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier;
+import org.apache.cxf.rs.security.jose.jws.JwsUtils;
+import org.apache.cxf.rs.security.jose.jws.JwsVerificationSignature;
+
+public class JwsMultipartSignatureInFilter implements MultipartInputFilter {
+
+    private JwsSignatureVerifier verifier;
+    private boolean supportSinglePartOnly;
+    
+    public JwsMultipartSignatureInFilter(boolean supportSinglePartOnly) {
+        this(null, supportSinglePartOnly);
+    }
+    
+    public JwsMultipartSignatureInFilter(JwsSignatureVerifier verifier, boolean supportSinglePartOnly) {
+        this.verifier = verifier;
+        this.supportSinglePartOnly = supportSinglePartOnly;
+    }
+    
+    @Override
+    public void filter(List<Attachment> atts) {
+        if (atts.size() < 2 || supportSinglePartOnly && atts.size() > 2) {
+            throw ExceptionUtils.toBadRequestException(null, null);
+        }
+        Attachment sigPart = atts.remove(atts.size() - 1);
+        
+        String encodedJws = null;
+        try {
+            encodedJws = IOUtils.readStringFromStream(sigPart.getDataHandler().getInputStream());
+        } catch (IOException ex) {
+            throw ExceptionUtils.toBadRequestException(null, null);
+        }
+        String[] parts = JoseUtils.getCompactParts(encodedJws);
+        // Detached signature
+        if (parts.length != 3 || parts[1].length() > 0) {
+            throw ExceptionUtils.toBadRequestException(null, null);
+        }
+        JwsHeaders headers = new JwsHeaders(
+                                 new JsonMapObjectReaderWriter().fromJson(
+                                     JoseUtils.decodeToString(parts[0])));
+        JwsSignatureVerifier theVerifier = 
+            verifier == null ? JwsUtils.loadSignatureVerifier(headers, true) : verifier;
+        
+        JwsVerificationSignature sig = theVerifier.createJwsVerificationSignature(headers);
+        if (sig == null) {
+            throw ExceptionUtils.toBadRequestException(null, null);
+        }
+        byte[] signatureBytes = JoseUtils.decode(parts[2]);
+        
+        int attSize = atts.size();
+        for (int i = 0; i < attSize; i++) {
+            Attachment dataPart = atts.remove(i);
+            InputStream dataPartStream = null;
+            try {
+                dataPartStream = dataPart.getDataHandler().getDataSource().getInputStream();
+            } catch (IOException ex) {
+                throw ExceptionUtils.toBadRequestException(ex, null);
+            }
+            boolean verifyOnLastRead = i == attSize - 1 ? true : false;
+            JwsInputStream jwsStream = new JwsInputStream(dataPartStream, sig, signatureBytes, verifyOnLastRead);
+            Attachment newDataPart = new Attachment(jwsStream, dataPart.getHeaders());
+            atts.add(i, newDataPart);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartSignatureOutFilter.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartSignatureOutFilter.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartSignatureOutFilter.java
new file mode 100644
index 0000000..c0305a5
--- /dev/null
+++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartSignatureOutFilter.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.rs.security.jose.jaxrs.multipart;
+
+import java.io.OutputStream;
+import java.util.List;
+
+import javax.activation.DataHandler;
+
+import org.apache.cxf.attachment.ByteDataSource;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartOutputFilter;
+import org.apache.cxf.rs.security.jose.jws.JwsException;
+import org.apache.cxf.rs.security.jose.jws.JwsOutputStream;
+import org.apache.cxf.rs.security.jose.jws.JwsSignature;
+
+public class JwsMultipartSignatureOutFilter implements MultipartOutputFilter {
+
+    private JwsSignature sig;
+    public JwsMultipartSignatureOutFilter(JwsSignature sig) {
+        this.sig = sig;
+    }
+    @Override
+    public void filter(List<Attachment> parts) {
+        for (int i = 0; i < parts.size() - 1; i++) {
+            Attachment dataPart = parts.get(i);
+            DataHandler handler = dataPart.getDataHandler();
+            dataPart.setDataHandler(new JwsSignatureDataHandler(handler));
+        }
+    }
+
+    private class JwsSignatureDataHandler extends DataHandler {
+        private DataHandler handler;
+        JwsSignatureDataHandler(DataHandler handler) {
+            super(new ByteDataSource("1".getBytes()));
+            this.handler = handler;
+        }
+        @Override
+        public String getContentType() {
+            return handler.getContentType();
+        }
+        @Override
+        public void writeTo(OutputStream os) {
+            JwsOutputStream jwsOutStream = new JwsOutputStream(os, sig, false);
+            try {
+                handler.writeTo(jwsOutStream);
+                jwsOutStream.flush();
+            } catch (Exception ex) {
+                throw new JwsException(JwsException.Error.INVALID_SIGNATURE);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartSignatureProvider.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartSignatureProvider.java b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartSignatureProvider.java
new file mode 100644
index 0000000..f6cd1fa
--- /dev/null
+++ b/rt/rs/security/jose-parent/jose-jaxrs/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/multipart/JwsMultipartSignatureProvider.java
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.rs.security.jose.jaxrs.multipart;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.apache.cxf.common.util.Base64UrlUtility;
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter;
+import org.apache.cxf.rs.security.jose.common.JoseUtils;
+import org.apache.cxf.rs.security.jose.jws.JwsDetachedSignature;
+
+public class JwsMultipartSignatureProvider implements MessageBodyWriter<JwsDetachedSignature> {
+    private JsonMapObjectReaderWriter writer = new JsonMapObjectReaderWriter();
+    @Override
+    public long getSize(JwsDetachedSignature arg0, Class<?> arg1, Type arg2, Annotation[] arg3, MediaType arg4) {
+        return -1;
+    }
+
+    @Override
+    public boolean isWriteable(Class<?> cls, Type t, Annotation[] anns, MediaType mt) {
+        return true;
+    }
+
+    @Override
+    public void writeTo(JwsDetachedSignature parts, Class<?> cls, Type t, Annotation[] anns, MediaType mt,
+                        MultivaluedMap<String, Object> headers, OutputStream os)
+                            throws IOException, WebApplicationException {
+        JoseUtils.traceHeaders(parts.getHeaders());
+        byte[] headerBytes = StringUtils.toBytesUTF8(writer.toJson(parts.getHeaders()));
+        Base64UrlUtility.encodeAndStream(headerBytes, 0, headerBytes.length, os);
+        os.write(new byte[]{'.'});
+        
+        byte[] finalBytes = parts.getSignature().sign();
+        os.write(new byte[]{'.'});
+        Base64UrlUtility.encodeAndStream(finalBytes, 0, finalBytes.length, os);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/HmacJwsSignatureVerifier.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/HmacJwsSignatureVerifier.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/HmacJwsSignatureVerifier.java
index 09afa5c..56b3c62 100644
--- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/HmacJwsSignatureVerifier.java
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/HmacJwsSignatureVerifier.java
@@ -22,6 +22,8 @@ import java.security.MessageDigest;
 import java.security.spec.AlgorithmParameterSpec;
 import java.util.logging.Logger;
 
+import javax.crypto.Mac;
+
 import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.rs.security.jose.common.JoseUtils;
 import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils;
@@ -82,4 +84,33 @@ public class HmacJwsSignatureVerifier implements JwsSignatureVerifier {
     public SignatureAlgorithm getAlgorithm() {
         return supportedAlgo;
     }
+    @Override
+    public JwsVerificationSignature createJwsVerificationSignature(JwsHeaders headers) {
+        final String sigAlgo = checkAlgorithm(headers.getSignatureAlgorithm());
+        Mac mac = HmacUtils.getInitializedMac(key,
+                                     AlgorithmUtils.toJavaName(sigAlgo),
+                                     hmacSpec);
+        return new HmacJwsVerificationSignature(mac);
+    }
+    
+    private static class HmacJwsVerificationSignature implements JwsVerificationSignature {
+
+        private Mac mac;
+        
+        HmacJwsVerificationSignature(Mac mac) {
+            this.mac = mac;
+        }
+
+        @Override
+        public void update(byte[] src, int off, int len) {
+            mac.update(src, off, len);
+        }
+
+        @Override
+        public boolean verify(byte[] signature) {
+            byte[] macBytes = mac.doFinal();
+            return MessageDigest.isEqual(macBytes, signature);
+        }
+        
+    }
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsDetachedSignature.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsDetachedSignature.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsDetachedSignature.java
new file mode 100644
index 0000000..4de23f1
--- /dev/null
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsDetachedSignature.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.rs.security.jose.jws;
+
+public class JwsDetachedSignature {
+    private JwsHeaders headers;
+    private JwsSignature signature;
+    public JwsDetachedSignature(JwsHeaders headers, JwsSignature signature) {
+        this.headers = headers;
+        this.signature = signature;
+    }
+    public JwsHeaders getHeaders() {
+        return headers;
+    }
+    public JwsSignature getSignature() {
+        return signature;
+    }
+    
+    
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsInputStream.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsInputStream.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsInputStream.java
new file mode 100644
index 0000000..b94eef7
--- /dev/null
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsInputStream.java
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.rs.security.jose.jws;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+public class JwsInputStream extends FilterInputStream {
+    private JwsVerificationSignature signature;
+    private byte[] signatureBytes;
+    private boolean verifyOnLastRead;
+    public JwsInputStream(InputStream out, 
+                          JwsVerificationSignature signature,
+                          byte[] signatureBytes,
+                          boolean verifyOnLastRead) {
+        super(out);
+        this.signature = signature;
+        this.signatureBytes = signatureBytes;
+        this.verifyOnLastRead = verifyOnLastRead;
+    }
+
+    @Override
+    public int read() throws IOException {
+        int value = super.read();
+        if (value != -1) {
+            byte[] bytes = ByteBuffer.allocate(Integer.SIZE / 8).putInt(value).array();
+            signature.update(bytes, 0, bytes.length);
+        } else {
+            verify();
+        }
+        return value;
+    }
+ 
+    public int read(byte b[], int off, int len) throws IOException {
+        int num = in.read(b, off, len);
+        if (num != -1) {
+            signature.update(b, off, num);
+        } else {
+            verify();
+        }
+        return num;
+    }
+    
+    private void verify() {
+        if (verifyOnLastRead && !signature.verify(signatureBytes)) {
+            throw new JwsException(JwsException.Error.INVALID_SIGNATURE);    
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsOutputStream.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsOutputStream.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsOutputStream.java
index e5951ff..d7d5cf1 100644
--- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsOutputStream.java
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsOutputStream.java
@@ -28,9 +28,11 @@ import org.apache.cxf.common.util.Base64UrlUtility;
 public class JwsOutputStream extends FilterOutputStream {
     private boolean flushed;
     private JwsSignature signature;
-    public JwsOutputStream(OutputStream out, JwsSignature signature) {
+    private boolean writeSignature;
+    public JwsOutputStream(OutputStream out, JwsSignature signature, boolean writeSignature) {
         super(out);
         this.signature = signature;
+        this.writeSignature = writeSignature;
     }
 
     @Override
@@ -50,9 +52,13 @@ public class JwsOutputStream extends FilterOutputStream {
         if (flushed) {
             return;
         }
-        byte[] finalBytes = signature.sign();
-        out.write(new byte[]{'.'});
-        Base64UrlUtility.encodeAndStream(finalBytes, 0, finalBytes.length, out);
+        if (writeSignature) {
+            byte[] finalBytes = signature.sign();
+            out.write(new byte[]{'.'});
+            Base64UrlUtility.encodeAndStream(finalBytes, 0, finalBytes.length, out);
+        } else {
+            super.flush();
+        }
         flushed = true;
     }
 

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignatureVerifier.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignatureVerifier.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignatureVerifier.java
index c44a678..067ac99 100644
--- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignatureVerifier.java
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsSignatureVerifier.java
@@ -23,4 +23,8 @@ import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
 public interface JwsSignatureVerifier {
     SignatureAlgorithm getAlgorithm();
     boolean verify(JwsHeaders headers, String unsignedText, byte[] signature);
+    /**
+     * Create a verification signature handler capable of accumulating the input (optional operation)
+     */
+    JwsVerificationSignature createJwsVerificationSignature(JwsHeaders headers);
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsVerificationSignature.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsVerificationSignature.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsVerificationSignature.java
new file mode 100644
index 0000000..d9e13a1
--- /dev/null
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsVerificationSignature.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.rs.security.jose.jws;
+
+
+public interface JwsVerificationSignature {
+    void update(byte[] src, int off, int len);
+    boolean verify(byte[] signature);
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/NoneJwsSignatureVerifier.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/NoneJwsSignatureVerifier.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/NoneJwsSignatureVerifier.java
index de1bcc3..22b51a5 100644
--- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/NoneJwsSignatureVerifier.java
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/NoneJwsSignatureVerifier.java
@@ -33,4 +33,22 @@ public class NoneJwsSignatureVerifier implements JwsSignatureVerifier {
         return SignatureAlgorithm.NONE;
     }
 
+    @Override
+    public JwsVerificationSignature createJwsVerificationSignature(JwsHeaders headers) {
+        return new NoneJwsVerificationSignature();
+    }
+
+    private static class NoneJwsVerificationSignature implements JwsVerificationSignature {
+
+        @Override
+        public void update(byte[] src, int off, int len) {
+            // complete
+        }
+
+        @Override
+        public boolean verify(byte[] signature) {
+            return signature.length == 0;
+        }
+
+    }
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PublicKeyJwsSignatureVerifier.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PublicKeyJwsSignatureVerifier.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PublicKeyJwsSignatureVerifier.java
index d76474f..5b1faae 100644
--- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PublicKeyJwsSignatureVerifier.java
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/PublicKeyJwsSignatureVerifier.java
@@ -19,6 +19,7 @@
 package org.apache.cxf.rs.security.jose.jws;
 
 import java.security.PublicKey;
+import java.security.Signature;
 import java.security.cert.X509Certificate;
 import java.security.spec.AlgorithmParameterSpec;
 import java.util.logging.Logger;
@@ -97,4 +98,38 @@ public class PublicKeyJwsSignatureVerifier implements JwsSignatureVerifier {
     public X509Certificate getX509Certificate() {
         return cert;
     }
+    @Override
+    public JwsVerificationSignature createJwsVerificationSignature(JwsHeaders headers) {
+        Signature sig = CryptoUtils.getVerificationSignature(key,
+                                    AlgorithmUtils.toJavaName(checkAlgorithm(
+                                                              headers.getSignatureAlgorithm())),
+                                    signatureSpec);
+        return new PublicKeyJwsVerificationSignature(sig);
+    }
+    
+    private class PublicKeyJwsVerificationSignature implements JwsVerificationSignature {
+        private Signature sig;
+        PublicKeyJwsVerificationSignature(Signature sig) {
+            this.sig = sig;
+        }
+
+        @Override
+        public void update(byte[] src, int off, int len) {
+            try {
+                sig.update(src, off, len);
+            } catch (Exception ex) {
+                throw new JwsException(JwsException.Error.INVALID_SIGNATURE, ex);
+            }
+        }
+
+        @Override
+        public boolean verify(byte[] signatureBytes) {
+            try {
+                return sig.verify(signatureBytes);
+            } catch (Exception ex) {
+                throw new JwsException(JwsException.Error.INVALID_SIGNATURE, ex);
+            }
+        }
+        
+    }
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/CryptoUtils.java
----------------------------------------------------------------------
diff --git a/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/CryptoUtils.java b/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/CryptoUtils.java
index 2d098e7..94f651f 100644
--- a/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/CryptoUtils.java
+++ b/rt/security/src/main/java/org/apache/cxf/rt/security/crypto/CryptoUtils.java
@@ -348,6 +348,20 @@ public final class CryptoUtils {
         }
     }
 
+    public static Signature getVerificationSignature(PublicKey key, 
+                                                        String signAlgo, 
+                                                        AlgorithmParameterSpec params) {
+        try {
+            Signature s = Signature.getInstance(signAlgo);
+            s.initVerify(key);
+            if (params != null) {
+                s.setParameter(params);
+            }
+            return s;
+        } catch (Exception ex) {
+            throw new SecurityException(ex);
+        }
+    }
     public static boolean verifySignature(byte[] data, byte[] signature, PublicKey key, String signAlgo) {
         return verifySignature(data, signature, key, signAlgo, null);
     }
@@ -355,11 +369,7 @@ public final class CryptoUtils {
     public static boolean verifySignature(byte[] data, byte[] signature, PublicKey key, String signAlgo,
                                 AlgorithmParameterSpec params) {
         try {
-            Signature s = Signature.getInstance(signAlgo);
-            s.initVerify(key);
-            if (params != null) {
-                s.setParameter(params);
-            }
+            Signature s = getVerificationSignature(key, signAlgo, params);
             s.update(data);
             return s.verify(signature);
         } catch (Exception ex) {

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/BookStore.java
----------------------------------------------------------------------
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/BookStore.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/BookStore.java
index 6bc56b8d..fee5675 100644
--- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/BookStore.java
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/BookStore.java
@@ -20,11 +20,14 @@
 package org.apache.cxf.systest.jaxrs.security.jose;
 
 
+import java.util.List;
+
 import javax.ws.rs.Consumes;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 
+import org.apache.cxf.jaxrs.ext.multipart.Multipart;
 import org.apache.cxf.systest.jaxrs.security.Book;
 
 @Path("/bookstore")
@@ -53,9 +56,24 @@ public class BookStore {
     @Path("/books")
     @Produces("application/xml")
     @Consumes("application/xml")
-    public Book echoBook2(Book book) {
+    public Book echoBookXml(Book book) {
         return book;
     }
+    
+    @POST
+    @Path("/books")
+    @Produces("application/xml")
+    @Consumes("multipart/related")
+    public Book echoBookMultipart(@Multipart(type = "application/xml") Book book) {
+        return book;
+    }
+    @POST
+    @Path("/booksList")
+    @Produces("application/xml")
+    @Consumes("multipart/related")
+    public List<Book> echoBooksMultipart(@Multipart(type = "application/xml") List<Book> books) {
+        return books;
+    }
 
 }
 

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/BookServerJwsMultipart.java
----------------------------------------------------------------------
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/BookServerJwsMultipart.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/BookServerJwsMultipart.java
new file mode 100644
index 0000000..ceef39b
--- /dev/null
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/BookServerJwsMultipart.java
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.jaxrs.security.jose.jwejws;
+
+import java.net.URL;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
+import org.apache.cxf.testutil.common.TestUtil;
+
+public class BookServerJwsMultipart extends AbstractBusTestServerBase {
+    public static final String PORT = TestUtil.getPortNumber("jaxrs-jws-multipart");
+    private static final URL SERVER_CONFIG_FILE =
+        BookServerJwsMultipart.class.getResource("serverMultipart.xml");
+
+    protected void run() {
+        SpringBusFactory bf = new SpringBusFactory();
+        Bus springBus = bf.createBus(SERVER_CONFIG_FILE);
+        BusFactory.setDefaultBus(springBus);
+        setBus(springBus);
+
+        try {
+            new BookServerJwsMultipart();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void main(String[] args) {
+        try {
+            BookServerJwsMultipart s = new BookServerJwsMultipart();
+            s.start();
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            System.exit(-1);
+        } finally {
+            System.out.println("done!");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJweJwsTest.java
----------------------------------------------------------------------
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJweJwsTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJweJwsTest.java
index 84f466f..2b2b38a 100644
--- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJweJwsTest.java
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJweJwsTest.java
@@ -441,7 +441,7 @@ public class JAXRSJweJwsTest extends AbstractBusClientServerTestBase {
         BookStore bs = createJweJwsBookStore(address, null, null);
         Book book = new Book();
         book.setName("book");
-        book = bs.echoBook2(book);
+        book = bs.echoBookXml(book);
         assertEquals("book", book.getName());
     }
 

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJwsJsonTest.java
----------------------------------------------------------------------
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJwsJsonTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJwsJsonTest.java
index 56c75ab..206eb61 100644
--- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJwsJsonTest.java
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJwsJsonTest.java
@@ -146,7 +146,7 @@ public class JAXRSJwsJsonTest extends AbstractBusClientServerTestBase {
         map.put(JoseConstants.RSSEC_SIGNATURE_IN_PROPS,
                 "org/apache/cxf/systest/jaxrs/security/secret.jwk.properties");
         BookStore bs = createBookStore(address, map, null);
-        Book book = bs.echoBook2(new Book("book", 123L));
+        Book book = bs.echoBookXml(new Book("book", 123L));
         assertEquals("book", book.getName());
         assertEquals(123L, book.getId());
     }

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJwsMultipartTest.java
----------------------------------------------------------------------
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJwsMultipartTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJwsMultipartTest.java
new file mode 100644
index 0000000..bab7b25
--- /dev/null
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JAXRSJwsMultipartTest.java
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.systest.jaxrs.security.jose.jwejws;
+
+import java.net.URL;
+import java.security.Security;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.WebApplicationException;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
+import org.apache.cxf.rs.security.jose.jaxrs.multipart.JwsMultipartClientRequestFilter;
+import org.apache.cxf.rs.security.jose.jaxrs.multipart.JwsMultipartSignatureProvider;
+import org.apache.cxf.systest.jaxrs.security.Book;
+import org.apache.cxf.systest.jaxrs.security.jose.BookStore;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class JAXRSJwsMultipartTest extends AbstractBusClientServerTestBase {
+    public static final String PORT = BookServerJwsMultipart.PORT;
+    @BeforeClass
+    public static void startServers() throws Exception {
+        assertTrue("server did not launch correctly",
+                   launchServer(BookServerJwsMultipart.class, true));
+        registerBouncyCastleIfNeeded();
+    }
+
+    private static void registerBouncyCastleIfNeeded() throws Exception {
+        // Still need it for Oracle Java 7 and Java 8
+        Security.addProvider(new BouncyCastleProvider());
+    }
+    @AfterClass
+    public static void unregisterBouncyCastleIfNeeded() throws Exception {
+        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
+    }
+        
+    @Test
+    public void testJwsJwkBookHMacMultipart() throws Exception {
+        String address = "https://localhost:" + PORT + "/jwsjwkhmacSinglePart";
+        BookStore bs = createJwsBookStoreMultipart(address, true);
+        Book book = bs.echoBookMultipart(new Book("book", 123L));
+        assertEquals("book", book.getName());
+        assertEquals(123L, book.getId());
+    }
+    @Test
+    public void testJwsJwkBooksHMacMultipart() throws Exception {
+        String address = "https://localhost:" + PORT + "/jwsjwkhmacManyParts";
+        BookStore bs = createJwsBookStoreMultipart(address, false);
+        List<Book> books = new LinkedList<Book>();
+        books.add(new Book("book", 123L));
+        books.add(new Book("book2", 124L));
+        List<Book> returnBooks = bs.echoBooksMultipart(books);
+        assertEquals("book", returnBooks.get(0).getName());
+        assertEquals(123L, returnBooks.get(0).getId());
+        assertEquals("book2", returnBooks.get(1).getName());
+        assertEquals(124L, returnBooks.get(1).getId());
+    }
+    @Test(expected = ProcessingException.class)
+    public void testJwsJwkBooksHMacMultipartClientRestriction() throws Exception {
+        String address = "https://localhost:" + PORT + "/jwsjwkhmacManyParts";
+        BookStore bs = createJwsBookStoreMultipart(address, true);
+        List<Book> books = new LinkedList<Book>();
+        books.add(new Book("book", 123L));
+        books.add(new Book("book2", 124L));
+        bs.echoBooksMultipart(books);
+    }
+    @Test(expected = BadRequestException.class)
+    public void testJwsJwkBooksHMacMultipartServerRestriction() throws Exception {
+        String address = "https://localhost:" + PORT + "/jwsjwkhmacSinglePart";
+        BookStore bs = createJwsBookStoreMultipart(address, false);
+        List<Book> books = new LinkedList<Book>();
+        books.add(new Book("book", 123L));
+        books.add(new Book("book2", 124L));
+        bs.echoBooksMultipart(books);
+    }
+    
+    @Test(expected = BadRequestException.class)
+    public void testJwsJwkBooksHMacMultipartUnsigned() throws Exception {
+        String address = "https://localhost:" + PORT + "/jwsjwkhmacSinglePart";
+        BookStore bs = JAXRSClientFactory.create(address, BookStore.class, 
+                            JAXRSJwsMultipartTest.class.getResource("client.xml").toString());
+        bs.echoBookMultipart(new Book("book", 123L));
+    }
+    @Test
+    public void testJwsJwkBookHMacMultipartModified() throws Exception {
+        String address = "https://localhost:" + PORT + "/jwsjwkhmacSinglePartModified";
+        BookStore bs = createJwsBookStoreMultipart(address, true);
+        try {
+            bs.echoBookMultipart(new Book("book", 123L));
+            fail("Exception is expected");
+        } catch (WebApplicationException ex) {
+            // expected
+        }
+    }
+    private BookStore createJwsBookStoreMultipart(String address, boolean supportSinglePart) throws Exception {
+        JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
+        SpringBusFactory bf = new SpringBusFactory();
+        URL busFile = JAXRSJwsMultipartTest.class.getResource("client.xml");
+        Bus springBus = bf.createBus(busFile.toString());
+        bean.setBus(springBus);
+        bean.setServiceClass(BookStore.class);
+        bean.setAddress(address);
+        List<Object> providers = new LinkedList<Object>();
+        JwsMultipartClientRequestFilter filter = new JwsMultipartClientRequestFilter();
+        filter.setSupportSinglePartOnly(supportSinglePart);
+        providers.add(filter);
+        providers.add(new JwsMultipartSignatureProvider());
+        bean.setProviders(providers);
+        bean.getProperties(true).put("rs.security.signature.properties",
+            "org/apache/cxf/systest/jaxrs/security/secret.jwk.properties");
+        return bean.create(BookStore.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/MultipartModificationFilter.java
----------------------------------------------------------------------
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/MultipartModificationFilter.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/MultipartModificationFilter.java
new file mode 100644
index 0000000..3f63fe2
--- /dev/null
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/MultipartModificationFilter.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.systest.jaxrs.security.jose.jwejws;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.List;
+
+import javax.annotation.Priority;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartInputFilter;
+import org.apache.cxf.jaxrs.utils.multipart.AttachmentUtils;
+import org.apache.cxf.rs.security.jose.jaxrs.Priorities;
+
+@PreMatching
+@Priority(Priorities.JWS_SERVER_READ_PRIORITY - 1)
+public class MultipartModificationFilter 
+    implements ContainerRequestFilter, MultipartInputFilter {
+    
+    @Override
+    public void filter(ContainerRequestContext ctx) throws IOException {
+        AttachmentUtils.addMultipartInFilter(this); 
+    }
+
+    @Override
+    public void filter(List<Attachment> atts) {
+        Attachment dataPart = atts.remove(0);
+        Attachment newDataPart = new Attachment(new ByteArrayInputStream("Hi".getBytes()), 
+                                                dataPart.getHeaders());
+        atts.add(0, newDataPart);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/17b0b5f4/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwejws/serverMultipart.xml
----------------------------------------------------------------------
diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwejws/serverMultipart.xml b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwejws/serverMultipart.xml
new file mode 100644
index 0000000..3cfbebc
--- /dev/null
+++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwejws/serverMultipart.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:http="http://cxf.apache.org/transports/http/configuration" xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration" xmlns:sec="http://cxf.apache.org/configuration/security" xmlns:cxf="http://cxf.apache.org/core" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xsi:schemaLocation="         http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd         http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd         http://www.springframework.org/schema/beans                 http://www.springframework.org/schema/beans/spring-beans-4.2.xsd         http://cxf.apache.org/transports/http/configuration         http://cxf.apache.org/schemas/configuration/http-conf.xsd         http://cxf.apache.org/transports/http-jetty/configuration   http://cxf.apache.org/schemas/configuration/http-jetty.xsd         http://cxf.apache.org/configuration/security
                 http://cxf.apache.org/schemas/configuration/security.xsd         ">
+    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
+    <cxf:bus>
+        <cxf:features>
+            <cxf:logging/>
+        </cxf:features>
+    </cxf:bus>
+    <httpj:engine-factory id="port-9095-tls-config">
+        <httpj:engine port="${testutil.ports.jaxrs-jws-multipart}">
+            <httpj:tlsServerParameters>
+                <sec:keyManagers keyPassword="password">
+                    <sec:keyStore type="JKS" password="password" resource="keys/Bethal.jks"/>
+                </sec:keyManagers>
+                <sec:trustManagers>
+                    <sec:keyStore type="JKS" password="password" resource="keys/Truststore.jks"/>
+                </sec:trustManagers>
+                <sec:clientAuthentication want="true" required="true"/>
+            </httpj:tlsServerParameters>
+        </httpj:engine>
+    </httpj:engine-factory>
+    <bean id="serviceBean" class="org.apache.cxf.systest.jaxrs.security.jose.BookStore"/>
+    
+    <bean id="jwsInMultipartFilter" class="org.apache.cxf.rs.security.jose.jaxrs.multipart.JwsMultipartContainerRequestFilter"/>
+    <bean id="jwsInMultipartModificationFilter" class="org.apache.cxf.systest.jaxrs.security.jose.jwejws.MultipartModificationFilter"/>
+    
+    <jaxrs:server address="https://localhost:${testutil.ports.jaxrs-jws-multipart}/jwsjwkhmacSinglePart">
+        <jaxrs:serviceBeans>
+            <ref bean="serviceBean"/>
+        </jaxrs:serviceBeans>
+        <jaxrs:providers>
+            <ref bean="jwsInMultipartFilter"/>
+        </jaxrs:providers>
+        <jaxrs:properties>
+            <entry key="rs.security.signature.properties" value="org/apache/cxf/systest/jaxrs/security/secret.jwk.properties"/>
+        </jaxrs:properties>
+    </jaxrs:server>
+    <jaxrs:server address="https://localhost:${testutil.ports.jaxrs-jws-multipart}/jwsjwkhmacSinglePartModified">
+        <jaxrs:serviceBeans>
+            <ref bean="serviceBean"/>
+        </jaxrs:serviceBeans>
+        <jaxrs:providers>
+            <ref bean="jwsInMultipartModificationFilter"/>
+            <ref bean="jwsInMultipartFilter"/>
+        </jaxrs:providers>
+        <jaxrs:properties>
+            <entry key="rs.security.signature.properties" value="org/apache/cxf/systest/jaxrs/security/secret.jwk.properties"/>
+        </jaxrs:properties>
+    </jaxrs:server>
+    <bean id="jwsInMultipartFilterManyParts" class="org.apache.cxf.rs.security.jose.jaxrs.multipart.JwsMultipartContainerRequestFilter">
+        <property name="supportSinglePartOnly" value="false"/>
+    </bean>
+    <jaxrs:server address="https://localhost:${testutil.ports.jaxrs-jws-multipart}/jwsjwkhmacManyParts">
+        <jaxrs:serviceBeans>
+            <ref bean="serviceBean"/>
+        </jaxrs:serviceBeans>
+        <jaxrs:providers>
+            <ref bean="jwsInMultipartFilterManyParts"/>
+        </jaxrs:providers>
+        <jaxrs:properties>
+            <entry key="rs.security.signature.properties" value="org/apache/cxf/systest/jaxrs/security/secret.jwk.properties"/>
+        </jaxrs:properties>
+    </jaxrs:server>
+</beans>


Mime
View raw message