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.List;
26  
27  import javax.crypto.SecretKey;
28  
29  import org.w3c.dom.Document;
30  import org.w3c.dom.Element;
31  import org.w3c.dom.Node;
32  
33  import org.apache.wss4j.common.bsp.BSPEnforcer;
34  import org.apache.wss4j.common.bsp.BSPRule;
35  import org.apache.wss4j.common.crypto.AlgorithmSuite;
36  import org.apache.wss4j.common.crypto.AlgorithmSuiteValidator;
37  import org.apache.wss4j.common.ext.WSSecurityException;
38  import org.apache.wss4j.common.principal.WSDerivedKeyTokenPrincipal;
39  import org.apache.wss4j.common.token.SecurityTokenReference;
40  import org.apache.wss4j.common.util.KeyUtils;
41  import org.apache.wss4j.common.util.XMLUtils;
42  import org.apache.wss4j.dom.WSConstants;
43  import org.apache.wss4j.dom.WSDataRef;
44  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
45  import org.apache.wss4j.dom.handler.RequestData;
46  import org.apache.wss4j.dom.str.STRParser;
47  import org.apache.wss4j.dom.str.STRParserParameters;
48  import org.apache.wss4j.dom.str.STRParserResult;
49  import org.apache.wss4j.dom.str.SecurityTokenRefSTRParser;
50  import org.apache.wss4j.dom.util.EncryptionUtils;
51  import org.apache.wss4j.dom.util.SignatureUtils;
52  import org.apache.wss4j.dom.util.X509Util;
53  
54  public class ReferenceListProcessor implements Processor {
55      private static final org.slf4j.Logger LOG =
56          org.slf4j.LoggerFactory.getLogger(ReferenceListProcessor.class);
57  
58      public List<WSSecurityEngineResult> handleToken(
59          Element elem,
60          RequestData data
61      ) throws WSSecurityException {
62          LOG.debug("Found reference list element");
63          List<WSDataRef> dataRefs = handleReferenceList(elem, data);
64          WSSecurityEngineResult result =
65              new WSSecurityEngineResult(WSConstants.ENCR, dataRefs);
66          String tokenId = elem.getAttributeNS(null, "Id");
67          if (tokenId.length() != 0) {
68              result.put(WSSecurityEngineResult.TAG_ID, tokenId);
69          }
70          data.getWsDocInfo().addTokenElement(elem);
71          data.getWsDocInfo().addResult(result);
72          return Collections.singletonList(result);
73      }
74  
75      /**
76       * Dereferences and decodes encrypted data elements.
77       *
78       * @param elem contains the <code>ReferenceList</code> to the encrypted
79       *             data elements
80       */
81      private List<WSDataRef> handleReferenceList(
82          Element elem,
83          RequestData data
84      ) throws WSSecurityException {
85          List<WSDataRef> dataRefs = new ArrayList<>();
86          for (Node node = elem.getFirstChild();
87              node != null;
88              node = node.getNextSibling()
89          ) {
90              if (Node.ELEMENT_NODE == node.getNodeType()
91                  && WSConstants.ENC_NS.equals(node.getNamespaceURI())
92                  && "DataReference".equals(node.getLocalName())) {
93                  String dataRefURI = ((Element) node).getAttributeNS(null, "URI");
94                  dataRefURI = XMLUtils.getIDFromReference(dataRefURI);
95  
96                  // See whether we have already processed the encrypted node
97                  if (!data.getWsDocInfo().hasResult(WSConstants.ENCR, dataRefURI)) {
98                      WSDataRef dataRef =
99                          decryptDataRefEmbedded(elem.getOwnerDocument(), dataRefURI, data);
100                     dataRefs.add(dataRef);
101                 }
102             }
103         }
104 
105         return dataRefs;
106     }
107 
108 
109     /**
110      * Decrypt an (embedded) EncryptedData element referenced by dataRefURI.
111      */
112     private WSDataRef decryptDataRefEmbedded(
113         Document doc,
114         String dataRefURI,
115         RequestData data
116     ) throws WSSecurityException {
117         LOG.debug("Found data reference: {}", dataRefURI);
118         //
119         // Find the encrypted data element referenced by dataRefURI
120         //
121         Element encryptedDataElement =
122             EncryptionUtils.findEncryptedDataElement(data.getWsDocInfo(), dataRefURI);
123 
124         if (encryptedDataElement != null && data.isRequireSignedEncryptedDataElements()) {
125             List<WSSecurityEngineResult> signedResults =
126                 data.getWsDocInfo().getResultsByTag(WSConstants.SIGN);
127             SignatureUtils.verifySignedElement(encryptedDataElement, signedResults);
128         }
129         //
130         // Prepare the SecretKey object to decrypt EncryptedData
131         //
132         String symEncAlgo = X509Util.getEncAlgo(encryptedDataElement);
133         Element keyInfoElement =
134             XMLUtils.getDirectChildElement(
135                     encryptedDataElement, "KeyInfo", WSConstants.SIG_NS
136                 );
137         // KeyInfo cannot be null
138         if (keyInfoElement == null) {
139             throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, "noKeyinfo");
140         }
141         // Check BSP compliance
142         checkBSPCompliance(keyInfoElement, symEncAlgo, data.getBSPEnforcer());
143 
144         //
145         // Try to get a security reference token, if none found try to get a
146         // shared key using a KeyName.
147         //
148         Element secRefToken =
149             XMLUtils.getDirectChildElement(
150                 keyInfoElement, "SecurityTokenReference", WSConstants.WSSE_NS
151             );
152         SecretKey symmetricKey = null;
153         Principal principal = null;
154         if (secRefToken == null) {
155             byte[] decryptedData =
156                 X509Util.getSecretKey(keyInfoElement, symEncAlgo, data.getCallbackHandler());
157             symmetricKey = KeyUtils.prepareSecretKey(symEncAlgo, decryptedData);
158         } else {
159             STRParserParameters parameters = new STRParserParameters();
160             parameters.setData(data);
161             parameters.setStrElement(secRefToken);
162             if (symEncAlgo != null) {
163                 parameters.setDerivationKeyLength(KeyUtils.getKeyLength(symEncAlgo));
164             }
165 
166             STRParser strParser = new SecurityTokenRefSTRParser();
167             STRParserResult parserResult = strParser.parseSecurityTokenReference(parameters);
168             byte[] secretKey = parserResult.getSecretKey();
169             principal = parserResult.getPrincipal();
170             symmetricKey = KeyUtils.prepareSecretKey(symEncAlgo, secretKey);
171         }
172 
173         // Check for compliance against the defined AlgorithmSuite
174         AlgorithmSuite algorithmSuite = data.getAlgorithmSuite();
175         if (algorithmSuite != null) {
176             AlgorithmSuiteValidator algorithmSuiteValidator = new
177                 AlgorithmSuiteValidator(algorithmSuite);
178 
179             if (principal instanceof WSDerivedKeyTokenPrincipal) {
180                 algorithmSuiteValidator.checkDerivedKeyAlgorithm(
181                     ((WSDerivedKeyTokenPrincipal)principal).getAlgorithm()
182                 );
183                 algorithmSuiteValidator.checkEncryptionDerivedKeyLength(
184                     ((WSDerivedKeyTokenPrincipal)principal).getLength()
185                 );
186             }
187 
188             algorithmSuiteValidator.checkSymmetricKeyLength(symmetricKey.getEncoded().length);
189             algorithmSuiteValidator.checkSymmetricEncryptionAlgorithm(symEncAlgo);
190         }
191 
192         return
193             EncryptionUtils.decryptEncryptedData(
194                 doc, dataRefURI, encryptedDataElement, symmetricKey, symEncAlgo, data.getAttachmentCallbackHandler(),
195                 data.getEncryptionSerializer()
196             );
197     }
198 
199     /**
200      * Check for BSP compliance
201      * @param keyInfoElement The KeyInfo element child
202      * @param encAlgo The encryption algorithm
203      * @throws WSSecurityException
204      */
205     private static void checkBSPCompliance(
206         Element keyInfoElement,
207         String encAlgo,
208         BSPEnforcer bspEnforcer
209     ) throws WSSecurityException {
210         // We can only have one token reference
211         int result = 0;
212         Node node = keyInfoElement.getFirstChild();
213         Element child = null;
214         while (node != null) {
215             if (Node.ELEMENT_NODE == node.getNodeType()) {
216                 result++;
217                 child = (Element)node;
218             }
219             node = node.getNextSibling();
220         }
221         if (result != 1) {
222             bspEnforcer.handleBSPRule(BSPRule.R5424);
223         }
224 
225         if (child == null || !WSConstants.WSSE_NS.equals(child.getNamespaceURI())
226             || !SecurityTokenReference.SECURITY_TOKEN_REFERENCE.equals(child.getLocalName())) {
227             bspEnforcer.handleBSPRule(BSPRule.R5426);
228         }
229 
230         // EncryptionAlgorithm cannot be null
231         if (encAlgo == null) {
232             bspEnforcer.handleBSPRule(BSPRule.R5601);
233         }
234         // EncryptionAlgorithm must be 3DES, or AES128, or AES256
235         if (!WSConstants.TRIPLE_DES.equals(encAlgo)
236             && !WSConstants.AES_128.equals(encAlgo)
237             && !WSConstants.AES_128_GCM.equals(encAlgo)
238             && !WSConstants.AES_256.equals(encAlgo)
239             && !WSConstants.AES_256_GCM.equals(encAlgo)) {
240             bspEnforcer.handleBSPRule(BSPRule.R5620);
241         }
242     }
243 
244 }
245