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 }