Malformed Reference Element

2019-01-20 11:37发布

问题:

I am attempting to add References to my Security Header and am running into a fairly generic error:

Malformed Reference Element

I have tried the following with similar results:

  1. Referencing the element within the document by passing in the ID of the element as the URI of the Reference object.
  2. Passing an XmlElement object to the Reference through the LoadXml() method. I am retrieving the XmlElement reference by using the overloaded GetIdElement found on this StackOverflow post.

When I pass in an empty string as the URI, the ComputeSignature() method of the SignedXml works as expected. However, I need to add up to 3 references to the Security Header.

UPDATE #1
Thanks to this blog post, I was able to create the simplified version from that and I believe what is causing my problem is the use of the Namespace attributes and prefixes.

UPDATE #2
It appears as though the Namespace declaration on the Id attribute of the <Timestamp> element is causing this error to occur.

UPDATE #3
I think I got this working. See my answer post below.

Working Sample:
Please note the Id XAttribute with the Namespace defined does not work; while the Id XAttribute without the Namespace defined does work.

private void CreateSecurityAndTimestampXML(string fileName)
{
    TimestampID = "TS-E" + GUID.NewGuid();
    DateTime SecurityTimestampUTC = DateTime.UtcNow;

    XDocument xdoc = new XDocument(
        new XElement(wsse + "Security",
            new XAttribute(XNamespace.Xmlns + "wsse", wsse.NamespaceName),
            new XAttribute(XNamespace.Xmlns + "wsu", wsu.NamespaceName),
            new XElement(wsu + "Timestamp",
                // new XAttribute(wsu + "Id", TimestampID), // <-- Does Not Work
                new XAttribute("Id", TimestampID), // <-- Works
                new XElement(wsu + "Created", SecurityTimestampUTC.ToString(_timestampFormat)),
                new XElement(wsu + "Expires", SecurityTimestampUTC.AddMinutes(10).ToString(_timestampFormat))
            )
        )
    );

    using (XmlTextWriter tw = new XmlTextWriter(fileName, new UTF8Encoding(false)))
    {
        xdoc.WriteTo(tw);
    }
}

// Snippet
string[] elements = { TimestampID };

foreach (string s in elements)
{
    Reference reference = new Reference()
    {
        Uri = "#" + s
    };

    XmlDsigExcC14NTransform env = new XmlDsigExcC14NTransform();
    env.InclusiveNamespacesPrefixList = _includedPrefixList;
    reference.AddTransform(env);

    xSigned.AddReference(reference);
}

// Add Key Info Here.

// Compute the Signature.
xSigned.ComputeSignature();

回答1:

It looks like, at least for the time being, the answer to getting this working is as follows.

Instead of using the SignedXml class, create a new SignedXmlWithId object, using the class detailed here.

// Create a new XML Document.
XmlDocument xdoc = new XmlDocument();

xdoc.PreserveWhitespace = true;

// Load the passed XML File using its name.
xdoc.Load(new XmlTextReader(fileName));

// Create a SignedXml Object.
//SignedXml xSigned = new SignedXml(xdoc);
SignedXmlWithId xSigned = new SignedXmlWithId(xdoc); // Use this class instead of the SignedXml class above.

// Add the key to the SignedXml document.
xSigned.SigningKey = cert.PrivateKey;
xSigned.Signature.Id = SignatureID;
xSigned.SignedInfo.CanonicalizationMethod = 
        SignedXml.XmlDsigExcC14NWithCommentsTransformUrl;

//Initialize a variable to contain the ID of the Timestamp element.
string elementId = TimestampID;

Reference reference = new Reference()
{
    Uri = "#" + elementId
};

XmlDsigExcC14NTransform env = new XmlDsigExcC14NTransform();
env.InclusiveNamespacesPrefixList = _includedPrefixList;
reference.AddTransform(env);

xSigned.AddReference(reference);

// Add Key Information (omitted)

// Compute Signature
xSigned.ComputeSignature();