Как зашифровать несколько копий элемента XML в одном документе XML - PullRequest
0 голосов
/ 28 января 2010

У меня есть процедура шифрования XML, основанная на статье MSDN http://msdn.microsoft.com/en-us/library/sb7w85t6.aspx

Мой код здесь

public static void Encrypt(XmlDocument Doc, string ElementName, SymmetricAlgorithm Key)
        {
            ////////////////////////////////////////////////
            // Check the arguments.  
            ////////////////////////////////////////////////
            if (Doc == null || (Doc.ToString() == string.Empty))
                throw new ArgumentNullException("The XML document was not given or it is empty");

            if (ElementName == null || ElementName == string.Empty)
                throw new ArgumentNullException("The XML element was not given or it is empty");

            if (Key == null || Key.ToString() == string.Empty)
                throw new ArgumentNullException("The encryption algorithm was not given or it is empty");

            try
            {
                ////////////////////////////////////////////////
                // Find the specified element in the XmlDocument
                // object and create a new XmlElemnt object.
                ////////////////////////////////////////////////
                foreach (XmlElement elementToEncrypt in Doc.GetElementsByTagName("schema"))
                {
                    //XmlElement elementToEncrypt = Doc.GetElementsByTagName("schema")[0] as XmlElement;
                    // Throw an XmlException if the element was not found.
                    if (elementToEncrypt == null || elementToEncrypt.ToString() == string.Empty)
                    {
                        throw new XmlException("The specified element was not found or it is empty");
                    }

                    //////////////////////////////////////////////////
                    // Create a new instance of the EncryptedXml class 
                    // and use it to encrypt the XmlElement with the 
                    // symmetric key.
                    //////////////////////////////////////////////////
                    EncryptedXml eXml = new EncryptedXml();
                    byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, Key, false);

                    ////////////////////////////////////////////////
                    // Construct an EncryptedData object and populate
                    // it with the desired encryption information.
                    ////////////////////////////////////////////////
                    EncryptedData edElement = new EncryptedData();
                    edElement.Type = EncryptedXml.XmlEncElementUrl;

                    ////////////////////////////////////////////////
                    // Create an EncryptionMethod element so that the 
                    // receiver knows which algorithm to use for decryption.
                    // Determine what kind of algorithm is being used and
                    // supply the appropriate URL to the EncryptionMethod element.
                    ////////////////////////////////////////////////
                    string encryptionMethod = null;

                    if (Key is TripleDES)
                    {
                        encryptionMethod = EncryptedXml.XmlEncTripleDESUrl;
                    }
                    else if (Key is DES)
                    {
                        encryptionMethod = EncryptedXml.XmlEncDESUrl;
                    }
                    if (Key is Rijndael)
                    {
                        switch (Key.KeySize)
                        {
                            case 128:
                                encryptionMethod = EncryptedXml.XmlEncAES128Url;
                                break;
                            case 192:
                                encryptionMethod = EncryptedXml.XmlEncAES192Url;
                                break;
                            case 256:
                                encryptionMethod = EncryptedXml.XmlEncAES256Url;
                                break;
                        }
                    }
                    else
                    {
                        // Throw an exception if the transform is not in the previous categories
                        throw new CryptographicException("The specified algorithm is not supported for XML Encryption.");
                    }

                    edElement.EncryptionMethod = new EncryptionMethod(encryptionMethod);

                    ////////////////////////////////////////////////
                    // 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 (XmlException xexc)
            {
                throw new Exception(xexc.Message);
            }
            catch (Exception exc)
            {
                throw new XmlException("The XML document could not be used to encrypt the elements given: " + exc.Message);
            }
            finally
            {
                ////////////////////////////////////////////////////
                // Replace the XML file with the encrypted version
                ////////////////////////////////////////////////////
                Doc.Save("testMIPS.config");
            }
        }

Это работает для первого найденного элемента «схема», и я вижу строку Doc.XML, показывающую, что первый элемент был зашифрован.

Но когда цикл снова проходит, чтобы предположительно зашифровать следующий элемент "схемы", программа выдает это исключение ...

"Список элементов изменился. Операция перечисления не может быть продолжена."

Но я еще не сохранил файл Doc.XML.

Почему это происходит?

Заранее благодарим за ваше время.

Ответы [ 2 ]

0 голосов
/ 18 ноября 2015

У меня была такая же проблема, и я пришел с кодом ниже. У меня это работает.

test.xml:

<root>
    <creditcard id="1">
        <number>19834209</number>
        <expiry>02/02/2002</expiry>
    </creditcard>
    <creditcard id="2">
        <number>34834210</number>
        <expiry>03/03/2003</expiry>
    </creditcard>
    <creditcard id="3">
        <number>34834211</number>
        <expiry>04/04/2003</expiry>
    </creditcard>
</root>

C #:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;

namespace EncryptXmlTest1
{
    class Program
    {
        static void Main(string[] args)
        {
            RijndaelManaged key = null;

            try
            {
                // Create a new Rijndael key.
                key = new RijndaelManaged();
                // Load an XML document.
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.PreserveWhitespace = true;
                xmlDoc.Load("test.xml");

                // Encrypt the "creditcard" element.
                //Encrypt(xmlDoc, "creditcard", key);
                EncryptMultiple(xmlDoc, "creditcard", key);

                Console.WriteLine("The element was encrypted");

                Console.WriteLine(xmlDoc.InnerXml);

                //Decrypt(xmlDoc, key);
                DecryptMultiple(xmlDoc, key);

                Console.WriteLine("The element was decrypted");

                Console.WriteLine(xmlDoc.InnerXml);
                Console.ReadKey();

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            finally
            {
                // Clear the key.
                if (key != null)
                {
                    key.Clear();
                }
            }
        }

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

            ////////////////////////////////////////////////
            // Find the specified element in the XmlDocument
            // object and create a new XmlElemnt object.
            ////////////////////////////////////////////////
            XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementName)[0] as XmlElement;
            XmlNodeList elementsToEncrypt = Doc.GetElementsByTagName(ElementName);
            // Throw an XmlException if the element was not found.
            if (elementsToEncrypt == null)
            {
                throw new XmlException("The specified elements were not found");

            }

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

            EncryptedXml eXml = new EncryptedXml();

            byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, Key, false);

            ////////////////////////////////////////////////
            // Construct an EncryptedData object and populate
            // it with the desired encryption information.
            ////////////////////////////////////////////////

            EncryptedData edElement = new EncryptedData();
            edElement.Type = EncryptedXml.XmlEncElementUrl;

            // Create an EncryptionMethod element so that the 
            // receiver knows which algorithm to use for decryption.
            // Determine what kind of algorithm is being used and
            // supply the appropriate URL to the EncryptionMethod element.

            string encryptionMethod = null;

            if (Key is TripleDES)
            {
                encryptionMethod = EncryptedXml.XmlEncTripleDESUrl;
            }
            else if (Key is DES)
            {
                encryptionMethod = EncryptedXml.XmlEncDESUrl;
            }
            if (Key is Rijndael)
            {
                switch (Key.KeySize)
                {
                    case 128:
                        encryptionMethod = EncryptedXml.XmlEncAES128Url;
                        break;
                    case 192:
                        encryptionMethod = EncryptedXml.XmlEncAES192Url;
                        break;
                    case 256:
                        encryptionMethod = EncryptedXml.XmlEncAES256Url;
                        break;
                }
            }
            else
            {
                // Throw an exception if the transform is not in the previous categories
                throw new CryptographicException("The specified algorithm is not supported for XML Encryption.");
            }

            edElement.EncryptionMethod = new EncryptionMethod(encryptionMethod);

            // 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);
        }

        public static void EncryptMultiple(XmlDocument Doc, string ElementName, SymmetricAlgorithm Key)
        {
            // Check the arguments.  
            if (Doc == null)
                throw new ArgumentNullException("Doc");
            if (ElementName == null)
                throw new ArgumentNullException("ElementToEncrypt");
            if (Key == null)
                throw new ArgumentNullException("Alg");

            XmlNodeList elementsToEncrypt = Doc.GetElementsByTagName(ElementName);

            if (elementsToEncrypt == null)
            {
                throw new XmlException("The specified elements were not found");
            }

            //XmlNodeList elementsToEncryptCloned = Doc.GetElementsByTagName(ElementName);
            List<string> ids = new List<string>();

            for (int i = 0; i < elementsToEncrypt.Count; i++)
            {
                string attrVal = elementsToEncrypt[i].Attributes["id"].Value;
                ids.Add(attrVal);
            }

            foreach(string id in ids)
            {
                string query = string.Format("//*[@id='{0}']", id);
                XmlElement elementFetched = (XmlElement)Doc.SelectSingleNode(query);

                if (elementFetched == null)
                    continue;

                EncryptedXml eXml = new EncryptedXml();
                byte[] encryptedElement = eXml.EncryptData(elementFetched, Key, false);

                EncryptedData edElement = new EncryptedData();
                edElement.Type = EncryptedXml.XmlEncElementUrl;
                edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
                edElement.CipherData.CipherValue = encryptedElement;

                EncryptedXml.ReplaceElement(elementFetched, edElement, false);
            }

        }

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

            // Find the EncryptedData element in the XmlDocument.
            XmlElement encryptedElement = Doc.GetElementsByTagName("EncryptedData")[0] as XmlElement;

            // If the EncryptedData element was not found, throw an exception.
            if (encryptedElement == null)
            {
                throw new XmlException("The EncryptedData element was not found.");
            }

            // Create an EncryptedData object and populate it.
            EncryptedData edElement = new EncryptedData();
            edElement.LoadXml(encryptedElement);

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


            // Decrypt the element using the symmetric key.
            byte[] rgbOutput = exml.DecryptData(edElement, Alg);

            // Replace the encryptedData element with the plaintext XML element.
            exml.ReplaceData(encryptedElement, rgbOutput);

        }

        public static void DecryptMultiple(XmlDocument Doc, SymmetricAlgorithm Alg)
        {
            // Check the arguments.  
            if (Doc == null)
                throw new ArgumentNullException("Doc");
            if (Alg == null)
                throw new ArgumentNullException("Alg");

            XmlNodeList encryptedElements = Doc.GetElementsByTagName("EncryptedData");

            if (encryptedElements == null)
            {
                throw new XmlException("The EncryptedData elements were not found.");
            }

            List<XmlNode> nodes = new List<XmlNode>();

            foreach (XmlNode n in encryptedElements)
            {
                if (n == null)
                    continue;

                nodes.Add(n.Clone());
            }

            foreach (XmlElement ele in nodes)
            {
                XmlElement elementFetched = encryptedElements[0] as XmlElement;

                EncryptedData edElement = new EncryptedData();
                edElement.LoadXml(ele);

                EncryptedXml exml = new EncryptedXml();

                byte[] rgbOutput = exml.DecryptData(edElement, Alg);

                exml.ReplaceData(elementFetched, rgbOutput);
            }
        }
    }
}
0 голосов
/ 28 января 2010
Doc.GetElementsByTagName("schema") 

Возвращает итератор к узлу, который также является ленивым оценщиком. При изменении значения XmlElement итерация становится недействительной, потому что вы изменили данные, над которыми работал итератор. Попробуйте добавить полученный элемент из GetElementsByTagName() в некоторый List<XmlElement>, а затем перечислить этот список и зашифровать каждый элемент.

Дополнительная информация добавлена ​​

Это очень просто, если у вас есть список

    List<int> n =new List<int>(new int{1,2,3}); 
   //now if iterate it 
    foreach(int i in n){ 
      n.Add(i)
    } 

Цикл изменяет список, который он перечисляет, поэтому он выдаст ошибку, аналогичную приведенной в вашем случае. Вы можете решить проблему, выполнив

    foreach(int i in n.ToArray()){
        n.Add(i)
     } 

, поскольку в этом случае неподвижное изображение n захватывается перед циклом, и я могу свободно изменять его внутри цикла. Ваш цикл повторяется по дереву, пока вы меняете его при перечислении. Это не правильно и даст вам исключение. Вы должны сначала собрать все элементы, которые вы хотите изменить в списке, а затем перечислить этот список и зашифровать их. Это также обновит исходный XML-документ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...