Utility.cs

A general utility class for things such as BASE64 encoding and decoding etc

// ------------------------------------------------------------------------------------------
// <copyright file="Utility.cs" company="SecMaker AB">
//   SecMaker AB
// </copyright>
// <summary>
//   Defines the Utility class.
// </summary>
// ------------------------------------------------------------------------------------------

namespace SecMaker.NiP.Client
{
    using System;
    using System.Linq;
    using System.IO;
    using System.IO.Compression;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Xml;
    using System.Xml.Linq;
    using System.Security.Cryptography;
    using Newtonsoft.Json;
    public class Utility
    {
        public static string ToString(DateTime dateTime)
        {
            var sSortableDateTime =
                dateTime.ToString("s");

            var sDateTime =
                sSortableDateTime.ToUpper().Replace("T", " ");

            return sDateTime;
        }

        public static string ToBase64String(byte[] data)
        {
            return Convert.ToBase64String(data);
        }

        public static byte[] ToBytes(string value)
        {
            value =
                value.Replace("\r", string.Empty);

            value =
                value.Replace("\n", string.Empty);

            var regex =
                new Regex(@"^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$",
                    RegexOptions.Compiled);

            return
                regex.IsMatch(value)
                    ? Convert.FromBase64String(value)
                    : Encoding.UTF8.GetBytes(value);
        }

        public static string ParseXmlElem(string xmlFile, string xmlTag)
        {
            var sSubStr =
                xmlFile.Substring(xmlFile.IndexOf(xmlTag, StringComparison.Ordinal) + xmlTag.Length);

            sSubStr =
                sSubStr.Substring(0, sSubStr.IndexOf("<", StringComparison.Ordinal));

            if (sSubStr.Contains("<") || sSubStr.Contains(">"))
            {
                return string.Empty;
            }

            return sSubStr;
        }

        public static byte[] GenerateNonce(int size)
        {
            var bytes = new byte[size];

            using (var rnd = RandomNumberGenerator.Create())
            {
                rnd.GetBytes(bytes);
            }

            return bytes;
        }

        public static byte[] GenerateHmac(string hashAlgorithm, byte[] value, byte[] key)
        {
            HashAlgorithm hashGen;

            switch (hashAlgorithm)
            {
                case "1.3.14.3.2.26":
                    {
                        hashGen = new HMACSHA1(key);
                        break;
                    }

                case "2.16.840.1.101.3.4.2.1":
                    {
                        hashGen = new HMACSHA256(key);
                        break;
                    }

                case "2.16.840.1.101.3.4.2.2":
                    {
                        hashGen = new HMACSHA384(key);
                        break;
                    }

                case "2.16.840.1.101.3.4.2.3":
                    {
                        hashGen = new HMACSHA512(key);
                        break;
                    }

                default:
                    {
                        hashGen = new HMACSHA1(key);
                        break;
                    }
            }

            var hash =
                hashGen.ComputeHash(value);

            return hash;
        }

        /// <summary>
        /// Formats the info blob correctly for the encryption process
        /// </summary>
        /// <param name="data">XML formatted input to format</param>
        /// <param name="mode">JSON format or not. In the future more modes than JSON or XML may be used.</param>
        /// <returns></returns>
        public static string FormatInfoBlob(string data, string mode)
        {
            if (string.IsNullOrEmpty(data))
            {
                return data;
            }

            string toReturn = null;

            switch (mode)
            {
                case "XML":
                    {
                        toReturn = data;
                        break;
                    }
                case "JSON":
                    {
                        XmlDocument doc = new XmlDocument();
                        doc.LoadXml(data);

                        //Set JSON NameSpace so that we can add json:array to force list of only one obj to be properly formatted
                        XmlAttribute jsonNS = doc.CreateAttribute("xmlns", "json", "http://www.w3.org/2000/xmlns/");
                        jsonNS.Value = "http://james.newtonking.com/projects/json";
                        doc.DocumentElement.SetAttributeNode(jsonNS);
                        //Force all XML lists to be rendered as arrays, this is to avoid error with lists of length one.
                        var xpath = "//*[contains(local-name(), 'List')]";
                        var elements = doc.SelectNodes(xpath);
                        foreach (var element in elements)
                        {
                            var el = element as XmlElement;

                            if (el != null)
                            {
                                var jsonArray = doc.CreateAttribute("json", "Array", "http://james.newtonking.com/projects/json");
                                jsonArray.Value = "true";
                                el.SetAttributeNode(jsonArray);
                            }
                        }

                        //Now convert
                        string jsonText = JsonConvert.SerializeXmlNode(doc);
                        toReturn = jsonText.Substring(8, jsonText.Length - 9); //send info object without info:tag
                        break;
                    }
            }
            return toReturn;
        }
        /// <summary>
        /// Reformats the infoblob to xml
        /// </summary>
        /// <param name="data">Data either XML or JSON formatted for now</param>
        /// <param name="mode">The current format for the encrypted blob</param>
        /// <returns></returns>
        public static string ReformatInfoBlob(string data, string mode)
        {
            if (string.IsNullOrEmpty(data))
            {
                return data;
            }

            string toReturn = null;

            switch (mode)
            {
                case "XML":
                    {
                        toReturn = data;
                        break;
                    }
                case "JSON":
                    {
                        data = "{\"Info\":" + data+ "}";
                        string xml = (JsonConvert.DeserializeXmlNode(data)).OuterXml;
                        toReturn = xml;
                        break;
                    }
            }
            return toReturn;
        }



        public static byte[] Compress(byte[] data, string mode)
        {
            if (string.IsNullOrEmpty(mode) || mode == "NONE")
            {
                return data;
            }

            byte[] toReturn = null;

            switch (mode)
            {
                case "GZ":
                    {
                        using (var memory = new MemoryStream())
                        {
                            using (var gzip = new GZipStream(memory, CompressionMode.Compress, true))
                            {
                                gzip.Write(data, 0, data.Length);
                            }

                            toReturn =
                                memory.ToArray();

                            break;
                        }
                    }
            }

            return toReturn;
        }

        public static byte[] Decompress(byte[] data, string mode)
        {
            if (string.IsNullOrEmpty(mode) || mode == "NONE")
            {
                return data;
            }

            byte[] toReturn = null;

            switch (mode)
            {
                case "GZ":
                    {
                        using (var stream = new GZipStream(new MemoryStream(data), CompressionMode.Decompress))
                        {
                            const int size =
                                4096;

                            var buffer =
                                new byte[size];

                            using (var memory = new MemoryStream())
                            {
                                int count;

                                do
                                {
                                    count =
                                        stream.Read(buffer, 0, size);

                                    if (count > 0)
                                    {
                                        memory.Write(buffer, 0, count);
                                    }
                                } while (count > 0);
                                {
                                    toReturn =
                                        memory.ToArray();
                                }
                            }
                        }

                        break;
                    }
            }

            return toReturn;
        }

        public static byte[] AesEncryption(byte[] data, byte[] key, byte[] iv)
        {
            if (data == null)
            {
                throw new Exception("Data is null.");
            }

            if (key == null)
            {
                throw new Exception("Key is null.");
            }

            if (iv == null)
            {
                throw new Exception("IV is null.");
            }

            using (var provider = new AesCryptoServiceProvider())
            {
                provider.IV = iv;
                provider.Key = key;

                using (var encryptor = provider.CreateEncryptor(provider.Key, provider.IV))
                {
                    using (var ms = new MemoryStream())
                    {
                        using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                        {
                            cs.Write(data, 0, data.Length);
                            cs.Close();

                            return ms.ToArray();
                        }
                    }
                }
            }
        }

        public static byte[] AesDecryption(byte[] data, byte[] key, byte[] iv)
        {
            if (data == null)
            {
                throw new Exception("Data is null.");
            }

            if (key == null)
            {
                throw new Exception("Key is null.");
            }

            if (iv == null)
            {
                throw new Exception("IV is null.");
            }

            using (var provider = new AesCryptoServiceProvider())
            {
                provider.Key = key;
                provider.IV = iv;

                using (var decryptor = provider.CreateDecryptor(provider.Key, provider.IV))
                {
                    using (var memoryStream = new MemoryStream(data))
                    {
                        using (var cs = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                        {
                            var buffer = new byte[data.Length];

                            using (var ms = new MemoryStream())
                            {
                                int read;

                                while ((read = cs.Read(buffer, 0, buffer.Length)) > 0)
                                {
                                    ms.Write(buffer, 0, read);
                                }

                                return ms.ToArray();
                            }
                        }
                    }
                }
            }
        }

        public static string ToString(byte[] bytes)
        {
            string output;

            using (var stream = new MemoryStream(bytes))
            {
                stream.Position = 0;

                using (var reader = new StreamReader(stream, Encoding.UTF8))
                {
                    output = reader.ReadToEnd();
                }
            }

            return output;
        }

        public static string GetNodeName(string xml)
        {
            var xDoc = new XmlDocument();
            xDoc.LoadXml(xml);

            var elem =
                xDoc.DocumentElement;

            if (elem == null)
            {
                return string.Empty;
            }

            var node =
                elem.FirstChild;

            var childNode = node.LastChild;

            return childNode.Name;
        }

        public static string GetNodeContent(string document, string nodePath)
        {
            if (string.IsNullOrEmpty(document))
            {
                return string.Empty;
            }

            var xDoc = new XmlDocument();
            xDoc.LoadXml(document);

            var xRoot =
                xDoc.DocumentElement;

            if (xRoot == null)
            {
                const string sMessage =
                    "DocumentElement is null.";

                throw new Exception(sMessage);
            }

            var childNode =
                xRoot.SelectSingleNode("//" + nodePath);

            return
                childNode == null
                    ? string.Empty
                    : childNode.InnerXml;
        }

        public static string GetInfoTest(string message)
        {
            message =
                RemoveNameSpaces(message);

            var xDoc = new XmlDocument();
            xDoc.LoadXml(message);

            var xRoot =
                xDoc.DocumentElement;

            if (xRoot == null)
            {
                const string sMessage =
                    "DocumentElement is null.";

                throw new Exception(sMessage);
            }

            xDoc.LoadXml(xRoot.InnerXml);

            var node =
                xDoc.LastChild;

            node =
                node.LastChild;
            return node.InnerXml;
        }

        public static string RemoveNameSpaces(string xml)
        {
            var xElem =
                RemoveNameSpaces(XElement.Parse(xml));

            return xElem.ToString();
        }

        private static XElement RemoveNameSpaces(XElement xmlDocument)
        {
            if (!xmlDocument.HasElements)
            {
                var xElem = new XElement(xmlDocument.Name.LocalName)
                {
                    Value = xmlDocument.Value
                };

                foreach (var attribute in xmlDocument.Attributes())
                {
                    xElem.Add(attribute);
                }

                return xElem;
            }

            return new XElement(xmlDocument.Name.LocalName, xmlDocument.Elements().Select(RemoveNameSpaces));
        }
        /// <summary>
        /// Verifies that the response from server was a success. If not, throws an exception
        /// </summary>
        /// <param name="soapMessage"></param>
        /// <remarks>
        /// You may not want to be so harsh as to throw an exception. Your call.
        /// </remarks>
        static public bool VerifySuccess(String soapMessage)
        {
            if (soapMessage.IndexOf("NPR_SUCCESS") == -1)
            {
                var soapError =
              soapMessage.Substring(soapMessage.IndexOf("<Status>", StringComparison.Ordinal));

                soapError =
                    soapError.Substring(0, soapError.IndexOf("</Status>", StringComparison.Ordinal) + "</Status>".Length);

                throw new Exception("API Call failed, error was: " + soapError);
            }
            return true;
        }

        /// <summary>
        /// Returns the Task ID from a given <Task></Task> XML
        /// </summary>
        /// <param name="taskXml"></param>
        /// <returns></returns>
        public static string GetTaskIdFromTask(string taskXml)
        {
            XmlDocument task = new XmlDocument();
            task.LoadXml(taskXml);
            return task.SelectSingleNode(".//Id").InnerText;
        }
        /// <summary>
        /// Quick and naive way to get contents of xml tag. Instead of loading entire soap response as xml we handle it as a string
        /// </summary>
        /// <param name="xmlString">String representation of the xml</param>
        /// <param name="tagName"></param>
        /// <param name="keepOuterTags">If true returns OuterXML, if false returns innerXML</param>
        /// <returns>An empty string if the tag doesnt exist. If not returns the inner/outerXML for that tag</returns>
        static public string NaiveParseXmlTag(String xmlString, String tagName, bool keepOuterTags)
        {
            string openTag = "<" + tagName + ">";
            string closeTag = "</" + tagName + ">";
            if (!xmlString.Contains(openTag))
            {
                return "";
            }
            int offSetStart = keepOuterTags ? 0 : openTag.Length;
            int offSetEnd = keepOuterTags ? closeTag.Length : 0;
            string returnValue = xmlString.Substring(xmlString.IndexOf(openTag, StringComparison.Ordinal) + offSetStart);
            returnValue =
                returnValue.Substring(0, returnValue.LastIndexOf(closeTag, StringComparison.Ordinal) + offSetEnd);
            return returnValue;
        }
        /// <summary>
        /// Parses out the <Task></Task> tag from a soapMessage if there is one
        /// </summary>
        /// <param name="xmlFile"></param>
        /// <returns>The <Task></Task> elements outerXML</returns>
        public static string ParseTaskElem(string xmlFile)
        {
            if (xmlFile.IndexOf("NPR_SUCCESS") == -1)
            {
                throw new Exception("Task creation failed: response was " + xmlFile);
            }
            var task =
                xmlFile.Substring(xmlFile.IndexOf("<Task>", StringComparison.Ordinal));

            task =
                task.Substring(0, task.IndexOf("</Task>", StringComparison.Ordinal) + "</Task>".Length);

            return task;
        }



    }

}