views:

153

answers:

1

Hi again everyone. I have an XML encryption routine based on the MSDN article http://msdn.microsoft.com/en-us/library/sb7w85t6.aspx

My code is here

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

This works for the first "schema" element it finds and I can see the Doc.XML string showing the first element having been encrypted.

But when the loop traverses again to supposedly encrypt the next "schema" element, the program throws this exception...

"The element list has changed. The enumeration operation failed to continue."

But I have not actually saved the Doc.XML file as yet.

Why is this happening?

Thank you in advance for your time.

A: 
Doc.GetElementsByTagName("schema") 

Returns iterator to node which is also a lazy evaluator. When change the XmlElement the iterate become invalid bcz you have changed the data on which the iterator was working. Try to add resulting element from GetElementsByTagName() in some List<XmlElement> and than enumerate that list and encrypt each element.

More info added

Its very simple if you have list

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

the loop is changing the list that its enumerating so it will give a error similar to one given in your case. You can solve the problem by doing

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

since in this case a still image of n is capture before loop and i can freey alter it inside the loop. Your loop iterating over a tree while you change while you enumerating it. That not right and will give you exception. You have to first collect all the element you want to change in a List and then enumerate this list and encrypt them. This will update the original xml document as well.

affan
Thanks Affan. So you're saying store the encrypted XML element into a separate list<> so as to leave the original XML object intact throughout the process? And then iterate through the list and re-save/build the XML file with the encrypted elements? Just trying to understand your response. Thanks again
ElCaito