From 8b0fa56eb105198180172aaf2456db072b8f5c80 Mon Sep 17 00:00:00 2001 From: Samo Prelog Date: Tue, 12 Mar 2024 16:01:13 -0700 Subject: [PATCH] Fixing SignedXml.CheckSignature for enveloped signature with `#xpointer(/)` Reference This additionally improves support for URI-less Reference elements. Co-authored-by: Kevin Jones --- .../System.Security.Cryptography.Xml.csproj | 2 + .../Security/Cryptography/Xml/Reference.cs | 25 ++++++++--- .../tests/SignedXmlTest.cs | 41 +++++++++++++++++++ 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System.Security.Cryptography.Xml.csproj b/src/libraries/System.Security.Cryptography.Xml/src/System.Security.Cryptography.Xml.csproj index 5aea1a156c97a..dacbf6255861a 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System.Security.Cryptography.Xml.csproj +++ b/src/libraries/System.Security.Cryptography.Xml/src/System.Security.Cryptography.Xml.csproj @@ -4,6 +4,8 @@ true $(NoWarn);CA1850 true + true + 1 Provides classes to support the creation and validation of XML digital signatures. The classes in this namespace implement the World Wide Web Consortium Recommendation, "XML-Signature Syntax and Processing", described at http://www.w3.org/TR/xmldsig-core/. Commonly Used Types: diff --git a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Reference.cs b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Reference.cs index ba3a46bc7a7a4..4e146c45e079f 100644 --- a/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Reference.cs +++ b/src/libraries/System.Security.Cryptography.Xml/src/System/Security/Cryptography/Xml/Reference.cs @@ -266,18 +266,31 @@ public void LoadXml(XmlElement value) // let the transform read the children of the transformElement for data transform.LoadInnerXml(transformElement.ChildNodes); // Hack! this is done to get around the lack of here() function support in XPath - if (transform is XmlDsigEnvelopedSignatureTransform - && _uri != null && (_uri.Length == 0 || _uri[0] == '#')) + if (transform is XmlDsigEnvelopedSignatureTransform) { // Walk back to the Signature tag. Find the nearest signature ancestor // Signature-->SignedInfo-->Reference-->Transforms-->Transform XmlNode? signatureTag = transformElement.SelectSingleNode("ancestor::ds:Signature[1]", nsm); // Resolve the reference to get starting point for position calculation. - XmlNode? referenceTarget = - _uri.Length == 0 - ? transformElement.OwnerDocument - : SignedXml!.GetIdElement(transformElement.OwnerDocument, Utils.GetIdFromLocalUri(_uri, out bool _)); + // This needs to match the way CalculateSignature resolves URI references. + XmlNode? referenceTarget = null; + if (_uri == null || _uri.Length == 0) + { + referenceTarget = transformElement.OwnerDocument; + } + else if (_uri[0] == '#') + { + string idref = Utils.ExtractIdFromLocalUri(_uri); + if (idref == "xpointer(/)") + { + referenceTarget = transformElement.OwnerDocument; + } + else + { + referenceTarget = SignedXml!.GetIdElement(transformElement.OwnerDocument, idref); + } + } XmlNodeList? signatureList = referenceTarget?.SelectNodes(".//ds:Signature", nsm); if (signatureList != null) diff --git a/src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs b/src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs index 3db8c44aed827..bd22c5b835eed 100644 --- a/src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs +++ b/src/libraries/System.Security.Cryptography.Xml/tests/SignedXmlTest.cs @@ -9,6 +9,7 @@ // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com) // Copyright (C) 2004-2005, 2008 Novell, Inc (http://www.novell.com) +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Net; @@ -1993,5 +1994,45 @@ public void CheckSignatureHandlesIncorrectOrTamperedReferenceWithMultipleEnvelop Assert.False(subject.CheckSignature()); } + + public static object[][] EnvelopedSignatureWithRootXpointerReference = new object[][] + { + new object[] { true, """HiSVaCE5w9iLXTVYTKP1t/yjjmPXvWovMYpgljGgpgz2Y=dqcBmS1ZvDJNhmCEgobpAb+A2XaiuB69dfGIhisZvqoxaWqAqv/0w49jp38+usJ5t3wcq3aMC631QE8iln+lHWrarojDMDWLa00isv3oE3q9UgOIV9e6MUSoRTTvQkmlK/LSYV9T/SKx6h03vLLcIkUMXaTkC/n2kthlJTGkLbU=t6qV1iTlkCPoaIeOTvnDczQv5pytUxMoyNXws5vaMQYxfJMKos47dvmiLtfWUDLYXFX3Yf/JMC14plJw2JA5jLrlHLnZj/vCjRtXckmWW/wGYewXUqrgR1CytStUeQKj9mNsi76erukua10UhzIrWG+H6YQ/qS4AMMJZU6jBvO0=AQAB""" }, + new object[] { false, """Tempered worldSVaCE5w9iLXTVYTKP1t/yjjmPXvWovMYpgljGgpgz2Y=dqcBmS1ZvDJNhmCEgobpAb+A2XaiuB69dfGIhisZvqoxaWqAqv/0w49jp38+usJ5t3wcq3aMC631QE8iln+lHWrarojDMDWLa00isv3oE3q9UgOIV9e6MUSoRTTvQkmlK/LSYV9T/SKx6h03vLLcIkUMXaTkC/n2kthlJTGkLbU=t6qV1iTlkCPoaIeOTvnDczQv5pytUxMoyNXws5vaMQYxfJMKos47dvmiLtfWUDLYXFX3Yf/JMC14plJw2JA5jLrlHLnZj/vCjRtXckmWW/wGYewXUqrgR1CytStUeQKj9mNsi76erukua10UhzIrWG+H6YQ/qS4AMMJZU6jBvO0=AQAB""" }, + }; + + [Theory] + [MemberData(nameof(EnvelopedSignatureWithRootXpointerReference))] + public void CheckSignatureHandlesEnvelopedSignatureWithRootXpointerReference(bool isValid, string xml) + { + XmlDocument xmlDoc = new (); + xmlDoc.LoadXml(xml); + SignedXml signedXml = new (xmlDoc); + signedXml.LoadXml(xmlDoc.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl)[0] as XmlElement); + + Assert.Equal(isValid, signedXml.CheckSignature()); + } + + + public static object[][] EnvelopedSignatureWithEmptyReference = new object[][] + { + new object[] { true, """HiSVaCE5w9iLXTVYTKP1t/yjjmPXvWovMYpgljGgpgz2Y=CiB9jgIS7+Wq+lpyzCGsBZQcQ2BxqQuEU9VCvb3Li5jMtjwRV1bMO+4Wfnb4VWhEtEUq6NdiVGXhC1xvtVLnnLDX7CD/jG6NvM1Yd0/rf0UUceBhzYLFE9HLsopsBmmm3t8FO6ZtRr1QqKM0XDaQleGK9vYd2m2Jq8OR3r/w4OY=vcM1wQVmLB9DwdnAym8l8nw63/HlTVzgTDhIwNzWPhsPE/qr2wlK4TEQ3rjU+RAdNytfFNCnuuh75ZVMjAWCV9h6VDlp0DOvBhb6GenhymtTAdJJKzBXKJP6mNPga9cPOP31IZ36Ui00G3fjBBPrHa7nStludgL9Wi0dBU28DjU=AQAB""" }, + new object[] { false, """HISVaCE5w9iLXTVYTKP1t/yjjmPXvWovMYpgljGgpgz2Y=CiB9jgIS7+Wq+lpyzCGsBZQcQ2BxqQuEU9VCvb3Li5jMtjwRV1bMO+4Wfnb4VWhEtEUq6NdiVGXhC1xvtVLnnLDX7CD/jG6NvM1Yd0/rf0UUceBhzYLFE9HLsopsBmmm3t8FO6ZtRr1QqKM0XDaQleGK9vYd2m2Jq8OR3r/w4OY=vcM1wQVmLB9DwdnAym8l8nw63/HlTVzgTDhIwNzWPhsPE/qr2wlK4TEQ3rjU+RAdNytfFNCnuuh75ZVMjAWCV9h6VDlp0DOvBhb6GenhymtTAdJJKzBXKJP6mNPga9cPOP31IZ36Ui00G3fjBBPrHa7nStludgL9Wi0dBU28DjU=AQAB""" }, + }; + + [Theory] + [MemberData(nameof(EnvelopedSignatureWithEmptyReference))] + public void CheckSignatureHandlesEnvelopedSignatureWithEmptyReference(bool isValid, string xml) + { + XmlDocument xmlDoc = new (); + xmlDoc.LoadXml(xml); + SignedXml signedXml = new (xmlDoc); + signedXml.LoadXml(xmlDoc.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl)[0] as XmlElement); + + // without this, CheckSignature throws + ((Reference)signedXml.SignedInfo.References[0]).TransformChain[0].LoadInput(xmlDoc); + + Assert.Equal(isValid, signedXml.CheckSignature()); + } } }