Приветствую.
если в xml-е несколько узлов подписано несколькими подписями (всмысле не один Signature + много Referencе'ов, а много Signature). То наблюдается крайне странная картина: если узел Signature является дочерним узлом по отношению к подписываемому, то валидируется только первая подпись — остальные якобы невалидны. Если же узел Signature вынести за пределы подписываемого узла, то валидируются все подписи.
Воспрос — это баг или фича? Куда копать.
простенький пример.
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Xml;
namespace MultiSign
{
class Program
{
private static readonly RSACryptoServiceProvider KEY = new RSACryptoServiceProvider();
static void Main(string[] args)
{
var doc = MakeDocument();
SignNodes(doc, "//test");
// doc.Save("test_signed.xml");
Console.WriteLine("Verifing document:");
VerifyDocument(doc);
Console.WriteLine("Verifing nodes:");
VerifyNodesAsSingleDocument(doc, "//test");
Console.ReadLine();
}
private static XmlDocument MakeDocument()
{
var doc = new XmlDocument() { PreserveWhitespace = true };
doc.LoadXml("<root>"
+ " <sub>"
+ " <test id='1'>"
+ " <data>data1</data>"
+ " </test>"
+ " </sub>"
+ " <sub>"
+ " <test id='2'>"
+ " <data>data2</data>"
+ " </test>"
+ " </sub>"
+ " <sub>"
+ " <test id='3'>"
+ " <data>data3</data>"
+ " </test>"
+ " </sub>"
+ "</root>");
return doc;
}
private static void SignNodes(XmlDocument doc, string xpath)
{
var nodes = doc.SelectNodes(xpath);
foreach (XmlNode node in nodes)
{
SignNode(node);
}
}
private static void SignNode(XmlNode node)
{
var doc = node.OwnerDocument;
var signedXml = new SignedXml(doc) { SigningKey = KEY };
var reference = new Reference { Uri = "#" + node.Attributes["id"].Value };
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
signedXml.AddReference(reference);
signedXml.ComputeSignature();
node.AppendChild(doc.ImportNode(signedXml.GetXml(), true)); // add signature as child of signed node. Will fail on second+ signatures
//doc.DocumentElement.AppendChild(doc.ImportNode(signedXml.GetXml(), true)); // add signature somewhere else. All works good.
}
private static void VerifyDocument(XmlDocument doc)
{
var signs = doc.GetElementsByTagName("Signature");
foreach (XmlElement sign in signs)
{
var signedXml = new SignedXml(doc);
signedXml.LoadXml(sign);
Console.WriteLine("verified: '{0}'", signedXml.CheckSignature(KEY));
}
}
private static void VerifyNodesAsSingleDocument(XmlDocument doc, string xpath)
{
var nodes = doc.SelectNodes(xpath);
foreach (XmlNode node in nodes)
{
var tempDoc = new XmlDocument();
tempDoc.AppendChild(tempDoc.ImportNode(node, true));
VerifyDocument(tempDoc);
}
}
}
}