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.security.Principal;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.LinkedList;
26  import java.util.List;
27  
28  import javax.crypto.SecretKey;
29  import javax.xml.namespace.QName;
30  
31  import org.apache.wss4j.common.bsp.BSPEnforcer;
32  import org.apache.wss4j.common.bsp.BSPRule;
33  import org.apache.wss4j.common.crypto.AlgorithmSuite;
34  import org.apache.wss4j.common.crypto.AlgorithmSuiteValidator;
35  import org.apache.wss4j.common.ext.WSSecurityException;
36  import org.apache.wss4j.common.principal.WSDerivedKeyTokenPrincipal;
37  import org.apache.wss4j.common.util.KeyUtils;
38  import org.apache.wss4j.common.util.XMLUtils;
39  import org.apache.wss4j.dom.WSConstants;
40  import org.apache.wss4j.dom.WSDataRef;
41  import org.apache.wss4j.dom.engine.WSSConfig;
42  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
43  import org.apache.wss4j.dom.handler.RequestData;
44  import org.apache.wss4j.dom.str.STRParser;
45  import org.apache.wss4j.dom.str.STRParserParameters;
46  import org.apache.wss4j.dom.str.STRParserResult;
47  import org.apache.wss4j.dom.str.SecurityTokenRefSTRParser;
48  import org.apache.wss4j.dom.util.EncryptionUtils;
49  import org.apache.wss4j.dom.util.SignatureUtils;
50  import org.apache.wss4j.dom.util.X509Util;
51  import org.w3c.dom.Element;
52  
53  /**
54   * This will process incoming <code>xenc:EncryptedData</code> elements.
55   * This processor will not be invoked for encrypted content referenced by a
56   * <code>xenc:ReferenceList</code>.
57   */
58  public class EncryptedDataProcessor implements Processor {
59  
60      private static final org.slf4j.Logger LOG =
61          org.slf4j.LoggerFactory.getLogger(EncryptedDataProcessor.class);
62  
63      public List<WSSecurityEngineResult> handleToken(
64          Element elem,
65          RequestData data
66      ) throws WSSecurityException {
67          LOG.debug("Found EncryptedData element");
68  
69          final String encryptedDataId = elem.getAttributeNS(null, "Id");
70  
71          Element kiElem =
72              XMLUtils.getDirectChildElement(elem, "KeyInfo", WSConstants.SIG_NS);
73          // KeyInfo cannot be null
74          if (kiElem == null) {
75              throw new WSSecurityException(
76                  WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, "noKeyinfo"
77              );
78          }
79  
80          String symEncAlgo = X509Util.getEncAlgo(elem);
81          checkBSPCompliance(symEncAlgo, data.getBSPEnforcer());
82  
83          // Get the Key either via a SecurityTokenReference or an EncryptedKey
84          Element secRefToken =
85              XMLUtils.getDirectChildElement(
86                  kiElem, "SecurityTokenReference", WSConstants.WSSE_NS
87              );
88          Element encryptedKeyElement =
89              XMLUtils.getDirectChildElement(
90                  kiElem, WSConstants.ENC_KEY_LN, WSConstants.ENC_NS
91              );
92          Element retrievalMethodElement =
93              XMLUtils.getDirectChildElement(
94                  kiElem, "RetrievalMethod", WSConstants.SIG_NS
95              );
96  
97          if (data.isRequireSignedEncryptedDataElements()) {
98              List<WSSecurityEngineResult> signedResults =
99                  data.getWsDocInfo().getResultsByTag(WSConstants.SIGN);
100             SignatureUtils.verifySignedElement(elem, signedResults);
101         }
102 
103         SecretKey key = null;
104         List<WSSecurityEngineResult> encrKeyResults = null;
105         Principal principal = null;
106         if (secRefToken != null) {
107             STRParserParameters parameters = new STRParserParameters();
108             parameters.setData(data);
109             parameters.setStrElement(secRefToken);
110             if (symEncAlgo != null) {
111                 parameters.setDerivationKeyLength(KeyUtils.getKeyLength(symEncAlgo));
112             }
113 
114             STRParser strParser = new SecurityTokenRefSTRParser();
115             STRParserResult parserResult = strParser.parseSecurityTokenReference(parameters);
116             byte[] secretKey = parserResult.getSecretKey();
117             principal = parserResult.getPrincipal();
118             key = KeyUtils.prepareSecretKey(symEncAlgo, secretKey);
119             encrKeyResults = new ArrayList<>();
120         } else if (encryptedKeyElement != null && data.getWssConfig() != null) {
121             WSSConfig wssConfig = data.getWssConfig();
122             Processor encrKeyProc = wssConfig.getProcessor(WSConstants.ENCRYPTED_KEY);
123             encrKeyResults = encrKeyProc.handleToken(encryptedKeyElement, data);
124             byte[] symmKey =
125                 (byte[])encrKeyResults.get(0).get(WSSecurityEngineResult.TAG_SECRET);
126             key = KeyUtils.prepareSecretKey(symEncAlgo, symmKey);
127         } else if (retrievalMethodElement != null
128             && "http://www.w3.org/2001/04/xmlenc#EncryptedKey".equals(
129                 retrievalMethodElement.getAttributeNS(null, "Type"))) {
130             String uri = retrievalMethodElement.getAttributeNS(null, "URI");
131             uri = XMLUtils.getIDFromReference(uri);
132             WSSecurityEngineResult result = data.getWsDocInfo().getResult(uri);
133             if (result != null) {
134                 byte[] symmKey = (byte[])result.get(WSSecurityEngineResult.TAG_SECRET);
135                 key = KeyUtils.prepareSecretKey(symEncAlgo, symmKey);
136             }
137         } else {
138             throw new WSSecurityException(
139                 WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, "noEncKey"
140             );
141         }
142 
143         if (key == null) {
144             throw new WSSecurityException(
145                 WSSecurityException.ErrorCode.UNSUPPORTED_ALGORITHM, "noEncKey"
146             );
147         }
148 
149         // Check for compliance against the defined AlgorithmSuite
150         AlgorithmSuite algorithmSuite = data.getAlgorithmSuite();
151         if (algorithmSuite != null) {
152             AlgorithmSuiteValidator algorithmSuiteValidator = new
153                 AlgorithmSuiteValidator(algorithmSuite);
154 
155             if (principal instanceof WSDerivedKeyTokenPrincipal) {
156                 algorithmSuiteValidator.checkDerivedKeyAlgorithm(
157                     ((WSDerivedKeyTokenPrincipal)principal).getAlgorithm()
158                 );
159                 algorithmSuiteValidator.checkEncryptionDerivedKeyLength(
160                     ((WSDerivedKeyTokenPrincipal)principal).getLength()
161                 );
162             }
163             algorithmSuiteValidator.checkSymmetricKeyLength(key.getEncoded().length);
164             algorithmSuiteValidator.checkSymmetricEncryptionAlgorithm(symEncAlgo);
165         }
166 
167         WSDataRef dataRef = EncryptionUtils.decryptEncryptedData(
168                 elem.getOwnerDocument(), encryptedDataId, elem, key, symEncAlgo,
169                 data.getAttachmentCallbackHandler(), data.getEncryptionSerializer());
170 
171         WSSecurityEngineResult result =
172                 new WSSecurityEngineResult(WSConstants.ENCR, Collections.singletonList(dataRef));
173         if (encryptedDataId.length() != 0) {
174             result.put(WSSecurityEngineResult.TAG_ID, encryptedDataId);
175         }
176         data.getWsDocInfo().addResult(result);
177         data.getWsDocInfo().addTokenElement(elem);
178 
179         List<WSSecurityEngineResult> completeResults = new LinkedList<>();
180         if (encrKeyResults != null) {
181             completeResults.addAll(encrKeyResults);
182         }
183         completeResults.add(result);
184 
185         WSSConfig wssConfig = data.getWssConfig();
186         if (wssConfig != null) {
187             // Get hold of the plain text element
188             Element decryptedElem = dataRef.getProtectedElement();
189             if (decryptedElem != null) { //is null if we processed an attachment
190                 QName el = new QName(decryptedElem.getNamespaceURI(), decryptedElem.getLocalName());
191                 Processor proc = data.getWssConfig().getProcessor(el);
192                 if (proc != null) {
193                     LOG.debug("Processing decrypted element with: {}", proc.getClass().getName());
194                     List<WSSecurityEngineResult> results = proc.handleToken(decryptedElem, data);
195                     completeResults.addAll(0, results);
196                     return completeResults;
197                 }
198             }
199         }
200         return completeResults;
201     }
202 
203     /**
204      * Check for BSP compliance
205      * @param encAlgo The encryption algorithm
206      * @throws WSSecurityException
207      */
208     private static void checkBSPCompliance(
209         String encAlgo, BSPEnforcer bspEnforcer
210     ) throws WSSecurityException {
211         // EncryptionAlgorithm cannot be null
212         if (encAlgo == null) {
213             bspEnforcer.handleBSPRule(BSPRule.R5601);
214         }
215         // EncryptionAlgorithm must be 3DES, or AES128, or AES256
216         if (!WSConstants.TRIPLE_DES.equals(encAlgo)
217             && !WSConstants.AES_128.equals(encAlgo)
218             && !WSConstants.AES_128_GCM.equals(encAlgo)
219             && !WSConstants.AES_256.equals(encAlgo)
220             && !WSConstants.AES_256_GCM.equals(encAlgo)) {
221             bspEnforcer.handleBSPRule(BSPRule.R5620);
222         }
223     }
224 }