I have a response from a 3-rd party web service. I load an XmlDocument with that response.
string txt = readStream.ReadToEnd();
response = new XmlDocument();
response.PreserveWhitespace = true;
response.LoadXml(txt);
return response;
Now I would like to verify that the respones is signed using the certificate. I have a VerifyXmlDoc(XmlDocument xmlDoc)
method which I have found on msdn.
I know that the message is correct.
public bool VerifyXmlDoc(XmlDocument xmlDoc)
{
SignedXml signed = new SignedXml(xmlDoc);
XmlNodeList signatureNodeList = xmlDoc.GetElementsByTagName("Signature");
signed.LoadXml((XmlElement)signatureNodeList[0]);
X509Certificate2 serviceCertificate = null;
foreach (KeyInfoClause clause in signed.KeyInfo)
{
if (clause is KeyInfoX509Data)
{
if (((KeyInfoX509Data)clause).Certificates.Count > 0)
{
serviceCertificate = (X509Certificate2)((KeyInfoX509Data)clause).Certificates[0];
}
}
}
bool result = signed.CheckSignature(serviceCertificate, true);
return result;
}
If I set target framework of my project to .NET 3.5 or .NET 3, or .NET 2 it works great. Result is true. But if I change target framework to .NET 4 result is false. (And I have to use .NET 4)
Any ideas on how to solve this problem?
This is a known issue. The Canonicalization implementation between .NET 3.5 and .NET 4.0 has changed.
I don't know if this works on all XML signatures but the following works from the testing that I've done.
Add the following C14N Transform class to your project:
public class MyXmlDsigC14NTransform: XmlDsigC14NTransform {
static XmlDocument _document;
public static XmlDocument document {
set {
_document = value;
}
}
public MyXmlDsigC14NTransform() {}
public override Object GetOutput() {
return base.GetOutput();
}
public override void LoadInnerXml(XmlNodeList nodeList) {
base.LoadInnerXml(nodeList);
}
protected override XmlNodeList GetInnerXml() {
XmlNodeList nodeList = base.GetInnerXml();
return nodeList;
}
public XmlElement GetXml() {
return base.GetXml();
}
public override void LoadInput(Object obj) {
int n;
bool fDefaultNS = true;
XmlElement element = ((XmlDocument) obj).DocumentElement;
if (element.Name.Contains("SignedInfo")) {
XmlNodeList DigestValue = element.GetElementsByTagName("DigestValue", element.NamespaceURI);
string strHash = DigestValue[0].InnerText;
XmlNodeList nodeList = _document.GetElementsByTagName(element.Name);
for (n = 0; n < nodeList.Count; n++) {
XmlNodeList DigestValue2 = ((XmlElement) nodeList[n]).GetElementsByTagName("DigestValue", ((XmlElement) nodeList[n]).NamespaceURI);
string strHash2 = DigestValue2[0].InnerText;
if (strHash == strHash2) break;
}
XmlNode node = nodeList[n];
while (node.ParentNode != null) {
XmlAttributeCollection attrColl = node.ParentNode.Attributes;
if (attrColl != null) {
for (n = 0; n < attrColl.Count; n++) {
XmlAttribute attr = attrColl[n];
if (attr.Prefix == "xmlns") {
element.SetAttribute(attr.Name, attr.Value);
} else if (attr.Name == "xmlns") {
if (fDefaultNS) {
element.SetAttribute(attr.Name, attr.Value);
fDefaultNS = false;
}
}
}
}
node = node.ParentNode;
}
}
base.LoadInput(obj);
}
}
Register the class using the CryptoConfig.AddAlgorithm method.
CryptoConfig.AddAlgorithm(typeof(MyXmlDsigC14NTransform), "http://www.w3.org/TR/2001/REC-xml-c14n-20010315");
var message = new XmlDocument();
message.PreserveWhitespace = true;
message.Load("XmlSig.xml");
MyXmlDsigC14NTransform.document = message; // The transform class needs the xml document
// Validate signature as normal.
That should do it.