XMLSigner No longer works in 4.6.2 - Malformed reference element XMLSigner No longer works in 4.6.2 - Malformed reference element xml xml

XMLSigner No longer works in 4.6.2 - Malformed reference element


The relevant piece of code - CalculateHashValue of System.Security.Cryptography.Xml.Reference:

// for "Head01" this is "#Head01"if (this.m_uri[0] == '#'){   // idFromLocalUri is set to "Head01"   string idFromLocalUri = Utils.GetIdFromLocalUri(this.m_uri, out flag);   ...   // there is no element with Id="Head01" - so xmlElement is null   var xmlElement = this.SignedXml.GetIdElement(document, idFromLocalUri);   ...   if (xmlElement == null)   {     // this is the error you're getting     throw new CryptographicException(SecurityResources.GetResourceString("Cryptography_Xml_InvalidReference"));   }}

So you fail on reference validation - there is no element with this id in document - btw. experimentally changing "_ID" to "Id" in your xml fixed the issue.

Good news is the SignedXml class is extensible and you can overload the XmlElement GetIdElement(XmlDocument document, string idValue) method to take "_ID" into an account.

// just a sampleclass MyCustomSignedXml : SignedXml {   ...   override XmlElement GetIdElement(XmlDocument document, string idValue) {     var element = document.SelectSingleNode($"//*[@_ID='{idValue}']") as XmlElement;     if (element != null) {       return element;     }      return base.GetIdElement(document, idValue);   }}


With Ondrej Svejdar's hints I was able to get this working. It turns out I needed two classes for this to work. I haven't been able to test in UAT, but so far I needed two classes, and a registry edit. A Custom XmlUrlResolver to allow the DTDs to be in a separate location and point to the same folder as the XML for the external references, and a modified SignedXml class to handle IDs.

Registry edit: https://support.microsoft.com/en-us/help/3148821/after-you-apply-security-update-3141780-net-framework-applications-enc

Modified SignedXml class:

    public class CustomIdSignedXml : SignedXml{    private static readonly string[] idAttrs = new string[]    {    "_id",    "_Id",    "_ID"    };    public CustomIdSignedXml(XmlDocument doc) : base(doc)    {        return;    }    public override XmlElement GetIdElement(XmlDocument doc, string id)    {        XmlElement idElem = null;        // check to see if it's a standard ID reference        //XmlElement idElem = base.GetIdElement(doc, id);        //if (idElem != null)        //  return idElem;        //I get the feeling this is horridly insecure        XmlElement elementById1 = doc.GetElementById(id);        if (elementById1 != null) return elementById1;        // if not, search for custom ids        foreach (string idAttr in idAttrs)        {            idElem = doc.SelectSingleNode("//*[@" + idAttr + "=\"" + id + "\"]") as XmlElement;            if (idElem != null)                break;        }        return idElem;    }}

Modified XmlResolver:

public class DTDAndSignatureResolver : XmlUrlResolver{    private readonly Uri DTDUri;    private readonly List<string> XmlExtensions = new List<string>() { ".xml" };    private readonly List<string> DTDExtensions = new List<string>() { ".dtd", ".ent" };    private ICredentials credentials;    public DTDAndSignatureResolver(Uri DTDUri)    {        this.DTDUri = DTDUri;    }    public override ICredentials Credentials    {        set { credentials = value; }    }    public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)    {        if (DTDExtensions.Any(e => absoluteUri.ToString().ToLower().EndsWith(e)) || XmlExtensions.Any(e => absoluteUri.ToString().ToLower().EndsWith(e)))        {            return base.GetEntity(absoluteUri, role, ofObjectToReturn); //For DTD/ENT/XML lookup        }        else        {            return base.GetEntity(DTDUri, null, ofObjectToReturn); //For signature image lookup        }    }    public override Uri ResolveUri(Uri uri, string relativeUri)    {        return base.ResolveUri(DTDUri, relativeUri);    }}

With both of these modifications my .Net 4.6.2 code was able to verify the Signed XML documents from .Net 3.5, and vice versa.