将应用程序从3.5升级到4.6.2后面的代码块不再有效。我得到"格式错误的参考元素"错误,即使它作为3.5应用程序工作得很好。代码失败,上面的错误应该是一个很好的参考。我已经尝试过我能想到的一切都无法让ASP.Net版本正常工作。我已经构建了一个测试平台版本作为控制台应用程序,它可以正常工作,直到它到达最后一个失败的引用,并且#34;无法解析Uri Signature1.jpg。"我已经读过XMLSigner不接受除id,ID和Id以外的任何内容作为要匹配引用的元素,但是我不相信这是因为它适用于控制台应用程序。
问题的核心是:
signedXMl.AddReference(new Reference("#Head01"));
有问题的功能:
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>
答案 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。
经过修改的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文档,反之亦然。