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.security.Provider;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import javax.crypto.SecretKey;
27  import javax.crypto.spec.SecretKeySpec;
28  import javax.security.auth.DestroyFailedException;
29  
30  import org.apache.wss4j.common.WSEncryptionPart;
31  import org.apache.wss4j.common.crypto.Crypto;
32  import org.apache.wss4j.common.ext.WSSecurityException;
33  import org.apache.wss4j.common.token.Reference;
34  import org.apache.wss4j.common.token.SecurityTokenReference;
35  import org.apache.wss4j.common.util.XMLUtils;
36  import org.apache.wss4j.dom.WSConstants;
37  import org.apache.wss4j.dom.message.token.KerberosSecurity;
38  import org.apache.wss4j.dom.util.WSSecurityUtil;
39  import org.apache.xml.security.algorithms.JCEMapper;
40  import org.apache.xml.security.encryption.Serializer;
41  import org.apache.xml.security.keys.KeyInfo;
42  import org.w3c.dom.Document;
43  import org.w3c.dom.Element;
44  
45  /**
46   * Encrypts a parts of a message according to WS Specification, X509 profile,
47   * and adds the encryption data.
48   */
49  public class WSSecEncrypt extends WSSecEncryptedKey {
50      private static final org.slf4j.Logger LOG =
51          org.slf4j.LoggerFactory.getLogger(WSSecEncrypt.class);
52  
53      /**
54       * SecurityTokenReference to be inserted into EncryptedData/keyInfo element.
55       */
56      private SecurityTokenReference securityTokenReference;
57  
58      /**
59       * Indicates whether to encrypt the symmetric key into an EncryptedKey
60       * or not.
61       */
62      private boolean encryptSymmKey = true;
63  
64      /**
65       * Custom reference value
66       */
67      private String customReferenceValue;
68  
69      /**
70       * True if the encKeyId is a direct reference to a key identifier instead of a URI to a key
71       */
72      private boolean encKeyIdDirectId;
73  
74      private boolean embedEncryptedKey;
75  
76      private List<Element> attachmentEncryptedDataElements;
77  
78      private Serializer encryptionSerializer;
79  
80      /**
81       * Algorithm to be used with the ephemeral key
82       */
83      private String symEncAlgo = WSConstants.AES_128;
84  
85      public WSSecEncrypt(WSSecHeader securityHeader) {
86          super(securityHeader);
87      }
88  
89      public WSSecEncrypt(Document doc) {
90          super(doc);
91      }
92  
93      public WSSecEncrypt(Document doc, Provider provider) {
94          super(doc, provider);
95      }
96  
97      /**
98       * Initialize a WSSec Encrypt.
99       *
100      * The method prepares and initializes a WSSec Encrypt structure after the
101      * relevant information was set. After preparation of the token references
102      * can be added and encrypted.
103      *
104      * This method does not add any element to the security header. This must be
105      * done explicitly.
106      *
107      * @param crypto An instance of the Crypto API to handle keystore and certificates
108      * @param symmetricKey The symmetric key to use for encryption
109      * @throws WSSecurityException
110      */
111     public void prepare(Crypto crypto, SecretKey symmetricKey) throws WSSecurityException {
112         attachmentEncryptedDataElements = new ArrayList<>();
113 
114         if (encryptSymmKey) {
115             super.prepare(crypto, symmetricKey);
116         } else {
117             setEncryptedKeySHA1(symmetricKey.getEncoded());
118         }
119     }
120 
121 
122     /**
123      * Builds the SOAP envelope with encrypted Body and adds encrypted key.
124      *
125      * This is a convenience method and for backward compatibility. The method
126      * calls the single function methods in order to perform a <i>one shot
127      * encryption</i>.
128      *
129      * @param crypto an instance of the Crypto API to handle keystore and Certificates
130      * @param symmetricKey The symmetric key to use for encryption
131      * @return the SOAP envelope with encrypted Body as <code>Document</code>
132      * @throws WSSecurityException
133      */
134     public Document build(Crypto crypto, SecretKey symmetricKey)
135         throws WSSecurityException {
136 
137         prepare(crypto, symmetricKey);
138 
139         LOG.debug("Beginning Encryption...");
140 
141         Element refs = encrypt(symmetricKey);
142 
143         addAttachmentEncryptedDataElements();
144         if (getEncryptedKeyElement() != null) {
145             addInternalRefElement(refs);
146             prependToHeader();
147         } else {
148             addExternalRefElement(refs);
149         }
150 
151         prependBSTElementToHeader();
152 
153         LOG.debug("Encryption complete.");
154         return getDocument();
155     }
156 
157     /**
158      * Perform encryption using the given symmetric key
159      * @param symmetricKey The symmetric key to use for encryption
160      * @return the EncryptedData element
161      * @throws WSSecurityException
162      */
163     public Element encrypt(SecretKey symmetricKey) throws WSSecurityException {
164         if (getParts().isEmpty()) {
165             getParts().add(WSSecurityUtil.getDefaultEncryptionPart(getDocument()));
166         }
167 
168         return encryptForRef(null, getParts(), symmetricKey);
169     }
170 
171     /**
172      * Encrypt one or more parts or elements of the message.
173      *
174      * This method takes a list of <code>WSEncryptionPart</code> object that
175      * contain information about the elements to encrypt. The method call the
176      * encryption method, takes the reference information generated during
177      * encryption and add this to the <code>xenc:Reference</code> element.
178      * This method can be called after <code>prepare()</code> and can be
179      * called multiple times to encrypt a number of parts or elements.
180      *
181      * The method generates a <code>xenc:Reference</code> element that <i>must</i>
182      * be added to this token. See <code>addInternalRefElement()</code>.
183      *
184      * If the <code>dataRef</code> parameter is <code>null</code> the method
185      * creates and initializes a new Reference element.
186      *
187      * @param dataRef A <code>xenc:Reference</code> element or <code>null</code>
188      * @param references A list containing WSEncryptionPart objects
189      * @param symmetricKey The symmetric key to use for encryption
190      * @return Returns the updated <code>xenc:Reference</code> element
191      * @throws WSSecurityException
192      */
193     public Element encryptForRef(
194         Element dataRef,
195         List<WSEncryptionPart> references,
196         SecretKey symmetricKey
197     ) throws WSSecurityException {
198         KeyInfo keyInfo = createKeyInfo();
199         //the sun/oracle jce provider doesn't like a foreign SecretKey impl.
200         //this occurs e.g. with a kerberos session-key. It doesn't matter for the bouncy-castle provider
201         //so create a new secretKeySpec to make everybody happy.
202         String keyAlgorithm = JCEMapper.getJCEKeyAlgorithmFromURI(getSymmetricEncAlgorithm());
203         SecretKeySpec secretKeySpec = new SecretKeySpec(symmetricKey.getEncoded(), keyAlgorithm);
204 
205         Encryptor encryptor = new Encryptor();
206         encryptor.setDoc(getDocument());
207         encryptor.setSecurityHeader(getSecurityHeader());
208         encryptor.setIdAllocator(getIdAllocator());
209         encryptor.setCallbackLookup(callbackLookup);
210         encryptor.setAttachmentCallbackHandler(attachmentCallbackHandler);
211         encryptor.setStoreBytesInAttachment(storeBytesInAttachment);
212         encryptor.setEncryptionSerializer(getEncryptionSerializer());
213         encryptor.setExpandXopInclude(isExpandXopInclude());
214         encryptor.setWsDocInfo(getWsDocInfo());
215         List<String> encDataRefs =
216             encryptor.doEncryption(keyInfo, secretKeySpec, getSymmetricEncAlgorithm(), references, attachmentEncryptedDataElements);
217 
218         // Clean the secret key from memory now that we're done with it
219         try {
220             secretKeySpec.destroy();
221         } catch (DestroyFailedException e) {
222             LOG.debug("Error destroying key: {}", e.getMessage());
223         }
224 
225         if (encDataRefs.isEmpty()) {
226             return null;
227         }
228 
229         if (dataRef == null) {
230             dataRef =
231                 getDocument().createElementNS(
232                     WSConstants.ENC_NS,
233                     WSConstants.ENC_PREFIX + ":ReferenceList"
234                 );
235             //
236             // If we're not placing the ReferenceList in an EncryptedKey structure,
237             // then add the ENC namespace
238             //
239             if (!encryptSymmKey) {
240                 XMLUtils.setNamespace(
241                     dataRef, WSConstants.ENC_NS, WSConstants.ENC_PREFIX
242                 );
243             }
244         }
245         return createDataRefList(getDocument(), dataRef, encDataRefs);
246     }
247 
248     /**
249      * Adds the internal Reference element to this Encrypt data.
250      *
251      * The reference element <i>must</i> be created by the
252      * <code>encryptForInternalRef()</code> method. The reference element is
253      * added to the <code>EncryptedKey</code> element of this encrypt block.
254      *
255      * @param dataRef The internal <code>enc:Reference</code> element
256      */
257     public void addInternalRefElement(Element dataRef) {
258         if (dataRef != null) {
259             getEncryptedKeyElement().appendChild(dataRef);
260         }
261     }
262 
263     /**
264      * Adds (prepends) the external Reference element to the Security header.
265      *
266      * The reference element <i>must</i> be created by the
267      * <code>encryptForExternalRef() </code> method. The method prepends the
268      * reference element in the SecurityHeader.
269      *
270      * @param dataRef The external <code>enc:Reference</code> element
271      */
272     public void addExternalRefElement(Element dataRef) {
273         if (dataRef != null) {
274             Element secHeaderElement = getSecurityHeader().getSecurityHeaderElement();
275             WSSecurityUtil.prependChildElement(secHeaderElement, dataRef);
276         }
277     }
278 
279     public void addAttachmentEncryptedDataElements() {
280         if (attachmentEncryptedDataElements != null) {
281             for (Element encryptedData : attachmentEncryptedDataElements) {
282                 Element secHeaderElement = getSecurityHeader().getSecurityHeaderElement();
283                 WSSecurityUtil.prependChildElement(secHeaderElement, encryptedData);
284             }
285         }
286     }
287 
288     /**
289      * Create a KeyInfo object
290      */
291     private KeyInfo createKeyInfo() throws WSSecurityException {
292 
293         KeyInfo keyInfo = new KeyInfo(getDocument());
294         if (embedEncryptedKey) {
295             keyInfo.addUnknownElement(getEncryptedKeyElement());
296         } else if (keyIdentifierType == WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER) {
297             SecurityTokenReference secToken = new SecurityTokenReference(getDocument());
298             if (addWSUNamespace) {
299                 secToken.addWSUNamespace();
300             }
301             secToken.addWSSENamespace();
302             if (customReferenceValue != null) {
303                 secToken.setKeyIdentifierEncKeySHA1(customReferenceValue);
304             } else {
305                 secToken.setKeyIdentifierEncKeySHA1(getEncryptedKeySHA1());
306             }
307             secToken.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
308             keyInfo.addUnknownElement(secToken.getElement());
309         } else if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(customReferenceValue)) {
310             SecurityTokenReference secToken = new SecurityTokenReference(getDocument());
311             if (addWSUNamespace) {
312                 secToken.addWSUNamespace();
313             }
314             secToken.addWSSENamespace();
315             secToken.addTokenType(WSConstants.WSS_SAML_TOKEN_TYPE);
316             secToken.setKeyIdentifier(WSConstants.WSS_SAML_KI_VALUE_TYPE, getId());
317             keyInfo.addUnknownElement(secToken.getElement());
318         } else if (WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(customReferenceValue)) {
319             SecurityTokenReference secToken = new SecurityTokenReference(getDocument());
320             if (addWSUNamespace) {
321                 secToken.addWSUNamespace();
322             }
323             secToken.addWSSENamespace();
324             secToken.addTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE);
325             secToken.setKeyIdentifier(WSConstants.WSS_SAML2_KI_VALUE_TYPE, getId());
326             keyInfo.addUnknownElement(secToken.getElement());
327         } else if (WSConstants.WSS_KRB_KI_VALUE_TYPE.equals(customReferenceValue)) {
328             SecurityTokenReference secToken = new SecurityTokenReference(getDocument());
329             if (addWSUNamespace) {
330                 secToken.addWSUNamespace();
331             }
332             secToken.addWSSENamespace();
333             secToken.addTokenType(WSConstants.WSS_GSS_KRB_V5_AP_REQ);
334             secToken.setKeyIdentifier(customReferenceValue, getId(), true);
335             keyInfo.addUnknownElement(secToken.getElement());
336         } else if (securityTokenReference != null) {
337             Element tmpE = securityTokenReference.getElement();
338             tmpE.setAttributeNS(
339                 WSConstants.XMLNS_NS, "xmlns:" + tmpE.getPrefix(), tmpE.getNamespaceURI()
340             );
341             keyInfo.addUnknownElement(securityTokenReference.getElement());
342         } else if (getId() != null) {
343             SecurityTokenReference secToken = new SecurityTokenReference(getDocument());
344             if (addWSUNamespace) {
345                 secToken.addWSUNamespace();
346             }
347             secToken.addWSSENamespace();
348             Reference ref = new Reference(getDocument());
349             if (encKeyIdDirectId) {
350                 ref.setURI(getId());
351             } else {
352                 ref.setURI("#" + getId());
353             }
354             if (customReferenceValue != null) {
355                 ref.setValueType(customReferenceValue);
356             }
357             secToken.setReference(ref);
358             if (KerberosSecurity.isKerberosToken(customReferenceValue)) {
359                 secToken.addTokenType(customReferenceValue);
360             } else if (!WSConstants.WSS_USERNAME_TOKEN_VALUE_TYPE.equals(customReferenceValue)) {
361                 secToken.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
362             }
363             keyInfo.addUnknownElement(secToken.getElement());
364         } else if (!encryptSymmKey && (keyIdentifierType == WSConstants.ISSUER_SERIAL
365                 || keyIdentifierType == WSConstants.ISSUER_SERIAL_QUOTE_FORMAT)) {
366             SecurityTokenReference secToken = new SecurityTokenReference(getDocument());
367             if (addWSUNamespace) {
368                 secToken.addWSUNamespace();
369             }
370             secToken.addWSSENamespace();
371             if (customReferenceValue != null) {
372                 secToken.setKeyIdentifierEncKeySHA1(customReferenceValue);
373             } else {
374                 secToken.setKeyIdentifierEncKeySHA1(getEncryptedKeySHA1());
375             }
376             secToken.addTokenType(WSConstants.WSS_ENC_KEY_VALUE_TYPE);
377             keyInfo.addUnknownElement(secToken.getElement());
378         }
379 
380         Element keyInfoElement = keyInfo.getElement();
381         keyInfoElement.setAttributeNS(
382             WSConstants.XMLNS_NS, "xmlns:" + WSConstants.SIG_PREFIX, WSConstants.SIG_NS
383         );
384 
385         return keyInfo;
386     }
387 
388     /**
389      * Create DOM subtree for <code>xenc:EncryptedKey</code>
390      *
391      * @param doc the SOAP envelope parent document
392      * @param referenceList
393      * @param encDataRefs
394      * @return an <code>xenc:EncryptedKey</code> element
395      */
396     public static Element createDataRefList(
397         Document doc,
398         Element referenceList,
399         List<String> encDataRefs
400     ) {
401         for (String dataReferenceUri : encDataRefs) {
402             Element dataReference =
403                 doc.createElementNS(
404                     WSConstants.ENC_NS, WSConstants.ENC_PREFIX + ":DataReference"
405                 );
406             dataReference.setAttributeNS(null, "URI", dataReferenceUri);
407             referenceList.appendChild(dataReference);
408         }
409         return referenceList;
410     }
411 
412     /**
413      * @return Return the SecurityTokenRefernce
414      */
415     public SecurityTokenReference getSecurityTokenReference() {
416         return securityTokenReference;
417     }
418 
419     /**
420      * @param reference
421      */
422     public void setSecurityTokenReference(SecurityTokenReference reference) {
423         securityTokenReference = reference;
424     }
425 
426     public boolean isEncryptSymmKey() {
427         return encryptSymmKey;
428     }
429 
430     public void setEncryptSymmKey(boolean encryptSymmKey) {
431         this.encryptSymmKey = encryptSymmKey;
432     }
433 
434     public void setCustomReferenceValue(String customReferenceValue) {
435         this.customReferenceValue = customReferenceValue;
436     }
437 
438     public void setEncKeyIdDirectId(boolean b) {
439         encKeyIdDirectId = b;
440     }
441 
442     public void setEmbedEncryptedKey(boolean embedEncryptedKey) {
443         this.embedEncryptedKey = embedEncryptedKey;
444     }
445 
446     public boolean isEmbedEncryptedKey() {
447         return embedEncryptedKey;
448     }
449 
450     public List<Element> getAttachmentEncryptedDataElements() {
451         return attachmentEncryptedDataElements;
452     }
453 
454     public Serializer getEncryptionSerializer() {
455         return encryptionSerializer;
456     }
457 
458     public void setEncryptionSerializer(Serializer encryptionSerializer) {
459         this.encryptionSerializer = encryptionSerializer;
460     }
461 
462     /**
463      * Set the name of the symmetric encryption algorithm to use.
464      *
465      * This encryption algorithm is used to encrypt the data. If the algorithm
466      * is not set then AES128 is used. Refer to WSConstants which algorithms are
467      * supported.
468      *
469      * @param algo Is the name of the encryption algorithm
470      * @see WSConstants#TRIPLE_DES
471      * @see WSConstants#AES_128
472      * @see WSConstants#AES_192
473      * @see WSConstants#AES_256
474      */
475     public void setSymmetricEncAlgorithm(String algo) {
476         symEncAlgo = algo;
477     }
478 
479 
480     /**
481      * Get the name of symmetric encryption algorithm to use.
482      *
483      * The name of the encryption algorithm to encrypt the data, i.e. the SOAP
484      * Body. Refer to WSConstants which algorithms are supported.
485      *
486      * @return the name of the currently selected symmetric encryption algorithm
487      * @see WSConstants#TRIPLE_DES
488      * @see WSConstants#AES_128
489      * @see WSConstants#AES_192
490      * @see WSConstants#AES_256
491      */
492     public String getSymmetricEncAlgorithm() {
493         return symEncAlgo;
494     }
495 
496 }