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