We are trying to hash a xml file, i already have it working that it hashes the contents of the XML.
For which i am using the following code:
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.Load(txtFile.Text);
XmlNodeList list = doc.GetElementsByTagName("Document");
XmlElement node = (XmlElement)list[0];
//node.SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
string s = node.OuterXml;
using (MemoryStream msIn = new MemoryStream(Encoding.UTF8.GetBytes(s)))
{
XmlDsigC14NTransform t = new XmlDsigC14NTransform(true);
t.LoadInput(msIn);
using (var hash = new SHA256Managed())
{
byte[] digest = t.GetDigestedOutput(hash);
txtHash.Text = BitConverter.ToString(digest).Replace("-", String.Empty);
}
}
however, this only hashes the contents of the XML.
What i need is to hash the complete XML instead of only the contents.
If we only hash the contents, our hash doesnt compare with the control we get.
You can read the file contents without creating a XmlDocument and hash the contents:
var file = File.ReadAllBytes(txtFile.Text);
using (var hash = new SHA256Managed())
{
byte[] digest = hash.ComputeHash(file);
txtHash.Text = BitConverter.ToString(digest).Replace("-", String.Empty);
}
Related
I am fairly new coding with C#. I would like to calculate an IRmark of an xml file saved on my c drive.
The IRmark calculation is based on the HMRC's specification on https://www.gov.uk/government/publications/hmrc-irmark-generic-irmark-specification
I have found a code online that I could use to do this. I do not know how to direct it to read and convert the xml file on the c drive instead. I will appreciate your help. Thank you
Here is the code
public static string GetIRMark(byte[] Xml)
{
string vbLf = "\n";
string vbCrLf = "\r\n";
// Convert Byte array to string
string text = Encoding.UTF8.GetString(Xml);
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.LoadXml(text);
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("env", doc.DocumentElement.NamespaceURI);
XmlNode Body = doc.SelectSingleNode("//env:Body", ns);
ns.AddNamespace("tax", Body.FirstChild.NextSibling.NamespaceURI);
// Create an XML document of just the body section
XmlDocument xmlBody = new XmlDocument();
xmlBody.PreserveWhitespace = true;
xmlBody.LoadXml(Body.OuterXml);
// Remove any existing IRMark
XmlNode nodeIr = xmlBody.SelectSingleNode("//tax:IRmark", ns);
if (nodeIr != null)
{
nodeIr.ParentNode.RemoveChild(nodeIr);
}
// Normalise the document using C14N (Canonicalisation)
XmlDsigC14NTransform c14n = new XmlDsigC14NTransform();
c14n.LoadInput(xmlBody);
using (Stream S = (Stream)c14n.GetOutput())
{
byte[] Buffer = new byte[S.Length];
// Convert to string and normalise line endings
S.Read(Buffer, 0, (int)S.Length);
text = Encoding.UTF8.GetString(Buffer);
text = text.Replace("
", "");
text = text.Replace(vbCrLf, vbLf);
text = text.Replace(vbCrLf, vbLf);
// Convert the final document back into a byte array
byte[] b = Encoding.UTF8.GetBytes(text);
// Create the SHA-1 hash from the final document
SHA1 SHA = SHA1.Create();
byte[] hash = SHA.ComputeHash(b);
return Convert.ToBase64String(hash);
}
I have tried this code
string text = Encoding.UTF8.GetString(#"c:\myfile.xml");
but I get an error message
I am converting xml to html using xslt 3.0 saxon-HE 9.8 library. Using it in c# code.
I am passing xml and xslt file path in input to get it transformed and get output.
Can anyone please let me know how can I pass xml as string and xslt as string as input in c# code for processing it.
Below is my code.
public static string Transform_XML(string param, string inputfile, string xsltfilename)
{
var xslt = new FileInfo(xsltfilename);
var input = new FileInfo(inputfile);
// Compile stylesheet
var processor = new Processor();
var compiler = processor.NewXsltCompiler();
var executable = compiler.Compile(new Uri(xslt.FullName));
XPathDocument doc = new XPathDocument(new StringReader(param));
DocumentBuilder db = processor.NewDocumentBuilder();
XdmNode xml;
using (XmlReader xr = XmlReader.Create(new StringReader(param)))
{
xml = db.Build(xr);
}
// Do transformation to a destination
var destination = new DomDestination();
using (var inputStream = input.OpenRead())
{
var transformer = executable.Load();
transformer.SetParameter(new QName("", "", "user_entry"), xml);
transformer.SetInputStream(inputStream, new Uri(input.DirectoryName));
transformer.Run(destination);
}
return destination.XmlDocument.InnerXml.ToString();
}
Want to pass xml and xslt as string instead of file path.
UPDATE 1
Got the solution for passing xml and xsl as string in c#. Below is the updated code.
private string Transform_XML(string param, string param_name, string inputfile, string xsltfilename)
{
string xslt_input = System.IO.File.ReadAllText(xsltfilename + ".xslt");
string xml_input = System.IO.File.ReadAllText(inputfile + ".xml");
// Compile stylesheet
var processor = new Processor();
var compiler = processor.NewXsltCompiler();
compiler.BaseUri=new Uri(Server.MapPath("/"));
var executable = compiler.Compile(new XmlTextReader(new StringReader(xslt_input)));
XPathDocument doc = new XPathDocument(new StringReader(param));
DocumentBuilder db = processor.NewDocumentBuilder();
XdmNode xml;
using (XmlReader xr = XmlReader.Create(new StringReader(param)))
{
xml = db.Build(xr);
}
//xml input
DocumentBuilder builder = processor.NewDocumentBuilder();
builder.BaseUri= new Uri(Server.MapPath("/"));
MemoryStream ms = new MemoryStream();
StreamWriter tw = new StreamWriter(ms);
tw.Write(xml_input);
tw.Flush();
Stream instr = new MemoryStream(ms.GetBuffer(), 0, (int)ms.Length);
XdmNode input = builder.Build(instr);
// Do transformation to a destination
var destination = new DomDestination();
var transformer = executable.Load();
//Set the parameter with xml value
transformer.SetParameter(new QName("", "", param_name), xml);
// Set the root node of the source document to be the initial context node
transformer.InitialContextNode = input;
transformer.Run(destination);
// Get result
return destination.XmlDocument.InnerXml.ToString();
}
The XsltTransformer has a method SetInputStream() that allows you to supply the input as a stream (which indeed you appear to be using).
This post How do I generate a stream from a string? tells you how to create a stream from a string.
This is a very strange question.
I using C# to create a pass.json and save it to memoryStream, it work normally. After that I create the manifest.json SHA1 data which including that pass.json, the string of manifest.json like this and it is totally correct.
{"icon.png": "9423bd00e2b01c59a3265c38b5062fac7da0752d",
"icon#2x.png": "4d1db55bdaca70b685c013529a1c0dcbd7046524",
"logo.png": "ee5b053e63dbfe3b78378c15d163331d68a0ede8",
"logo#2x.png": "2f9e3a55bded1163620719a4d6c1ad496ed40c17",
"pass.json": "fd68bf77757d3057263a9aca0e5110ddd933934a"}
After generate pkpass as my phone, it can't open. I change the pass.json SHA1 code as "fd68bf77757d3057263a9aca0e5110ddd933934a" without using a value to save it, it work.
The coding like following:
// This version run success
var strPass = JavascriptSerialize(details);
var sw = new StreamWriter(assetsFolder + #"pass.json");
sw.Write(strPass);
sw.Close();
manifest.passjson = GetSha1Hash(assetsFolder + manifest.GetAssetBoardingPass(libPkPass_object_boardingPass.JsonObjects.AssetTypes.passjson));
//manifest.passjson = "2f9e3a55bded1163620719a4d6c1ad496ed40c17"
// end
// This version run fail
var strPass = JavascriptSerialize(details);
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(strPass);
writer.Write(s);
writer.Flush();
stream.Position = 0;
var a = GetSha1HashMemory(passStream);
private static string GetSha1HashMemory(Stream passStream)
{
//var bs = new BufferedStream(passStream);
using (SHA1Managed sha = new SHA1Managed())
{
byte[] checksum = sha.ComputeHash(passStream);
string sendCheckSum = BitConverter.ToString(checksum)
.Replace("-", string.Empty);
return sendCheckSum.ToString().ToLower();
}
}
manifest.passjson = a;
//manifest.passjson = "2f9e3a55bded1163620719a4d6c1ad496ed40c17" (same data )
//end
What is going on?????? I can find out any question that string is wrong.
The pkpass provide in here (sendspace).
Can any body told me where is wrong?
Big Thank!
Two mistakes :
ComputeHash(Stream) and using Stream
ComputeHash(Stream) : ComputeHash stream only using System.IO.Stream, but not MemoryStream, change to ComputeHash(bytes[]) can handle it
using Stream: I try to pass the stream to other function, it is not a good example, the stream need to create a new one and it may replace some bytes at your computer stream. In this case, I just need to call this function will out open new one, it will fix
StringBuilder formatted;
using (var sha1 = new SHA1Managed())
{
//var bytePass = ReadFully(passStream);
var bytePass = passStream.ToArray();
var hash = sha1.ComputeHash(bytePass);
formatted = new StringBuilder(2 * hash.Length);
foreach (var b in hash)
{
formatted.AppendFormat("{0:X2}", b);
}
}
manifest.passjson = formatted.ToString().ToLower();
This is a follow-on to this SO post in which I learned to generate the RSA key pair and store the Public key in the Settings. I generated my key by:
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = "XML_ENC_RSA_KEY";
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
string keyXml = rsaKey.ToXmlString(true);
I copied the public key part of that string into my program settings and it looks like:
"<RSAKeyValue><Modulus>mfXS3Na0XfkjhpjS3sL5XcC9o+j6KXi1LB9yBc4SsTMo1Yk/pFsXr74gNj4aRxKB45+hZH/lSo933NCDEh25du1iMsaH4TGQNkCqi+HDLQjOrdXMMNmaQrLXGlY7UCCfFUnkEUxX51AlyVLzqLycaAt6zm5ljnDXojMC7JoCrTM=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>"
Does that look valid?
Then I am taking my XML document and trying to convert it to a byte[] for the Encrypt function:
string fileName = System.IO.Path.Combine(Application.StartupPath, "alphaService.xml");
XDocument doc = new XDocument();
XElement xml = new XElement("Info",
new XElement("DatabaseServerName", txtServerName.Text),
new XElement("DatabaseUserName", txtDatabaseUserName.Text),
new XElement("DatabasePassword", txtDatabasePassword.Text),
new XElement("ServiceAccount", txtAccount.Text),
new XElement("ServicePassword", txtServicePassword.Text),
new XElement("RegistrationCode", txtRegistrationCode.Text));
doc.Add(xml);
doc.Save(fileName);
// Convert XML doc to byte stream
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(fileName);
byte[] fileBytes = Encoding.Default.GetBytes(xmlDoc.OuterXml);
Encrypt(fileBytes);
I am getting a "Syntax Error line1" from the Encrypt function which is:
private static byte[] Encrypt(byte[] bytes)
{
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(Properties.Settings.Default.PublicKeyXml);
return rsa.Encrypt(bytes, true);
}
}
Any ideas? EDIT: The actual error is:
rsa.FromXmlString(Properties.Settings.Default.PublicKeyXml);
I was just looking at your post here looking for information on the RSACryptoServiceProvider. I tried your code and it worked for me, Well, sort of, I never got the error you did until I started reading your message again.
Remove the quotes from your public key in the Properties.Settings. When I saw what you had posted for your public key I went in and added the quotes to my string and I get the exact same error you did.
Where I did get an error but different from yours was getting a bad length error on the encrypt. However, I figured out that if I change the line for converting the XmL to a byte to .ToString() and not .OuterXML it works.
private void button4_Click(object sender, EventArgs e)
{
string fileName = System.IO.Path.Combine(Application.StartupPath, "alphaService.xml");
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(fileName);
byte[] fileBytes = Encoding.ASCII.GetBytes(xmlDoc.ToString());
byte[] EncryptedBytes = Encrypt(fileBytes);
string EncryptedString = Encoding.ASCII.GetString(EncryptedBytes);
}
private static byte[] Encrypt(byte[] bytes)
{
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(Properties.Settings.Default.PublicKeyXml);
return rsa.Encrypt(bytes, false);
}
}
I changed and encoding to ASCII so I could convert the byte array to a string and it's better to do that if you convert to the byte array using the same method.
I have a dictionary, which I turn in to XML and then hash with SHA1.
string xmlMessageCode = inputDictionary.ToXML(); //Extension method.
UnicodeEncoding UE = new UnicodeEncoding();
SHA1Managed hasher = SHA1Managed();
byte[] hashString = Encoding.UTF8.GetBytes(xmlMessageCode.ToCharArray());
byte[] hashCode = hasher.ComputeHash(hashString);
string computedHashString = UTF8Encoding.UTF8.GetString(hashCode);
return computedHashString;
After that I put the value in an object property and then serialize a collection of these objects to XML:
XmlSerializer ser = new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings()
{
Indent = false,
OmitXmlDecleration = false,
Encoding = Encoding.UTF8
};
using(StringWriter sr = new StringWriter)
{
using(XmlWriter xmlr = XmlWriter.Create(sr, settings))
{
ser.Serialize(sr, newList);
}
return sr.ToString();
}
This produces XML, but when I try to validate the resulting XML, I get an error inside the property which was created from the hashed string.
What would be the best way to resolve this?
Should I strip the invalid characters or is there a more elegant solutions?
XML is a text based representation - you can not embed binary information directly into it.
Therefore you have to convert the binary data to a text - usually Base64 encoding is used for that purpose.
hence instead of
string computedHashString = UTF8Encoding.UTF8.GetString(hashCode);
you should use
string computedHashString = System.Convert.ToBase64String(hashCode);