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.transform;
21  
22  import java.io.ByteArrayInputStream;
23  import java.io.ByteArrayOutputStream;
24  import java.io.OutputStream;
25  import java.security.InvalidAlgorithmParameterException;
26  import java.security.spec.AlgorithmParameterSpec;
27  import java.util.Iterator;
28  
29  import javax.xml.crypto.Data;
30  import javax.xml.crypto.MarshalException;
31  import javax.xml.crypto.NodeSetData;
32  import javax.xml.crypto.OctetStreamData;
33  import javax.xml.crypto.XMLCryptoContext;
34  import javax.xml.crypto.XMLStructure;
35  import javax.xml.crypto.dom.DOMCryptoContext;
36  import javax.xml.crypto.dsig.TransformException;
37  import javax.xml.crypto.dsig.TransformService;
38  import javax.xml.crypto.dsig.spec.TransformParameterSpec;
39  
40  import org.apache.wss4j.common.bsp.BSPEnforcer;
41  import org.apache.wss4j.common.token.PKIPathSecurity;
42  import org.apache.wss4j.common.token.SecurityTokenReference;
43  import org.apache.wss4j.common.token.X509Security;
44  import org.apache.wss4j.common.util.XMLUtils;
45  import org.apache.wss4j.dom.WSConstants;
46  import org.apache.wss4j.dom.WSDocInfo;
47  import org.apache.xml.security.c14n.Canonicalizer;
48  import org.apache.xml.security.signature.XMLSignatureInput;
49  import org.apache.xml.security.signature.XMLSignatureStreamInput;
50  import org.w3c.dom.Document;
51  import org.w3c.dom.Element;
52  import org.w3c.dom.Node;
53  
54  
55  /**
56   * Class STRTransform.
57   */
58  public class STRTransform extends TransformService {
59  
60      public static final String TRANSFORM_URI =
61          "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#STR-Transform";
62  
63      public static final String TRANSFORM_WS_DOC_INFO = "transform_ws_doc_info";
64  
65      private TransformParameterSpec params;
66  
67      private Element transformElement;
68  
69      private static final org.slf4j.Logger LOG =
70          org.slf4j.LoggerFactory.getLogger(STRTransform.class);
71  
72      public final AlgorithmParameterSpec getParameterSpec() {
73          return params;
74      }
75  
76      public void init(TransformParameterSpec params)
77          throws InvalidAlgorithmParameterException {
78          this.params = params;
79      }
80  
81      public void init(XMLStructure parent, XMLCryptoContext context)
82      throws InvalidAlgorithmParameterException {
83          if (context != null && !(context instanceof DOMCryptoContext)) {
84              throw new ClassCastException("context must be of type DOMCryptoContext");
85          }
86          if (!(parent instanceof javax.xml.crypto.dom.DOMStructure)) {
87              throw new ClassCastException("parent must be of type DOMStructure");
88          }
89          transformElement = (Element)
90              ((javax.xml.crypto.dom.DOMStructure) parent).getNode();
91      }
92  
93      public void marshalParams(XMLStructure parent, XMLCryptoContext context)
94      throws MarshalException {
95          if (context != null && !(context instanceof DOMCryptoContext)) {
96              throw new ClassCastException("context must be of type DOMCryptoContext");
97          }
98          if (!(parent instanceof javax.xml.crypto.dom.DOMStructure)) {
99              throw new ClassCastException("parent must be of type DOMStructure");
100         }
101         Element transformElement2 = (Element)
102             ((javax.xml.crypto.dom.DOMStructure) parent).getNode();
103         appendChild(transformElement2, transformElement);
104         transformElement = transformElement2;
105     }
106 
107 
108     public Data transform(Data data, XMLCryptoContext xc)
109         throws TransformException {
110         if (data == null) {
111             throw new NullPointerException("data must not be null");
112         }
113         return transformIt(data, xc, null);
114     }
115 
116     public Data transform(Data data, XMLCryptoContext xc, OutputStream os)
117         throws TransformException {
118         if (data == null) {
119             throw new NullPointerException("data must not be null");
120         }
121         if (os == null) {
122             throw new NullPointerException("output stream must not be null");
123         }
124         return transformIt(data, xc, os);
125     }
126 
127 
128     private Data transformIt(Data data, XMLCryptoContext xc, OutputStream os)
129         throws TransformException {
130 
131         //
132         // First step: Get the required c14n argument and get the specified
133         // Canonicalizer
134         //
135         String canonAlgo = null;
136         Element transformParams = XMLUtils.getDirectChildElement(
137             transformElement, "TransformationParameters", WSConstants.WSSE_NS
138         );
139         if (transformParams != null) {
140             Element canonElem =
141                 XMLUtils.getDirectChildElement(
142                     transformParams, "CanonicalizationMethod", WSConstants.SIG_NS
143                 );
144             canonAlgo = canonElem.getAttributeNS(null, "Algorithm");
145         }
146         try {
147             //
148             // Get the input (node) to transform.
149             //
150             Element str = null;
151             if (data instanceof NodeSetData) {
152                 NodeSetData nodeSetData = (NodeSetData)data;
153                 Iterator<?> iterator = nodeSetData.iterator();
154                 while (iterator.hasNext()) {
155                     Node node = (Node)iterator.next();
156                     if (node instanceof Element && "SecurityTokenReference".equals(node.getLocalName())) {
157                         str = (Element)node;
158                         break;
159                     }
160                 }
161             } else {
162                 try {
163                     XMLSignatureInput xmlSignatureInput = //NOPMD
164                         new XMLSignatureStreamInput(((OctetStreamData)data).getOctetStream());
165                     str = (Element)xmlSignatureInput.getSubNode();
166                 } catch (Exception ex) {
167                     throw new TransformException(ex);
168                 }
169             }
170             if (str == null) {
171                 throw new TransformException("No SecurityTokenReference found");
172             }
173             //
174             // The element to transform MUST be a SecurityTokenReference
175             // element.
176             //
177             SecurityTokenReference secRef = new SecurityTokenReference(str, new BSPEnforcer());
178 
179             Canonicalizer canon = Canonicalizer.getInstance(canonAlgo);
180 
181             //
182             // Third and fourth step are performed by dereferenceSTR()
183             //
184             Object wsDocInfoObject = xc.getProperty(TRANSFORM_WS_DOC_INFO);
185             WSDocInfo wsDocInfo = null;
186             if (wsDocInfoObject instanceof WSDocInfo) {
187                 wsDocInfo = (WSDocInfo)wsDocInfoObject;
188             }
189             if (wsDocInfo == null) {
190                 LOG.debug("STRTransform: no WSDocInfo found");
191             }
192 
193             Document doc = str.getOwnerDocument();
194             Element dereferencedToken =
195                 STRTransformUtil.dereferenceSTR(doc, secRef, wsDocInfo);
196 
197             if (dereferencedToken != null) {
198                 String type = dereferencedToken.getAttributeNS(null, "ValueType");
199                 if (X509Security.X509_V3_TYPE.equals(type)
200                     || PKIPathSecurity.getType().equals(type)) {
201                     //
202                     // Add the WSSE/WSU namespaces to the element for C14n
203                     //
204                     XMLUtils.setNamespace(
205                         dereferencedToken, WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX
206                     );
207                     XMLUtils.setNamespace(
208                         dereferencedToken, WSConstants.WSU_NS, WSConstants.WSU_PREFIX
209                     );
210                 }
211             }
212 
213             //
214             // C14n with specified algorithm. According to WSS Specification.
215             //
216             if (os != null) {
217                 canon.canonicalizeSubtree(dereferencedToken, "#default", true, os);
218                 return null;
219             }
220 
221             try (ByteArrayOutputStream writer = new ByteArrayOutputStream()) {
222                 canon.canonicalizeSubtree(dereferencedToken, "#default", true, writer);
223                 return new OctetStreamData(new ByteArrayInputStream(writer.toByteArray()));
224             }
225         } catch (Exception ex) {
226             throw new TransformException(ex);
227         }
228     }
229 
230 
231     public final boolean isFeatureSupported(String feature) {
232         if (feature == null) {
233             throw new NullPointerException();
234         } else {
235             return false;
236         }
237     }
238 
239     private static void appendChild(Node parent, Node child) {
240         Document ownerDoc = null;
241         if (parent.getNodeType() == Node.DOCUMENT_NODE) {
242             ownerDoc = (Document)parent;
243         } else {
244             ownerDoc = parent.getOwnerDocument();
245         }
246         if (child.getOwnerDocument() != ownerDoc) {
247             parent.appendChild(ownerDoc.importNode(child, true));
248         } else {
249             parent.appendChild(child);
250         }
251     }
252 
253 }