using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Xml; using System.Diagnostics; using System.Reflection; using System.Security.Cryptography; using Aitex.Core.RT.Log; namespace Aitex.Core.Util { /// /// This class provides a set of static APIs that add digital signature to Xml files and verify if Xml files /// or Xml memory streams have valid signature. /// public static class FileSigner { /* This API didn't pass unit test. Will be uncommented in the future. /// /// Checks to see if the memory stream is a valid xml stream with valid signature. /// /// /// False if the stream is empty or null, or embedded signature doesn't match, or there is no the signature. public static bool IsValid(Stream stream) { bool retVal = false; try { Logger.LogTrace(MethodBase.GetCurrentMethod(), Logger.TraceEntry); XmlDocument doc = new XmlDocument(); doc.Load(stream); Debug.Assert(doc != null && doc.DocumentElement != null); // Get root element XmlElement elemRoot = doc.DocumentElement; // Get signature element XmlElement elemSignature = elemRoot["Signature"]; if (elemSignature == null) { return false; // The stream was not signed. } // Remove signature element from document elemRoot.RemoveChild(elemSignature); // Calculate hash code from file (after removing Signature element) UnicodeEncoding ue = new UnicodeEncoding(); SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider(); char[] innerXMLArray = elemRoot.InnerXml.ToCharArray(); byte[] bytes = ue.GetBytes(innerXMLArray); // byte[] signature = sha1.ComputeHash(ue.GetBytes(elemRoot.InnerXml)); byte[] signature = sha1.ComputeHash(bytes); string strSignature = Convert.ToBase64String(signature); // Add signature back to document elemRoot.AppendChild(elemSignature); // Compare embedded signature to calculated value if (elemSignature.InnerText == strSignature) { retVal = true; } } catch (System.Exception ex) { retVal = false; Logger.LogError(MethodBase.GetCurrentMethod(), ex); throw; } finally { Logger.LogTrace(MethodBase.GetCurrentMethod(), Logger.TraceExit); } return retVal; } */ /// /// Checks to see if the file is a valid xml file with valid signature. /// /// /// False if the embedded signature doesn't match, or there is no the signature. public static bool IsValid(string fileName) { bool retVal = false; try { XmlDocument doc = new XmlDocument(); doc.Load(fileName); Debug.Assert(doc != null && doc.DocumentElement != null); // Get root element XmlElement elemRoot = doc.DocumentElement; // Get signature element XmlElement elemSignature = elemRoot["Signature"]; if (elemSignature == null) { return false; // The file was not signed. } // Remove signature element from document elemRoot.RemoveChild(elemSignature); // Calculate hash code from file (after removing Signature element) UnicodeEncoding ue = new UnicodeEncoding(); SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider(); byte[] signature = sha1.ComputeHash(ue.GetBytes(elemRoot.InnerXml)); string strSignature = Convert.ToBase64String(signature); // Add signature back to document elemRoot.AppendChild(elemSignature); // Compare embedded signature to calculated value if (elemSignature.InnerText == strSignature) { retVal = true; } } catch (System.Exception e) { retVal = false; LOG.Write(e); throw; } finally { } return retVal; } /* This API didn't pass unit test. Will be uncommented in the future. /// /// Add signature to the end of the xml memory stream /// /// /// public static Stream Sign(Stream stream) { try { Logger.LogTrace(MethodBase.GetCurrentMethod(), Logger.TraceEntry); XmlDocument doc = new XmlDocument(); if (stream == null || stream.Length == 0) { throw new InvalidOperationException("Empty stream, no XML file to Sign"); } else { doc.Load(stream); XmlElement elemRoot = doc.DocumentElement; // Remove any existing signature XmlElement elemSignature = elemRoot["Signature"]; if (elemSignature != null) { elemRoot.RemoveChild(elemSignature); } // Calculate hash code (after removing Signature element) UnicodeEncoding ue = new UnicodeEncoding(); SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider(); char[] innerXMLArray = elemRoot.InnerXml.ToCharArray(); byte[] bytes = ue.GetBytes(innerXMLArray); // byte[] nSignature = sha1.ComputeHash(ue.GetBytes(elemRoot.InnerXml)); byte[] nSignature = sha1.ComputeHash(bytes); string strSignature = Convert.ToBase64String(nSignature); // Add signature to XML document elemSignature = doc.CreateElement("Signature"); elemSignature.InnerText = strSignature; elemRoot.AppendChild(elemSignature); // memStream.Flush(); stream.Seek(0, SeekOrigin.Begin); doc.Save(stream); return stream; } } catch (Exception ex) { Logger.LogError(MethodBase.GetCurrentMethod(), ex); throw; } finally { Logger.LogTrace(MethodBase.GetCurrentMethod(), Logger.TraceExit); } } */ /// /// Add signature to the end of the xml file. /// /// public static void Sign(string fileName) { try { XmlDocument doc = new XmlDocument(); bool writeable = true; if (File.Exists(fileName) && (File.GetAttributes(fileName) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) { writeable = false; File.SetAttributes(fileName, FileAttributes.Normal); } doc.Load(fileName); XmlElement elemRoot = doc.DocumentElement; // Remove any existing signature XmlElement elemSignature = elemRoot["Signature"]; if (elemSignature != null) { elemRoot.RemoveChild(elemSignature); } // Calculate hash code (after removing Signature element) UnicodeEncoding ue = new UnicodeEncoding(); SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider(); byte[] nSignature = sha1.ComputeHash(ue.GetBytes(elemRoot.InnerXml)); string strSignature = Convert.ToBase64String(nSignature); // Add signature to XML document elemSignature = doc.CreateElement("Signature"); elemSignature.InnerText = strSignature; elemRoot.AppendChild(elemSignature); doc.Save(fileName); if (!writeable) { File.SetAttributes(fileName, FileAttributes.ReadOnly); } } catch (Exception ex) { LOG.Write(ex); throw; } finally { } } } }