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.NoSuchAlgorithmException;
26  import java.security.NoSuchProviderException;
27  import java.security.PrivateKey;
28  import java.security.Provider;
29  import java.security.PublicKey;
30  import java.security.cert.X509Certificate;
31  import java.security.spec.MGF1ParameterSpec;
32  import java.util.ArrayList;
33  import java.util.Collections;
34  import java.util.List;
35  
36  import javax.crypto.Cipher;
37  import javax.crypto.KeyGenerator;
38  import javax.crypto.SecretKey;
39  import javax.crypto.spec.OAEPParameterSpec;
40  import javax.crypto.spec.PSource;
41  import javax.xml.crypto.dsig.XMLSignatureFactory;
42  
43  import org.w3c.dom.Document;
44  import org.w3c.dom.Element;
45  import org.w3c.dom.Node;
46  import org.apache.wss4j.common.bsp.BSPEnforcer;
47  import org.apache.wss4j.common.bsp.BSPRule;
48  import org.apache.wss4j.common.crypto.AlgorithmSuite;
49  import org.apache.wss4j.common.crypto.AlgorithmSuiteValidator;
50  import org.apache.wss4j.common.crypto.CryptoType;
51  import org.apache.wss4j.common.ext.WSSecurityException;
52  import org.apache.wss4j.common.token.DOMX509IssuerSerial;
53  import org.apache.wss4j.common.token.SecurityTokenReference;
54  import org.apache.wss4j.common.util.KeyUtils;
55  import org.apache.wss4j.common.util.XMLUtils;
56  import org.apache.wss4j.dom.WSConstants;
57  import org.apache.wss4j.dom.WSDataRef;
58  import org.apache.wss4j.dom.WSDocInfo;
59  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
60  import org.apache.wss4j.dom.handler.RequestData;
61  import org.apache.wss4j.dom.str.EncryptedKeySTRParser;
62  import org.apache.wss4j.dom.str.STRParser;
63  import org.apache.wss4j.dom.str.STRParserParameters;
64  import org.apache.wss4j.dom.str.STRParserResult;
65  import org.apache.wss4j.dom.util.EncryptionUtils;
66  import org.apache.wss4j.dom.util.SignatureUtils;
67  import org.apache.wss4j.dom.util.WSSecurityUtil;
68  import org.apache.wss4j.dom.util.X509Util;
69  import org.apache.xml.security.algorithms.JCEMapper;
70  import org.apache.xml.security.encryption.XMLCipher;
71  
72  public class EncryptedKeyProcessor implements Processor {
73      private static final org.slf4j.Logger LOG =
74          org.slf4j.LoggerFactory.getLogger(EncryptedKeyProcessor.class);
75  
76      private final Provider provider;
77  
78      public EncryptedKeyProcessor() {
79          this(null);
80      }
81  
82      public EncryptedKeyProcessor(Provider provider) {
83          this.provider = provider;
84      }
85  
86      public List<WSSecurityEngineResult> handleToken(
87          Element elem,
88          RequestData data
89      ) throws WSSecurityException {
90          return handleToken(elem, data, data.getAlgorithmSuite());
91      }
92  
93      public List<WSSecurityEngineResult> handleToken(
94          Element elem,
95          RequestData data,
96          AlgorithmSuite algorithmSuite
97      ) throws WSSecurityException {
98          LOG.debug("Found encrypted key element");
99  
100         // See if this key has already been processed. If so then just return the result
101         String id = elem.getAttributeNS(null, "Id");
102         if (id.length() != 0) {
103              WSSecurityEngineResult result = data.getWsDocInfo().getResult(id);
104              if (result != null
105                  && WSConstants.ENCR == (Integer)result.get(WSSecurityEngineResult.TAG_ACTION)
106              ) {
107                  return Collections.singletonList(result);
108              }
109         }
110 
111         if (data.getCallbackHandler() == null) {
112             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noCallback");
113         }
114         //
115         // lookup xenc:EncryptionMethod, get the Algorithm attribute to determine
116         // how the key was encrypted. Then check if we support the algorithm
117         //
118         String encryptedKeyTransportMethod = X509Util.getEncAlgo(elem);
119         if (encryptedKeyTransportMethod == null) {
120             throw new WSSecurityException(
121                 WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, "noEncAlgo"
122             );
123         }
124         if (WSConstants.KEYTRANSPORT_RSA15.equals(encryptedKeyTransportMethod)
125             && !data.isAllowRSA15KeyTransportAlgorithm()
126             && (algorithmSuite == null
127               || !algorithmSuite.getKeyWrapAlgorithms().contains(WSConstants.KEYTRANSPORT_RSA15))) {
128             LOG.debug(
129                 "The Key transport method does not match the requirement"
130             );
131             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY);
132         }
133 
134         // Check BSP Compliance
135         checkBSPCompliance(elem, encryptedKeyTransportMethod, data.getBSPEnforcer());
136 
137         //
138         // Now lookup CipherValue.
139         //
140         Element xencCipherValue = EncryptionUtils.getCipherValueFromEncryptedData(elem);
141         if (xencCipherValue == null) {
142             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "noCipher");
143         }
144 
145         Element keyInfoChildElement = getKeyInfoChildElement(elem, data);
146 
147         X509Certificate[] certs = null;
148         STRParser.REFERENCE_TYPE referenceType = null;
149         PublicKey publicKey = null;
150         boolean symmetricKeyWrap = isSymmetricKeyWrap(encryptedKeyTransportMethod);
151         if (!symmetricKeyWrap) {
152             if (SecurityTokenReference.SECURITY_TOKEN_REFERENCE.equals(keyInfoChildElement.getLocalName())
153                 && WSConstants.WSSE_NS.equals(keyInfoChildElement.getNamespaceURI())) {
154                 STRParserParameters parameters = new STRParserParameters();
155                 parameters.setData(data);
156                 parameters.setStrElement(keyInfoChildElement);
157 
158                 STRParser strParser = new EncryptedKeySTRParser();
159                 STRParserResult parserResult = strParser.parseSecurityTokenReference(parameters);
160 
161                 certs = parserResult.getCertificates();
162                 publicKey = parserResult.getPublicKey();
163                 referenceType = parserResult.getCertificatesReferenceType();
164             } else {
165                 certs = getCertificatesFromX509Data(keyInfoChildElement, data);
166                 if (certs == null || certs.length == 0) {
167                     XMLSignatureFactory signatureFactory;
168                     if (provider == null) {
169                         // Try to install the Santuario Provider - fall back to the JDK provider if this does
170                         // not work
171                         try {
172                             signatureFactory = XMLSignatureFactory.getInstance("DOM", "ApacheXMLDSig");
173                         } catch (NoSuchProviderException ex) {
174                             signatureFactory = XMLSignatureFactory.getInstance("DOM");
175                         }
176                     } else {
177                         signatureFactory = XMLSignatureFactory.getInstance("DOM", provider);
178                     }
179 
180                     publicKey = X509Util.parseKeyValue((Element)keyInfoChildElement.getParentNode(),
181                                                        signatureFactory);
182                 }
183             }
184 
185             if (publicKey == null && (certs == null || certs.length < 1 || certs[0] == null)) {
186                 throw new WSSecurityException(
187                                           WSSecurityException.ErrorCode.FAILURE,
188                                           "noCertsFound",
189                                           new Object[] {"decryption (KeyId)"});
190             }
191             if (certs != null && certs.length > 0) {
192                 publicKey = certs[0].getPublicKey();
193             }
194         }
195 
196         // Check for compliance against the defined AlgorithmSuite
197         if (algorithmSuite != null) {
198             AlgorithmSuiteValidator algorithmSuiteValidator = new
199                 AlgorithmSuiteValidator(algorithmSuite);
200 
201             if (!symmetricKeyWrap) {
202                 algorithmSuiteValidator.checkAsymmetricKeyLength(publicKey);
203             }
204             algorithmSuiteValidator.checkEncryptionKeyWrapAlgorithm(
205                 encryptedKeyTransportMethod
206             );
207         }
208 
209         byte[] encryptedEphemeralKey = null;
210         byte[] decryptedBytes = null;
211         Element refList =
212             XMLUtils.getDirectChildElement(elem, "ReferenceList", WSConstants.ENC_NS);
213 
214         // Get the key bytes from CipherValue directly or via an attachment
215         String xopUri = EncryptionUtils.getXOPURIFromCipherValue(xencCipherValue);
216         if (xopUri != null && xopUri.startsWith("cid:")) {
217             encryptedEphemeralKey = WSSecurityUtil.getBytesFromAttachment(xopUri, data);
218         } else {
219             encryptedEphemeralKey = EncryptionUtils.getDecodedBase64EncodedData(xencCipherValue);
220         }
221 
222         if (symmetricKeyWrap) {
223             decryptedBytes = getSymmetricDecryptedBytes(data, data.getWsDocInfo(), keyInfoChildElement, refList);
224         } else {
225             PrivateKey privateKey = getPrivateKey(data, certs, publicKey);
226             decryptedBytes = getAsymmetricDecryptedBytes(data, data.getWsDocInfo(), encryptedKeyTransportMethod,
227                                                          encryptedEphemeralKey, refList,
228                                                          elem, privateKey);
229         }
230 
231         List<WSDataRef> dataRefs = decryptDataRefs(refList, data.getWsDocInfo(), decryptedBytes, data);
232 
233         WSSecurityEngineResult result = new WSSecurityEngineResult(
234                 WSConstants.ENCR,
235                 decryptedBytes,
236                 encryptedEphemeralKey,
237                 dataRefs,
238                 certs
239             );
240         result.put(
241             WSSecurityEngineResult.TAG_ENCRYPTED_KEY_TRANSPORT_METHOD,
242             encryptedKeyTransportMethod
243         );
244         result.put(WSSecurityEngineResult.TAG_TOKEN_ELEMENT, elem);
245         String tokenId = elem.getAttributeNS(null, "Id");
246         if (tokenId.length() != 0) {
247             result.put(WSSecurityEngineResult.TAG_ID, tokenId);
248         }
249         if (referenceType != null) {
250             result.put(WSSecurityEngineResult.TAG_X509_REFERENCE_TYPE, referenceType);
251         }
252         if (publicKey != null) {
253             result.put(WSSecurityEngineResult.TAG_PUBLIC_KEY, publicKey);
254         }
255         data.getWsDocInfo().addResult(result);
256         data.getWsDocInfo().addTokenElement(elem);
257         return Collections.singletonList(result);
258     }
259 
260     private PrivateKey getPrivateKey(
261         RequestData data, X509Certificate[] certs, PublicKey publicKey
262     ) throws WSSecurityException {
263         try {
264             if (certs != null && certs.length > 0) {
265                 return data.getDecCrypto().getPrivateKey(certs[0], data.getCallbackHandler());
266             }
267             return data.getDecCrypto().getPrivateKey(publicKey, data.getCallbackHandler());
268         } catch (WSSecurityException ex) {
269             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex);
270         }
271     }
272 
273     private static byte[] getSymmetricDecryptedBytes(
274         RequestData data,
275         WSDocInfo wsDocInfo,
276         Element keyInfoChildElement,
277         Element refList
278     ) throws WSSecurityException {
279         // Get the (first) encryption algorithm
280         String uri = getFirstDataRefURI(refList);
281         String algorithmURI = null;
282         if (uri != null) {
283             Element ee =
284                 EncryptionUtils.findEncryptedDataElement(wsDocInfo, uri);
285             algorithmURI = X509Util.getEncAlgo(ee);
286         }
287         return X509Util.getSecretKey(keyInfoChildElement, algorithmURI, data.getCallbackHandler());
288     }
289 
290     private static byte[] getAsymmetricDecryptedBytes(
291         RequestData data,
292         WSDocInfo wsDocInfo,
293         String encryptedKeyTransportMethod,
294         byte[] encryptedEphemeralKey,
295         Element refList,
296         Element encryptedKeyElement,
297         PrivateKey privateKey
298     ) throws WSSecurityException {
299         if (data.getDecCrypto() == null) {
300             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noDecCryptoFile");
301         }
302         String cryptoProvider = data.getDecCrypto().getCryptoProvider();
303         Cipher cipher = KeyUtils.getCipherInstance(encryptedKeyTransportMethod, cryptoProvider);
304         try {
305             OAEPParameterSpec oaepParameterSpec = null;
306             if (WSConstants.KEYTRANSPORT_RSAOAEP.equals(encryptedKeyTransportMethod)
307                 || WSConstants.KEYTRANSPORT_RSAOAEP_XENC11.equals(encryptedKeyTransportMethod)) {
308                 // Get the DigestMethod if it exists
309                 String digestAlgorithm = EncryptionUtils.getDigestAlgorithm(encryptedKeyElement);
310                 String jceDigestAlgorithm = "SHA-1";
311                 if (digestAlgorithm != null && digestAlgorithm.length() != 0) {
312                     jceDigestAlgorithm = JCEMapper.translateURItoJCEID(digestAlgorithm);
313                 }
314 
315                 MGF1ParameterSpec mgfParameterSpec = new MGF1ParameterSpec("SHA-1");
316                 if (WSConstants.KEYTRANSPORT_RSAOAEP_XENC11.equals(encryptedKeyTransportMethod)) {
317                     String mgfAlgorithm = EncryptionUtils.getMGFAlgorithm(encryptedKeyElement);
318                     if (WSConstants.MGF_SHA224.equals(mgfAlgorithm)) {
319                         mgfParameterSpec = new MGF1ParameterSpec("SHA-224");
320                     } else if (WSConstants.MGF_SHA256.equals(mgfAlgorithm)) {
321                         mgfParameterSpec = new MGF1ParameterSpec("SHA-256");
322                     } else if (WSConstants.MGF_SHA384.equals(mgfAlgorithm)) {
323                         mgfParameterSpec = new MGF1ParameterSpec("SHA-384");
324                     } else if (WSConstants.MGF_SHA512.equals(mgfAlgorithm)) {
325                         mgfParameterSpec = new MGF1ParameterSpec("SHA-512");
326                     }
327                 }
328 
329                 PSource.PSpecified pSource = PSource.PSpecified.DEFAULT;
330                 byte[] pSourceBytes = EncryptionUtils.getPSource(encryptedKeyElement);
331                 if (pSourceBytes != null && pSourceBytes.length > 0) {
332                     pSource = new PSource.PSpecified(pSourceBytes);
333                 }
334 
335                 oaepParameterSpec =
336                     new OAEPParameterSpec(
337                         jceDigestAlgorithm, "MGF1", mgfParameterSpec, pSource
338                     );
339             }
340 
341             if (oaepParameterSpec == null) {
342                 cipher.init(Cipher.UNWRAP_MODE, privateKey);
343             } else {
344                 cipher.init(Cipher.UNWRAP_MODE, privateKey, oaepParameterSpec);
345             }
346         } catch (Exception ex) {
347             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex);
348         }
349 
350         try {
351             String keyAlgorithm = JCEMapper.translateURItoJCEID(encryptedKeyTransportMethod);
352             return cipher.unwrap(encryptedEphemeralKey, keyAlgorithm, Cipher.SECRET_KEY).getEncoded();
353         } catch (IllegalStateException ex) {
354             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, ex);
355         } catch (Exception ex) {
356             return getRandomKey(refList, wsDocInfo);
357         }
358     }
359 
360     private static boolean isSymmetricKeyWrap(String transportAlgorithm) {
361         return XMLCipher.AES_128_KeyWrap.equals(transportAlgorithm)
362             || XMLCipher.AES_192_KeyWrap.equals(transportAlgorithm)
363             || XMLCipher.AES_256_KeyWrap.equals(transportAlgorithm)
364             || XMLCipher.TRIPLEDES_KeyWrap.equals(transportAlgorithm)
365             || XMLCipher.CAMELLIA_128_KeyWrap.equals(transportAlgorithm)
366             || XMLCipher.CAMELLIA_192_KeyWrap.equals(transportAlgorithm)
367             || XMLCipher.CAMELLIA_256_KeyWrap.equals(transportAlgorithm)
368             || XMLCipher.SEED_128_KeyWrap.equals(transportAlgorithm);
369     }
370 
371     /**
372      * Generates a random secret key using the algorithm specified in the
373      * first DataReference URI
374      */
375     private static byte[] getRandomKey(Element refList, WSDocInfo wsDocInfo) throws WSSecurityException {
376         try {
377             String alg = "AES";
378             int size = 16;
379             String uri = getFirstDataRefURI(refList);
380 
381             if (uri != null) {
382                 Element ee =
383                     EncryptionUtils.findEncryptedDataElement(wsDocInfo, uri);
384                 String algorithmURI = X509Util.getEncAlgo(ee);
385                 alg = JCEMapper.getJCEKeyAlgorithmFromURI(algorithmURI);
386                 size = KeyUtils.getKeyLength(algorithmURI);
387             }
388             KeyGenerator kgen = KeyGenerator.getInstance(alg);
389             kgen.init(size * 8);
390             SecretKey k = kgen.generateKey();
391             return k.getEncoded();
392         } catch (Throwable ex) {
393             // Fallback to just using AES to avoid attacks on EncryptedData algorithms
394             try {
395                 KeyGenerator kgen = KeyGenerator.getInstance("AES");
396                 kgen.init(128);
397                 SecretKey k = kgen.generateKey();
398                 return k.getEncoded();
399             } catch (NoSuchAlgorithmException e) {
400                 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK, e);
401             }
402         }
403     }
404 
405     private static String getFirstDataRefURI(Element refList) {
406         // Lookup the references that are encrypted with this key
407         if (refList != null) {
408             for (Node node = refList.getFirstChild(); node != null; node = node.getNextSibling()) {
409                 if (Node.ELEMENT_NODE == node.getNodeType()
410                         && WSConstants.ENC_NS.equals(node.getNamespaceURI())
411                         && "DataReference".equals(node.getLocalName())) {
412                     String dataRefURI = ((Element) node).getAttributeNS(null, "URI");
413                     return XMLUtils.getIDFromReference(dataRefURI);
414                 }
415             }
416         }
417         return null;
418     }
419 
420     private Element getKeyInfoChildElement(
421         Element xencEncryptedKey, RequestData data
422     ) throws WSSecurityException {
423         Element keyInfo =
424             XMLUtils.getDirectChildElement(xencEncryptedKey, "KeyInfo", WSConstants.SIG_NS);
425         if (keyInfo != null) {
426             Element strElement = null;
427 
428             int result = 0;
429             Node node = keyInfo.getFirstChild();
430             while (node != null) {
431                 if (Node.ELEMENT_NODE == node.getNodeType()) {
432                     result++;
433                     strElement = (Element)node;
434                 }
435                 node = node.getNextSibling();
436             }
437             if (result != 1) {
438                 data.getBSPEnforcer().handleBSPRule(BSPRule.R5424);
439             }
440 
441             if (strElement == null) {
442                 throw new WSSecurityException(
443                     WSSecurityException.ErrorCode.INVALID_SECURITY, "noSecTokRef"
444                 );
445             }
446 
447             return strElement;
448         } else {
449             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "noKeyinfo");
450         }
451     }
452 
453     private X509Certificate[] getCertificatesFromX509Data(
454         Element keyInfoChildElement,
455         RequestData data
456     ) throws WSSecurityException {
457 
458         if (WSConstants.SIG_NS.equals(keyInfoChildElement.getNamespaceURI())
459             && WSConstants.X509_DATA_LN.equals(keyInfoChildElement.getLocalName())) {
460             data.getBSPEnforcer().handleBSPRule(BSPRule.R5426);
461 
462             Element x509Child = getFirstElement(keyInfoChildElement);
463 
464             if (x509Child != null && WSConstants.SIG_NS.equals(x509Child.getNamespaceURI())) {
465                 if (WSConstants.X509_ISSUER_SERIAL_LN.equals(x509Child.getLocalName())) {
466                     DOMX509IssuerSerial issuerSerial = new DOMX509IssuerSerial(x509Child);
467                     CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ISSUER_SERIAL);
468                     cryptoType.setIssuerSerial(issuerSerial.getIssuer(), issuerSerial.getSerialNumber());
469                     return data.getDecCrypto().getX509Certificates(cryptoType);
470                 } else if (WSConstants.X509_CERT_LN.equals(x509Child.getLocalName())) {
471                     byte[] token = EncryptionUtils.getDecodedBase64EncodedData(x509Child);
472                     if (token == null || token.length == 0) {
473                         throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "invalidCertData",
474                                                       new Object[] {"0"});
475                     }
476                     try (InputStream in = new ByteArrayInputStream(token)) {
477                         X509Certificate cert = data.getDecCrypto().loadCertificate(in);
478                         if (cert != null) {
479                             return new X509Certificate[]{cert};
480                         }
481                     } catch (IOException e) {
482                         throw new WSSecurityException(
483                             WSSecurityException.ErrorCode.SECURITY_TOKEN_UNAVAILABLE, e, "parseError"
484                         );
485                     }
486                 }
487             }
488         }
489 
490         return new X509Certificate[0];
491     }
492 
493     private Element getFirstElement(Element element) {
494         for (Node currentChild = element.getFirstChild();
495              currentChild != null;
496              currentChild = currentChild.getNextSibling()
497         ) {
498             if (Node.ELEMENT_NODE == currentChild.getNodeType()) {
499                 return (Element) currentChild;
500             }
501         }
502         return null;
503     }
504 
505     /**
506      * Decrypt all data references
507      */
508     protected List<WSDataRef> decryptDataRefs(Element refList, WSDocInfo docInfo,
509                                             byte[] decryptedBytes, RequestData data
510     ) throws WSSecurityException {
511         //
512         // At this point we have the decrypted session (symmetric) key. According
513         // to W3C XML-Enc this key is used to decrypt _any_ references contained in
514         // the reference list
515         if (refList == null) {
516             return Collections.emptyList();
517         }
518 
519         List<WSDataRef> dataRefs = new ArrayList<>();
520         for (Node node = refList.getFirstChild(); node != null; node = node.getNextSibling()) {
521             if (Node.ELEMENT_NODE == node.getNodeType()
522                     && WSConstants.ENC_NS.equals(node.getNamespaceURI())
523                     && "DataReference".equals(node.getLocalName())) {
524                 String dataRefURI = ((Element) node).getAttributeNS(null, "URI");
525                 dataRefURI = XMLUtils.getIDFromReference(dataRefURI);
526 
527                 WSDataRef dataRef =
528                     decryptDataRef(refList.getOwnerDocument(), dataRefURI, docInfo, decryptedBytes, data);
529                 dataRefs.add(dataRef);
530             }
531         }
532 
533         return dataRefs;
534     }
535 
536     /**
537      * Decrypt an EncryptedData element referenced by dataRefURI
538      */
539     protected WSDataRef decryptDataRef(
540         Document doc,
541         String dataRefURI,
542         WSDocInfo docInfo,
543         byte[] decryptedData,
544         RequestData data
545     ) throws WSSecurityException {
546         LOG.debug("found data reference: {}", dataRefURI);
547         //
548         // Find the encrypted data element referenced by dataRefURI
549         //
550         Element encryptedDataElement =
551             EncryptionUtils.findEncryptedDataElement(docInfo, dataRefURI);
552         if (encryptedDataElement != null && data.isRequireSignedEncryptedDataElements()) {
553             List<WSSecurityEngineResult> signedResults =
554                 docInfo.getResultsByTag(WSConstants.SIGN);
555             SignatureUtils.verifySignedElement(encryptedDataElement, signedResults);
556         }
557         //
558         // Prepare the SecretKey object to decrypt EncryptedData
559         //
560         String symEncAlgo = X509Util.getEncAlgo(encryptedDataElement);
561 
562         // EncryptionAlgorithm cannot be null
563         if (symEncAlgo == null) {
564             LOG.warn("No encryption algorithm was specified in the request");
565             throw new WSSecurityException(WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, "badEncAlgo",
566                                           new Object[] {null});
567 
568         }
569         // EncryptionAlgorithm must be 3DES, or AES128, or AES256
570         if (!WSConstants.TRIPLE_DES.equals(symEncAlgo)
571             && !WSConstants.AES_128.equals(symEncAlgo)
572             && !WSConstants.AES_128_GCM.equals(symEncAlgo)
573             && !WSConstants.AES_256.equals(symEncAlgo)
574             && !WSConstants.AES_256_GCM.equals(symEncAlgo)) {
575             data.getBSPEnforcer().handleBSPRule(BSPRule.R5620);
576         }
577 
578         SecretKey symmetricKey = null;
579         try {
580             symmetricKey = KeyUtils.prepareSecretKey(symEncAlgo, decryptedData);
581         } catch (IllegalArgumentException ex) {
582             throw new WSSecurityException(
583                 WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, ex, "badEncAlgo",
584                 new Object[] {symEncAlgo});
585         }
586 
587         // Check for compliance against the defined AlgorithmSuite
588         AlgorithmSuite algorithmSuite = data.getAlgorithmSuite();
589         if (algorithmSuite != null) {
590             AlgorithmSuiteValidator algorithmSuiteValidator = new
591                 AlgorithmSuiteValidator(algorithmSuite);
592 
593             algorithmSuiteValidator.checkSymmetricKeyLength(symmetricKey.getEncoded().length);
594             algorithmSuiteValidator.checkSymmetricEncryptionAlgorithm(symEncAlgo);
595         }
596 
597         return EncryptionUtils.decryptEncryptedData(
598             doc, dataRefURI, encryptedDataElement, symmetricKey, symEncAlgo, data.getAttachmentCallbackHandler(),
599             data.getEncryptionSerializer()
600         );
601     }
602 
603     /**
604      * A method to check that the EncryptedKey is compliant with the BSP spec.
605      * @throws WSSecurityException
606      */
607     private void checkBSPCompliance(
608         Element elem, String encAlgo, BSPEnforcer bspEnforcer
609     ) throws WSSecurityException {
610         String attribute = elem.getAttributeNS(null, "Type");
611         if (attribute != null && attribute.length() != 0) {
612             bspEnforcer.handleBSPRule(BSPRule.R3209);
613         }
614         attribute = elem.getAttributeNS(null, "MimeType");
615         if (attribute != null && attribute.length() != 0) {
616             bspEnforcer.handleBSPRule(BSPRule.R5622);
617         }
618         attribute = elem.getAttributeNS(null, "Encoding");
619         if (attribute != null && attribute.length() != 0) {
620             bspEnforcer.handleBSPRule(BSPRule.R5623);
621         }
622         attribute = elem.getAttributeNS(null, "Recipient");
623         if (attribute != null && attribute.length() != 0) {
624             bspEnforcer.handleBSPRule(BSPRule.R5602);
625         }
626 
627         // EncryptionAlgorithm must be RSA15, or RSAOEP.
628         if (!(WSConstants.KEYTRANSPORT_RSA15.equals(encAlgo)
629             || WSConstants.KEYTRANSPORT_RSAOAEP.equals(encAlgo)
630             || WSConstants.KEYTRANSPORT_RSAOAEP_XENC11.equals(encAlgo))) {
631             bspEnforcer.handleBSPRule(BSPRule.R5621);
632         }
633     }
634 
635 }