Is there a way to sign an XML file with RSA and to have the namespace prefix "ds:Signature" instead of "Signature"? I spent many hourstrying to solve this and from what I can see there is no solution.
It seems that it is hard-coded in the class System.Security.Cryptography.Xml.Signature.
XmlElement element = document.CreateElement("Signature", "http://www.w3.org/2000/09/xmldsig#");
If anyone knows a solution, I need to sign it like that cause the software importing it verifies it with "ds:signature", so with "ds" prefix the software verifies it like this:
public static bool VerifySignature(XmlDocument doc, RSA key, string prefix)
{
SignedXml xml = new SignedXml(doc);
string str = "Signature";
if (!string.IsNullOrEmpty(prefix))
{
str = string.Format("{0}:{1}", prefix, str);
}
XmlNodeList elementsByTagName = doc.GetElementsByTagName(str);
xml.LoadXml((XmlElement)elementsByTagName[0]);
return xml.CheckSignature(key);
}
VerifySignature(xmlDoc, rsa, "ds");
normally it signs like this:
<kk>blabla<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /><DigestValue>rVL2nKjPTBhL9IDHYpu69OiE8gI=</DigestValue></Reference></SignedInfo><SignatureValue>CfXW9D/ErmHjzxIjy0/54/V3nst6j/XXcu7keR17LApfOZEpxjEvAlG3VnBZIi3jxQzU6t9RkmfDyngcRZccJByuuA6YDwFTQxZNRgu2GRoZxMKWnkm+MtQ0jH0Fo78GivCxV+iIewZvsrUQLzG01cXuZSH/k2eeMUaEooJaLQiYpO2aNVn5xbosTPtGlsACzFWz34E69/ZeeLZbXLc3jpDO+opxdYJ5e+Tnk/UM2Klt+N+m7Gh/sUNTPgkDiwP3q3y3O9tvCT0G2XmQaWBP4rw9TIoYHQtucm2b8R2JeggbeRKOetbRYV218RT8CK2Yuy0FIUlQXdabKyp9F96Yc55g8eNe10FGtgietH2iqquIVFLCA8fu3SZNLDPMoyHnVNKdBvI35+S8hrAaybEkMvo7iYnUSY5KrlGSfGGtfQXdaISutAzcnGPDFXgZXPNzNy7eL0u+Lt3yWWkj7wh6Zeh4fH2+nXDWYCWbLpegAEX4ZWSI5Ts6D1TplMJTGH1F0GyflehH4u+W4Lc3TvkB4dWjEuiKgnpl3hcvoj2CWFaeAxXMd/64tU/YMm8+1gSBjkVH6oV+QlI/m0z6M8FPVEVC2as0wLG2woVwmzVLcaQKyPi7NN4eO9ea7QNfaRHaofU4LQO/Y3FNJOP+uMfYlGJKWSr3qv29+BQjeNldNJY=</SignatureValue></Signature></kk>
and I need it to do it like this:
<kk>blabla<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /><ds:DigestValue>rVL2nKjPTBhL9IDHYpu69OiE8gI=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>CfXW9D/ErmHjzxIjy0/54/V3nst6j/XXcu7keR17LApfOZEpxjEvAlG3VnBZIi3jxQzU6t9RkmfDyngcRZccJByuuA6YDwFTQxZNRgu2GRoZxMKWnkm+MtQ0jH0Fo78GivCxV+iIewZvsrUQLzG01cXuZSH/k2eeMUaEooJaLQiYpO2aNVn5xbosTPtGlsACzFWz34E69/ZeeLZbXLc3jpDO+opxdYJ5e+Tnk/UM2Klt+N+m7Gh/sUNTPgkDiwP3q3y3O9tvCT0G2XmQaWBP4rw9TIoYHQtucm2b8R2JeggbeRKOetbRYV218RT8CK2Yuy0FIUlQXdabKyp9F96Yc55g8eNe10FGtgietH2iqquIVFLCA8fu3SZNLDPMoyHnVNKdBvI35+S8hrAaybEkMvo7iYnUSY5KrlGSfGGtfQXdaISutAzcnGPDFXgZXPNzNy7eL0u+Lt3yWWkj7wh6Zeh4fH2+nXDWYCWbLpegAEX4ZWSI5Ts6D1TplMJTGH1F0GyflehH4u+W4Lc3TvkB4dWjEuiKgnpl3hcvoj2CWFaeAxXMd/64tU/YMm8+1gSBjkVH6oV+QlI/m0z6M8FPVEVC2as0wLG2woVwmzVLcaQKyPi7NN4eO9ea7QNfaRHaofU4LQO/Y3FNJOP+uMfYlGJKWSr3qv29+BQjeNldNJY=</ds:SignatureValue></ds:Signature></kk>
if anyone know a solution, i need to sign it like that cause the software importing it verifies it with "ds:signature" , so with "ds" prefix
The prefix should be unimportant - all that should matter is what namespace the element is in. It shouldn't matter how that namespace is expressed. If it does, that shows brokenness in the verifying code, I'd say.
However, if you really want to do this, is there any reason you don't want to just replace the element with one with the same contents, but using the prefix you want? It shouldn't be hard to do that in LINQ to XML.
i found the solution here
using System;
using System.Reflection;
using System.Security.Cryptography.Xml;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Text;
using System.Xml;
namespace mysign
{
public class PrefixedSignedXML : SignedXml
{
public PrefixedSignedXML(XmlDocument document)
: base(document)
{ }
public PrefixedSignedXML(XmlElement element)
: base(element)
{ }
public PrefixedSignedXML()
: base()
{ }
public void ComputeSignature(string prefix)
{
this.BuildDigestedReferences();
AsymmetricAlgorithm signingKey = this.SigningKey;
if (signingKey == null)
{
throw new CryptographicException("Cryptography_Xml_LoadKeyFailed");
}
if (this.SignedInfo.SignatureMethod == null)
{
if (!(signingKey is DSA))
{
if (!(signingKey is RSA))
{
throw new CryptographicException("Cryptography_Xml_CreatedKeyFailed");
}
if (this.SignedInfo.SignatureMethod == null)
{
this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
}
}
else
{
this.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
}
}
SignatureDescription description = CryptoConfig.CreateFromName(this.SignedInfo.SignatureMethod) as SignatureDescription;
if (description == null)
{
throw new CryptographicException("Cryptography_Xml_SignatureDescriptionNotCreated");
}
HashAlgorithm hash = description.CreateDigest();
if (hash == null)
{
throw new CryptographicException("Cryptography_Xml_CreateHashAlgorithmFailed");
}
this.GetC14NDigest(hash, prefix);
this.m_signature.SignatureValue = description.CreateFormatter(signingKey).CreateSignature(hash);
}
public XmlElement GetXml(string prefix)
{
XmlElement e = this.GetXml();
SetPrefix(prefix, e);
return e;
}
//Invocar por reflexión al método privado SignedXml.BuildDigestedReferences
private void BuildDigestedReferences()
{
Type t = typeof(SignedXml);
MethodInfo m = t.GetMethod("BuildDigestedReferences", BindingFlags.NonPublic | BindingFlags.Instance);
m.Invoke(this, new object[] { });
}
private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
//string securityUrl = (this.m_containingDocument == null) ? null : this.m_containingDocument.BaseURI;
//XmlResolver xmlResolver = new XmlSecureResolver(new XmlUrlResolver(), securityUrl);
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = true;
XmlElement e = this.SignedInfo.GetXml();
document.AppendChild(document.ImportNode(e, true));
//CanonicalXmlNodeList namespaces = (this.m_context == null) ? null : Utils.GetPropagatedAttributes(this.m_context);
//Utils.AddNamespaces(document.DocumentElement, namespaces);
Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;
//canonicalizationMethodObject.Resolver = xmlResolver;
//canonicalizationMethodObject.BaseURI = securityUrl;
SetPrefix(prefix, document.DocumentElement); //establecemos el prefijo antes de se que calcule el hash (o de lo contrario la firma no será válida)
canonicalizationMethodObject.LoadInput(document);
return canonicalizationMethodObject.GetDigestedOutput(hash);
}
private void SetPrefix(string prefix, XmlNode node)
{
foreach (XmlNode n in node.ChildNodes)
SetPrefix(prefix, n);
node.Prefix = prefix;
}
}
}
I tried these solutions, but they didn't worked out. However, by looking at .NET source code (http://referencesource.microsoft.com/) we can see that this can be acomplished easily by providing a derived XmlDocument class to SignedXml, where namespace can be added. However, having the "ds" prefix within 'SignedInfo' and descendants will cause siganture to fail. Here is the best I could do without breaking the signature:
XmlDsigDocument.cs
using System;
using System.Collections.Generic;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Xml;
namespace CustomSecurity
{
class XmlDsigDocument : XmlDocument
{
// Constants
public const string XmlDsigNamespacePrefix = "ds";
/// <summary>
/// Override CreateElement function as it is extensively used by SignedXml
/// </summary>
/// <param name="prefix"></param>
/// <param name="localName"></param>
/// <param name="namespaceURI"></param>
/// <returns></returns>
public override XmlElement CreateElement(string prefix, string localName, string namespaceURI)
{
// CAntonio. If this is a Digital signature security element, add the prefix.
if (string.IsNullOrEmpty(prefix))
{
// !!! Note: If you comment this line, you'll get a valid signed file! (but without ds prefix)
// !!! Note: If you uncomment this line, you'll get an invalid signed file! (with ds prefix within 'Signature' object)
//prefix = GetPrefix(namespaceURI);
// The only way to get a valid signed file is to prevent 'Prefix' on 'SignedInfo' and descendants.
List<string> SignedInfoAndDescendants = new List<string>();
SignedInfoAndDescendants.Add("SignedInfo");
SignedInfoAndDescendants.Add("CanonicalizationMethod");
SignedInfoAndDescendants.Add("InclusiveNamespaces");
SignedInfoAndDescendants.Add("SignatureMethod");
SignedInfoAndDescendants.Add("Reference");
SignedInfoAndDescendants.Add("Transforms");
SignedInfoAndDescendants.Add("Transform");
SignedInfoAndDescendants.Add("InclusiveNamespaces");
SignedInfoAndDescendants.Add("DigestMethod");
SignedInfoAndDescendants.Add("DigestValue");
if (!SignedInfoAndDescendants.Contains(localName))
{
prefix = GetPrefix(namespaceURI);
}
}
return base.CreateElement(prefix, localName, namespaceURI);
}
/// <summary>
/// Select the standar prefix for the namespaceURI provided
/// </summary>
/// <param name="namespaceURI"></param>
/// <returns></returns>
public static string GetPrefix(string namespaceURI)
{
if (namespaceURI == "http://www.w3.org/2001/10/xml-exc-c14n#")
return "ec";
else if (namespaceURI == SignedXml.XmlDsigNamespaceUrl)
return "ds";
return string.Empty;
}
}
}
This is used on the SignedXml Creation:
// Create a new XML document.
XmlDsigDocument doc = new XmlDsigDocument();
// Load the passed XML file using its name.
doc.Load(new XmlTextReader(FileName));
// Create a SignedXml object.
SignedXml signedXml = new SignedXml(doc);
You can see full source files at:
https://social.msdn.microsoft.com/Forums/en-US/cd595379-f66a-49c8-8ca2-62acdc58b252/add-prefixds-signedxml?forum=xmlandnetfx
May be correctly SetPrefix code looks like this:
private void SetPrefix(String prefix, XmlNode node) {
foreach (XmlNode n in node.ChildNodes)
{
SetPrefix(prefix, n);
n.Prefix = prefix;
}
}
I agree that Prefix should not be important, but...
XML becomes much easier in C# if you use XPath:
var s = signedXml.GetXml();
XmlNodeList nodes = s.SelectNodes("descendant-or-self::*");
foreach (XmlNode childNode in nodes)
{
childNode.Prefix = "dsig";
}
The code George Dima provide works.
I will explain how it works.
When you call the ComputeSignature method this will generate the Signature Value by digesting the value of the SignedInfo node.
The code provided by George Dima adds the Prefix to the SignedInfo node and its children BEFORE getting the digest value. This won't add the prefix to the whole xml structure
this is the method that generates the digest value of the signedinfo node
private byte[] GetC14NDigest(HashAlgorithm hash, string prefix)
{
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = true;
XmlElement e = this.SignedInfo.GetXml();
document.AppendChild(document.ImportNode(e, true));
Transform canonicalizationMethodObject = this.SignedInfo.CanonicalizationMethodObject;
SetPrefix(prefix, document.DocumentElement); //HERE'S WHERE THE PREFIX IS ADDED TO GET THE DIGEST VALUE
canonicalizationMethodObject.LoadInput(document);
return canonicalizationMethodObject.GetDigestedOutput(hash);
}
So you now have the digest value of the SignedInfo node WITH the prefix, and this value will be use to get the Signature Value, but you still DON'T have the xml with the prefix yet, so if you just do this
signedXml.GetXml();
you will get the xml without the prefix and of course because the signature value was calculated considering the ds prefix you will have an invalid signature so what you have to do is call the GetXml passing it the value of the prefix, in this case "ds" like this
signedXml.GetXml("ds");
Related
I have tried to setup a Dialogue tree within unity using XML (I have not used XML much before so am unsure if the way i am going is correct at all)
So I am trying to get the first text element from this dialogue tree but when i call the XML file and say where it is i am getting the everything stored in that branch.
Am i using the correct .XML to be able to do this also as i seen people say use .XML.LINQ or .XML.Serialization not just .XML is this correct for my case ??
Code:
using UnityEngine;
using System.Collections;
using System.IO;
using System.Xml;
using UnityEngine.UI;
using System.Collections.Generic;
public class DialogTree
{
public string text;
public List<string> dialogText;
public List<DialogTree> nodes;
public void parseXML(string xmlData)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(new StringReader(xmlData));
XmlNode node = xmlDoc.SelectSingleNode("dialoguetree/dialoguebranch");
text = node.InnerXml;
XmlNodeList myNodeList = xmlDoc.SelectNodes("dialoguebranch/dialoguebranch");
foreach (XmlNode node1 in myNodeList)
{
if (node1.InnerXml.Length > 0)
{
DialogTree dialogtreenode = new DialogTree();
dialogtreenode.parseXML(node1.InnerXml);
nodes.Add(dialogtreenode);
}
}
}
}
And here is a picture of the XML.
So i am trying to grab the first element of text then late on there response it will go to branch 1 or 2
<?xml version='1.0'?>
<dialoguetree>
<dialoguebranch>
<text>Testing if the test prints</text>
<dialoguebranch>
<text>Branch 1</text>
<dialoguebranch>
<text>Branch 1a</text>
</dialoguebranch>
<dialoguebranch>
<text>Branch 1b</text>
</dialoguebranch>
</dialoguebranch>
<dialoguebranch>
<text>Branch 2</text>
</dialoguebranch>
</dialoguebranch>
</dialoguetree>
You're getting everything in that branch because XmlNode.InnerXML returns everything in that node. See the documentation for more information on that.
You should use the branch as the base for only looking at its children, instead of starting at xmlDoc every time. Also, you need an entry point to get inside of the first dialoguetree element and then ignore that. Finally, I would only create one XmlDocument and just pass around nodes in your recursion.
Altogether, this might look like this:
public class DialogTree
{
public string text;
public List<DialogTree> nodes = new List<DialogTree>();
public static DialogTree ParseXMLStart(string xmlData)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(new Stringreader(xmlData));
XmlNode rootNode = xmlDoc.SelectSingleNode("dialoguetree/dialoguebranch");
DialogTree dialogTree = new DialogTree();
dialogTree.ParseXML(rootNode);
return dialogTree;
}
public void ParseXML(XmlNode parentNode)
{
XmlNode textNode = parentNode.SelectSingleNode("text");
text = textNode.InnerText;
XmlNodeList myNodeList = parentNode.SelectNodes("dialoguebranch");
foreach (XmlNode curNode in myNodeList)
{
if (curNode.InnerXml.Length > 0)
{
DialogTree dialogTree = new DialogTree();
dialogTree.ParseXML(curNode);
nodes.Add(dialogTree);
}
}
}
}
And you could use it like so:
string xmlStringFromFile;
DialogTree dialogue = DialogTree.ParseXMLStart(xmlStringFromFile);
All of this code is untested but I hope the general idea is clear. Let me know if you find any errors in the comments below and I will try to fix them.
I have revised the question and included the code I have write so far for this question. Below is an example of what the output must look like to be compatible with the VeriFone MX915 Payment terminal. In this specific part, I am trying to send the POS register items to the display.
<TRANSACTION>
<FUNCTION_TYPE>LINE_ITEM</FUNCTION_TYPE>
<COMMAND>ADD</COMMAND>
<COUNTER>1</COUNTER>
<MAC> … </MAC>
<MAC_LABEL>REG2</MAC_LABEL>
<RUNNING_SUB_TOTAL>7.00</RUNNING_SUB_TOTAL>
<RUNNING_TRANS_AMOUNT>7.42</RUNNING_TRANS_AMOUNT>
<RUNNING_TAX_AMOUNT>0.42</RUNNING_TAX_AMOUNT>
<LINE_ITEMS>
<MERCHANDISE>
<UNIT_PRICE>5.00</UNIT_PRICE>
<DESCRIPTION>#1 Combo Meal</DESCRIPTION>
<LINE_ITEM_ID>1695155651</LINE_ITEM_ID>
<EXTENDED_PRICE>5.00</EXTENDED_PRICE>
<QUANTITY>1</QUANTITY>
</MERCHANDISE>
</LINE_ITEMS>
</TRANSACTION>
The SDK supplied by VeriFone has made some of the methods needed to communicate with the device. So the following code has method calls and class level variables that are written but not included in the following example:
/// <summary>
/// Send 1 or more items to the Verifone display. Include subtotal, tax and total
/// </summary>
/// <param name="nSubTotal">Subtotal of transaction</param>
/// <param name="nTax">Current Tax of transaction</param>
/// <param name="nTotal">Total of transaction</param>
/// <param name="UPC">Item Code</param>
/// <param name="ShortDescription">Small description</param>
/// <param name="nItemAmount">Item Amt</param>
/// <param name="nQty">Quantity</param>
/// <param name="nExtendedAmount">Quantity X Item Amt</param>
/// <returns></returns>
public bool AddLineItem(double nSubTotal, double nTax, double nTotal, Int32 nItemID, string UPC, string ShortDescription, double nItemAmount, Int32 nQty, double nExtendedAmount)
{
// get counter and calculate Mac
var nextCounter = (++counter).ToString();
var mac = PrintMacAsBase64(macKey, nextCounter);
// build request
var request = new XDocument();
using (var writer = request.CreateWriter())
{
//populate the elements
writer.WriteStartDocument();
writer.WriteStartElement("TRANSACTION");
writer.WriteElementString("FUNCTION_TYPE", "LINE_ITEM");
writer.WriteElementString("COMMAND", "ADD");
writer.WriteElementString("MAC_LABEL", macLabel);
writer.WriteElementString("COUNTER", nextCounter);
writer.WriteElementString("MAC", mac);
writer.WriteElementString("RUNNING_SUB_TOTAL",nSubTotal.ToString("c"));
writer.WriteElementString("RUNNING_TAX_AMOUNT",nTax.ToString("c"));
writer.WriteElementString("RUNNING_TRANS_AMOUNT",nTotal.ToString("c"));
//HERE IS WHERE I NEED TO WRITE THE CHILD ELEMENT(s):
//example below of what they or it would look like
//(collection of merchandise elements under Line_items)
//I know this example will have only one item because of parameters
/*
<LINE_ITEMS>
<MERCHANDISE>
<LINE_ITEM_ID>10</LINE_ITEM_ID>
<DESCRIPTION>This is a dummy</DESCRIPTION>
<UNIT_PRICE>1.00</UNIT_PRICE>
<QUANTITY>1</QUANTITY>
<EXTENDED_PRICE>1.00</EXTENDED_PRICE>
</MERCHANDISE>
</LINE_ITEMS>
*/
writer.WriteEndElement();
writer.WriteEndDocument();
}
// transmit to Point Solution and interrogate the response
var responseXml = Send(address, port, request);
//DO SOMETHING HERE WITH THE RESPONSE
// validate that the RESULT_CODE came back a SUCCESS
if ("-1" != responseXml.Element("RESPONSE").Element("RESULT_CODE").Value)
{
throw new Exception(responseXml.Element("RESPONSE").Element("RESULT_TEXT").Value ?? "unknown error");
}
return true;
}
If any one can help me understand how to populate the child elements as indicated where I put comments in the code I will be very thankful.
Thanks to the moderators for requesting more information on this.
I think using XmlSerializer is a good idea, maybe codes below could help you.
XmlSerializer is more intuitive than XmlWriter to generate Xml. : )
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
class Program
{
static void Main(string[] args)
{
Transaction transaction = new Transaction();
transaction.Function_Type = "LINE_ITEM";
transaction.LineItems = new List<Merchandise>();
transaction.LineItems.Add(new Merchandise() { UnitPrice = "5.00" });
//Create our own namespaces for the output
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
//Add an empty namespace and empty value
ns.Add("", "");
using (XmlWriter writer = XmlWriter.Create(Console.Out, new XmlWriterSettings { OmitXmlDeclaration = true }))
{
new XmlSerializer(typeof(Transaction)).Serialize(writer, transaction, ns);
}
Console.Read();
}
}
[XmlRoot("TRANSACTION")]
public class Transaction
{
[XmlElement("FUNCTION_TYPE")]
public string Function_Type { get; set; }
[XmlArray("LINE_ITEMS")]
[XmlArrayItem("MERCHANDISE")]
public List<Merchandise> LineItems { get; set; }
}
[XmlRoot("MERCHANDISE")]
public class Merchandise
{
[XmlElement("UNIT_PRICE")]
public string UnitPrice { get; set; }
}
Results:
You could use XmlDocument
XmlDocument xmlDoc = new XmlDocument();
XmlNode rootNode = xmlDoc.CreateElement("RootNode");
xmlDoc.AppendChild(rootNode);
foreach (Class objItem in classArray)
{
XmlNode firstNode= xmlDoc.CreateElement("First");
XmlNode second= xmlDoc.CreateElement("Second");
second.InnerText = objItem .Text;
firstNode.AppendChild(second);
rootNode.AppendChild(firstNode);
}
StringWriter stringWriter = new StringWriter();
XmlTextWriter textWriter = new XmlTextWriter(stringWriter);
xmlDoc.WriteTo(textWriter);
i am trying to add XmlNodes from one XmlDocument to another as a new node.
My main issue is that i can import only the First or Last child of the document but not the ones in between. all of the nodes i am trying to import have the same layout but i cant seem to create any iteration for importing them all since i can only select either the FirstChild or LastChild - Please note this is also across 2 forms.
I am new to Xml but do not want to re-write my whole Xml Document over again in an XDocument, any help would be greatly appreciated, Thanks.
Code Below:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace XmlCreator
{
public partial class Form_Member : Form
{
string MPlayID, MNick, MName, MMail, MICQ, MRemark;
public static XmlDocument xmlMembers = null;
static XmlNode rootNode = null;
public Form_Member()
{
InitializeComponent();
}
private void btnAdd_Click(object sender, EventArgs e)
{
if (xmlMembers != null)
{
XmlNode member = xmlMembers.CreateElement("member");
XmlAttribute attID = xmlMembers.CreateAttribute("id");
attID.Value = MPlayID;
member.Attributes.Append(attID);
XmlAttribute attNick = xmlMembers.CreateAttribute("nick");
attNick.Value = MNick;
member.Attributes.Append(attNick);
rootNode.AppendChild(member);
XmlNode MNameNode = xmlMembers.CreateElement("name");
MNameNode.InnerText = MName;
member.AppendChild(MNameNode);
XmlNode MMailNode = xmlMembers.CreateElement("email");
MMailNode.InnerText = MMail;
member.AppendChild(MMailNode);
XmlNode MICQNode = xmlMembers.CreateElement("icq");
MICQNode.InnerText = MICQ;
member.AppendChild(MICQNode);
XmlNode MRemarkNode = xmlMembers.CreateElement("remark");
MRemarkNode.InnerText = MRemark;
member.AppendChild(MRemarkNode);
xmlMembers.Save("memberXML.xml");
clearTextFields();
}
}
private void Form_Member_Load(object sender, EventArgs e)
{
xmlMembers = new XmlDocument();
rootNode = xmlMembers.CreateElement("members");
xmlMembers.AppendChild(rootNode);
}
}
}
This is the form of which the Xml file i am trying to import is being created from, i am trying to import this to another form with the following code on the form i am trying to import it to.
code below:
XmlNode memberNode = xmlSquad.ImportNode(Form_Member.xmlMembers.DocumentElement.FirstChild, true);
xmlSquad.DocumentElement.AppendChild(memberNode);
To conclude, it is importing the FirstChild, however, i am making more than 1 memberNode in the xmlMembers.xml file from the other form which i can't find a way of copying over.
Any help will be appreciated, Thank you.
You need the following extension method:
public static class XmlNodeExtensions
{
/// <summary>
/// Copy all child XmlNodes from the source to the destination.
/// </summary>
/// <param name="source">Copy children FROM this XmlNode</param>
/// <param name="destination">Copy children TO this XmlNode</param>
public static void CopyChildren(this XmlNode source, XmlNode destination)
{
if (source == null || destination == null)
throw new ArgumentNullException();
var doc = destination.OwnerDocument;
if (doc == null)
throw new InvalidOperationException("null document");
// Clone the array to prevent infinite loops when the two nodes are from the same document.
foreach (var child in source.ChildNodes.Cast<XmlNode>().ToArray())
{
var copy = doc.ImportNode(child, true);
destination.AppendChild(copy);
}
}
}
You can then use it like:
Form_Member.xmlMembers.DocumentElement.CopyChildren(xmlSquad.DocumentElement);
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.
I had the same problem but none of those answers helped me. In this case it works or not depending on the operative system I used, not on the .Net version.
I've enabled the SignedXML log by adding this code in the app.config to see what happened behind:
<system.diagnostics>
<sources>
<source name="System.Security.Cryptography.Xml.SignedXml" switchName="XmlDsigLogSwitch">
<listeners>
<add name="logFile" />
</listeners>
</source>
</sources>
<switches>
<add name="XmlDsigLogSwitch" value="Verbose" />
</switches>
<sharedListeners>
<add name="logFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="XmlDsigLog.txt"/>
</sharedListeners>
<trace autoflush="true">
<listeners>
<add name="logFile" />
</listeners>
</trace>
</system.diagnostics>
It wrote this particular line:
System.Security.Cryptography.Xml.SignedXml Information: 17 : [SignedXml#033ec00f, UnsafeTransformMethod] Canonicalization method "http://www.w3.org/TR/1999/REC-xpath-19991116" is not on the safe list. Safe canonicalization methods are: "http://www.w3.org/TR/2001/REC-xml-c14n-20010315", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments", "http://www.w3.org/2001/10/xml-exc-c14n#", "http://www.w3.org/2001/10/xml-exc-c14n#WithComments", "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2000/09/xmldsig#base64", "urn:mpeg:mpeg21:2003:01-REL-R-NS:licenseTransform", "http://www.w3.org/2002/07/decrypt#XML".
I found this Microsoft Support article which tries to fix an error introduced by the security update 3141780:
https://support.microsoft.com/en-us/kb/3148821
Into that article, in Scenario 2 section, there are 2 solutions, I fixed the problem applying the registry key related to the XPath Transform Method:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft.NETFramework\Security\SafeTransformMethods#XmlDsigXPathTransform=http://www.w3.org/TR/1999/REC-xpath-19991116
Try explicitly setting the Canonicalization method for the SignedInfo property of the SignedXml class.. It appears there has been a change in default behaviour here between .Net 2.0 and .Net 4.0
signed.SignedInfo.CanonicalizationMethod = Signed.XmlDsigExcC14NTransformUrl;
Reference:
This answer
// Assume the data to sign is in the data.xml file, load it, and
// set up the signature object.
XmlDocument doc = new XmlDocument();
doc.Load(#"D:\Example.xml");
SignedXml sig = new SignedXml(doc);
// Make a random RSA key, and set it on the signature for signing.
RSA key = new RSACryptoServiceProvider();
sig.SigningKey = key;
// Create a Reference to the containing document, add the enveloped
// transform, and then add the Reference to the signature
Reference refr = new Reference("");refr.AddTransform(new XmlDsigEnvelopedSignatureTransform());
sig.AddReference(refr);
// Compute the signature, add it to the XML document, and save
sig.ComputeSignature();
doc.DocumentElement.AppendChild(sig.GetXml());
doc.Save("data-signed.xml");
// Load the signed data
//XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.Load("data-signed.xml");
// Find the Signature element in the document
XmlNamespaceManager nsm = new XmlNamespaceManager(new NameTable());
nsm.AddNamespace("dsig", SignedXml.XmlDsigNamespaceUrl);
XmlElement sigElt = (XmlElement)doc.SelectSingleNode("//dsig:Signature", nsm);
// Load the signature for verification
//SignedXml sig = new SignedXml(doc);
sig.LoadXml(sigElt);
// Verify the signature, assume the public key part of the
// signing key is in the key variable
if (sig.CheckSignature(key))
Console.WriteLine("Signature verified");
else
Console.WriteLine("Signature not valid");
From .NET framework 4/4.5 the classes to work with x509 certificates and other security features are located in System.IdentityModel.dll. Search the respective classes in the namespace mentioned.
I solved this problem adding same namespace from Signature tag to SignedInfo.
Like this:
Before:
After:
In order to check the signature on NET 4.0+ you have to change the context of your CanonicalizationMethod, therefore you have to initialize your signedXml object in the following way:
XmlNodeList signatureNodeList = xmlDoc.GetElementsByTagName("Signature");
SignedXml signedXml = new SignedXml((XmlElement)signatureNodeList[0]);
signedXml.LoadXml((XmlElement)signatureNodeList[0]);
//Then you proceed your check as usual
public static Boolean VerifyDetachedSignature(string XmlSigFileName)
{
// Create a new XML document.
XmlDocument xmlDocument = new XmlDocument();
// Load the passed XML file into the document.
xmlDocument.Load(XmlSigFileName);
// Find the "Signature" node and create a new XmlNodeList object.
XmlNodeList nodeList = xmlDocument.GetElementsByTagName("Signature");
// Create a new SignedXMl object.
SignedXml signedXml = new SignedXml();
// Load the signature node.
signedXml.LoadXml((XmlElement)nodeList[0]);
// Check the signature and return the result.
return signedXml.CheckSignature();
}
Thank you very much for reading my question.
the bottom is the sample of my xml file.please refer that.
i did some xml files before, but by "CMarkXml". "IntoElement, OutofElement", is very clear.
but when C#...i was lost..
1: how to read & write my xml file without using the tag name. i see some articles about operation on xml file by c#, but all assumed that known the tag name.
2: if without tag name, it is very difficult or not recommend. then how to read & write my xml file by XmlDocument? (sorry, but no Ling please, i am very faint with that...).
3: my idear is, for the xml file, get out some section, we still could parse the section by xmldocument.
4: for the write/modify the xml file, of course, should contain delete some section, delete some "leaf", change the attributes...
Thank you very much for reading the long question, and any help i will very appreciate. If you have a good sample code but not continent paste them here, could you send it to "erlvde#gmail.com"?
<root>
<a>i belong to a</a>
<b>
<bb>
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
</bb>
<bb>
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
<bb>
....(other <bb>)
</b>
</root>
Read your xml into XmlDocument:
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml("XML HERE");
Access child nodes:
xmlDocument.ChildNodes[1]
But it's also true that it's very error prone
You can also check if you have child nodes at all:
xmlDocument.HasChildNodes
And get number of child nodes:
xmlDocument.ChildNodes.Count
It looks to me like your elements names contain identifiers. If that is the case, and you have control over the XML schema, I would highly recommend changing your XML to contain elements and/or attributes indicating your identifiers and then use the built in XmlSerializer class for serializing to and from XML. It has many modifiers available, such as XmlElement and XmlAttribute among many others, for formatting the output.
Here is a tutorial to get you started.
If possible, change your XML to something like following which would make it far simpler to manipulate...again if changing the schema is a possibility.
<root>
<a>i belong to a</a>
<b>
<bb id="1">
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
</bb>
<bb id="2">
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
<bb>
</b>
</root>
Edit this edit reflects the changes you made to your XML
Here is a simple console application which will serialize an object to an XML file and then rehydrate it.
Expected XML
<?xml version="1.0" encoding="utf-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<a>i belong to a</a>
<b>
<bb>
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
</bb>
<bb>
<bb>1</bb>
<bb>2</bb>
<bb>3</bb>
<bb>4</bb>
<bb>5</bb>
</bb>
</b>
</root>
Simple Console Application Demonstration
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var items = new root
{
a = "i belong to a",
b = new List<bb>
{
new bb
{
bbClassProperty = new List<int>
{
1,
2,
3,
4,
5
}
},
new bb
{
bbClassProperty= new List<int>
{
1,
2,
3,
4,
5
}
}
}
};
XmlSerializer serializer = new XmlSerializer(typeof(root));
using (var textWriter = new StreamWriter(#"C:\root.xml"))
{
serializer.Serialize(textWriter, items);
textWriter.Close();
}
using (var stream = new StreamReader(#"C:\root.xml"))
{
var yourObject = serializer.Deserialize(stream);
}
Console.Read();
}
}
#region [Classes]
public class root
{
public string a { get; set; }
public List<bb> b { get; set; }
}
public class bb
{
[XmlElement("bb")]
public List<int> bbClassProperty { get; set; }
}
#endregion
}
Look into the ChildNodes (and similar) properties and methods on your XmlElement object. These will let you iterate over the children of a node and you can then ask that node for its name.
If you have a XmlNode object, you can use XMLNode.FirstChild to get the child, if it has any. You can also use XMLNode.NextSibling to get the next Node of the same parent node.
Why can't you use the names of the nodes? It's the easiest and most common way. Especially if you use XPath or similar.
XPath is also the answer to your second question.
U can use the class XML reader, a simple example is given here.
using System;
using System.Xml;
class Program
{
static void Main()
{
// Create an XML reader for this file.
using (XmlReader reader = XmlReader.Create("perls.xml"))
{
while (reader.Read())
{
// Only detect start elements.
if (reader.IsStartElement())
{
// Get element name and switch on it.
switch (reader.Name)
{
case "perls":
// Detect this element.
Console.WriteLine("Start <perls> element.");
break;
case "article":
// Detect this article element.
Console.WriteLine("Start <article> element.");
// Search for the attribute name on this current node.
string attribute = reader["name"];
if (attribute != null)
{
Console.WriteLine(" Has attribute name: " + attribute);
}
// Next read will contain text.
if (reader.Read())
{
Console.WriteLine(" Text node: " + reader.Value.Trim());
}
break;
}
}
}
}
}
}
The input file text is:
<?xml version="1.0" encoding="utf-8" ?>
<perls>
<article name="backgroundworker">
Example text.
</article>
<article name="threadpool">
More text.
</article>
<article></article>
<article>Final text.</article>
</perls>
Output
Start element.
Start element.
Has attribute name: backgroundworker
Text node: Example text.
Start element.
Has attribute name: threadpool
Text node: More text.
Start element.
Text node:
Start element.
Text node: Final text.enter code here
You can use the following code to if the file does not contain the headers, in the example above.
XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
reader = XmlReader.Create(filePath, settings)
Would something like this help?
void Iterate(XmlNode parent) {
//do something with
//parent.Name
//parent.Value
//parent.Attributes
foreach(XmlNode child in parent.ChildNodes) {
Iterate(child);
}
}
XmlDocument document = new XmlDocument();
document.Load(filename);
XmlNode parent = document.DocumentElement;
Iterate(parent);
You could also store it like that (sorry for any syntactical error, didn't run it)
public class Document {
public Element DocumentElement { set; get; }
private void Load(string fileName) {
XmlDocument document = new XmlDocument();
document.Load(fileName);
DocumentElement = new Element(this, null);
DocumentElement.Load(document.DocumentElement);
}
}
public class Element {
public string Name { set; get; }
public string Value { set; get; }
//other attributes
private Document document = null;
private Element parent = null;
public Element Parent { get { return parent; } }
public List<Element> Children { set; get; }
private int order = 0;
public Element(Document document, Element parent) {
Name = "";
Value = "";
Children = new List<LayoutElement>();
this.document = document;
this.parent = parent;
order = parent != null ? parent.Children.Count + 1 : 1;
}
private Element GetSibling(bool left) {
if(parent == null) return null;
int add = left ? -1 : +1;
Element sibling = parent.Children.Find(child => child.order == order + add);
return sibling;
}
public Element GetLeftSibling() {
return GetSibling(true);
}
public Element GetRightSibling() {
return GetSibling(false);
}
public void Load(XmlNode node) {
Name = node.Name;
Value = node.Value;
//other attributes
foreach(XmlNode nodeChild in node.Children) {
Element child = new Element(document, this);
child.Load(nodeChild);
Children.Add(child);
}
}
}
Document document = new Document();
document.Load(fileName);
For changing/deleting right now you could iterate the tree and find elements by name, but since name is not unique, you would affect many elements at once. You could add an unique id in every tag like
<bb id="bb1"/>
Then read it in Load function like
id = ((XmlElement)node).GetAttribute("id");
and use this id to iterate through the tree. Sorry I don't have time right now to provide something more detailed.