XMLSigner不再适用于4.6.2 - 格式错误的引用元素

时间:2017-09-04 17:56:00

标签: c# asp.net xml cryptography signedxml

将应用程序从3.5升级到4.6.2后面的代码块不再有效。我得到"格式错误的参考元素"错误,即使它作为3.5应用程序工作得很好。代码失败,上面的错误应该是一个很好的参考。我已经尝试过我能想到的一切都无法让ASP.Net版本正常工作。我已经构建了一个测试平台版本作为控制台应用程序,它可以正常工作,直到它到达最后一个失败的引用,并且#34;无法解析Uri Signature1.jpg。"我已经读过XMLSigner不接受除id,ID和Id以外的任何内容作为要匹配引用的元素,但是我不相信这是因为它适用于控制台应用程序。

问题的核心是:

  1. 为什么我会收到"格式错误的参考元素" signedXMl.AddReference(new Reference("#Head01"));
  2. 如何修复对客户签名的引用" src =" Signature1.jpg""
  3. 有问题的功能:

    private XmlDocument SignDoc(XmlDocument doc, RSA key, X509Certificate x509cert, ArrayList alSignatures)
        {
            string signatureID = "TamperSealer01";
            Uri uri = new Uri(ConfigurationManager.AppSettings["SomeSetting"]); 
            XmlResolver resolver = new XmlSignatureResolver(uri);
            SignedXml signedXml = new SignedXml(doc);
            signedXml.Signature.Id = signatureID;
            signedXml.Resolver = resolver;
    
            // Add the key to the SignedXml responseDocument. 
            signedXml.SigningKey = key;
    
            // Create a new KeyInfo object.
            KeyInfo keyInfo = new KeyInfo();
            keyInfo.AddClause(new RSAKeyValue(key));
    
            KeyInfoX509Data x509Data = new KeyInfoX509Data(x509cert);
    
            string subjectName = x509cert.Subject;
            subjectName = subjectName.Replace("S=", "ST=");
            string tmpSubName = subjectName;
    
            tmpSubName = tmpSubName.Replace("O=A", "O=B");
            tmpSubName = tmpSubName.Replace("CN=A", "CN=B");
    
            x509Data.AddSubjectName(tmpSubName);
            x509Data.AddIssuerSerial(x509cert.Issuer, x509cert.GetSerialNumberString()); //GetIssuerName
            keyInfo.AddClause(x509Data);
            signedXml.KeyInfo = keyInfo;
    
            //TIMESTAMP
            XmlElement signaturePropertiesRoot = doc.CreateElement("SignatureProperties", "http://www.w3.org/2000/09/xmldsig#");
    
            DataObject signatureProperties = new DataObject();
            signatureProperties.Id = "TimeStamp";
            signatureProperties.Data = signaturePropertiesRoot.SelectNodes(".");
            signedXml.AddObject(signatureProperties);
    
            // and add a reference to the data object
            Reference propertiesRef = new Reference();
            propertiesRef.Uri = "#TimeStamp";
            propertiesRef.Type = "http://www.w3.org/2000/09/xmldsig#SignatureProperties";
            signedXml.AddReference(propertiesRef);
    
            XmlElement property = doc.CreateElement("SignatureProperty", "http://www.w3.org/2000/09/xmldsig#");
            property.SetAttribute("Id", "TamperSealer01TimeStamp");
            property.SetAttribute("Target", "#" + signedXml.Signature.Id);
            signaturePropertiesRoot.AppendChild(property);
    
            XmlElement timestamp = doc.CreateElement("DateTimeStamp", "http://www.w3.org/2000/09/xmldsig#");
            timestamp.SetAttribute("DateTime", String.Format("{0:s}Z", DateTime.Now.ToUniversalTime()));
            property.AppendChild(timestamp);
            signedXml.ComputeSignature();
    
            // References contains three strings "Head01", "Data01", and "View01"
            foreach (string docRef in references) 
            {
                // Create a reference to be signed.
                Reference tempRef = new Reference();
                tempRef.Uri = "#" + docRef;
                Logger.Current.LogInformation("DocRef: #" + docRef + ".");
                // Add the reference to the SignedXml object
                signedXml.AddReference(tempRef);
                signedXml.ComputeSignature(); //Immediately Fails here
            }
    
            // alSignatures only contains "Signature1.jpg" in this case. Don't yell at me for this crappy code, I didn't write it and plan on fixing it when everything else works.
            int ctr = 0;
            foreach (string str in alSignatures)
            {
                Reference testRef = new Reference();
                Uri relativeUri = new Uri(alSignatures[ctr].ToString(), UriKind.RelativeOrAbsolute);
                Logger.Current.LogInformation("Signature Reference: " + alSignatures[ctr].ToString());
                testRef.Uri = alSignatures[ctr].ToString();
                signedXml.AddReference(testRef);
                ctr += 1;
            }
    
            // Compute the signature.
            signedXml.ComputeSignature();
    
            // Get the XML representation of the signature and save it to an XmlElement object.
            XmlElement xmlDigitalSignature = signedXml.GetXml();
            XmlElement signaturesElement = doc.CreateElement("SIGNATURES", "http://www.mismo.org");
    
            signaturesElement.AppendChild(signedXml.GetXml());
            doc.DocumentElement.AppendChild(signaturesElement);
    
            key.Clear();
            key.Dispose();
            return doc;
    }
    

    它应签署的XML最低限度:

    <?xml version="1.0" encoding="UTF-8"?>
    <DOCUMENT MISMOVersionIdentifier="1.02">
        <HEADER _ID="Head01">
            <SIGNATURE_MODEL>
                <SIGNER AreaIDREF="Borrower1SignatureArea" SectionIDREF="BorrowerSignatures" SignatureIDREF="Borrower1SignatureLine" SignatureType="Image" TargetsIDREFS="View01" _RoleType="Borrower" _SignatureOrderNumber="1" />
                <SIGNER SignatureIDREF="TamperSealer01" SignatureType="DigitalSignature" TargetsIDREFS="Head01 Data01 View01" _RoleType="TamperSealer" _SignatureOrderNumber="1" />
            </SIGNATURE_MODEL>
        </HEADER>
        <DATA _ID="Data01">
            <MAIN>
            </MAIN>
        </DATA>
        <VIEW _ID="View01" _MIMETypeDescription="text/html" _TaggedIndicator="True">
            <html xmlns="http://www.w3.org/1999/xhtml">
                <body>
                    <span class="dataEntered" id="BORROWER_Signer-Info">
                        <SIGNATURE_SECTION _ID="BorrowerSignatures">
                            <SIGNATURE_AREA _ID="Borrower1SignatureArea">
                                <p class="right">
                                    <SIGNATURE_ABOVE_LINE />
                                    <SIGNATURE_IMAGE _EncodingTypeDescription="None" _ID="Borrower1SignatureLine" _MIMEType="image/jpeg">
                                        <img align="right" alt="Signature file is missing - Invalid Document" src="Signature1.jpg" />
                                    </SIGNATURE_IMAGE>
                                </p>
                                <p>04/12/2011 12:00 PM</p>
                            </SIGNATURE_AREA>
                        </SIGNATURE_SECTION>
                    </span>
                </body>
            </html>
        </VIEW>
    </DOCUMENT>
    

2 个答案:

答案 0 :(得分:2)

相关的代码段 - CalculateHashValue 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"));
   }
}

所以你在参考验证上失败了 - 文档中没有带有这个id的元素 - 顺便说一句。在xml中通过实验将“_ID”更改为“Id”解决了这个问题。

好消息是SignedXml类是可扩展的,您可以重载XmlElement GetIdElement(XmlDocument document, string idValue)方法以将“_ID”带入帐户。

// just a sample
class 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);
   }
}

答案 1 :(得分:0)

有了Ondrej Svejdar的提示,我才能够使它工作。事实证明,我需要两节课才能工作。我无法在UAT中进行测试,但到目前为止,我需要两个类和一个注册表编辑器。一个自定义XmlUrlResolver允许DTD放在单独的位置,并指向与XML相同的文件夹作为外部引用,并带有一个经过修改的SignedXml类来处理ID。

注册表编辑: https://support.microsoft.com/en-us/help/3148821/after-you-apply-security-update-3141780-net-framework-applications-enc

经过修改的SignedXml类:

    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;
    }
}

修改后的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);
    }
}

通过这两个修改,我的.Net 4.6.2代码能够验证来自.Net 3.5的Signed XML文档,反之亦然。