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 dk.sosi.seal.transform.internal;
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.apache.ws.security.WSConstants;
25  import org.apache.ws.security.WSDocInfo;
26  import org.apache.ws.security.WSDocInfoStore;
27  import org.apache.ws.security.WSSecurityException;
28  import org.apache.ws.security.util.WSSecurityUtil;
29  import org.apache.xml.security.c14n.CanonicalizationException;
30  import org.apache.xml.security.c14n.Canonicalizer;
31  import org.apache.xml.security.c14n.InvalidCanonicalizerException;
32  import org.apache.xml.security.signature.XMLSignatureInput;
33  import org.apache.xml.security.transforms.Transform;
34  import org.apache.xml.security.transforms.TransformSpi;
35  import org.apache.xml.security.utils.XMLUtils;
36  import org.w3c.dom.Document;
37  import org.w3c.dom.Element;
38  
39  import java.io.ByteArrayOutputStream;
40  import java.io.IOException;
41  
42  /**
43   * Class STRTransform
44   * 
45   * @author Werner Dittmann (Werner.Dittmann@siemens.com)
46   * @version 1.0
47   */
48  public class STRTransform extends TransformSpi {
49  
50      /**
51       * Field implementedTransformURI
52       */
53      public static final String implementedTransformURI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#STR-Transform";
54  
55      private static Log log = LogFactory.getLog(STRTransform.class.getName());
56  
57      private static boolean doDebug = false;
58  
59      private static String XMLNS = "xmlns=";
60  
61      private WSDocInfo wsDocInfo = null;
62      
63      public boolean wantsOctetStream() {
64          return false;
65      }
66  
67      public boolean wantsNodeSet() {
68          return true;
69      }
70  
71      public boolean returnsOctetStream() {
72          return true;
73      }
74  
75      public boolean returnsNodeSet() {
76          return false;
77      }
78  
79      /**
80       * Method engineGetURI
81       */
82      protected String engineGetURI() {
83          return STRTransform.implementedTransformURI;
84      }
85  
86      /**
87       * Method enginePerformTransform
88       * 
89       * @param input
90       * @throws CanonicalizationException
91       * @throws InvalidCanonicalizerException
92       */
93      protected XMLSignatureInput enginePerformTransform(XMLSignatureInput input, 
94                                                         Transform transformObject)
95          throws IOException, CanonicalizationException, InvalidCanonicalizerException {
96          doDebug = log.isDebugEnabled();
97  
98          if (doDebug) {
99              log.debug("Beginning STRTransform..." + input.toString());
100         }
101 
102         try {
103             //
104             // Get the main document, that is the complete SOAP request document
105             //
106             Document thisDoc = transformObject.getDocument();
107 
108             //
109             // Here we get some information about the document that is being
110             // processed, in particular the crypto implementation, and already
111             // detected BST that may be used later during dereferencing.
112             //
113             wsDocInfo = WSDocInfoStore.lookup(thisDoc);
114             if (wsDocInfo == null) {
115                 throw (new CanonicalizationException("no WSDocInfo found"));
116             }
117             //
118             // According to the OASIS WS Specification "Web Services Security:
119             // SOAP Message Security 1.0" Monday, 19 January 2004, chapter 8.3
120             // describes that the input node set must be processed by the c14n
121             // that is specified in the argument element of the STRTransform
122             // element.
123             // 
124             // First step: Get the required c14n argument and get the specified
125             // Canonicalizer
126             //
127             String canonAlgo = null;
128             if (transformObject.length(
129                 WSConstants.WSSE_NS, "TransformationParameters") == 1) {
130                 Element tmpE = 
131                     XMLUtils.selectNode(
132                         transformObject.getElement().getFirstChild(), 
133                         WSConstants.WSSE_NS,
134                         "TransformationParameters", 
135                         0
136                     );
137                 Element canonElem = 
138                     (Element) WSSecurityUtil.getDirectChild(
139                         tmpE, "CanonicalizationMethod", WSConstants.SIG_NS
140                     );
141                 canonAlgo = canonElem.getAttribute("Algorithm");
142                 if (doDebug) {
143                     log.debug("CanonAlgo: " + canonAlgo);
144                 }
145             }
146             Canonicalizer canon = Canonicalizer.getInstance(canonAlgo);
147 
148             ByteArrayOutputStream bos = null;
149             byte[] buf = null;
150             if (doDebug) {
151                 buf = input.getBytes();
152                 bos = new ByteArrayOutputStream(buf.length);
153                 bos.write(buf, 0, buf.length);
154                 log.debug("canon bos: " + bos.toString());
155             }
156 
157             //
158             // Get the input (node) to transform. Currently we support only an
159             // Element as input format. If other formats are required we must
160             // get it as bytes and probably reparse it into a DOM tree (How to
161             // work with nodesets? how to select the right node from a nodeset?)
162             //
163             Element str = null;
164             if (input.isElement()) {
165                 str = (Element) input.getSubNode();
166             } else {
167                 throw new CanonicalizationException(
168                     "Wrong input format - only element input supported"
169                 );
170             }
171 
172             if (doDebug) {
173                 log.debug("STR: " + str.toString());
174             }
175             //
176             // The element to transform MUST be a SecurityTokenReference
177             // element.
178             //
179             SecurityTokenReference secRef = new SecurityTokenReference(str);
180             //
181             // Third and forth step are performed by derefenceSTR()
182             //
183             Element dereferencedToken = STRTransformUtil.dereferenceSTR(
184                     thisDoc, secRef, wsDocInfo);
185             //
186             // C14n with specified algorithm. According to WSS Specification.
187             //
188             buf = canon.canonicalizeSubtree(dereferencedToken, "#default");
189             if (doDebug) {
190                 bos = new ByteArrayOutputStream(buf.length);
191                 bos.write(buf, 0, buf.length);
192                 log.debug("after c14n: " + bos.toString());
193             }
194 
195             //
196             // Alert: Hacks ahead According to WSS spec an Apex node must
197             // contain a default namespace. If none is availabe in the first
198             // node of the c14n output (this is the apex element) then we do
199             // some editing to insert an empty default namespace
200             // 
201             // TODO: Rework theses hacks after c14n was updated and can be
202             // instructed to insert empty default namespace if required
203             //
204             // If the problem with c14n method is solved then just do:
205             // return new XMLSignatureInput(buf);
206             
207             // start of HACK
208             StringBuffer bf = new StringBuffer(new String(buf));
209             String bf1 = bf.toString();
210 
211             //
212             // Find start and end of first element <....>, this is the Apex node
213             //
214             int gt = bf1.indexOf(">");
215             //
216             // Lookup the default namespace
217             //
218             int idx = bf1.indexOf(XMLNS);
219             //
220             // If none found or if it is outside of this (Apex) element look for
221             // first blank in, insert default namespace there (this is the
222             // correct place according to c14n specification)
223             //
224             if (idx < 0 || idx > gt) {
225                 idx = bf1.indexOf(" ");
226                 bf.insert(idx + 1, "xmlns=\"\" ");
227                 bf1 = bf.toString();
228             }
229             if (doDebug) {
230                 log.debug("last result: ");
231                 log.debug(bf1);
232             }
233             return new XMLSignatureInput(bf1.getBytes());
234         }
235         // End of HACK
236         catch (WSSecurityException ex) {
237             log.debug(ex.getMessage(), ex);
238             throw (new CanonicalizationException("c14n.Canonicalizer.Exception", ex));
239         }
240     }    
241 }