C# xml 常规 保护 方法总结

 使用xsd模式文件验证xml文件

xml文件:

<?xml version="1.0" encoding="utf-8" ?>
<Books>
  <Book>
    <Title>ExampleTitle</Title>
    <Author>John Smith</Author>
    <Pages>500</Pages>
  </Book>
<Book>
    <Title>Another Title</Title>
    <Author>John Doe</Author>
    <Pages>250</Pages>
  </Book>
</Books>

 xsd文件:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Books">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="Book">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="Title" type="xs:string" minOccurs="1" maxOccurs="1" />
              <xs:element name="Author" type="xs:string" />
              <xs:element name="Pages" type="xs:unsignedShort" minOccurs="1" maxOccurs="1" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

 验证代码:

   static bool XmlValidate(string xmlPath, string xsdPath, out string message)
        {
            bool isXmlValid = true;
            string errorMessage = string.Empty;
            XmlReaderSettings settings = new XmlReaderSettings()
            {
                ValidationType = ValidationType.Schema,
                ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings
            };
            settings.ValidationEventHandler += (o, e) =>
            {
                isXmlValid = false;
                errorMessage = e.Message;
            };
            XmlSchema schema = XmlSchema.Read(new StreamReader(xsdPath), null);
            settings.Schemas.Add(schema);
            XmlDocument xmlDocument = new XmlDocument();
            XmlReader xmlReader = XmlReader.Create(xmlPath, settings);
            xmlDocument.Load(xmlReader);
            message = errorMessage;
            return isXmlValid;
        }

 调用代码:

  string xmlPath = "books.xml";
            string xsd = "books.xsd";
            string message = string.Empty;
            bool validate = XmlValidate(xmlPath, xsd, out message);

xml查询时需要注意XQuery注入(XPath注入),通常xPath语句如下:

static string GetBookTitle(string author, int page)
        {
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load("books.xml");
            XPathNavigator navigator = xmlDocument.CreateNavigator();
            string xquery = string.Format("string(//Book[Author/text()='{0}' and Pages/text()={1}]/Title/text())", author, page.ToString());
            XPathExpression expression = navigator.Compile(xquery);
            return navigator.Evaluate(expression).ToString();
        }

 但是这样的代码很容易遭到攻击,这里我们用Mvp.xml(开源库http://mvpxml.codeplex.com/)来实现参数化查询,防止xquery注入。

 修改后的代码:

   static string GetBookTitle(string author, int page)
        {
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.Load("books.xml");
            string xquery = "string(//Book[Author/text()=$author and Pages/text()=$page]/Title/text())";
            XPathNavigator navigator = xmlDocument.CreateNavigator();
            XPathExpression expression = DynamicContext.Compile(xquery);

            DynamicContext ctx = new DynamicContext();
            ctx.AddVariable("author", author);
            ctx.AddVariable("page",page.ToString());
            expression.SetContext(ctx);

            return navigator.Evaluate(expression).ToString();
        }

采用对称加密,加密xml节点新建xml文件如下: 

<?xml version="1.0" encoding="utf-8" ?>
<envelope>
  <to>ma.jiang@wipro.com</to>
  <from>gavin_ma@vfc.com</from>
  <message>You have just been paid.</message>
</envelope>

 

加密和解密代码如下:

  static void Encrypt(XmlDocument document, string elementNameToEntrypt, SymmetricAlgorithm algorithm)
        {
            if (document == null)
                throw new ArgumentNullException("document");
            if (elementNameToEntrypt == null)
                throw new ArgumentNullException("elementNameToEntrypt");
            if (algorithm == null)
                throw new ArgumentNullException("key");
            XmlElement elementToEncrypt = document.GetElementsByTagName(elementNameToEntrypt)[0] as XmlElement;
            if (elementToEncrypt == null)
                throw new XmlException("The specified element was not found");
            EncryptedXml exml = new EncryptedXml();

            byte[] encryptedElement = exml.EncryptData(elementToEncrypt, algorithm, false);
            EncryptedData encryptedData = new EncryptedData { Type = EncryptedXml.XmlEncElementUrl };
            string encryptionMethod = string.Empty;
            if (algorithm is TripleDES)
                encryptionMethod = EncryptedXml.XmlEncTripleDESUrl;
            else if (algorithm is DES)
                encryptionMethod = EncryptedXml.XmlEncDESUrl;
            else if (algorithm is Rijndael)
            {
                switch (algorithm.KeySize)
                {
                    case 128:
                        encryptionMethod = EncryptedXml.XmlEncAES128Url;
                        break;
                    case 192:
                        encryptionMethod = EncryptedXml.XmlEncAES192Url;
                        break;
                    case 256:
                        encryptionMethod = EncryptedXml.XmlEncAES256Url;
                        break;
                }
            }
            else
            {
                throw new CryptographicException("Specificed algorithm is not supported");
            }
            encryptedData.EncryptionMethod = new EncryptionMethod(encryptionMethod);
            encryptedData.CipherData.CipherValue = encryptedElement;
            EncryptedXml.ReplaceElement(elementToEncrypt, encryptedData, false);
        }
        static void Decrypt(XmlDocument document, SymmetricAlgorithm algorithm)
        {
            if (document == null)
                throw new ArgumentNullException("document");
           
            if (algorithm == null)
                throw new ArgumentNullException("key");
            XmlElement encryptedElement = document.GetElementsByTagName("EncryptedData")[0] as XmlElement;
            if (encryptedElement == null)
                throw new XmlException("No encrypted element was found.");
            EncryptedData encryptedData = new EncryptedData();
            encryptedData.LoadXml(encryptedElement);
            EncryptedXml encryptedXml = new EncryptedXml();
            byte[] rgbOutput = encryptedXml.DecryptData(encryptedData, algorithm);
            encryptedXml.ReplaceData(encryptedElement,rgbOutput);
        }

 调用方法如下:

   string xmlPath = "encrypt.xml";
            XmlDocument document = new XmlDocument();
            document.Load(xmlPath);
            RijndaelManaged rijndael = new RijndaelManaged();
            Encrypt(document, "message", rijndael);
            Decrypt(document, rijndael);

加密后的数据如图:

 采用非对成加密技术,加密xml节点

加密和解密代码如下:

  public static void Encrypt(XmlDocument Doc, string ElementToEncrypt, string EncryptionElementID, RSA Alg, string KeyName)
        {
            // Check the arguments.
            if (Doc == null)
                throw new ArgumentNullException("Doc");
            if (ElementToEncrypt == null)
                throw new ArgumentNullException("ElementToEncrypt");
            if (EncryptionElementID == null)
                throw new ArgumentNullException("EncryptionElementID");
            if (Alg == null)
                throw new ArgumentNullException("Alg");
            if (KeyName == null)
                throw new ArgumentNullException("KeyName");

            ////////////////////////////////////////////////
            // Find the specified element in the XmlDocument
            // object and create a new XmlElemnt object.
            ////////////////////////////////////////////////
            XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;

            // Throw an XmlException if the element was not found.
            if (elementToEncrypt == null)
            {
                throw new XmlException("The specified element was not found");

            }
            RijndaelManaged sessionKey = null;

            try
            {
                //////////////////////////////////////////////////
                // Create a new instance of the EncryptedXml class
                // and use it to encrypt the XmlElement with the
                // a new random symmetric key.
                //////////////////////////////////////////////////

                // Create a 256 bit Rijndael key.
                sessionKey = new RijndaelManaged();
                sessionKey.KeySize = 256;

                EncryptedXml eXml = new EncryptedXml();

                byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);
                ////////////////////////////////////////////////
                // Construct an EncryptedData object and populate
                // it with the desired encryption information.
                ////////////////////////////////////////////////

                EncryptedData edElement = new EncryptedData();
                edElement.Type = EncryptedXml.XmlEncElementUrl;
                edElement.Id = EncryptionElementID;
                // Create an EncryptionMethod element so that the
                // receiver knows which algorithm to use for decryption.

                edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
                // Encrypt the session key and add it to an EncryptedKey element.
                EncryptedKey ek = new EncryptedKey();

                byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, Alg, false);

                ek.CipherData = new CipherData(encryptedKey);

                ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);

                // Create a new DataReference element
                // for the KeyInfo element.  This optional
                // element specifies which EncryptedData
                // uses this key.  An XML document can have
                // multiple EncryptedData elements that use
                // different keys.
                DataReference dRef = new DataReference();

                // Specify the EncryptedData URI.
                dRef.Uri = "#" + EncryptionElementID;

                // Add the DataReference to the EncryptedKey.
                ek.AddReference(dRef);
                // Add the encrypted key to the
                // EncryptedData object.

                edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
                // Set the KeyInfo element to specify the
                // name of the RSA key.


                // Create a new KeyInfoName element.
                KeyInfoName kin = new KeyInfoName();

                // Specify a name for the key.
                kin.Value = KeyName;

                // Add the KeyInfoName element to the
                // EncryptedKey object.
                ek.KeyInfo.AddClause(kin);
                // Add the encrypted element data to the
                // EncryptedData object.
                edElement.CipherData.CipherValue = encryptedElement;
                ////////////////////////////////////////////////////
                // Replace the element from the original XmlDocument
                // object with the EncryptedData element.
                ////////////////////////////////////////////////////
                EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
            }
            catch (Exception e)
            {
                // re-throw the exception.
                throw e;
            }
            finally
            {
                if (sessionKey != null)
                {
                    sessionKey.Clear();
                }

            }

        }

        public static void Decrypt(XmlDocument Doc, RSA Alg, string KeyName)
        {
            // Check the arguments.  
            if (Doc == null)
                throw new ArgumentNullException("Doc");
            if (Alg == null)
                throw new ArgumentNullException("Alg");
            if (KeyName == null)
                throw new ArgumentNullException("KeyName");

            // Create a new EncryptedXml object.
            EncryptedXml exml = new EncryptedXml(Doc);

            // Add a key-name mapping.
            // This method can only decrypt documents
            // that present the specified key name.
            exml.AddKeyNameMapping(KeyName, Alg);

            // Decrypt the element.
            exml.DecryptDocument();

        }

 代码调用如下:

           string xmlPath = "encrypt.xml";
            XmlDocument document = new XmlDocument();
            document.PreserveWhitespace = true;
            document.Load(xmlPath);
            UnicodeEncoding ByteConverter = new UnicodeEncoding();
            CspParameters cspParams = new CspParameters { KeyContainerName = "XML_ENC_RSA_KEY" };
            using ( RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams))
            {
                Encrypt(document, "message", "EncryptedElementID", rsaKey, "rsaKey");
                // document.Save("test.xml");
                     Decrypt(document, rsaKey, "rsaKey");
            };

运行结果如图:

 用非对称密钥签名xml

实现代码如下:

  static void SignXml(XmlDocument document, RSA algorithm)
        {
            SignedXml signxml = new SignedXml(document) { SigningKey = algorithm };
            Reference reference = new Reference { Uri = string.Empty };
            XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
            reference.AddTransform(env);
            signxml.AddReference(reference);
            signxml.ComputeSignature();
            XmlElement xmlDigitalSignature = signxml.GetXml();
            document.DocumentElement.AppendChild(document.ImportNode(xmlDigitalSignature,true));
        }
        static bool IsSignedXmlValid(XmlDocument document, RSA algorithm)
        {
            SignedXml signxml = new SignedXml(document);
            XmlNodeList nodelist = document.GetElementsByTagName("Signature");
            if (nodelist.Count < 1)
            {
                throw new CryptographicException("No signature found");
            }
            signxml.LoadXml((XmlElement)nodelist[0]);
            return signxml.CheckSignature(algorithm);
        }
View Code

调用代码如下:

  string xmlPath = "encrypt.xml";
            XmlDocument document = new XmlDocument();
            document.PreserveWhitespace = true;
            document.Load(xmlPath);
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            SignXml(document, rsa);
            IsSignedXmlValid(document, rsa);

运行结果如图:

个人认为证书在xml中使用情况不常见,并且也比较简单,这里就省略了。

总结一下:

在使用xml之前,必须使用严格的架构(模式文件)来验证,尽可能使用架构的本地副本,以便缓存解析器来缓存他们。

选择一个合适的加密算法,如果应用程序需要加密和解密相同的应用程序,那么选择对称加密;应用程序需要和外部系统进行交流,那么选择非对称加密。

如果需要确保数据没有被更改,就需要始终使用数字签名

posted on 2013-07-06 17:37  dz45693  阅读(1689)  评论(0编辑  收藏  举报

导航