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.ws.security.processor;
21  
22  import java.security.Principal;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  
29  import javax.crypto.SecretKey;
30  import javax.xml.namespace.QName;
31  
32  import org.apache.ws.security.WSConstants;
33  import org.apache.ws.security.WSDataRef;
34  import org.apache.ws.security.WSDerivedKeyTokenPrincipal;
35  import org.apache.ws.security.WSDocInfo;
36  import org.apache.ws.security.WSSConfig;
37  import org.apache.ws.security.WSSecurityEngineResult;
38  import org.apache.ws.security.WSSecurityException;
39  import org.apache.ws.security.components.crypto.AlgorithmSuite;
40  import org.apache.ws.security.components.crypto.AlgorithmSuiteValidator;
41  import org.apache.ws.security.handler.RequestData;
42  import org.apache.ws.security.str.STRParser;
43  import org.apache.ws.security.str.SecurityTokenRefSTRParser;
44  import org.apache.ws.security.util.WSSecurityUtil;
45  import org.apache.xml.security.encryption.XMLCipher;
46  import org.apache.xml.security.encryption.XMLEncryptionException;
47  import org.w3c.dom.Element;
48  import org.w3c.dom.Node;
49  
50  /**
51   * This will process incoming <code>xenc:EncryptedData</code> elements.
52   * This processor will not be invoked for encrypted content referenced by a 
53   * <code>xenc:ReferenceList</code>.
54   */
55  public class EncryptedDataProcessor implements Processor {
56      
57      private static org.apache.commons.logging.Log log = 
58          org.apache.commons.logging.LogFactory.getLog(EncryptedDataProcessor.class);
59      
60      public List<WSSecurityEngineResult> handleToken(
61          Element elem,
62          RequestData request,
63          WSDocInfo wsDocInfo
64      ) throws WSSecurityException {
65          if (log.isDebugEnabled()) {
66              log.debug("Found EncryptedData element");
67          }
68          Element kiElem = 
69              WSSecurityUtil.getDirectChildElement(elem, "KeyInfo", WSConstants.SIG_NS);
70          // KeyInfo cannot be null
71          if (kiElem == null) {
72              throw new WSSecurityException(
73                  WSSecurityException.UNSUPPORTED_ALGORITHM, "noKeyinfo"
74              );
75          }
76          
77          String symEncAlgo = X509Util.getEncAlgo(elem);
78          // Check BSP compliance
79          if (request.getWssConfig().isWsiBSPCompliant()) {
80              checkBSPCompliance(symEncAlgo);
81          }
82          
83          // Get the Key either via a SecurityTokenReference or an EncryptedKey
84          Element secRefToken = 
85              WSSecurityUtil.getDirectChildElement(
86                  kiElem, "SecurityTokenReference", WSConstants.WSSE_NS
87              );
88          Element encryptedKeyElement = 
89              WSSecurityUtil.getDirectChildElement(
90                  kiElem, WSConstants.ENC_KEY_LN, WSConstants.ENC_NS
91              );
92          
93          if (elem != null && request.isRequireSignedEncryptedDataElements()) {
94              WSSecurityUtil.verifySignedElement(elem, elem.getOwnerDocument(), wsDocInfo.getSecurityHeader());
95          }
96          
97          SecretKey key = null;
98          List<WSSecurityEngineResult> encrKeyResults = null;
99          Principal principal = null;
100         if (secRefToken != null) {
101             STRParser strParser = new SecurityTokenRefSTRParser();
102             Map<String, Object> parameters = new HashMap<String, Object>();
103             parameters.put(SecurityTokenRefSTRParser.SIGNATURE_METHOD, symEncAlgo);
104             strParser.parseSecurityTokenReference(
105                 secRefToken, request,
106                 wsDocInfo, parameters
107             );
108             byte[] secretKey = strParser.getSecretKey();
109             principal = strParser.getPrincipal();
110             key = WSSecurityUtil.prepareSecretKey(symEncAlgo, secretKey);
111         } else if (encryptedKeyElement != null) {
112             EncryptedKeyProcessor encrKeyProc = new EncryptedKeyProcessor();
113             encrKeyResults = encrKeyProc.handleToken(encryptedKeyElement, request, wsDocInfo);
114             byte[] symmKey = 
115                 (byte[])encrKeyResults.get(0).get(WSSecurityEngineResult.TAG_SECRET);
116             key = WSSecurityUtil.prepareSecretKey(symEncAlgo, symmKey);
117         } else {
118             throw new WSSecurityException(
119                 WSSecurityException.UNSUPPORTED_ALGORITHM, "noEncKey"
120             );
121         }
122         
123         // Check for compliance against the defined AlgorithmSuite
124         AlgorithmSuite algorithmSuite = request.getAlgorithmSuite();
125         if (algorithmSuite != null) {
126             AlgorithmSuiteValidator algorithmSuiteValidator = new
127                 AlgorithmSuiteValidator(algorithmSuite);
128 
129             if (principal instanceof WSDerivedKeyTokenPrincipal) {
130                 algorithmSuiteValidator.checkDerivedKeyAlgorithm(
131                     ((WSDerivedKeyTokenPrincipal)principal).getAlgorithm()
132                 );
133                 algorithmSuiteValidator.checkEncryptionDerivedKeyLength(
134                     ((WSDerivedKeyTokenPrincipal)principal).getLength()
135                 );
136             }
137             algorithmSuiteValidator.checkSymmetricKeyLength(key.getEncoded().length);
138             algorithmSuiteValidator.checkSymmetricEncryptionAlgorithm(symEncAlgo);
139         }
140         
141         // initialize Cipher ....
142         XMLCipher xmlCipher = null;
143         try {
144             xmlCipher = XMLCipher.getInstance(symEncAlgo);
145             xmlCipher.setSecureValidation(true);
146             xmlCipher.init(XMLCipher.DECRYPT_MODE, key);
147         } catch (XMLEncryptionException ex) {
148             throw new WSSecurityException(
149                 WSSecurityException.UNSUPPORTED_ALGORITHM, null, null, ex
150             );
151         }
152         Node previousSibling = elem.getPreviousSibling();
153         Node parent = elem.getParentNode();
154         try {
155             xmlCipher.doFinal(elem.getOwnerDocument(), elem, false);
156         } catch (Exception e) {
157             throw new WSSecurityException(
158                 WSSecurityException.FAILED_CHECK, null, null, e
159             );
160         }
161         
162         WSDataRef dataRef = new WSDataRef();
163         dataRef.setWsuId(elem.getAttributeNS(null, "Id"));
164         dataRef.setAlgorithm(symEncAlgo);
165         dataRef.setContent(false);
166         
167         Node decryptedNode;
168         if (previousSibling == null) {
169             decryptedNode = parent.getFirstChild();
170         } else {
171             decryptedNode = previousSibling.getNextSibling();
172         }
173         if (decryptedNode != null && Node.ELEMENT_NODE == decryptedNode.getNodeType()) {
174             dataRef.setProtectedElement((Element)decryptedNode);
175         }
176         dataRef.setXpath(ReferenceListProcessor.getXPath(decryptedNode));
177         
178         WSSecurityEngineResult result = 
179                 new WSSecurityEngineResult(WSConstants.ENCR, Collections.singletonList(dataRef));
180         result.put(WSSecurityEngineResult.TAG_ID, elem.getAttributeNS(null, "Id"));
181         wsDocInfo.addResult(result);
182         wsDocInfo.addTokenElement(elem);
183         
184         WSSConfig wssConfig = request.getWssConfig();
185         if (wssConfig != null) {
186             // Get hold of the plain text element
187             Element decryptedElem;
188             if (previousSibling == null) {
189                 decryptedElem = (Element)parent.getFirstChild();
190             } else {
191                 decryptedElem = (Element)previousSibling.getNextSibling();
192             }
193             QName el = new QName(decryptedElem.getNamespaceURI(), decryptedElem.getLocalName());
194             Processor proc = request.getWssConfig().getProcessor(el);
195             if (proc != null) {
196                 if (log.isDebugEnabled()) {
197                     log.debug("Processing decrypted element with: " + proc.getClass().getName());
198                 }
199                 List<WSSecurityEngineResult> results = 
200                     proc.handleToken(decryptedElem, request, wsDocInfo);
201                 List<WSSecurityEngineResult> completeResults = 
202                     new ArrayList<WSSecurityEngineResult>();
203                 if (encrKeyResults != null) {
204                     completeResults.addAll(encrKeyResults);
205                 }
206                 completeResults.add(result);
207                 completeResults.addAll(0, results);
208                 return completeResults;
209             }
210         }
211         encrKeyResults.add(result);
212         return encrKeyResults;
213     }
214     
215     /**
216      * Check for BSP compliance
217      * @param encAlgo The encryption algorithm
218      * @throws WSSecurityException
219      */
220     private static void checkBSPCompliance(
221         String encAlgo
222     ) throws WSSecurityException {
223         // EncryptionAlgorithm cannot be null
224         if (encAlgo == null) {
225             throw new WSSecurityException(
226                 WSSecurityException.UNSUPPORTED_ALGORITHM, "noEncAlgo"
227             );
228         }
229         // EncryptionAlgorithm must be 3DES, or AES128, or AES256
230         if (!WSConstants.TRIPLE_DES.equals(encAlgo)
231             && !WSConstants.AES_128.equals(encAlgo)
232             && !WSConstants.AES_128_GCM.equals(encAlgo)
233             && !WSConstants.AES_256.equals(encAlgo)
234             && !WSConstants.AES_256_GCM.equals(encAlgo)) {
235             throw new WSSecurityException(
236                 WSSecurityException.INVALID_SECURITY, "badEncAlgo", new Object[]{encAlgo}
237             );
238         }
239     }
240 
241 }