/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.wss4j.dom.transform; import java.io.ByteArrayInputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.spec.AlgorithmParameterSpec; import java.util.Iterator; import javax.xml.crypto.Data; import javax.xml.crypto.MarshalException; import javax.xml.crypto.NodeSetData; import javax.xml.crypto.OctetStreamData; import javax.xml.crypto.XMLCryptoContext; import javax.xml.crypto.XMLStructure; import javax.xml.crypto.dom.DOMCryptoContext; import javax.xml.crypto.dsig.TransformException; import javax.xml.crypto.dsig.TransformService; import javax.xml.crypto.dsig.spec.TransformParameterSpec; import org.apache.wss4j.common.bsp.BSPEnforcer; import org.apache.wss4j.common.token.PKIPathSecurity; import org.apache.wss4j.common.token.SecurityTokenReference; import org.apache.wss4j.common.token.X509Security; import org.apache.wss4j.common.util.XMLUtils; import org.apache.wss4j.dom.WSConstants; import org.apache.wss4j.dom.WSDocInfo; import org.apache.xml.security.c14n.Canonicalizer; import org.apache.xml.security.signature.XMLSignatureInput; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * Class STRTransform. */ public class STRTransform extends TransformService { public static final String TRANSFORM_URI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#STR-Transform"; public static final String TRANSFORM_WS_DOC_INFO = "transform_ws_doc_info"; private TransformParameterSpec params; private Element transformElement; private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(STRTransform.class); public final AlgorithmParameterSpec getParameterSpec() { return params; } public void init(TransformParameterSpec params) throws InvalidAlgorithmParameterException { this.params = params; } public void init(XMLStructure parent, XMLCryptoContext context) throws InvalidAlgorithmParameterException { if (context != null && !(context instanceof DOMCryptoContext)) { throw new ClassCastException("context must be of type DOMCryptoContext"); } if (!(parent instanceof javax.xml.crypto.dom.DOMStructure)) { throw new ClassCastException("parent must be of type DOMStructure"); } transformElement = (Element) ((javax.xml.crypto.dom.DOMStructure) parent).getNode(); } public void marshalParams(XMLStructure parent, XMLCryptoContext context) throws MarshalException { if (context != null && !(context instanceof DOMCryptoContext)) { throw new ClassCastException("context must be of type DOMCryptoContext"); } if (!(parent instanceof javax.xml.crypto.dom.DOMStructure)) { throw new ClassCastException("parent must be of type DOMStructure"); } Element transformElement2 = (Element) ((javax.xml.crypto.dom.DOMStructure) parent).getNode(); appendChild(transformElement2, transformElement); transformElement = transformElement2; } public Data transform(Data data, XMLCryptoContext xc) throws TransformException { if (data == null) { throw new NullPointerException("data must not be null"); } return transformIt(data, xc, null); } public Data transform(Data data, XMLCryptoContext xc, OutputStream os) throws TransformException { if (data == null) { throw new NullPointerException("data must not be null"); } if (os == null) { throw new NullPointerException("output stream must not be null"); } return transformIt(data, xc, os); } private Data transformIt(Data data, XMLCryptoContext xc, OutputStream os) throws TransformException { // // First step: Get the required c14n argument and get the specified // Canonicalizer // String canonAlgo = null; Element transformParams = XMLUtils.getDirectChildElement( transformElement, "TransformationParameters", WSConstants.WSSE_NS ); if (transformParams != null) { Element canonElem = XMLUtils.getDirectChildElement( transformParams, "CanonicalizationMethod", WSConstants.SIG_NS ); canonAlgo = canonElem.getAttributeNS(null, "Algorithm"); } try { // // Get the input (node) to transform. // Element str = null; if (data instanceof NodeSetData) { NodeSetData nodeSetData = (NodeSetData)data; Iterator<?> iterator = nodeSetData.iterator(); while (iterator.hasNext()) { Node node = (Node)iterator.next(); if (node instanceof Element && "SecurityTokenReference".equals(node.getLocalName())) { str = (Element)node; break; } } } else { try { XMLSignatureInput xmlSignatureInput = new XMLSignatureInput(((OctetStreamData)data).getOctetStream()); str = (Element)xmlSignatureInput.getSubNode(); } catch (Exception ex) { throw new TransformException(ex); } } if (str == null) { throw new TransformException("No SecurityTokenReference found"); } // // The element to transform MUST be a SecurityTokenReference // element. // SecurityTokenReference secRef = new SecurityTokenReference(str, new BSPEnforcer()); Canonicalizer canon = Canonicalizer.getInstance(canonAlgo); byte[] buf = null; // // Third and fourth step are performed by dereferenceSTR() // Object wsDocInfoObject = xc.getProperty(TRANSFORM_WS_DOC_INFO); WSDocInfo wsDocInfo = null; if (wsDocInfoObject instanceof WSDocInfo) { wsDocInfo = (WSDocInfo)wsDocInfoObject; } if (wsDocInfo == null) { LOG.debug("STRTransform: no WSDocInfo found"); } Document doc = str.getOwnerDocument(); Element dereferencedToken = STRTransformUtil.dereferenceSTR(doc, secRef, wsDocInfo); if (dereferencedToken != null) { String type = dereferencedToken.getAttributeNS(null, "ValueType"); if (X509Security.X509_V3_TYPE.equals(type) || PKIPathSecurity.getType().equals(type)) { // // Add the WSSE/WSU namespaces to the element for C14n // XMLUtils.setNamespace( dereferencedToken, WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX ); XMLUtils.setNamespace( dereferencedToken, WSConstants.WSU_NS, WSConstants.WSU_PREFIX ); } } // // C14n with specified algorithm. According to WSS Specification. // buf = canon.canonicalizeSubtree(dereferencedToken, "#default", true); if (LOG.isDebugEnabled()) { LOG.debug("after c14n: " + new String(buf, StandardCharsets.UTF_8)); } if (os != null) { os.write(buf); return null; } return new OctetStreamData(new ByteArrayInputStream(buf)); } catch (Exception ex) { throw new TransformException(ex); } } public final boolean isFeatureSupported(String feature) { if (feature == null) { throw new NullPointerException(); } else { return false; } } private static void appendChild(Node parent, Node child) { Document ownerDoc = null; if (parent.getNodeType() == Node.DOCUMENT_NODE) { ownerDoc = (Document)parent; } else { ownerDoc = parent.getOwnerDocument(); } if (child.getOwnerDocument() != ownerDoc) { parent.appendChild(ownerDoc.importNode(child, true)); } else { parent.appendChild(child); } } }