Unable to sign element by Id attribute when there's a namespace prefix:
void Main()
{
var doc = new XmlDocument();
doc.LoadXml("<root xmlns:u=\"myuri\"><test u:Id=\"_0\">Zebra</test></root>");
SignedXml signedXml = new SignedXml(doc);
signedXml.SigningKey = new RSACryptoServiceProvider();
Reference reference = new Reference("#_0");
signedXml.AddReference(reference);
signedXml.ComputeSignature();
}
ComputeSignature()
will fail here with 'Malformed Reference Element' how should this be done?
The approach we used was to subclass System.Security.Cryptography.Xml.SignedXml
class...
public class SignedXmlWithId : SignedXml
{
public SignedXmlWithId(XmlDocument xml) : base(xml)
{
}
public SignedXmlWithId(XmlElement xmlElement)
: base(xmlElement)
{
}
public override XmlElement GetIdElement(XmlDocument doc, string id)
{
// check to see if it's a standard ID reference
XmlElement idElem = base.GetIdElement(doc, id);
if (idElem == null)
{
XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
nsManager.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
idElem = doc.SelectSingleNode("//*[@wsu:Id=\"" + id + "\"]", nsManager) as XmlElement;
}
return idElem;
}
}
var reference = new Reference(""); // This will sign the entire document
It should be noted that you will need to use SignedXmlWithId
object instead of SignedXml
object in order to be able to use the overridden GetIdElement()
method. Once I did that, I was able to sign an XmlElement
and get around the Malformed Reference Element error.
See my post about this topic here.
SignedXml does not recognize u:Id as a valid XML ID, and the XML Signature does require it to be an XML ID.
You can either use the Schema (http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd if your trying to use a WS-Security Id) or add an DTD to the XML fragment. ( ]> for an XML fragment). Adding a DTD to just your LoadXml will make SignedXml recognize the Id, but since SOAP does not allow DTD's, don't include the DTD in your on-the-wire SOAP.