C# xml 常规 保护 方法总结

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

xml文件:

[html][/html] view plaincopyprint?

  1. <?xml version=”1.0″ encoding=”utf-8″ ?>
  2. <Books>
  3.   <Book>
  4.     <Title>ExampleTitle</Title>
  5.     <Author>John Smith</Author>
  6.     <Pages>500</Pages>
  7.   </Book>
  8. <Book>
  9.     <Title>Another Title</Title>
  10.     <Author>John Doe</Author>
  11.     <Pages>250</Pages>
  12.   </Book>
  13. </Books>

 

xsd文件:

 

[html][/html] view plaincopyprint?

  1. <?xml version=”1.0″ encoding=”utf-8″?>
  2. <xs:schema attributeFormDefault=”unqualified” elementFormDefault=”qualified” xmlns:xs=”http://www.w3.org/2001/XMLSchema”>
  3.   <xs:element name=”Books”>
  4.     <xs:complexType>
  5.       <xs:sequence>
  6.         <xs:element maxOccurs=”unbounded” name=”Book”>
  7.           <xs:complexType>
  8.             <xs:sequence>
  9.               <xs:element name=”Title” type=”xs:string” minOccurs=”1″ maxOccurs=”1″ />
  10.               <xs:element name=”Author” type=”xs:string” />
  11.               <xs:element name=”Pages” type=”xs:unsignedShort” minOccurs=”1″ maxOccurs=”1″ />
  12.             </xs:sequence>
  13.           </xs:complexType>
  14.         </xs:element>
  15.       </xs:sequence>
  16.     </xs:complexType>
  17.   </xs:element>
  18. </xs:schema>

验证代码:

 

 

[html][/html] view plaincopyprint?

  1. static bool XmlValidate(string xmlPath, string xsdPath, out string message)
  2.      {
  3.          bool isXmlValid = true;
  4.          string errorMessage = string.Empty;
  5.          XmlReaderSettings settings = new XmlReaderSettings()
  6.          {
  7.              ValidationType = ValidationType.Schema,
  8.              ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings
  9.          };
  10.          settings.ValidationEventHandler += (o, e) =>
  11.          {
  12.              isXmlValid = false;
  13.              errorMessage = e.Message;
  14.          };
  15.          XmlSchema schema = XmlSchema.Read(new StreamReader(xsdPath), null);
  16.          settings.Schemas.Add(schema);
  17.          XmlDocument xmlDocument = new XmlDocument();
  18.          XmlReader xmlReader = XmlReader.Create(xmlPath, settings);
  19.          xmlDocument.Load(xmlReader);
  20.          message = errorMessage;
  21.          return isXmlValid;
  22.      }

 

 

调用代码:

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

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

[html][/html] view plaincopyprint?

  1. static string GetBookTitle(string author, int page)
  2.         {
  3.             XmlDocument xmlDocument = new XmlDocument();
  4.             xmlDocument.Load(“books.xml”);
  5.             XPathNavigator navigator = xmlDocument.CreateNavigator();
  6.             string xquery = string.Format(“string(//Book[Author/text()='{0}’ and Pages/text()={1}]/Title/text())”, author, page.ToString());
  7.             XPathExpression expression = navigator.Compile(xquery);
  8.             return navigator.Evaluate(expression).ToString();
  9.         }

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

修改后的代码:

[csharp][/csharp] view plaincopyprint?

  1. static string GetBookTitle(string author, int page)
  2.      {
  3.          XmlDocument xmlDocument = new XmlDocument();
  4.          xmlDocument.Load(“books.xml”);
  5.          string xquery = “string(//Book[Author/text()=$author and Pages/text()=$page]/Title/text())”;
  6.          XPathNavigator navigator = xmlDocument.CreateNavigator();
  7.          XPathExpression expression = DynamicContext.Compile(xquery);
  8.          DynamicContext ctx = new DynamicContext();
  9.          ctx.AddVariable(“author”, author);
  10.          ctx.AddVariable(“page”,page.ToString());
  11.          expression.SetContext(ctx);
  12.          return navigator.Evaluate(expression).ToString();
  13.      }

采用对称加密,加密xml节点

新建xml文件如下:

 

[html][/html] view plaincopyprint?

  1. <?xml version=”1.0″ encoding=”utf-8″ ?>
  2. <envelope>
  3.   <to>ma.jiang@wipro.com</to>
  4.   <from>gavin_ma@vfc.com</from>
  5.   <message>You have just been paid.</message>
  6. </envelope>

加密和解密代码如下:

[csharp][/csharp] view plaincopyprint?

  1. static void Encrypt(XmlDocument document, string elementNameToEntrypt, SymmetricAlgorithm algorithm)
  2.       {
  3.           if (document == null)
  4.               throw new ArgumentNullException(“document”);
  5.           if (elementNameToEntrypt == null)
  6.               throw new ArgumentNullException(“elementNameToEntrypt”);
  7.           if (algorithm == null)
  8.               throw new ArgumentNullException(“key”);
  9.           XmlElement elementToEncrypt = document.GetElementsByTagName(elementNameToEntrypt)[0] as XmlElement;
  10.           if (elementToEncrypt == null)
  11.               throw new XmlException(“The specified element was not found”);
  12.           EncryptedXml exml = new EncryptedXml();
  13.           byte[] encryptedElement = exml.EncryptData(elementToEncrypt, algorithm, false);
  14.           EncryptedData encryptedData = new EncryptedData { Type = EncryptedXml.XmlEncElementUrl };
  15.           string encryptionMethod = string.Empty;
  16.           if (algorithm is TripleDES)
  17.               encryptionMethod = EncryptedXml.XmlEncTripleDESUrl;
  18.           else if (algorithm is DES)
  19.               encryptionMethod = EncryptedXml.XmlEncDESUrl;
  20.           else if (algorithm is Rijndael)
  21.           {
  22.               switch (algorithm.KeySize)
  23.               {
  24.                   case 128:
  25.                       encryptionMethod = EncryptedXml.XmlEncAES128Url;
  26.                       break;
  27.                   case 192:
  28.                       encryptionMethod = EncryptedXml.XmlEncAES192Url;
  29.                       break;
  30.                   case 256:
  31.                       encryptionMethod = EncryptedXml.XmlEncAES256Url;
  32.                       break;
  33.               }
  34.           }
  35.           else
  36.           {
  37.               throw new CryptographicException(“Specificed algorithm is not supported”);
  38.           }
  39.           encryptedData.EncryptionMethod = new EncryptionMethod(encryptionMethod);
  40.           encryptedData.CipherData.CipherValue = encryptedElement;
  41.           EncryptedXml.ReplaceElement(elementToEncrypt, encryptedData, false);
  42.       }
  43.       static void Decrypt(XmlDocument document, SymmetricAlgorithm algorithm)
  44.       {
  45.           if (document == null)
  46.               throw new ArgumentNullException(“document”);
  47.           if (algorithm == null)
  48.               throw new ArgumentNullException(“key”);
  49.           XmlElement encryptedElement = document.GetElementsByTagName(“EncryptedData”)[0] as XmlElement;
  50.           if (encryptedElement == null)
  51.               throw new XmlException(“No encrypted element was found.”);
  52.           EncryptedData encryptedData = new EncryptedData();
  53.           encryptedData.LoadXml(encryptedElement);
  54.           EncryptedXml encryptedXml = new EncryptedXml();
  55.           byte[] rgbOutput = encryptedXml.DecryptData(encryptedData, algorithm);
  56.           encryptedXml.ReplaceData(encryptedElement,rgbOutput);
  57.       }

调用方法如下:

 

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

加密后的数据如图:

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

加密和解密代码如下:

[csharp][/csharp] view plaincopyprint?

  1. public static void Encrypt(XmlDocument Doc, string ElementToEncrypt, string EncryptionElementID, RSA Alg, string KeyName)
  2.       {
  3.           // Check the arguments.
  4.           if (Doc == null)
  5.               throw new ArgumentNullException(“Doc”);
  6.           if (ElementToEncrypt == null)
  7.               throw new ArgumentNullException(“ElementToEncrypt”);
  8.           if (EncryptionElementID == null)
  9.               throw new ArgumentNullException(“EncryptionElementID”);
  10.           if (Alg == null)
  11.               throw new ArgumentNullException(“Alg”);
  12.           if (KeyName == null)
  13.               throw new ArgumentNullException(“KeyName”);
  14.           ////////////////////////////////////////////////
  15.           // Find the specified element in the XmlDocument
  16.           // object and create a new XmlElemnt object.
  17.           ////////////////////////////////////////////////
  18.           XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;
  19.           // Throw an XmlException if the element was not found.
  20.           if (elementToEncrypt == null)
  21.           {
  22.               throw new XmlException(“The specified element was not found”);
  23.           }
  24.           RijndaelManaged sessionKey = null;
  25.           try
  26.           {
  27.               //////////////////////////////////////////////////
  28.               // Create a new instance of the EncryptedXml class
  29.               // and use it to encrypt the XmlElement with the
  30.               // a new random symmetric key.
  31.               //////////////////////////////////////////////////
  32.               // Create a 256 bit Rijndael key.
  33.               sessionKey = new RijndaelManaged();
  34.               sessionKey.KeySize = 256;
  35.               EncryptedXml eXml = new EncryptedXml();
  36.               byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);
  37.               ////////////////////////////////////////////////
  38.               // Construct an EncryptedData object and populate
  39.               // it with the desired encryption information.
  40.               ////////////////////////////////////////////////
  41.               EncryptedData edElement = new EncryptedData();
  42.               edElement.Type = EncryptedXml.XmlEncElementUrl;
  43.               edElement.Id = EncryptionElementID;
  44.               // Create an EncryptionMethod element so that the
  45.               // receiver knows which algorithm to use for decryption.
  46.               edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
  47.               // Encrypt the session key and add it to an EncryptedKey element.
  48.               EncryptedKey ek = new EncryptedKey();
  49.               byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, Alg, false);
  50.               ek.CipherData = new CipherData(encryptedKey);
  51.               ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);
  52.               // Create a new DataReference element
  53.               // for the KeyInfo element.  This optional
  54.               // element specifies which EncryptedData
  55.               // uses this key.  An XML document can have
  56.               // multiple EncryptedData elements that use
  57.               // different keys.
  58.               DataReference dRef = new DataReference();
  59.               // Specify the EncryptedData URI.
  60.               dRef.Uri = “#” + EncryptionElementID;
  61.               // Add the DataReference to the EncryptedKey.
  62.               ek.AddReference(dRef);
  63.               // Add the encrypted key to the
  64.               // EncryptedData object.
  65.               edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
  66.               // Set the KeyInfo element to specify the
  67.               // name of the RSA key.
  68.               // Create a new KeyInfoName element.
  69.               KeyInfoName kin = new KeyInfoName();
  70.               // Specify a name for the key.
  71.               kin.Value = KeyName;
  72.               // Add the KeyInfoName element to the
  73.               // EncryptedKey object.
  74.               ek.KeyInfo.AddClause(kin);
  75.               // Add the encrypted element data to the
  76.               // EncryptedData object.
  77.               edElement.CipherData.CipherValue = encryptedElement;
  78.               ////////////////////////////////////////////////////
  79.               // Replace the element from the original XmlDocument
  80.               // object with the EncryptedData element.
  81.               ////////////////////////////////////////////////////
  82.               EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
  83.           }
  84.           catch (Exception e)
  85.           {
  86.               // re-throw the exception.
  87.               throw e;
  88.           }
  89.           finally
  90.           {
  91.               if (sessionKey != null)
  92.               {
  93.                   sessionKey.Clear();
  94.               }
  95.           }
  96.       }
  97.       public static void Decrypt(XmlDocument Doc, RSA Alg, string KeyName)
  98.       {
  99.           // Check the arguments.
  100.           if (Doc == null)
  101.               throw new ArgumentNullException(“Doc”);
  102.           if (Alg == null)
  103.               throw new ArgumentNullException(“Alg”);
  104.           if (KeyName == null)
  105.               throw new ArgumentNullException(“KeyName”);
  106.           // Create a new EncryptedXml object.
  107.           EncryptedXml exml = new EncryptedXml(Doc);
  108.           // Add a key-name mapping.
  109.           // This method can only decrypt documents
  110.           // that present the specified key name.
  111.           exml.AddKeyNameMapping(KeyName, Alg);
  112.           // Decrypt the element.
  113.           exml.DecryptDocument();
  114.       }

代码调用如下:

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

实现代码如下:

[csharp][/csharp] view plaincopyprint?

  1. static void SignXml(XmlDocument document, RSA algorithm)
  2.       {
  3.           SignedXml signxml = new SignedXml(document) { SigningKey = algorithm };
  4.           Reference reference = new Reference { Uri = string.Empty };
  5.           XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
  6.           reference.AddTransform(env);
  7.           signxml.AddReference(reference);
  8.           signxml.ComputeSignature();
  9.           XmlElement xmlDigitalSignature = signxml.GetXml();
  10.           document.DocumentElement.AppendChild(document.ImportNode(xmlDigitalSignature,true));
  11.       }
  12.       static bool IsSignedXmlValid(XmlDocument document, RSA algorithm)
  13.       {
  14.           SignedXml signxml = new SignedXml(document);
  15.           XmlNodeList nodelist = document.GetElementsByTagName(“Signature”);
  16.           if (nodelist.Count < 1)
  17.           {
  18.               throw new CryptographicException(“No signature found”);
  19.           }
  20.           signxml.LoadXml((XmlElement)nodelist[0]);
  21.           return signxml.CheckSignature(algorithm);
  22.       }

 

调用代码如下:

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之前,必须使用严格的架构(模式文件)来验证,尽可能使用架构的本地副本,以便缓存解析器来缓存他们。

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

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

标签