View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.wss4j.dom.message;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import javax.crypto.SecretKey;
26  
27  import org.apache.wss4j.common.WSEncryptionPart;
28  import org.apache.wss4j.common.derivedKey.ConversationConstants;
29  import org.apache.wss4j.common.ext.WSSecurityException;
30  import org.apache.wss4j.common.token.Reference;
31  import org.apache.wss4j.common.token.SecurityTokenReference;
32  import org.apache.wss4j.common.util.FIPSUtils;
33  import org.apache.wss4j.common.util.KeyUtils;
34  import org.apache.wss4j.dom.WSConstants;
35  import org.apache.wss4j.dom.util.WSSecurityUtil;
36  import org.apache.xml.security.encryption.Serializer;
37  import org.apache.xml.security.keys.KeyInfo;
38  import org.w3c.dom.Document;
39  import org.w3c.dom.Element;
40  import org.w3c.dom.Node;
41  
42  /**
43   * Encrypts and signs parts of a message with derived keys derived from a
44   * symmetric key. This symmetric key will be included as an EncryptedKey
45   */
46  public class WSSecDKEncrypt extends WSSecDerivedKeyBase {
47  
48      private String symEncAlgo = FIPSUtils.isFIPSEnabled()
49          ? WSConstants.AES_128_GCM : WSConstants.AES_128;
50      private int derivedKeyLength = -1;
51  
52      private List<Element> attachmentEncryptedDataElements;
53  
54      private Serializer encryptionSerializer;
55  
56      public WSSecDKEncrypt(WSSecHeader securityHeader) {
57          super(securityHeader);
58      }
59  
60      public WSSecDKEncrypt(Document doc) {
61          super(doc);
62      }
63  
64      @Override
65      public void prepare(byte[] ephemeralKey) throws WSSecurityException {
66          super.prepare(ephemeralKey);
67  
68          attachmentEncryptedDataElements = new ArrayList<>();
69      }
70  
71      public Document build(byte[] ephemeralKey) throws WSSecurityException {
72  
73          //
74          // Setup the encrypted key
75          //
76          prepare(ephemeralKey);
77          //
78          // prepend elements in the right order to the security header
79          //
80          prependDKElementToHeader();
81  
82          Element externRefList = encrypt();
83  
84          addAttachmentEncryptedDataElements();
85  
86          addExternalRefElement(externRefList);
87  
88          return getDocument();
89      }
90  
91      public void addAttachmentEncryptedDataElements() {
92          if (attachmentEncryptedDataElements != null) {
93              for (Element encryptedData : attachmentEncryptedDataElements) {
94                  Element securityHeaderElement = getSecurityHeader().getSecurityHeaderElement();
95                  WSSecurityUtil.prependChildElement(securityHeaderElement, encryptedData);
96              }
97          }
98      }
99  
100     public Element encrypt() throws WSSecurityException {
101         if (getParts().isEmpty()) {
102             getParts().add(WSSecurityUtil.getDefaultEncryptionPart(getDocument()));
103         }
104 
105         return encryptForExternalRef(null, getParts());
106     }
107 
108     /**
109      * Encrypt one or more parts or elements of the message (external).
110      *
111      * This method takes a vector of <code>WSEncryptionPart</code> object that
112      * contain information about the elements to encrypt. The method call the
113      * encryption method, takes the reference information generated during
114      * encryption and add this to the <code>xenc:Reference</code> element.
115      * This method can be called after <code>prepare()</code> and can be
116      * called multiple times to encrypt a number of parts or elements.
117      *
118      * The method generates a <code>xenc:Reference</code> element that <i>must</i>
119      * be added to the SecurityHeader. See <code>addExternalRefElement()</code>.
120      *
121      * If the <code>dataRef</code> parameter is <code>null</code> the method
122      * creates and initializes a new Reference element.
123      *
124      * @param dataRef A <code>xenc:Reference</code> element or <code>null</code>
125      * @param references A list containing WSEncryptionPart objects
126      * @return Returns the updated <code>xenc:Reference</code> element
127      * @throws WSSecurityException
128      */
129     public Element encryptForExternalRef(Element dataRef, List<WSEncryptionPart> references)
130         throws WSSecurityException {
131 
132         KeyInfo keyInfo = createKeyInfo();
133 
134         SecretKey key = getDerivedKey(symEncAlgo);
135 
136         Encryptor encryptor = new Encryptor();
137         encryptor.setDoc(getDocument());
138         encryptor.setSecurityHeader(getSecurityHeader());
139         encryptor.setIdAllocator(getIdAllocator());
140         encryptor.setCallbackLookup(callbackLookup);
141         encryptor.setAttachmentCallbackHandler(attachmentCallbackHandler);
142         encryptor.setStoreBytesInAttachment(storeBytesInAttachment);
143         encryptor.setEncryptionSerializer(encryptionSerializer);
144         encryptor.setWsDocInfo(getWsDocInfo());
145         List<String> encDataRefs =
146             encryptor.doEncryption(keyInfo, key, symEncAlgo, references, attachmentEncryptedDataElements);
147 
148         if (dataRef == null) {
149             dataRef =
150                 getDocument().createElementNS(
151                     WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":ReferenceList"
152                 );
153         }
154         return WSSecEncrypt.createDataRefList(getDocument(), dataRef, encDataRefs);
155     }
156 
157     /**
158      * Create a KeyInfo object.
159      *
160      * @return Returns the created KeyInfo object.
161      */
162     private KeyInfo createKeyInfo() {
163         KeyInfo keyInfo = new KeyInfo(getDocument());
164         SecurityTokenReference secToken = new SecurityTokenReference(getDocument());
165         secToken.addWSSENamespace();
166         if (addWSUNamespace) {
167             secToken.addWSUNamespace();
168         }
169         Reference ref = new Reference(getDocument());
170         ref.setURI("#" + getId());
171         String ns =
172             ConversationConstants.getWSCNs(getWscVersion())
173                 + ConversationConstants.TOKEN_TYPE_DERIVED_KEY_TOKEN;
174         ref.setValueType(ns);
175         secToken.setReference(ref);
176 
177         keyInfo.addUnknownElement(secToken.getElement());
178         Element keyInfoElement = keyInfo.getElement();
179         keyInfoElement.setAttributeNS(
180             WSConstants.XMLNS_NS, "xmlns:" + WSConstants.SIG_PREFIX, WSConstants.SIG_NS
181         );
182 
183         return keyInfo;
184     }
185 
186     /**
187      * Adds (prepends) the external Reference element to the Security header.
188      *
189      * The reference element <i>must</i> be created by the
190      * <code>encryptForExternalRef() </code> method. The method adds the
191      * reference element in the SecurityHeader.
192      *
193      * @param referenceList The external <code>enc:Reference</code> element
194      */
195     public void addExternalRefElement(Element referenceList) {
196         if (referenceList != null) {
197             Node node = getdktElement().getNextSibling();
198             Element securityHeaderElement = getSecurityHeader().getSecurityHeaderElement();
199             if (node != null && Node.ELEMENT_NODE == node.getNodeType()) {
200                 securityHeaderElement.insertBefore(referenceList, node);
201             } else {
202                 // If (at this moment) DerivedKeyToken is the LAST element of
203                 // the security header
204                 securityHeaderElement.appendChild(referenceList);
205             }
206         }
207     }
208 
209 
210     /**
211      * Set the symmetric encryption algorithm URI to use
212      * @param algo the symmetric encryption algorithm URI to use
213      */
214     public void setSymmetricEncAlgorithm(String algo) {
215         symEncAlgo = algo;
216     }
217 
218     protected int getDerivedKeyLength() throws WSSecurityException {
219         return derivedKeyLength > 0 ? derivedKeyLength : KeyUtils.getKeyLength(symEncAlgo);
220     }
221 
222     public void setDerivedKeyLength(int keyLength) {
223         derivedKeyLength = keyLength;
224     }
225 
226     public List<Element> getAttachmentEncryptedDataElements() {
227         return attachmentEncryptedDataElements;
228     }
229 
230     public Serializer getEncryptionSerializer() {
231         return encryptionSerializer;
232     }
233 
234     public void setEncryptionSerializer(Serializer encryptionSerializer) {
235         this.encryptionSerializer = encryptionSerializer;
236     }
237 }