After Upgrading an application from 3.5 to 4.6.2 The following block of code no longer works. I get "Malformed reference element" Errors, even though it worked just fine as a 3.5 application. The code fails with the above error on what should be a good reference. I've tried everything I can think of can cannot get the ASP.Net version working. I've built a testbed version as a console application which works fine until it gets to the last reference which fails with "Unable to resolve Uri Signature1.jpg." I've read that XMLSigner doesn't accept anything other than id, ID, and Id as elements to look for to match references, however I don't believe this to be the case because it works in the console application.
The core of the question is:
- Why am I getting a "Malformed reference element" for
signedXMl.AddReference(new Reference("#Head01"));
- How do I fix the reference to the customer Signatures "src="Signature1.jpg""
Function in question:
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;
}
The XML it should be signing is minimally thus:
<?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>
The relevant piece of code -
CalculateHashValue
ofSystem.Security.Cryptography.Xml.Reference
: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 theXmlElement GetIdElement(XmlDocument document, string idValue)
method to take "_ID" into an account.