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.processor;
21  
22  import java.io.ByteArrayInputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.security.*;
26  import java.security.cert.X509Certificate;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.List;
30  
31  import javax.crypto.Cipher;
32  import javax.crypto.KeyGenerator;
33  import javax.crypto.SecretKey;
34  import javax.crypto.spec.OAEPParameterSpec;
35  
36  import org.apache.wss4j.common.WSS4JConstants;
37  import org.apache.wss4j.common.token.DOMX509SKI;
38  import org.apache.xml.security.encryption.AgreementMethod;
39  import org.apache.xml.security.encryption.KeyDerivationMethod;
40  import org.apache.xml.security.encryption.XMLCipherUtil;
41  import org.apache.xml.security.encryption.keys.RecipientKeyInfo;
42  import org.apache.xml.security.encryption.keys.content.AgreementMethodImpl;
43  import org.apache.xml.security.encryption.params.KeyAgreementParameters;
44  import org.apache.xml.security.exceptions.XMLSecurityException;
45  import org.apache.xml.security.keys.content.keyvalues.DSAKeyValue;
46  import org.apache.xml.security.keys.content.keyvalues.ECKeyValue;
47  import org.apache.xml.security.keys.content.keyvalues.KeyValueContent;
48  import org.apache.xml.security.keys.content.keyvalues.RSAKeyValue;
49  import org.apache.xml.security.utils.Constants;
50  import org.apache.xml.security.utils.EncryptionConstants;
51  import org.w3c.dom.Document;
52  import org.w3c.dom.Element;
53  import org.w3c.dom.Node;
54  import org.apache.wss4j.common.bsp.BSPEnforcer;
55  import org.apache.wss4j.common.bsp.BSPRule;
56  import org.apache.wss4j.common.crypto.AlgorithmSuite;
57  import org.apache.wss4j.common.crypto.AlgorithmSuiteValidator;
58  import org.apache.wss4j.common.crypto.CryptoType;
59  import org.apache.wss4j.common.ext.WSSecurityException;
60  import org.apache.wss4j.common.token.DOMX509IssuerSerial;
61  import org.apache.wss4j.common.token.SecurityTokenReference;
62  import org.apache.wss4j.common.util.KeyUtils;
63  import org.apache.wss4j.common.util.XMLUtils;
64  import org.apache.wss4j.dom.WSConstants;
65  import org.apache.wss4j.dom.WSDataRef;
66  import org.apache.wss4j.dom.WSDocInfo;
67  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
68  import org.apache.wss4j.dom.handler.RequestData;
69  import org.apache.wss4j.dom.str.EncryptedKeySTRParser;
70  import org.apache.wss4j.dom.str.STRParser;
71  import org.apache.wss4j.dom.str.STRParserParameters;
72  import org.apache.wss4j.dom.str.STRParserResult;
73  import org.apache.wss4j.dom.util.EncryptionUtils;
74  import org.apache.wss4j.dom.util.SignatureUtils;
75  import org.apache.wss4j.dom.util.WSSecurityUtil;
76  import org.apache.wss4j.dom.util.X509Util;
77  import org.apache.xml.security.algorithms.JCEMapper;
78  import org.apache.xml.security.encryption.XMLCipher;
79  
80  public class EncryptedKeyProcessor implements Processor {
81      private static final org.slf4j.Logger LOG =
82          org.slf4j.LoggerFactory.getLogger(EncryptedKeyProcessor.class);
83  
84      public EncryptedKeyProcessor() {
85      }
86  
87      public List<WSSecurityEngineResult> handleToken(
88          Element elem,
89          RequestData data
90      ) throws WSSecurityException {
91          return handleToken(elem, data, data.getAlgorithmSuite());
92      }
93  
94      public List<WSSecurityEngineResult> handleToken(
95          Element elem,
96          RequestData data,
97          AlgorithmSuite algorithmSuite
98      ) throws WSSecurityException {
99          LOG.debug("Found encrypted key element");
100 
101         // See if this key has already been processed. If so then just return the result
102         String id = elem.getAttributeNS(null, "Id");
103         if (!id.isEmpty()) {
104              WSSecurityEngineResult result = data.getWsDocInfo().getResult(id);
105              if (result != null
106                  && WSConstants.ENCR == (Integer)result.get(WSSecurityEngineResult.TAG_ACTION)
107              ) {
108                  return Collections.singletonList(result);
109              }
110         }
111 
112         if (data.getCallbackHandler() == null) {
113             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noCallback");
114         }
115         //
116         // lookup xenc:EncryptionMethod, get the Algorithm attribute to determine
117         // how the key was encrypted. Then check if we support the algorithm
118         //
119         String encryptedKeyTransportMethod = X509Util.getEncAlgo(elem);
120         if (encryptedKeyTransportMethod == null) {
121             throw new WSSecurityException(
122                 WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, "noEncAlgo"
123             );
124         }
125         if (WSConstants.KEYTRANSPORT_RSA15.equals(encryptedKeyTransportMethod)
126             && !data.isAllowRSA15KeyTransportAlgorithm()
127             && (algorithmSuite == null
128               || !algorithmSuite.getKeyWrapAlgorithms().contains(WSConstants.KEYTRANSPORT_RSA15))) {
129             LOG.debug(
130                 "The Key transport method does not match the requirement"
131             );
132             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
133         }
134 
135         Element keyInfoChildElement = getKeyInfoChildElement(elem, data);
136         boolean isDHKeyWrap = isDiffieHellmanKeyWrap(keyInfoChildElement);
137         // Check BSP Compliance
138         checkBSPCompliance(elem, encryptedKeyTransportMethod, isDHKeyWrap, data.getBSPEnforcer());
139 
140         //
141         // Now lookup CipherValue.
142         //
143         Element xencCipherValue = EncryptionUtils.getCipherValueFromEncryptedData(elem);
144         if (xencCipherValue == null) {
145             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "noCipher");
146         }
147 
148         X509Certificate[] certs = null;
149         STRParser.REFERENCE_TYPE referenceType = null;
150         PublicKey publicKey = null;
151         boolean symmetricKeyWrap = isSymmetricKeyWrap(encryptedKeyTransportMethod);
152         AgreementMethod agreementMethod = null;
153         KeyDerivationMethod keyDerivationMethod = null;
154         if (isDHKeyWrap) {
155             // get key agreement method value
156             agreementMethod = getAgreementMethodFromElement(keyInfoChildElement);
157             keyDerivationMethod = getKeyDerivationFunction(agreementMethod);
158             //  get the recipient key info element
159             keyInfoChildElement = getRecipientKeyInfoChildElement(agreementMethod);
160             if (keyInfoChildElement == null) {
161                 throw new WSSecurityException(
162                         WSSecurityException.ErrorCode.INVALID_SECURITY, "noRecipientSecTokRef"
163                 );
164             }
165         }
166         if (!symmetricKeyWrap || isDHKeyWrap) {
167             CertificateResult certificateResult = getPublicKey(keyInfoChildElement, data);
168             certs = certificateResult.getCerts();
169             publicKey = certificateResult.getPublicKey();
170             referenceType = certificateResult.getCertificatesReferenceType();
171         }
172 
173         // Check for compliance against the defined AlgorithmSuite
174         if (algorithmSuite != null) {
175             AlgorithmSuiteValidator algorithmSuiteValidator = new
176                 AlgorithmSuiteValidator(algorithmSuite);
177 
178             if (!symmetricKeyWrap) {
179                 algorithmSuiteValidator.checkAsymmetricKeyLength(publicKey);
180             }
181             algorithmSuiteValidator.checkEncryptionKeyWrapAlgorithm(
182                 encryptedKeyTransportMethod
183             );
184             if (agreementMethod != null) {
185                 algorithmSuiteValidator.checkKeyAgreementMethodAlgorithm(
186                         agreementMethod.getAlgorithm()
187                 );
188             }
189             if (keyDerivationMethod != null) {
190                 algorithmSuiteValidator.checkKeyDerivationFunction(
191                         keyDerivationMethod.getAlgorithm()
192                 );
193             }
194         }
195 
196         byte[] encryptedEphemeralKey = null;
197         byte[] decryptedBytes = null;
198         Element refList =
199             XMLUtils.getDirectChildElement(elem, "ReferenceList", WSConstants.ENC_NS);
200 
201         // Get the key bytes from CipherValue directly or via an attachment
202         String xopUri = EncryptionUtils.getXOPURIFromCipherValue(xencCipherValue);
203         if (xopUri != null && xopUri.startsWith("cid:")) {
204             encryptedEphemeralKey = WSSecurityUtil.getBytesFromAttachment(xopUri, data);
205         } else {
206             encryptedEphemeralKey = EncryptionUtils.getDecodedBase64EncodedData(xencCipherValue);
207         }
208 
209         if (isDHKeyWrap) {
210             PrivateKey privateKey = getPrivateKey(data, certs, publicKey);
211             decryptedBytes = getDiffieHellmanDecryptedBytes(data, agreementMethod,
212                     encryptedKeyTransportMethod, encryptedEphemeralKey, privateKey);
213         } else if (symmetricKeyWrap) {
214             decryptedBytes = getSymmetricDecryptedBytes(data, data.getWsDocInfo(), keyInfoChildElement, refList);
215         } else {
216             PrivateKey privateKey = getPrivateKey(data, certs, publicKey);
217             decryptedBytes = getAsymmetricDecryptedBytes(data, data.getWsDocInfo(), encryptedKeyTransportMethod,
218                                                          encryptedEphemeralKey, refList,
219                                                          elem, privateKey);
220         }
221 
222         List<WSDataRef> dataRefs = decryptDataRefs(refList, data.getWsDocInfo(), decryptedBytes, data);
223 
224         WSSecurityEngineResult result = new WSSecurityEngineResult(
225                 WSConstants.ENCR,
226                 decryptedBytes,
227                 encryptedEphemeralKey,
228                 dataRefs,
229                 certs
230             );
231         result.put(
232             WSSecurityEngineResult.TAG_ENCRYPTED_KEY_TRANSPORT_METHOD,
233             encryptedKeyTransportMethod
234         );
235         result.put(WSSecurityEngineResult.TAG_TOKEN_ELEMENT, elem);
236         String tokenId = elem.getAttributeNS(null, "Id");
237         if (tokenId.length() != 0) {
238             result.put(WSSecurityEngineResult.TAG_ID, tokenId);
239         }
240         if (referenceType != null) {
241             result.put(WSSecurityEngineResult.TAG_X509_REFERENCE_TYPE, referenceType);
242         }
243         if (publicKey != null) {
244             result.put(WSSecurityEngineResult.TAG_PUBLIC_KEY, publicKey);
245         }
246         data.getWsDocInfo().addResult(result);
247         data.getWsDocInfo().addTokenElement(elem);
248         return Collections.singletonList(result);
249     }
250 
251     /**
252      * Resolve the KeyInfoType child element to locate the public key (with the X509Certificate chain if given )
253      * to use to decrypt the EncryptedKey.
254      *
255      * @param keyValueElement The element to get the child element from
256      * @param data            The RequestData context
257      * @return The CertificateResult object containing the public key and optionally X509Certificate chain
258      * @throws WSSecurityException an error occurred when trying to resolve the key info
259      */
260     private CertificateResult getPublicKey(Element keyValueElement, RequestData data) throws WSSecurityException {
261         CertificateResult.Builder builder = CertificateResult.Builder.create();
262 
263         if (SecurityTokenReference.SECURITY_TOKEN_REFERENCE.equals(keyValueElement.getLocalName())
264                 && WSConstants.WSSE_NS.equals(keyValueElement.getNamespaceURI())) {
265             STRParserParameters parameters = new STRParserParameters();
266             parameters.setData(data);
267             parameters.setStrElement(keyValueElement);
268 
269             STRParser strParser = new EncryptedKeySTRParser();
270             STRParserResult result = strParser.parseSecurityTokenReference(parameters);
271             builder.certificates(result.getCertificates());
272             builder.publicKey(result.getPublicKey());
273             builder.certificatesReferenceType(result.getCertificatesReferenceType());
274         } else {
275             X509Certificate[] certs = getCertificatesFromX509Data(keyValueElement, data);
276             builder.certificates(certs);
277             if (certs == null || certs.length == 0) {
278                 PublicKey publicKey = getPublicKeyFromKeyValue(keyValueElement);
279                 builder.publicKey(publicKey);
280             }
281         }
282         return builder.build();
283     }
284 
285     private PublicKey getPublicKeyFromKeyValue(Element keyValueElement) throws WSSecurityException {
286         PublicKey publicKey = null;
287         KeyValueContent keyValue;
288         try {
289             Element keyValueChild = getFirstElement(keyValueElement);
290             if (keyValueChild == null) {
291                 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "unsupportedKeyInfo");
292             }
293             switch (keyValueChild.getLocalName()) {
294                 case "ECKeyValue":
295                     keyValue = new ECKeyValue(keyValueChild, Constants.SignatureSpec11NS);
296                     break;
297                 case "RSAKeyValue":
298                     keyValue = new RSAKeyValue(keyValueChild, Constants.SignatureSpecNS);
299                     break;
300                 case "DSAKeyValue":
301                     keyValue = new DSAKeyValue(keyValueChild, Constants.SignatureSpecNS);
302                     break;
303                 default:
304                     throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "unsupportedKeyInfo");
305             }
306 
307             publicKey = keyValue.getPublicKey();
308         } catch (XMLSecurityException e) {
309             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "unsupportedKeyInfo");
310         }
311         return publicKey;
312     }
313 
314     private PrivateKey getPrivateKey(
315         RequestData data, X509Certificate[] certs, PublicKey publicKey
316     ) throws WSSecurityException {
317         try {
318             if (certs != null && certs.length > 0) {
319                 return data.getDecCrypto().getPrivateKey(certs[0], data.getCallbackHandler());
320             }
321             return data.getDecCrypto().getPrivateKey(publicKey, data.getCallbackHandler());
322         } catch (WSSecurityException ex) {
323             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex);
324         }
325     }
326 
327     private static byte[] getSymmetricDecryptedBytes(
328         RequestData data,
329         WSDocInfo wsDocInfo,
330         Element keyInfoChildElement,
331         Element refList
332     ) throws WSSecurityException {
333         // Get the (first) encryption algorithm
334         String uri = getFirstDataRefURI(refList);
335         String algorithmURI = null;
336         if (uri != null) {
337             Element ee =
338                 EncryptionUtils.findEncryptedDataElement(wsDocInfo, uri);
339             algorithmURI = X509Util.getEncAlgo(ee);
340         }
341         return X509Util.getSecretKey(keyInfoChildElement, algorithmURI, data.getCallbackHandler());
342     }
343 
344     private static byte[] getAsymmetricDecryptedBytes(
345         RequestData data,
346         WSDocInfo wsDocInfo,
347         String encryptedKeyTransportMethod,
348         byte[] encryptedEphemeralKey,
349         Element refList,
350         Element encryptedKeyElement,
351         PrivateKey privateKey
352     ) throws WSSecurityException {
353         if (data.getDecCrypto() == null) {
354             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noDecCryptoFile");
355         }
356         String cryptoProvider = data.getDecCrypto().getCryptoProvider();
357         Cipher cipher = KeyUtils.getCipherInstance(encryptedKeyTransportMethod, cryptoProvider);
358         try {
359             OAEPParameterSpec oaepParameterSpec = null;
360             if (WSConstants.KEYTRANSPORT_RSAOAEP.equals(encryptedKeyTransportMethod)
361                 || WSConstants.KEYTRANSPORT_RSAOAEP_XENC11.equals(encryptedKeyTransportMethod)) {
362                 // Get the DigestMethod if it exists
363                 String digestAlgorithm = EncryptionUtils.getDigestAlgorithm(encryptedKeyElement);
364                 String mgfAlgorithm = EncryptionUtils.getMGFAlgorithm(encryptedKeyElement);
365                 byte[] pSourceBytes = EncryptionUtils.getPSource(encryptedKeyElement);
366                 oaepParameterSpec = XMLCipherUtil.constructOAEPParameters(encryptedKeyTransportMethod,
367                         digestAlgorithm, mgfAlgorithm, pSourceBytes);
368             }
369 
370             if (oaepParameterSpec == null) {
371                 cipher.init(Cipher.UNWRAP_MODE, privateKey);
372             } else {
373                 cipher.init(Cipher.UNWRAP_MODE, privateKey, oaepParameterSpec);
374             }
375         } catch (Exception ex) {
376             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex);
377         }
378 
379         try {
380             String keyAlgorithm = JCEMapper.translateURItoJCEID(encryptedKeyTransportMethod);
381             return cipher.unwrap(encryptedEphemeralKey, keyAlgorithm, Cipher.SECRET_KEY).getEncoded();
382         } catch (IllegalStateException ex) {
383             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex);
384         } catch (Exception ex) {
385             return getRandomKey(refList, wsDocInfo);
386         }
387     }
388 
389     /**
390      * Method decrypts encryptedEphemeralKey using Key Agreement algorithm to derive symmetric key
391      * for decryption of the key.
392      *
393      * @param data RequestData context
394      * @param agreementMethod AgreementMethod element
395      * @param encryptedKeyTransportMethod Algorithm used to encrypt the key
396      * @param encryptedEphemeralKey Encrypted ephemeral/transport key
397      * @param privateKey Private key of the recipient
398      * @return Decrypted bytes of the ephemeral/transport key
399      * @throws WSSecurityException if the key decryption fails
400      */
401     private static byte[] getDiffieHellmanDecryptedBytes(
402             RequestData data,
403             AgreementMethod agreementMethod,
404             String encryptedKeyTransportMethod,
405             byte[] encryptedEphemeralKey,
406             PrivateKey privateKey
407     ) throws WSSecurityException {
408 
409         SecretKey kek;
410         try {
411             KeyAgreementParameters parameterSpec = XMLCipherUtil.constructRecipientKeyAgreementParameters(
412                     encryptedKeyTransportMethod, agreementMethod, privateKey);
413 
414             kek = org.apache.xml.security.utils.KeyUtils.aesWrapKeyWithDHGeneratedKey(parameterSpec);
415         } catch (XMLSecurityException ex) {
416             LOG.debug("Error occurred while resolving the Diffie Hellman key: " + ex.getMessage());
417             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex);
418         }
419 
420         String cryptoProvider = data.getDecCrypto().getCryptoProvider();
421         Cipher cipher = KeyUtils.getCipherInstance(encryptedKeyTransportMethod, cryptoProvider);
422 
423         try {
424             cipher.init(Cipher.UNWRAP_MODE, kek);
425             String keyAlgorithm = JCEMapper.translateURItoJCEID(encryptedKeyTransportMethod);
426             return cipher.unwrap(encryptedEphemeralKey, keyAlgorithm, Cipher.SECRET_KEY).getEncoded();
427         } catch (InvalidKeyException | NoSuchAlgorithmException ex) {
428             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex);
429         }
430     }
431 
432     /**
433      * if keyInfo element contains AgreementMethod element then check if it is supported EC Diffie-Hellman key agreement algorithm
434      *
435      * @param keyInfoChildElement The KeyInfo child element
436      * @return true if AgreementMethod element is present and DH algorithm supported and false if AgreementMethod element is not present
437      * @throws WSSecurityException if AgreementMethod element is present but DH algorithm is not supported
438      */
439     private boolean isDiffieHellmanKeyWrap(Element keyInfoChildElement) throws WSSecurityException {
440         if (EncryptionConstants._TAG_AGREEMENTMETHOD.equals(keyInfoChildElement.getLocalName())
441                 && WSConstants.ENC_NS.equals(keyInfoChildElement.getNamespaceURI())) {
442             String algorithmURI = keyInfoChildElement.getAttributeNS(null, "Algorithm");
443             // Only ECDH_ES is supported for AgreementMethod
444             if (!(WSConstants.AGREEMENT_METHOD_ECDH_ES.equals(algorithmURI)
445                 || WSConstants.AGREEMENT_METHOD_X25519.equals(algorithmURI)
446                 || WSConstants.AGREEMENT_METHOD_X448.equals(algorithmURI))) {
447                 throw new WSSecurityException(
448                         WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM,
449                         "unknownAlgorithm", new Object[]{algorithmURI});
450             }
451             return true;
452         }
453         return false;
454     }
455 
456     /**
457      * Parse keyInfo content to AgreementMethod object.
458      *
459      * @param keyInfoChildElement The KeyInfo child element containing AgreementMethod data.
460      * @return the {@link AgreementMethod} object.
461      * @throws WSSecurityException if AgreementMethod element is invalid.
462      */
463     private AgreementMethod getAgreementMethodFromElement(Element keyInfoChildElement) throws WSSecurityException {
464         try {
465             return new AgreementMethodImpl(keyInfoChildElement);
466         } catch (XMLSecurityException ex) {
467             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, ex);
468         }
469     }
470 
471     /**
472      * Get the RecipientKeyInfo child element from the AgreementMethod element.
473      *
474      * @param agreementMethod The AgreementMethod element
475      * @return the RecipientKeyInfo child element which contains the recipient's public key.
476      * @throws WSSecurityException if the agreementMethod is null or RecipientKeyInfo element can not be retrieved.
477      */
478     private Element getRecipientKeyInfoChildElement(AgreementMethod agreementMethod) throws WSSecurityException {
479         if (agreementMethod == null) {
480             throw new WSSecurityException(
481                     WSSecurityException.ErrorCode.INVALID_SECURITY, "noAgreementMethod"
482             );
483         }
484         try {
485             RecipientKeyInfo recipientKeyInfo = agreementMethod.getRecipientKeyInfo();
486             if (recipientKeyInfo == null) {
487                 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "noRecipientKeyInfo");
488             }
489             Element receiverKeyInfoElement = recipientKeyInfo.getElement();
490             return getFirstElement(receiverKeyInfoElement);
491         } catch (XMLSecurityException ex) {
492             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, ex);
493         }
494     }
495 
496     private static boolean isSymmetricKeyWrap(String transportAlgorithm) {
497         return XMLCipher.AES_128_KeyWrap.equals(transportAlgorithm)
498             || XMLCipher.AES_192_KeyWrap.equals(transportAlgorithm)
499             || XMLCipher.AES_256_KeyWrap.equals(transportAlgorithm)
500             || XMLCipher.TRIPLEDES_KeyWrap.equals(transportAlgorithm)
501             || XMLCipher.CAMELLIA_128_KeyWrap.equals(transportAlgorithm)
502             || XMLCipher.CAMELLIA_192_KeyWrap.equals(transportAlgorithm)
503             || XMLCipher.CAMELLIA_256_KeyWrap.equals(transportAlgorithm)
504             || XMLCipher.SEED_128_KeyWrap.equals(transportAlgorithm);
505     }
506 
507     /**
508      * Generates a random secret key using the algorithm specified in the
509      * first DataReference URI
510      */
511     private static byte[] getRandomKey(Element refList, WSDocInfo wsDocInfo) throws WSSecurityException {
512         try {
513             String alg = "AES";
514             int size = 16;
515             String uri = getFirstDataRefURI(refList);
516 
517             if (uri != null) {
518                 Element ee =
519                     EncryptionUtils.findEncryptedDataElement(wsDocInfo, uri);
520                 String algorithmURI = X509Util.getEncAlgo(ee);
521                 alg = JCEMapper.getJCEKeyAlgorithmFromURI(algorithmURI);
522                 size = KeyUtils.getKeyLength(algorithmURI);
523             }
524             KeyGenerator kgen = KeyGenerator.getInstance(alg);
525             kgen.init(size * 8);
526             SecretKey k = kgen.generateKey();
527             return k.getEncoded();
528         } catch (Throwable ex) {
529             // Fallback to just using AES to avoid attacks on EncryptedData algorithms
530             try {
531                 KeyGenerator kgen = KeyGenerator.getInstance("AES");
532                 kgen.init(128);
533                 SecretKey k = kgen.generateKey();
534                 return k.getEncoded();
535             } catch (NoSuchAlgorithmException e) {
536                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, e);
537             }
538         }
539     }
540 
541     private static String getFirstDataRefURI(Element refList) {
542         // Lookup the references that are encrypted with this key
543         if (refList != null) {
544             for (Node node = refList.getFirstChild(); node != null; node = node.getNextSibling()) {
545                 if (Node.ELEMENT_NODE == node.getNodeType()
546                         && WSConstants.ENC_NS.equals(node.getNamespaceURI())
547                         && "DataReference".equals(node.getLocalName())) {
548                     String dataRefURI = ((Element) node).getAttributeNS(null, "URI");
549                     return XMLUtils.getIDFromReference(dataRefURI);
550                 }
551             }
552         }
553         return null;
554     }
555 
556     private Element getKeyInfoChildElement(
557         Element xencEncryptedKey, RequestData data
558     ) throws WSSecurityException {
559         Element keyInfo =
560             XMLUtils.getDirectChildElement(xencEncryptedKey, "KeyInfo", WSConstants.SIG_NS);
561         if (keyInfo != null) {
562             Element strElement = null;
563 
564             int result = 0;
565             Node node = keyInfo.getFirstChild();
566             while (node != null) {
567                 if (Node.ELEMENT_NODE == node.getNodeType()) {
568                     result++;
569                     strElement = (Element)node;
570                 }
571                 node = node.getNextSibling();
572             }
573             if (result != 1) {
574                 data.getBSPEnforcer().handleBSPRule(BSPRule.R5424);
575             }
576 
577             if (strElement == null) {
578                 throw new WSSecurityException(
579                     WSSecurityException.ErrorCode.INVALID_SECURITY, "noSecTokRef"
580                 );
581             }
582 
583             return strElement;
584         } else {
585             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "noKeyinfo");
586         }
587     }
588 
589     /**
590      * Method retrieved the KeyDerivationMethod child element from the AgreementMethod object.
591      *
592      * @param agreementMethod object containing th KeyDerivationMethod.
593      * @return the {@link KeyDerivationMethod} object or null if no KeyDerivation element is specified in the agreementMethod.
594      * @throws WSSecurityException if KeyDerivationMethod can not be parsed from Element structure.
595      */
596     private KeyDerivationMethod getKeyDerivationFunction(AgreementMethod agreementMethod) throws WSSecurityException {
597         if (agreementMethod == null) {
598             return null;
599         }
600         try {
601             return agreementMethod.getKeyDerivationMethod();
602         } catch (XMLSecurityException ex) {
603             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, ex);
604         }
605     }
606 
607     private X509Certificate[] getCertificatesFromX509Data(
608         Element keyInfoChildElement,
609         RequestData data
610     ) throws WSSecurityException {
611         X509Certificate[] certs = new X509Certificate[0];
612 
613         if (WSConstants.SIG_NS.equals(keyInfoChildElement.getNamespaceURI())
614             && WSConstants.X509_DATA_LN.equals(keyInfoChildElement.getLocalName())) {
615             data.getBSPEnforcer().handleBSPRule(BSPRule.R5426);
616 
617             Element issuerSerialElement = XMLUtils.getDirectChildElement(keyInfoChildElement, WSS4JConstants.X509_ISSUER_SERIAL_LN,
618                     WSS4JConstants.SIG_NS);
619             if (issuerSerialElement != null) {
620                 DOMX509IssuerSerial issuerSerial = new DOMX509IssuerSerial(issuerSerialElement);
621                 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ISSUER_SERIAL);
622                 cryptoType.setIssuerSerial(issuerSerial.getIssuer(), issuerSerial.getSerialNumber());
623                 certs = data.getDecCrypto().getX509Certificates(cryptoType);
624             }
625 
626             Element skiElement = XMLUtils.getDirectChildElement(keyInfoChildElement, WSS4JConstants.X509_SKI_LN, WSS4JConstants.SIG_NS);
627             if (skiElement != null && certs.length == 0) {
628                 DOMX509SKI x509SKI = new DOMX509SKI(skiElement);
629                 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.SKI_BYTES);
630                 cryptoType.setBytes(x509SKI.getSKIBytes());
631                 certs = data.getDecCrypto().getX509Certificates(cryptoType);
632             }
633 
634             Element x509CertElement = XMLUtils.getDirectChildElement(keyInfoChildElement,
635                 WSS4JConstants.X509_CERT_LN, WSS4JConstants.SIG_NS);
636             if (x509CertElement != null && certs.length == 0) {
637                 byte[] token = EncryptionUtils.getDecodedBase64EncodedData(x509CertElement);
638                 if (token == null || token.length == 0) {
639                     throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidCertData",
640                                                   new Object[] {"0"});
641                 }
642                 try (InputStream in = new ByteArrayInputStream(token)) {
643                     X509Certificate cert = data.getDecCrypto().loadCertificate(in);
644                     if (cert != null) {
645                         certs = new X509Certificate[]{cert};
646                     }
647                 } catch (IOException e) {
648                     throw new WSSecurityException(
649                         WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, e, "parseError"
650                     );
651                 }
652             }
653         }
654 
655         return certs;
656     }
657 
658     private Element getFirstElement(Element element) {
659         for (Node currentChild = element.getFirstChild();
660              currentChild != null;
661              currentChild = currentChild.getNextSibling()
662         ) {
663             if (Node.ELEMENT_NODE == currentChild.getNodeType()) {
664                 return (Element) currentChild;
665             }
666         }
667         return null;
668     }
669 
670     /**
671      * Decrypt all data references
672      */
673     protected List<WSDataRef> decryptDataRefs(Element refList, WSDocInfo docInfo,
674                                             byte[] decryptedBytes, RequestData data
675     ) throws WSSecurityException {
676         //
677         // At this point we have the decrypted session (symmetric) key. According
678         // to W3C XML-Enc this key is used to decrypt _any_ references contained in
679         // the reference list
680         if (refList == null) {
681             return Collections.emptyList();
682         }
683 
684         List<WSDataRef> dataRefs = new ArrayList<>();
685         for (Node node = refList.getFirstChild(); node != null; node = node.getNextSibling()) {
686             if (Node.ELEMENT_NODE == node.getNodeType()
687                     && WSConstants.ENC_NS.equals(node.getNamespaceURI())
688                     && "DataReference".equals(node.getLocalName())) {
689                 String dataRefURI = ((Element) node).getAttributeNS(null, "URI");
690                 dataRefURI = XMLUtils.getIDFromReference(dataRefURI);
691 
692                 WSDataRef dataRef =
693                     decryptDataRef(refList.getOwnerDocument(), dataRefURI, docInfo, decryptedBytes, data);
694                 dataRefs.add(dataRef);
695             }
696         }
697 
698         return dataRefs;
699     }
700 
701     /**
702      * Decrypt an EncryptedData element referenced by dataRefURI
703      */
704     protected WSDataRef decryptDataRef(
705         Document doc,
706         String dataRefURI,
707         WSDocInfo docInfo,
708         byte[] decryptedData,
709         RequestData data
710     ) throws WSSecurityException {
711         LOG.debug("found data reference: {}", dataRefURI);
712         //
713         // Find the encrypted data element referenced by dataRefURI
714         //
715         Element encryptedDataElement =
716             EncryptionUtils.findEncryptedDataElement(docInfo, dataRefURI);
717         if (encryptedDataElement != null && data.isRequireSignedEncryptedDataElements()) {
718             List<WSSecurityEngineResult> signedResults =
719                 docInfo.getResultsByTag(WSConstants.SIGN);
720             SignatureUtils.verifySignedElement(encryptedDataElement, signedResults);
721         }
722         //
723         // Prepare the SecretKey object to decrypt EncryptedData
724         //
725         String symEncAlgo = X509Util.getEncAlgo(encryptedDataElement);
726 
727         // EncryptionAlgorithm cannot be null
728         if (symEncAlgo == null) {
729             LOG.warn("No encryption algorithm was specified in the request");
730             throw new WSSecurityException(WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, "badEncAlgo",
731                                           new Object[] {null});
732 
733         }
734         // EncryptionAlgorithm must be 3DES, or AES128, or AES256
735         if (!WSConstants.TRIPLE_DES.equals(symEncAlgo)
736             && !WSConstants.AES_128.equals(symEncAlgo)
737             && !WSConstants.AES_128_GCM.equals(symEncAlgo)
738             && !WSConstants.AES_256.equals(symEncAlgo)
739             && !WSConstants.AES_256_GCM.equals(symEncAlgo)) {
740             data.getBSPEnforcer().handleBSPRule(BSPRule.R5620);
741         }
742 
743         SecretKey symmetricKey = null;
744         try {
745             symmetricKey = KeyUtils.prepareSecretKey(symEncAlgo, decryptedData);
746         } catch (IllegalArgumentException ex) {
747             throw new WSSecurityException(
748                 WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, ex, "badEncAlgo",
749                 new Object[] {symEncAlgo});
750         }
751 
752         // Check for compliance against the defined AlgorithmSuite
753         AlgorithmSuite algorithmSuite = data.getAlgorithmSuite();
754         if (algorithmSuite != null) {
755             AlgorithmSuiteValidator algorithmSuiteValidator = new
756                 AlgorithmSuiteValidator(algorithmSuite);
757 
758             algorithmSuiteValidator.checkSymmetricKeyLength(symmetricKey.getEncoded().length);
759             algorithmSuiteValidator.checkSymmetricEncryptionAlgorithm(symEncAlgo);
760         }
761 
762         return EncryptionUtils.decryptEncryptedData(
763             doc, dataRefURI, encryptedDataElement, symmetricKey, symEncAlgo, data.getAttachmentCallbackHandler(),
764             data.getEncryptionSerializer()
765         );
766     }
767 
768     /**
769      * A method to check that the EncryptedKey is compliant with the BSP spec.
770      * @throws WSSecurityException if the EncryptedKey is not BSP compliant
771      */
772     private void checkBSPCompliance(
773             Element elem, String encAlgo,
774             boolean useKeyWrap,
775             BSPEnforcer bspEnforcer
776     ) throws WSSecurityException {
777         String attribute = elem.getAttributeNS(null, "Type");
778         if (attribute != null && !attribute.isEmpty()) {
779             bspEnforcer.handleBSPRule(BSPRule.R3209);
780         }
781         attribute = elem.getAttributeNS(null, "MimeType");
782         if (attribute != null && !attribute.isEmpty()) {
783             bspEnforcer.handleBSPRule(BSPRule.R5622);
784         }
785         attribute = elem.getAttributeNS(null, "Encoding");
786         if (attribute != null && !attribute.isEmpty()) {
787             bspEnforcer.handleBSPRule(BSPRule.R5623);
788         }
789         attribute = elem.getAttributeNS(null, "Recipient");
790         if (attribute != null && !attribute.isEmpty()) {
791             bspEnforcer.handleBSPRule(BSPRule.R5602);
792         }
793 
794         if (useKeyWrap) {
795             if (!(WSConstants.KEYWRAP_AES128.equals(encAlgo)
796                     || WSConstants.KEYWRAP_AES192.equals(encAlgo)
797                     || WSConstants.KEYWRAP_AES256.equals(encAlgo)
798                     || WSConstants.KEYWRAP_TRIPLEDES.equals(encAlgo))) {
799                 bspEnforcer.handleBSPRule(BSPRule.R5625);
800             }
801         } else {
802             // EncryptionAlgorithm must be RSA15, or RSAOEP.
803             if (!(WSConstants.KEYTRANSPORT_RSA15.equals(encAlgo)
804                     || WSConstants.KEYTRANSPORT_RSAOAEP.equals(encAlgo)
805                     || WSConstants.KEYTRANSPORT_RSAOAEP_XENC11.equals(encAlgo))) {
806                 bspEnforcer.handleBSPRule(BSPRule.R5621);
807             }
808         }
809     }
810 }