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.message;
21  
22  import org.apache.ws.security.WSConstants;
23  import org.apache.ws.security.WSDocInfo;
24  import org.apache.ws.security.WSEncryptionPart;
25  import org.apache.ws.security.WSSConfig;
26  import org.apache.ws.security.WSSecurityException;
27  import org.apache.ws.security.transform.STRTransform;
28  import org.apache.ws.security.util.WSSecurityUtil;
29  import org.w3c.dom.Document;
30  import org.w3c.dom.Element;
31  import org.w3c.dom.NamedNodeMap;
32  import org.w3c.dom.Node;
33  
34  import java.util.ArrayList;
35  import java.util.Collections;
36  import java.util.List;
37  
38  import javax.xml.crypto.XMLStructure;
39  import javax.xml.crypto.dom.DOMStructure;
40  import javax.xml.crypto.dsig.DigestMethod;
41  import javax.xml.crypto.dsig.Transform;
42  import javax.xml.crypto.dsig.XMLSignatureFactory;
43  import javax.xml.crypto.dsig.spec.ExcC14NParameterSpec;
44  import javax.xml.crypto.dsig.spec.TransformParameterSpec;
45  
46  /**
47   * This is the base class for WS Security messages that are used for signature generation or
48   * verification.
49   */
50  public class WSSecSignatureBase extends WSSecBase {
51      
52      private static org.apache.commons.logging.Log log = 
53          org.apache.commons.logging.LogFactory.getLog(WSSecSignatureBase.class);
54      
55      public WSSecSignatureBase() {
56          super();
57      }
58      public WSSecSignatureBase(WSSConfig config) {
59          super(config);
60      }
61      
62      /**
63       * This method adds references to the Signature.
64       * 
65       * @param doc The parent document
66       * @param references The list of references to sign
67       * @param wsDocInfo The WSDocInfo object to store protection elements in
68       * @param signatureFactory The XMLSignature object
69       * @param secHeader The Security Header
70       * @param wssConfig The WSSConfig
71       * @param digestAlgo The digest algorithm to use
72       * @throws WSSecurityException
73       */
74      public List<javax.xml.crypto.dsig.Reference> addReferencesToSign(
75          Document doc,
76          List<WSEncryptionPart> references,
77          WSDocInfo wsDocInfo,
78          XMLSignatureFactory signatureFactory,
79          WSSecHeader secHeader,
80          WSSConfig wssConfig,
81          String digestAlgo
82      ) throws WSSecurityException {
83          DigestMethod digestMethod;
84          try {
85              digestMethod = signatureFactory.newDigestMethod(digestAlgo, null);
86          } catch (Exception ex) {
87              log.error("", ex);
88              throw new WSSecurityException(
89                  WSSecurityException.FAILED_SIGNATURE, "noXMLSig", null, ex
90              );
91          }
92          
93          List<javax.xml.crypto.dsig.Reference> referenceList = 
94              new ArrayList<javax.xml.crypto.dsig.Reference>();
95  
96          for (WSEncryptionPart encPart : references) {
97              String idToSign = encPart.getId();
98              String elemName = encPart.getName();
99              Element element = encPart.getElement();
100 
101             //
102             // Set up the elements to sign. There is one reserved element
103             // names: "STRTransform": Setup the ds:Reference to use STR Transform
104             //
105             try {
106                 if (idToSign != null) {
107                     Transform transform = null;
108                     if ("STRTransform".equals(elemName)) {
109                         Element ctx = createSTRParameter(doc);
110                         
111                         XMLStructure structure = new DOMStructure(ctx);
112                         transform =
113                             signatureFactory.newTransform(
114                                 STRTransform.TRANSFORM_URI,
115                                 structure
116                             );
117                     } else {
118                         TransformParameterSpec transformSpec = null;
119                         if (element == null) {
120                             if (callbackLookup == null) {
121                                 callbackLookup = new DOMCallbackLookup(doc);
122                             }
123                             element = callbackLookup.getElement(idToSign, null, false);
124                         }
125                         if (wssConfig.isWsiBSPCompliant()) {
126                             List<String> prefixes = getInclusivePrefixes(element);
127                             transformSpec = new ExcC14NParameterSpec(prefixes);
128                         }
129                         transform =
130                             signatureFactory.newTransform(
131                                 WSConstants.C14N_EXCL_OMIT_COMMENTS,
132                                 transformSpec
133                             );
134                     }
135                     if (element != null) {
136                         wsDocInfo.addTokenElement(element, false);
137                     }
138                     javax.xml.crypto.dsig.Reference reference = 
139                         signatureFactory.newReference(
140                             "#" + idToSign, 
141                             digestMethod,
142                             Collections.singletonList(transform),
143                             null,
144                             null
145                         );
146                     referenceList.add(reference);
147                 } else {
148                     String nmSpace = encPart.getNamespace();
149                     List<Element> elementsToSign = null;
150                     if (element != null) {
151                         elementsToSign = Collections.singletonList(element);
152                     } else {
153                         if (callbackLookup == null) {
154                             callbackLookup = new DOMCallbackLookup(doc);
155                         }
156                         elementsToSign = 
157                             WSSecurityUtil.findElements(encPart, callbackLookup, doc);
158                     }
159                     if (elementsToSign == null || elementsToSign.size() == 0) {
160                         throw new WSSecurityException(
161                             WSSecurityException.FAILURE, 
162                             "noEncElement",
163                             new Object[] {nmSpace + ", " + elemName}
164                         );
165                     }
166                     for (Element elementToSign : elementsToSign) {
167                         TransformParameterSpec transformSpec = null;
168                         if (wssConfig.isWsiBSPCompliant()) {
169                             List<String> prefixes = getInclusivePrefixes(elementToSign);
170                             transformSpec = new ExcC14NParameterSpec(prefixes);
171                         }
172                         Transform transform =
173                             signatureFactory.newTransform(
174                                 WSConstants.C14N_EXCL_OMIT_COMMENTS,
175                                 transformSpec
176                             );
177                         javax.xml.crypto.dsig.Reference reference = 
178                             signatureFactory.newReference(
179                                 "#" + setWsuId(elementToSign), 
180                                 digestMethod,
181                                 Collections.singletonList(transform),
182                                 null,
183                                 null
184                             );
185                         referenceList.add(reference);
186                         wsDocInfo.addTokenElement(elementToSign, false);
187                     }
188                 }
189             } catch (Exception ex) {
190                 log.error("", ex);
191                 throw new WSSecurityException(
192                     WSSecurityException.FAILED_SIGNATURE, "noXMLSig", null, ex
193                 );
194             }
195         }
196         
197         return referenceList;
198     }
199     
200     /**
201      * Get the List of inclusive prefixes from the DOM Element argument 
202      */
203     public List<String> getInclusivePrefixes(Element target) {
204         return getInclusivePrefixes(target, true);
205     }
206     
207     
208     /**
209      * Get the List of inclusive prefixes from the DOM Element argument 
210      */
211     public List<String> getInclusivePrefixes(Element target, boolean excludeVisible) {
212         List<String> result = new ArrayList<String>();
213         Node parent = target;
214         while (parent.getParentNode() != null &&
215             !(Node.DOCUMENT_NODE == parent.getParentNode().getNodeType())) {
216             parent = parent.getParentNode();
217             NamedNodeMap attributes = parent.getAttributes();
218             for (int i = 0; i < attributes.getLength(); i++) {
219                 Node attribute = attributes.item(i);
220                 if (WSConstants.XMLNS_NS.equals(attribute.getNamespaceURI())) {
221                     if ("xmlns".equals(attribute.getNodeName())) {
222                         result.add("#default");
223                     } else {
224                         result.add(attribute.getLocalName());
225                     }
226                 }
227             }
228         }
229 
230         if (excludeVisible) {
231             NamedNodeMap attributes = target.getAttributes();
232             for (int i = 0; i < attributes.getLength(); i++) {
233                 Node attribute = attributes.item(i);
234                 if (WSConstants.XMLNS_NS.equals(attribute.getNamespaceURI())) {
235                     if ("xmlns".equals(attribute.getNodeName())) {
236                         result.remove("#default");
237                     } else {
238                         result.remove(attribute.getLocalName());
239                     }
240                 }
241                 if (attribute.getPrefix() != null) {
242                     result.remove(attribute.getPrefix());
243                 }
244             }
245 
246             if (target.getPrefix() == null) {
247                 result.remove("#default");
248             } else {
249                 result.remove(target.getPrefix());
250             }
251         }
252 
253         return result;
254     }
255     
256     /**
257      * Create an STRTransformationParameters element
258      */
259     public Element createSTRParameter(Document doc) {
260         Element transformParam = 
261             doc.createElementNS(
262                 WSConstants.WSSE_NS,
263                 WSConstants.WSSE_PREFIX + ":TransformationParameters"
264             );
265 
266         Element canonElem = 
267             doc.createElementNS(
268                 WSConstants.SIG_NS,
269                 WSConstants.SIG_PREFIX + ":CanonicalizationMethod"
270             );
271 
272         canonElem.setAttributeNS(null, "Algorithm", WSConstants.C14N_EXCL_OMIT_COMMENTS);
273         transformParam.appendChild(canonElem);
274         return transformParam;
275     }
276     
277 }