1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Collections; 7 using System.IO; 8 using System.Runtime.InteropServices; 9 using System.Security; 10 using System.Text; 11 using System.Xml; 12 using System.Xml.XPath; 13 using System.Xml.Xsl; 14 15 namespace System.Security.Cryptography.Xml 16 { 17 public class XmlDsigEnvelopedSignatureTransform : Transform 18 { 19 private Type[] _inputTypes = { typeof(Stream), typeof(XmlNodeList), typeof(XmlDocument) }; 20 private Type[] _outputTypes = { typeof(XmlNodeList), typeof(XmlDocument) }; 21 private XmlNodeList _inputNodeList; 22 private bool _includeComments = false; 23 private XmlNamespaceManager _nsm = null; 24 private XmlDocument _containingDocument = null; 25 private int _signaturePosition = 0; 26 27 internal int SignaturePosition 28 { 29 set { _signaturePosition = value; } 30 } 31 XmlDsigEnvelopedSignatureTransform()32 public XmlDsigEnvelopedSignatureTransform() 33 { 34 Algorithm = SignedXml.XmlDsigEnvelopedSignatureTransformUrl; 35 } 36 37 /// <internalonly/> XmlDsigEnvelopedSignatureTransform(bool includeComments)38 public XmlDsigEnvelopedSignatureTransform(bool includeComments) 39 { 40 _includeComments = includeComments; 41 Algorithm = SignedXml.XmlDsigEnvelopedSignatureTransformUrl; 42 } 43 44 public override Type[] InputTypes 45 { 46 get { return _inputTypes; } 47 } 48 49 public override Type[] OutputTypes 50 { 51 get { return _outputTypes; } 52 } 53 54 // An enveloped signature has no inner XML elements LoadInnerXml(XmlNodeList nodeList)55 public override void LoadInnerXml(XmlNodeList nodeList) { } 56 57 // An enveloped signature has no inner XML elements GetInnerXml()58 protected override XmlNodeList GetInnerXml() 59 { 60 return null; 61 } 62 LoadInput(object obj)63 public override void LoadInput(object obj) 64 { 65 if (obj is Stream) 66 { 67 LoadStreamInput((Stream)obj); 68 return; 69 } 70 if (obj is XmlNodeList) 71 { 72 LoadXmlNodeListInput((XmlNodeList)obj); 73 return; 74 } 75 if (obj is XmlDocument) 76 { 77 LoadXmlDocumentInput((XmlDocument)obj); 78 return; 79 } 80 } 81 LoadStreamInput(Stream stream)82 private void LoadStreamInput(Stream stream) 83 { 84 XmlDocument doc = new XmlDocument(); 85 doc.PreserveWhitespace = true; 86 XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI)); 87 XmlReader xmlReader = Utils.PreProcessStreamInput(stream, resolver, BaseURI); 88 doc.Load(xmlReader); 89 _containingDocument = doc; 90 if (_containingDocument == null) 91 throw new CryptographicException(SR.Cryptography_Xml_EnvelopedSignatureRequiresContext); 92 _nsm = new XmlNamespaceManager(_containingDocument.NameTable); 93 _nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl); 94 } 95 LoadXmlNodeListInput(XmlNodeList nodeList)96 private void LoadXmlNodeListInput(XmlNodeList nodeList) 97 { 98 // Empty node list is not acceptable 99 if (nodeList == null) 100 throw new ArgumentNullException(nameof(nodeList)); 101 _containingDocument = Utils.GetOwnerDocument(nodeList); 102 if (_containingDocument == null) 103 throw new CryptographicException(SR.Cryptography_Xml_EnvelopedSignatureRequiresContext); 104 105 _nsm = new XmlNamespaceManager(_containingDocument.NameTable); 106 _nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl); 107 _inputNodeList = nodeList; 108 } 109 LoadXmlDocumentInput(XmlDocument doc)110 private void LoadXmlDocumentInput(XmlDocument doc) 111 { 112 if (doc == null) 113 throw new ArgumentNullException(nameof(doc)); 114 _containingDocument = doc; 115 _nsm = new XmlNamespaceManager(_containingDocument.NameTable); 116 _nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl); 117 } 118 GetOutput()119 public override object GetOutput() 120 { 121 if (_containingDocument == null) 122 throw new CryptographicException(SR.Cryptography_Xml_EnvelopedSignatureRequiresContext); 123 124 // If we have received an XmlNodeList as input 125 if (_inputNodeList != null) 126 { 127 // If the position has not been set, then we don't want to remove any signature tags 128 if (_signaturePosition == 0) return _inputNodeList; 129 XmlNodeList signatureList = _containingDocument.SelectNodes("//dsig:Signature", _nsm); 130 if (signatureList == null) return _inputNodeList; 131 132 CanonicalXmlNodeList resultNodeList = new CanonicalXmlNodeList(); 133 foreach (XmlNode node in _inputNodeList) 134 { 135 if (node == null) continue; 136 // keep namespaces 137 if (Utils.IsXmlNamespaceNode(node) || Utils.IsNamespaceNode(node)) 138 { 139 resultNodeList.Add(node); 140 } 141 else 142 { 143 // SelectSingleNode throws an exception for xmldecl PI for example, so we will just ignore those exceptions 144 try 145 { 146 // Find the nearest signature ancestor tag 147 XmlNode result = node.SelectSingleNode("ancestor-or-self::dsig:Signature[1]", _nsm); 148 int position = 0; 149 foreach (XmlNode node1 in signatureList) 150 { 151 position++; 152 if (node1 == result) break; 153 } 154 if (result == null || (result != null && position != _signaturePosition)) 155 { 156 resultNodeList.Add(node); 157 } 158 } 159 catch { } 160 } 161 } 162 return resultNodeList; 163 } 164 // Else we have received either a stream or a document as input 165 else 166 { 167 XmlNodeList signatureList = _containingDocument.SelectNodes("//dsig:Signature", _nsm); 168 if (signatureList == null) return _containingDocument; 169 if (signatureList.Count < _signaturePosition || _signaturePosition <= 0) return _containingDocument; 170 171 // Remove the signature node with all its children nodes 172 signatureList[_signaturePosition - 1].ParentNode.RemoveChild(signatureList[_signaturePosition - 1]); 173 return _containingDocument; 174 } 175 } 176 GetOutput(Type type)177 public override object GetOutput(Type type) 178 { 179 if (type == typeof(XmlNodeList) || type.IsSubclassOf(typeof(XmlNodeList))) 180 { 181 if (_inputNodeList == null) 182 { 183 _inputNodeList = Utils.AllDescendantNodes(_containingDocument, true); 184 } 185 return (XmlNodeList)GetOutput(); 186 } 187 else if (type == typeof(XmlDocument) || type.IsSubclassOf(typeof(XmlDocument))) 188 { 189 if (_inputNodeList != null) throw new ArgumentException(SR.Cryptography_Xml_TransformIncorrectInputType, nameof(type)); 190 return (XmlDocument)GetOutput(); 191 } 192 else 193 { 194 throw new ArgumentException(SR.Cryptography_Xml_TransformIncorrectInputType, nameof(type)); 195 } 196 } 197 } 198 } 199