You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ws.apache.org by co...@apache.org on 2015/07/03 16:54:24 UTC

svn commit: r1689028 - in /webservices/wss4j/trunk: src/site/xdoc/ ws-security-common/src/main/java/org/apache/wss4j/common/ ws-security-dom/src/main/java/org/apache/wss4j/dom/action/ ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/ ws-secur...

Author: coheigea
Date: Fri Jul  3 14:54:23 2015
New Revision: 1689028

URL: http://svn.apache.org/r1689028
Log:
[WSS-544] - Support the ability to store message bytes in attachments (when using MTOM)

Modified:
    webservices/wss4j/trunk/src/site/xdoc/config.xml
    webservices/wss4j/trunk/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionDerivedAction.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureAction.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureDerivedAction.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecBase.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecDKEncrypt.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncrypt.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/WSSecSignatureSAML.java
    webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/util/WSSecurityUtil.java

Modified: webservices/wss4j/trunk/src/site/xdoc/config.xml
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/src/site/xdoc/config.xml?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/src/site/xdoc/config.xml (original)
+++ webservices/wss4j/trunk/src/site/xdoc/config.xml Fri Jul  3 14:54:23 2015
@@ -595,6 +595,16 @@ only. The default is false. If set to tr
 key from the CallbackHandler instead of generating a random key internally.
 </td>
 </tr>
+<tr>
+<td><b>WSS4J 2.1.2/2.0.5</b> STORE_BYTES_IN_ATTACHMENT</td>
+<td>storeBytesInAttachment</td>
+<td>Whether to store bytes (CipherData or BinarySecurityToken) in an
+attachment. The default is false, meaning that bytes are BASE-64 encoded and
+"inlined" in the message. Setting this to true is more efficient, as it means
+that the BASE-64 encoding step can be skipped. For this to work, a
+CallbackHandler must be set on RequestData that can handle attachments.
+</td>
+</tr>
 </table>
 <p>
 The configuration tags for properties that are configured via a non-boolean

Modified: webservices/wss4j/trunk/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java (original)
+++ webservices/wss4j/trunk/ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java Fri Jul  3 14:54:23 2015
@@ -540,6 +540,14 @@ public class ConfigurationConstants {
      */
     public static final String GET_SECRET_KEY_FROM_CALLBACK_HANDLER = "getSecretKeyFromCallbackHandler";
     
+    /**
+     * Whether to store bytes (CipherData or BinarySecurityToken) in an attachment. The default is false,
+     * meaning that bytes are BASE-64 encoded and "inlined" in the message. Setting this to true is more
+     * efficient, as it means that the BASE-64 encoding step can be skipped. For this to work, a 
+     * CallbackHandler must be set on RequestData that can handle attachments.
+     */
+    public static final String STORE_BYTES_IN_ATTACHMENT = "storeBytesInAttachment";
+    
     //
     // (Non-boolean) Configuration parameters for the actions/processors
     //

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionAction.java Fri Jul  3 14:54:23 2015
@@ -120,6 +120,7 @@ public class EncryptionAction implements
         }
         
         wsEncrypt.setAttachmentCallbackHandler(reqData.getAttachmentCallbackHandler());
+        wsEncrypt.setStoreBytesInAttachment(reqData.isStoreBytesInAttachment());
         
         try {
             wsEncrypt.build(doc, encryptionToken.getCrypto(), reqData.getSecHeader());

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionDerivedAction.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionDerivedAction.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionDerivedAction.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/EncryptionDerivedAction.java Fri Jul  3 14:54:23 2015
@@ -83,6 +83,7 @@ public class EncryptionDerivedAction ext
         Element tokenElement = 
             setupTokenReference(reqData, encryptionToken, wsEncrypt, passwordCallback, doc);
         wsEncrypt.setAttachmentCallbackHandler(reqData.getAttachmentCallbackHandler());
+        wsEncrypt.setStoreBytesInAttachment(reqData.isStoreBytesInAttachment());
 
         try {
             List<WSEncryptionPart> parts = encryptionToken.getParts();

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureAction.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureAction.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureAction.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureAction.java Fri Jul  3 14:54:23 2015
@@ -98,6 +98,7 @@ public class SignatureAction implements
         }
 
         wsSign.setAttachmentCallbackHandler(reqData.getAttachmentCallbackHandler());
+        wsSign.setStoreBytesInAttachment(reqData.isStoreBytesInAttachment());
 
         try {
             wsSign.prepare(doc, signatureToken.getCrypto(), reqData.getSecHeader());

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureDerivedAction.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureDerivedAction.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureDerivedAction.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/action/SignatureDerivedAction.java Fri Jul  3 14:54:23 2015
@@ -87,6 +87,7 @@ public class SignatureDerivedAction exte
         Element tokenElement = 
             setupTokenReference(reqData, signatureToken, wsSign, passwordCallback, doc);
         wsSign.setAttachmentCallbackHandler(reqData.getAttachmentCallbackHandler());
+        wsSign.setStoreBytesInAttachment(reqData.isStoreBytesInAttachment());
 
         try {
             List<WSEncryptionPart> parts = signatureToken.getParts();

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/RequestData.java Fri Jul  3 14:54:23 2015
@@ -94,6 +94,7 @@ public class RequestData {
     private boolean use200512Namespace = true;
     private final List<String> audienceRestrictions = new ArrayList<>();
     private boolean requireTimestampExpires;
+    private boolean storeBytesInAttachment;
     
     /**
      * Whether to add an InclusiveNamespaces PrefixList as a CanonicalizationMethod
@@ -754,4 +755,12 @@ public class RequestData {
     public void setEncodePasswords(boolean encodePasswords) {
         this.encodePasswords = encodePasswords;
     }
+
+    public boolean isStoreBytesInAttachment() {
+        return storeBytesInAttachment;
+    }
+
+    public void setStoreBytesInAttachment(boolean storeBytesInAttachment) {
+        this.storeBytesInAttachment = storeBytesInAttachment;
+    }
 }

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/handler/WSHandler.java Fri Jul  3 14:54:23 2015
@@ -120,6 +120,10 @@ public abstract class WSHandler {
             reqData.setCallbackHandler(passwordCallbackHandler);
         }
         
+        boolean storeBytesInAttachment = 
+            decodeBooleanConfigValue(mc, WSHandlerConstants.STORE_BYTES_IN_ATTACHMENT, false);
+        reqData.setStoreBytesInAttachment(storeBytesInAttachment);
+        
         // Perform configuration
         for (HandlerAction actionToDo : actions) {
             if (actionToDo.getAction() == WSConstants.SC) {

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecBase.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecBase.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecBase.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecBase.java Fri Jul  3 14:54:23 2015
@@ -44,6 +44,7 @@ public class WSSecBase {
     protected boolean doDebug;
     protected CallbackLookup callbackLookup;
     protected CallbackHandler attachmentCallbackHandler;
+    protected boolean storeBytesInAttachment;
 
     private WsuIdAllocator idAllocator;
     private final List<WSEncryptionPart> parts = new ArrayList<>();
@@ -104,6 +105,10 @@ public class WSSecBase {
     public void setAttachmentCallbackHandler(CallbackHandler attachmentCallbackHandler) {
         this.attachmentCallbackHandler = attachmentCallbackHandler;
     }
+    
+    public void setStoreBytesInAttachment(boolean storeBytesInAttachment) {
+        this.storeBytesInAttachment = storeBytesInAttachment;
+    }
 
     /**
      * Looks up or adds a body id. <p/> First try to locate the

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecDKEncrypt.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecDKEncrypt.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecDKEncrypt.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecDKEncrypt.java Fri Jul  3 14:54:23 2015
@@ -128,7 +128,8 @@ public class WSSecDKEncrypt extends WSSe
 
         List<String> encDataRefs = 
             WSSecEncrypt.doEncryption(
-                document, getIdAllocator(), keyInfo, key, symEncAlgo, references, callbackLookup, attachmentCallbackHandler, attachmentEncryptedDataElements
+                document, getIdAllocator(), keyInfo, key, symEncAlgo, references, callbackLookup, 
+                attachmentCallbackHandler, attachmentEncryptedDataElements, storeBytesInAttachment
             );
         if (dataRef == null) {
             dataRef = 

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncrypt.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncrypt.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncrypt.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncrypt.java Fri Jul  3 14:54:23 2015
@@ -25,7 +25,9 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.IvParameterSpec;
@@ -50,16 +52,21 @@ import org.apache.wss4j.dom.WsuIdAllocat
 import org.apache.wss4j.dom.message.token.KerberosSecurity;
 import org.apache.wss4j.dom.util.WSSecurityUtil;
 import org.apache.xml.security.algorithms.JCEMapper;
+import org.apache.xml.security.c14n.Canonicalizer;
+import org.apache.xml.security.encryption.AbstractSerializer;
 import org.apache.xml.security.encryption.EncryptedData;
+import org.apache.xml.security.encryption.TransformSerializer;
 import org.apache.xml.security.encryption.XMLCipher;
 import org.apache.xml.security.encryption.XMLEncryptionException;
 import org.apache.xml.security.keys.KeyInfo;
 import org.apache.xml.security.utils.Base64;
+import org.apache.xml.security.utils.EncryptionConstants;
 import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 /**
  * Encrypts a parts of a message according to WS Specification, X509 profile,
@@ -245,7 +252,8 @@ public class WSSecEncrypt extends WSSecE
         List<String> encDataRefs = 
             doEncryption(
                 document, getIdAllocator(), keyInfo, secretKeySpec, getSymmetricEncAlgorithm(), references, 
-                    callbackLookup, attachmentCallbackHandler, attachmentEncryptedDataElements
+                    callbackLookup, attachmentCallbackHandler, attachmentEncryptedDataElements,
+                    storeBytesInAttachment
             );
         if (dataRef == null) {
             dataRef = 
@@ -326,7 +334,7 @@ public class WSSecEncrypt extends WSSecE
     ) throws WSSecurityException {
         return doEncryption(
                 doc, idAllocator, keyInfo, secretKey, encryptionAlgorithm,
-                references, callbackLookup, null, null);
+                references, callbackLookup, null, null, false);
     }
 
     public static List<String> doEncryption(
@@ -338,7 +346,8 @@ public class WSSecEncrypt extends WSSecE
             List<WSEncryptionPart> references,
             CallbackLookup callbackLookup,
             CallbackHandler attachmentCallbackHandler,
-            List<Element> attachmentEncryptedDataElements
+            List<Element> attachmentEncryptedDataElements,
+            boolean storeBytesInAttachment
     ) throws WSSecurityException {
 
         XMLCipher xmlCipher = null;
@@ -378,130 +387,245 @@ public class WSSecEncrypt extends WSSecE
                     new Object[] {"{" + encPart.getNamespace() + "}" + encPart.getName()});
             }
 
-            for (Element elementToEncrypt : elementsToEncrypt) {
-                String id = 
-                    encryptElement(doc, elementToEncrypt, encPart.getEncModifier(), idAllocator, xmlCipher,
-                                   secretKey, keyInfo);
-                encPart.setEncId(id);
-                encDataRef.add("#" + id);
-            }
-                
-            if (part != references.size() - 1) {
-                try {
-                    keyInfo = new KeyInfo((Element) keyInfo.getElement().cloneNode(true), null);
-                } catch (Exception ex) {
-                    throw new WSSecurityException(
-                        WSSecurityException.ErrorCode.FAILED_ENCRYPTION, ex
-                    );
+            if (storeBytesInAttachment) {
+                for (Element elementToEncrypt : elementsToEncrypt) {
+                    try {
+                        String id = 
+                            encryptElementInAttachment(doc, idAllocator, keyInfo, secretKey, encryptionAlgorithm,
+                                          attachmentCallbackHandler, encPart, elementToEncrypt);
+                        encPart.setEncId(id);
+                        encDataRef.add("#" + id);
+                    } catch (Exception ex) {
+                        throw new WSSecurityException(
+                            WSSecurityException.ErrorCode.FAILED_ENCRYPTION, ex
+                        );
+                    }
+                }
+            } else {
+                for (Element elementToEncrypt : elementsToEncrypt) {
+                    String id = 
+                        encryptElement(doc, elementToEncrypt, encPart.getEncModifier(), idAllocator, xmlCipher,
+                                       secretKey, keyInfo);
+                    encPart.setEncId(id);
+                    encDataRef.add("#" + id);
                 }
             }
         }
 
         if (attachmentEncryptionPart != null) {
-            // We have an attachment to encrypt
+            encryptAttachment(doc, idAllocator, keyInfo, secretKey, encryptionAlgorithm,
+                              attachmentCallbackHandler, attachmentEncryptionPart, encDataRef,
+                              attachmentEncryptedDataElements);
+        }
 
-            if (attachmentCallbackHandler == null) {
-                throw new WSSecurityException(
-                    WSSecurityException.ErrorCode.FAILURE,
-                    "empty", new Object[] {"no attachment callbackhandler supplied"}
-                );
+        return encDataRef;
+    }
+    
+    private static String encryptElementInAttachment(
+        Document doc,
+        WsuIdAllocator idAllocator,
+        KeyInfo keyInfo,
+        SecretKey secretKey,
+        String encryptionAlgorithm,
+        CallbackHandler attachmentCallbackHandler,
+        WSEncryptionPart encryptionPart,
+        Element elementToEncrypt
+   ) throws Exception {
+        
+        String type = EncryptionConstants.TYPE_ELEMENT;
+        if ("Content".equals(encryptionPart.getEncModifier())) {
+            type = EncryptionConstants.TYPE_CONTENT;
+        }
+        
+        final String attachmentId = idAllocator.createId("", doc);
+        String encEncryptedDataId = idAllocator.createId("ED-", attachmentId);
+
+        Element encryptedData =
+            doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":EncryptedData");
+        encryptedData.setAttributeNS(null, "Id", encEncryptedDataId);
+        encryptedData.setAttributeNS(null, "Type", type);
+
+        Element encryptionMethod =
+            doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":EncryptionMethod");
+        encryptionMethod.setAttributeNS(null, "Algorithm", encryptionAlgorithm);
+
+        encryptedData.appendChild(encryptionMethod);
+        encryptedData.appendChild(keyInfo.getElement());
+
+        Element cipherData =
+            doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":CipherData");
+        Element cipherValue =
+            doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":CipherValue");
+        cipherData.appendChild(cipherValue);
+        encryptedData.appendChild(cipherData);
+
+        Cipher cipher = createCipher(encryptionAlgorithm, secretKey);
+
+        // Serialize and encrypt the element
+        AbstractSerializer serializer = new TransformSerializer();
+        serializer.setCanonicalizer(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_PHYSICAL));
+        serializer.setSecureValidation(true);
+        
+        byte[] serializedOctets = null;
+        if (type.equals(EncryptionConstants.TYPE_CONTENT)) {
+            NodeList children = elementToEncrypt.getChildNodes();
+            if (null != children) {
+                serializedOctets = serializer.serializeToByteArray(children);
+            } else {
+                throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION, "Element has no content.");
             }
+        } else {
+            serializedOctets = serializer.serializeToByteArray(elementToEncrypt);
+        }
+        
+        byte[] encryptedBytes = null;
+        try {
+            encryptedBytes = cipher.doFinal(serializedOctets);
+        } catch (IllegalBlockSizeException ibse) {
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION, ibse);
+        } catch (BadPaddingException bpe) {
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION, bpe);
+        }
 
-            AttachmentRequestCallback attachmentRequestCallback = new AttachmentRequestCallback();
-            String id = attachmentEncryptionPart.getId().substring(4);
-            attachmentRequestCallback.setAttachmentId(id);
-            try {
-                attachmentCallbackHandler.handle(new Callback[]{attachmentRequestCallback});
-            } catch (Exception e) {
-                throw new WSSecurityException(
-                    WSSecurityException.ErrorCode.FAILED_ENCRYPTION, e
-                );
+        // Now build up to a properly XML Encryption encoded octet stream
+        byte[] iv = cipher.getIV();
+        byte[] finalEncryptedBytes = new byte[iv.length + encryptedBytes.length];
+        System.arraycopy(iv, 0, finalEncryptedBytes, 0, iv.length);
+        System.arraycopy(encryptedBytes, 0, finalEncryptedBytes, iv.length, encryptedBytes.length);
+        
+        if ("Content".equals(encryptionPart.getEncModifier())) {
+            Node child = elementToEncrypt.getFirstChild();
+            while (child != null) {
+                Node sibling = child.getNextSibling();
+                elementToEncrypt.removeChild(child);
+                child = sibling;
             }
-            String attachmentEncryptedDataType = WSConstants.SWA_ATTACHMENT_ENCRYPTED_DATA_TYPE_CONTENT_ONLY;
-            if ("Element".equals(attachmentEncryptionPart.getEncModifier())) {
-                attachmentEncryptedDataType = WSConstants.SWA_ATTACHMENT_ENCRYPTED_DATA_TYPE_COMPLETE;
-            }
-
-            for (Attachment attachment : attachmentRequestCallback.getAttachments()) {
-
-                final String attachmentId = attachment.getId();
-                String encEncryptedDataId = idAllocator.createId("ED-", attachmentId);
-                encDataRef.add("#" + encEncryptedDataId);
-
-                Element encryptedData =
-                    doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":EncryptedData");
-                encryptedData.setAttributeNS(null, "Id", encEncryptedDataId);
-                encryptedData.setAttributeNS(null, "MimeType", attachment.getMimeType());
-                encryptedData.setAttributeNS(null, "Type", attachmentEncryptedDataType);
-
-                Element encryptionMethod =
-                    doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":EncryptionMethod");
-                encryptionMethod.setAttributeNS(null, "Algorithm", encryptionAlgorithm);
-
-                encryptedData.appendChild(encryptionMethod);
-                encryptedData.appendChild(keyInfo.getElement());
-
-                Element cipherData =
-                    doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":CipherData");
-                Element cipherReference =
-                    doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":CipherReference");
-                cipherReference.setAttributeNS(null, "URI", "cid:" + attachmentId);
-
-                Element transforms = doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":Transforms");
-                Element transform = doc.createElementNS(WSConstants.SIG_NS, WSConstants.SIG_PREFIX + ":Transform");
-                transform.setAttributeNS(null, "Algorithm", WSConstants.SWA_ATTACHMENT_CIPHERTEXT_TRANS);
-                transforms.appendChild(transform);
-
-                cipherReference.appendChild(transforms);
-                cipherData.appendChild(cipherReference);
-                encryptedData.appendChild(cipherData);
-
-                attachmentEncryptedDataElements.add(encryptedData);
-
-                Attachment resultAttachment = new Attachment();
-                resultAttachment.setId(attachmentId);
-                resultAttachment.setMimeType("application/octet-stream");
-
-                String jceAlgorithm = JCEMapper.translateURItoJCEID(encryptionAlgorithm);
-                Cipher cipher = null;
-                try {
-                    cipher = Cipher.getInstance(jceAlgorithm);
-
-                    // The Spec mandates a 96-bit IV for GCM algorithms
-                    if (XMLCipher.AES_128_GCM.equals(encryptionAlgorithm)
-                        || XMLCipher.AES_192_GCM.equals(encryptionAlgorithm)
-                        || XMLCipher.AES_256_GCM.equals(encryptionAlgorithm)) {
-                        byte[] temp = WSSecurityUtil.generateNonce(12);
-                        IvParameterSpec paramSpec = new IvParameterSpec(temp);
-                        cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
-                    } else {
-                        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
-                    }
-                } catch (Exception e) {
-                    throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION, e);
-                }
+            elementToEncrypt.appendChild(encryptedData);
+        } else {
+            elementToEncrypt.getParentNode().replaceChild(encryptedData, elementToEncrypt);
+        }
+        
+        WSSecurityUtil.storeBytesInAttachment(cipherValue, doc, attachmentId, 
+                                              finalEncryptedBytes, attachmentCallbackHandler);
 
-                Map<String, String> headers = new HashMap<>(attachment.getHeaders());
-                resultAttachment.setSourceStream(
-                    AttachmentUtils.setupAttachmentEncryptionStream(
-                        cipher, "Element".equals(attachmentEncryptionPart.getEncModifier()),
-                        attachment, headers
-                    )
-                );
-                resultAttachment.addHeaders(headers);
+        return encEncryptedDataId;
+    }
+    
+    private static void encryptAttachment(
+        Document doc,
+        WsuIdAllocator idAllocator,
+        KeyInfo keyInfo,
+        SecretKey secretKey,
+        String encryptionAlgorithm,
+        CallbackHandler attachmentCallbackHandler,
+        WSEncryptionPart attachmentEncryptionPart,
+        List<String> encDataRef,
+        List<Element> attachmentEncryptedDataElements
+    ) throws WSSecurityException {
+        if (attachmentCallbackHandler == null) {
+            throw new WSSecurityException(
+                WSSecurityException.ErrorCode.FAILURE,
+                "empty", new Object[] {"no attachment callbackhandler supplied"}
+            );
+        }
 
-                AttachmentResultCallback attachmentResultCallback = new AttachmentResultCallback();
-                attachmentResultCallback.setAttachmentId(attachmentId);
-                attachmentResultCallback.setAttachment(resultAttachment);
-                try {
-                    attachmentCallbackHandler.handle(new Callback[]{attachmentResultCallback});
-                } catch (Exception e) {
-                    throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION, e);
-                }
+        AttachmentRequestCallback attachmentRequestCallback = new AttachmentRequestCallback();
+        String id = attachmentEncryptionPart.getId().substring(4);
+        attachmentRequestCallback.setAttachmentId(id);
+        try {
+            attachmentCallbackHandler.handle(new Callback[]{attachmentRequestCallback});
+        } catch (Exception e) {
+            throw new WSSecurityException(
+                WSSecurityException.ErrorCode.FAILED_ENCRYPTION, e
+            );
+        }
+        String attachmentEncryptedDataType = WSConstants.SWA_ATTACHMENT_ENCRYPTED_DATA_TYPE_CONTENT_ONLY;
+        if ("Element".equals(attachmentEncryptionPart.getEncModifier())) {
+            attachmentEncryptedDataType = WSConstants.SWA_ATTACHMENT_ENCRYPTED_DATA_TYPE_COMPLETE;
+        }
+
+        for (Attachment attachment : attachmentRequestCallback.getAttachments()) {
+
+            final String attachmentId = attachment.getId();
+            String encEncryptedDataId = idAllocator.createId("ED-", attachmentId);
+            encDataRef.add("#" + encEncryptedDataId);
+
+            Element encryptedData =
+                doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":EncryptedData");
+            encryptedData.setAttributeNS(null, "Id", encEncryptedDataId);
+            encryptedData.setAttributeNS(null, "MimeType", attachment.getMimeType());
+            encryptedData.setAttributeNS(null, "Type", attachmentEncryptedDataType);
+
+            Element encryptionMethod =
+                doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":EncryptionMethod");
+            encryptionMethod.setAttributeNS(null, "Algorithm", encryptionAlgorithm);
+
+            encryptedData.appendChild(encryptionMethod);
+            encryptedData.appendChild(keyInfo.getElement());
+
+            Element cipherData =
+                doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":CipherData");
+            Element cipherReference =
+                doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":CipherReference");
+            cipherReference.setAttributeNS(null, "URI", "cid:" + attachmentId);
+
+            Element transforms = doc.createElementNS(WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":Transforms");
+            Element transform = doc.createElementNS(WSConstants.SIG_NS, WSConstants.SIG_PREFIX + ":Transform");
+            transform.setAttributeNS(null, "Algorithm", WSConstants.SWA_ATTACHMENT_CIPHERTEXT_TRANS);
+            transforms.appendChild(transform);
+
+            cipherReference.appendChild(transforms);
+            cipherData.appendChild(cipherReference);
+            encryptedData.appendChild(cipherData);
+
+            attachmentEncryptedDataElements.add(encryptedData);
+
+            Attachment resultAttachment = new Attachment();
+            resultAttachment.setId(attachmentId);
+            resultAttachment.setMimeType("application/octet-stream");
+
+            Cipher cipher = createCipher(encryptionAlgorithm, secretKey);
+
+            Map<String, String> headers = new HashMap<>(attachment.getHeaders());
+            resultAttachment.setSourceStream(
+                AttachmentUtils.setupAttachmentEncryptionStream(
+                    cipher, "Element".equals(attachmentEncryptionPart.getEncModifier()),
+                    attachment, headers
+                )
+            );
+            resultAttachment.addHeaders(headers);
+
+            AttachmentResultCallback attachmentResultCallback = new AttachmentResultCallback();
+            attachmentResultCallback.setAttachmentId(attachmentId);
+            attachmentResultCallback.setAttachment(resultAttachment);
+            try {
+                attachmentCallbackHandler.handle(new Callback[]{attachmentResultCallback});
+            } catch (Exception e) {
+                throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION, e);
             }
         }
+    }
+    
+    private static Cipher createCipher(String encryptionAlgorithm, SecretKey secretKey) 
+        throws WSSecurityException {
+        String jceAlgorithm = JCEMapper.translateURItoJCEID(encryptionAlgorithm);
+        try {
+            Cipher cipher = Cipher.getInstance(jceAlgorithm);
 
-        return encDataRef;
+            // The Spec mandates a 96-bit IV for GCM algorithms
+            if (XMLCipher.AES_128_GCM.equals(encryptionAlgorithm)
+                || XMLCipher.AES_192_GCM.equals(encryptionAlgorithm)
+                || XMLCipher.AES_256_GCM.equals(encryptionAlgorithm)) {
+                byte[] iv = WSSecurityUtil.generateNonce(12);
+                IvParameterSpec paramSpec = new IvParameterSpec(iv);
+                cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
+            } else {
+                cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+            }
+            return cipher;
+        } catch (Exception e) {
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_ENCRYPTION, e);
+        }
     }
 
     /**

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecEncryptedKey.java Fri Jul  3 14:54:23 2015
@@ -297,8 +297,6 @@ public class WSSecEncryptedKey extends W
                 WSSecurityException.ErrorCode.FAILED_ENCRYPTION, ex
             );
         }
-        Text keyText = 
-            WSSecurityUtil.createBase64EncodedTextNode(document, encryptedEphemeralKey);
 
         //
         // Now we need to setup the EncryptedKey header block 1) create a
@@ -433,13 +431,18 @@ public class WSSecEncryptedKey extends W
         }
 
         Element xencCipherValue = createCipherValue(document, encryptedKeyElement);
-        xencCipherValue.appendChild(keyText);
+        if (storeBytesInAttachment) {
+            final String attachmentId = getIdAllocator().createId("", document);
+            WSSecurityUtil.storeBytesInAttachment(xencCipherValue, document, attachmentId, 
+                                                  encryptedEphemeralKey, attachmentCallbackHandler);
+        } else {
+            Text keyText = 
+                WSSecurityUtil.createBase64EncodedTextNode(document, encryptedEphemeralKey);
+            xencCipherValue.appendChild(keyText);
+        }
     }
     
     protected void prepareInternal(SecretKey secretKey) throws WSSecurityException {
-        Text keyText = 
-            WSSecurityUtil.createBase64EncodedTextNode(document, encryptedEphemeralKey);
-
         encryptedKeyElement = createEncryptedKey(document, keyEncAlgo);
         if (encKeyId == null || "".equals(encKeyId)) {
             encKeyId = IDGenerator.generateID("EK-");
@@ -517,7 +520,15 @@ public class WSSecEncryptedKey extends W
         }
 
         Element xencCipherValue = createCipherValue(document, encryptedKeyElement);
-        xencCipherValue.appendChild(keyText);
+        if (storeBytesInAttachment) {
+            final String attachmentId = getIdAllocator().createId("", document);
+            WSSecurityUtil.storeBytesInAttachment(xencCipherValue, document, attachmentId, 
+                                                  encryptedEphemeralKey, attachmentCallbackHandler);
+        } else {
+            Text keyText = 
+                WSSecurityUtil.createBase64EncodedTextNode(document, encryptedEphemeralKey);
+            xencCipherValue.appendChild(keyText);
+        }
     }
 
     /**

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/WSSecSignature.java Fri Jul  3 14:54:23 2015
@@ -20,6 +20,7 @@
 package org.apache.wss4j.dom.message;
 
 import java.security.NoSuchProviderException;
+import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 import java.util.List;
 
@@ -39,6 +40,7 @@ import javax.xml.crypto.dsig.spec.C14NMe
 import javax.xml.crypto.dsig.spec.ExcC14NParameterSpec;
 
 import org.apache.wss4j.common.WSEncryptionPart;
+import org.apache.wss4j.common.WSS4JConstants;
 import org.apache.wss4j.common.crypto.Crypto;
 import org.apache.wss4j.common.crypto.CryptoType;
 import org.apache.wss4j.common.ext.WSSecurityException;
@@ -85,7 +87,7 @@ public class WSSecSignature extends WSSe
     protected Document document;
     protected WSDocInfo wsDocInfo;
     protected String strUri;
-    protected BinarySecurity bstToken;
+    protected Element bstToken;
     protected String keyInfoUri;
     protected String certUri;
     protected byte[] signatureValue;
@@ -189,19 +191,15 @@ public class WSSecSignature extends WSSe
             case WSConstants.BST_DIRECT_REFERENCE:
                 Reference ref = new Reference(document);
                 ref.setURI("#" + certUri);
+                
+                addBST(certs);
                 if (!useSingleCert) {
-                    bstToken = new PKIPathSecurity(document);
-                    ((PKIPathSecurity) bstToken).setX509Certificates(certs, crypto);
                     secRef.addTokenType(PKIPathSecurity.PKI_TYPE);
+                    ref.setValueType(PKIPathSecurity.PKI_TYPE);
                 } else {
-                    bstToken = new X509Security(document);
-                    ((X509Security) bstToken).setX509Certificate(certs[0]);
+                    ref.setValueType(X509Security.X509_V3_TYPE);
                 }
-                bstAddedToSecurityHeader = false;
-                ref.setValueType(bstToken.getValueType());
                 secRef.setReference(ref);
-                bstToken.setID(certUri);
-                wsDocInfo.addTokenElement(bstToken.getElement(), false);
                 break;
     
             case WSConstants.ISSUER_SERIAL:
@@ -430,16 +428,46 @@ public class WSSecSignature extends WSSe
      * Add a BinarySecurityToken
      */
     private void addBST(X509Certificate[] certs) throws WSSecurityException {
-        if (!useSingleCert) {
-            bstToken = new PKIPathSecurity(document);
-            ((PKIPathSecurity) bstToken).setX509Certificates(certs, crypto);
+        if (storeBytesInAttachment) {
+            bstToken = 
+                document.createElementNS(WSS4JConstants.WSSE_NS, "wsse:BinarySecurityToken");
+            bstToken.setAttributeNS(null, "EncodingType", WSS4JConstants.BASE64_ENCODING);
+            bstToken.setAttributeNS(WSS4JConstants.WSU_NS, WSS4JConstants.WSU_PREFIX + ":Id", certUri);
+            
+            byte[] certBytes = null;
+            if (!useSingleCert) {
+                bstToken.setAttributeNS(null, "ValueType", PKIPathSecurity.PKI_TYPE);
+                certBytes = crypto.getBytesFromCertificates(certs);
+            } else {
+                bstToken.setAttributeNS(null, "ValueType", X509Security.X509_V3_TYPE);
+                try {
+                    certBytes = certs[0].getEncoded();
+                } catch (CertificateEncodingException e) {
+                    throw new WSSecurityException(
+                        WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, e, "encodeError"
+                    );
+                }
+            }
+            
+            final String attachmentId = getIdAllocator().createId("", document);
+            WSSecurityUtil.storeBytesInAttachment(bstToken, document, attachmentId, 
+                                                  certBytes, attachmentCallbackHandler);
+            wsDocInfo.addTokenElement(bstToken, false);
         } else {
-            bstToken = new X509Security(document);
-            ((X509Security) bstToken).setX509Certificate(certs[0]);
+            BinarySecurity binarySecurity = null;
+            if (!useSingleCert) {
+                binarySecurity = new PKIPathSecurity(document);
+                ((PKIPathSecurity) binarySecurity).setX509Certificates(certs, crypto);
+            } else {
+                binarySecurity = new X509Security(document);
+                ((X509Security) binarySecurity).setX509Certificate(certs[0]);
+            }
+            binarySecurity.setID(certUri);
+            bstToken = binarySecurity.getElement();
+            wsDocInfo.addTokenElement(bstToken, false);
         }
+        
         bstAddedToSecurityHeader = false;
-        bstToken.setID(certUri);
-        wsDocInfo.addTokenElement(bstToken.getElement(), false);
     }
     
     /**
@@ -454,7 +482,7 @@ public class WSSecSignature extends WSSe
      */
     public void prependBSTElementToHeader(WSSecHeader secHeader) {
         if (bstToken != null && !bstAddedToSecurityHeader) {
-            WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), bstToken.getElement());
+            WSSecurityUtil.prependChildElement(secHeader.getSecurityHeader(), bstToken);
             bstAddedToSecurityHeader = true;
         }
     }
@@ -466,7 +494,7 @@ public class WSSecSignature extends WSSe
     public void appendBSTElementToHeader(WSSecHeader secHeader) {
         if (bstToken != null && !bstAddedToSecurityHeader) {
             Element secHeaderElement = secHeader.getSecurityHeader();
-            secHeaderElement.appendChild(bstToken.getElement());
+            secHeaderElement.appendChild(bstToken);
             bstAddedToSecurityHeader = true;
         }
     }
@@ -698,7 +726,7 @@ public class WSSecSignature extends WSSe
         if (bstToken == null) {
             return null;
         }
-        return bstToken.getID();
+        return bstToken.getAttributeNS(WSS4JConstants.WSU_NS, "Id");
     }
     
     /**
@@ -747,10 +775,7 @@ public class WSSecSignature extends WSSe
      * @return the BST Token element
      */
     public Element getBinarySecurityTokenElement() {
-        if (bstToken != null) {
-            return bstToken.getElement();
-        }
-        return null;
+        return bstToken;
     }
     
     /**

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/WSSecSignatureSAML.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/WSSecSignatureSAML.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/WSSecSignatureSAML.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/saml/WSSecSignatureSAML.java Fri Jul  3 14:54:23 2015
@@ -42,6 +42,7 @@ import org.apache.wss4j.common.saml.Open
 import org.apache.wss4j.common.saml.SAMLKeyInfo;
 import org.apache.wss4j.common.saml.SAMLUtil;
 import org.apache.wss4j.common.saml.SamlAssertionWrapper;
+import org.apache.wss4j.common.token.BinarySecurity;
 import org.apache.wss4j.common.token.DOMX509Data;
 import org.apache.wss4j.common.token.DOMX509IssuerSerial;
 import org.apache.wss4j.common.token.Reference;
@@ -377,11 +378,12 @@ public class WSSecSignatureSAML extends
             case WSConstants.BST_DIRECT_REFERENCE:
                 Reference ref = new Reference(doc);
                 ref.setURI("#" + certUri);
-                bstToken = new X509Security(doc);
-                ((X509Security) bstToken).setX509Certificate(certs[0]);
-                bstToken.setID(certUri);
-                wsDocInfo.addTokenElement(bstToken.getElement(), false);
-                ref.setValueType(bstToken.getValueType());
+                BinarySecurity binarySecurity = new X509Security(doc);
+                ((X509Security) binarySecurity).setX509Certificate(certs[0]);
+                binarySecurity.setID(certUri);
+                bstToken = binarySecurity.getElement();
+                wsDocInfo.addTokenElement(bstToken, false);
+                ref.setValueType(binarySecurity.getValueType());
                 secRef.setReference(ref);
                 break;
                 

Modified: webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/util/WSSecurityUtil.java
URL: http://svn.apache.org/viewvc/webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/util/WSSecurityUtil.java?rev=1689028&r1=1689027&r2=1689028&view=diff
==============================================================================
--- webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/util/WSSecurityUtil.java (original)
+++ webservices/wss4j/trunk/ws-security-dom/src/main/java/org/apache/wss4j/dom/util/WSSecurityUtil.java Fri Jul  3 14:54:23 2015
@@ -30,6 +30,7 @@ import org.apache.wss4j.dom.WSSConfig;
 import org.apache.wss4j.common.WSEncryptionPart;
 import org.apache.wss4j.common.ext.Attachment;
 import org.apache.wss4j.common.ext.AttachmentRequestCallback;
+import org.apache.wss4j.common.ext.AttachmentResultCallback;
 import org.apache.wss4j.common.ext.WSSecurityException;
 import org.apache.wss4j.common.util.XMLUtils;
 import org.apache.wss4j.dom.handler.HandlerAction;
@@ -45,6 +46,7 @@ import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.Text;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -599,4 +601,32 @@ public final class WSSecurityUtil {
         }
     }
 
+    public static void storeBytesInAttachment(
+        Element parentElement,
+        Document doc,
+        String attachmentId,
+        byte[] bytes,
+        CallbackHandler attachmentCallbackHandler
+    ) throws WSSecurityException {
+        parentElement.setAttributeNS(XMLUtils.XMLNS_NS, "xmlns:xop", WSConstants.XOP_NS);
+        Element xopInclude =
+            doc.createElementNS(WSConstants.XOP_NS, "xop:Include");
+        xopInclude.setAttributeNS(null, "href", "cid:" + attachmentId);
+        parentElement.appendChild(xopInclude);
+        
+        Attachment resultAttachment = new Attachment();
+        resultAttachment.setId(attachmentId);
+        resultAttachment.setMimeType("application/ciphervalue");
+        resultAttachment.setSourceStream(new ByteArrayInputStream(bytes));
+        
+        AttachmentResultCallback attachmentResultCallback = new AttachmentResultCallback();
+        attachmentResultCallback.setAttachmentId(attachmentId);
+        attachmentResultCallback.setAttachment(resultAttachment);
+        try {
+            attachmentCallbackHandler.handle(new Callback[]{attachmentResultCallback});
+        } catch (Exception e) {
+            throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e);
+        }
+        
+    }
 }