Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question XmlException: Invalid character in the given encoding. Line 1, position 1.

Discussion in 'Scripting' started by Pietrofonix, Jun 7, 2022.

  1. Pietrofonix

    Pietrofonix

    Joined:
    Aug 1, 2020
    Posts:
    54
    Hi guys, i have a problem with the Xml encryption. Basically i'm trying to make a save system with a Xml encrypted file, but when i load a save, i get this message: XmlException: Invalid character in the given encoding. Line 1, position 1.
    I've read that could probably be an encoding issue, but i don't know how to fix it. Do you have any solution?

    P.S. Without the encryption the Xml serializaition works perfectly

    Code (CSharp):
    1. public class SaveLoadData : MonoBehaviour
    2. {
    3.     byte[] ivBytes = new byte[16];
    4.     byte[] keyBytes = new byte[16];
    5.     const string title = "Third person shooter";
    6.  
    7.     GameVariables variables = new GameVariables();
    8.  
    9.     // OPTIONS
    10.     public static int RT_language;
    11.  
    12.     // Player
    13.     public static int RT_health = 100;    // Variabili che vengono lette in runtime
    14.     public static int RT_mana = 50;
    15.     public static int RT_exp = 20;
    16.  
    17.     string path;
    18.  
    19.     void Awake()
    20.     {
    21.         if (!Directory.Exists(Application.dataPath + "/SaveDataBinary"))
    22.         {
    23.             DirectoryInfo myDirectory = Directory.CreateDirectory(Application.dataPath + "/SaveDataBinary");
    24.         }
    25.  
    26.         path = Application.dataPath + "/SaveDataBinary/";
    27.  
    28.         DecryptAndLoad("savegame001");
    29.  
    30.         Debug.Log("Health: " + RT_health);
    31.     }
    32.  
    33.  
    34.     void Update()
    35.     {
    36.         if (Keyboard.current.kKey.wasPressedThisFrame)
    37.         {
    38.             EncryptAndSave("savegame001");
    39.         }
    40.  
    41.         if (Keyboard.current.lKey.wasPressedThisFrame)
    42.         {
    43.             DecryptAndLoad("savegame001");
    44.         }
    45.  
    46.         if (Keyboard.current.jKey.wasPressedThisFrame)
    47.         {
    48.             AddHP();
    49.         }
    50.     }
    51.  
    52.     public void AddHP()
    53.     {
    54.         RT_health++;
    55.     }
    56.  
    57.     void GenerateIVBytes()
    58.     {
    59.         System.Random rnd = new System.Random();
    60.         rnd.NextBytes(ivBytes);
    61.     }
    62.  
    63.     void GenerateKeyBytes()
    64.     {
    65.         int sum = 0;
    66.  
    67.         foreach (char c in title)
    68.         {
    69.             sum += c;
    70.         }
    71.  
    72.         System.Random rnd2 = new System.Random(sum);
    73.         rnd2.NextBytes(keyBytes);
    74.     }
    75.  
    76.     public void EncryptAndSave(string fileName)
    77.     {
    78.         FileStream fileStream;
    79.  
    80.         GenerateIVBytes();
    81.         GenerateKeyBytes();
    82.  
    83.         SymmetricAlgorithm key = Aes.Create();
    84.  
    85.         if (File.Exists(path + fileName + ".xml"))
    86.         {
    87.             using (fileStream = File.Open(path + fileName + ".xml", FileMode.Open))
    88.             {
    89.                 variables.date = DateTime.Now.ToString("yyyy/MMMM/dd HH:mm:ss");
    90.  
    91.                 //  OPTIONS
    92.                 variables.language = RT_language;
    93.  
    94.                 // Player
    95.                 variables.health = RT_health;
    96.                 variables.mana = RT_mana;
    97.                 variables.exp = RT_exp;
    98.  
    99.                 using (CryptoStream cs = new CryptoStream(fileStream, key.CreateEncryptor(keyBytes, ivBytes), CryptoStreamMode.Write))
    100.                 {
    101.                     XmlSerializer formatter = new XmlSerializer(typeof(GameVariables));
    102.                     formatter.Serialize(cs, variables);
    103.                 }
    104.  
    105.                 Debug.Log("The file " + fileName + " is saved!");
    106.             }
    107.         }
    108.         else
    109.         {
    110.             using (fileStream = File.Open(path + fileName + ".xml", FileMode.Create))
    111.             {
    112.                 variables.date = DateTime.Now.ToString("yyyy/MMMM/dd HH:mm:ss");
    113.  
    114.                 //  OPTIONS
    115.                 variables.language = RT_language;
    116.  
    117.                 // Player
    118.                 variables.health = RT_health;
    119.                 variables.mana = RT_mana;
    120.                 variables.exp = RT_exp;
    121.  
    122.                 using (CryptoStream cs = new CryptoStream(fileStream, key.CreateEncryptor(keyBytes, ivBytes), CryptoStreamMode.Write))
    123.                 {
    124.                     XmlSerializer formatter = new XmlSerializer(typeof(GameVariables));
    125.                     formatter.Serialize(cs, variables);
    126.                 }
    127.  
    128.                 Debug.Log("The file " + fileName + " is saved!");
    129.             }
    130.         }  
    131.     }
    132.  
    133.     public void DecryptAndLoad(string fileName)
    134.     {
    135.         FileStream fileStream;
    136.  
    137.         GenerateIVBytes();
    138.         GenerateKeyBytes();
    139.  
    140.         SymmetricAlgorithm key = Aes.Create();
    141.  
    142.         if (File.Exists(path + fileName + ".xml"))
    143.         {                                                            
    144.             using (fileStream = File.Open(path + fileName + ".xml", FileMode.Open))
    145.             {
    146.                 using (CryptoStream cs = new CryptoStream(fileStream, key.CreateDecryptor(keyBytes, ivBytes), CryptoStreamMode.Read))
    147.                 {
    148.                     XmlSerializer formatter = new XmlSerializer(typeof(GameVariables));              
    149.                     variables = (GameVariables)formatter.Deserialize(cs);
    150.                 }
    151.  
    152.                 // OPTION
    153.                 RT_language = variables.language;
    154.  
    155.                 // Player
    156.                 RT_health = variables.health;
    157.                 RT_mana = variables.mana;
    158.                 RT_exp = variables.exp;
    159.  
    160.                 Debug.Log("The file " + fileName + " is loaded!");
    161.             }
    162.         }
    163.         else
    164.         {
    165.             Debug.Log("The file doesn't exist!");
    166.         }
    167.     }
    168. }
     
    Last edited: Jun 7, 2022
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,722
    Looks like when you write the file you're serializing to xml and then encrypting the XML (which seems correct).
    Your code for reading the file is wrong. It's trying to read the encrypted file as if it's a plain text XML file and then decrypt the result of that. That's backwards. You want to decrypt then parse the plaintext XML.
     
    Last edited: Jun 7, 2022
  3. Pietrofonix

    Pietrofonix

    Joined:
    Aug 1, 2020
    Posts:
    54
    Oh yes you're right, but after I changed, I get another error message: XmlException: Data at the root level is invalid. Line 1, position 1.
     
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,527
    You don't seem to store the initialization vector but just generate a random one. The initialization vector as well as the key have to be the same in order to properly decode the payload. Common solutions are generating a random initialization vector and storing it as the first part of the output stream. When decrypting you would first read the initialization vector from the stream before you start decoding. That way the initialization is always different but it's still available. See this SO question.

    ps: Note that you should not rely on the consistency of System.Random. Currently your approach only works if System.Random can reliably reproduce the same sequence. Yes, it uses a pseudo random number generator that when feeding the same seed would produce the same results. However which generator is used is an implementation detail that can change in the future. .NET specifically does not guarantee the consistency and does not recommend to use it for any kind of encryption. If you want to scramble the key you could use a well defined hashing algorithm instead and apply a certain number of "rounds".
     
    Pietrofonix and PraetorBlue like this.
  5. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,527
    Note: since you just sum up the characters of your title string, the seed for the random number generator would simply be "2006" which is not really a good seed. If someone bruteforces different System.Random seeds he could figure out the key quite fast :). In the end this is all just plain obfuscation and determined cheaters could always reverse engineer your code. You can make it a real pain by doing a lot of convoluted things, but in the end it does not really improve the quality of the key. Especially using such default algorithms makes it easier to crack it. Using more custom / non-standard permutations could be more efficient. In that case it becomes relatively hard to decrypt it using black box analyses.
     
    Pietrofonix likes this.
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,749
    That's great, just keep in mind that 100% of your effort is wasted.

    If you're concerned about the user 'hacking your save files,' or 'cheating in your game,' which is playing on their computer, just don't be.

    There's nothing you can do about it. Nothing is secure, it is not your computer, it is the user's computer.

    If it must be secure, store it on your own server and have the user connect to download it.

    Anything else is a waste of your time and the only person you're going to inconvenience is yourself when you're debugging the game and you have savegame errors. Work on your game instead.

    Remember, it only takes one 12-year-old in Finland to write a script to read/write your game files and everybody else can now use that script. Read about Cheat Engine to see more ways you cannot possibly control this.
     
    Pietrofonix likes this.
  7. Pietrofonix

    Pietrofonix

    Joined:
    Aug 1, 2020
    Posts:
    54
    Thank you very much for the explanation! Following your advice and searching on the web, i managed to solve it and now it seems to work perfectly. Anyway i'm new to this kind of things so i don't know if it's still wrong or something is still missing. For example, i still don't undertsand why the CryptoStream needs to write and read the initialization vector and its length; I don't even know what is the offset inside the cs.Write and cs.Read methods, and why the IV and the Key are 16 bytes. Anyway this is the code updated (about system.random, i'll try to change it later):
    Code (CSharp):
    1.    void GenerateIVBytes()
    2.     {
    3.         System.Random rnd = new System.Random();
    4.         rnd.NextBytes(ivBytes);
    5.     }
    6.  
    7.     void GenerateKeyBytes()
    8.     {
    9.         int sum = 0;
    10.  
    11.         foreach (char c in title)
    12.         {
    13.             sum += c;
    14.         }
    15.  
    16.         System.Random rnd2 = new System.Random(sum);
    17.         rnd2.NextBytes(keyBytes);
    18.     }
    19.  
    20.     public void EncryptAndSave(string fileName)
    21.     {
    22.         FileStream fileStream;
    23.  
    24.         GenerateIVBytes();
    25.         GenerateKeyBytes();
    26.  
    27.         SymmetricAlgorithm key = Aes.Create();
    28.         key.Key = keyBytes;
    29.         key.IV = ivBytes;
    30.         key.Mode = CipherMode.CBC;
    31.         key.Padding = PaddingMode.PKCS7;
    32.  
    33.         if (File.Exists(path + fileName + ".xml"))
    34.         {
    35.             using (fileStream = File.Open(path + fileName + ".xml", FileMode.Open))
    36.             {
    37.                 variables.date = DateTime.Now.ToString("yyyy/MMMM/dd HH:mm:ss");
    38.  
    39.                 //  OPTIONS
    40.                 variables.language = RT_language;
    41.  
    42.                 // Player
    43.                 variables.health = RT_health;   // Assegniamo le variabili sul disco alle variabili in runtime
    44.                 variables.mana = RT_mana;
    45.                 variables.exp = RT_exp;
    46.  
    47.                 using (CryptoStream cs = new CryptoStream(fileStream, key.CreateEncryptor(keyBytes, ivBytes), CryptoStreamMode.Write))
    48.                 {
    49.                     cs.Write(key.IV, 0, key.IV.Length);
    50.                     XmlSerializer formatter = new XmlSerializer(typeof(GameVariables));
    51.                     formatter.Serialize(cs, variables);
    52.                 }
    53.  
    54.                 Debug.Log("The file " + fileName + " has been saved!");
    55.             }
    56.         }
    57.         else
    58.         {
    59.             using (fileStream = File.Open(path + fileName + ".xml", FileMode.Create))
    60.             {
    61.                 variables.date = DateTime.Now.ToString("yyyy/MMMM/dd HH:mm:ss");
    62.  
    63.                 //  OPTIONS
    64.                 variables.language = RT_language;
    65.  
    66.                 // Player
    67.                 variables.health = RT_health;   // Assegniamo le variabili sul disco alle variabili in runtime
    68.                 variables.mana = RT_mana;
    69.                 variables.exp = RT_exp;
    70.  
    71.                 using (CryptoStream cs = new CryptoStream(fileStream, key.CreateEncryptor(keyBytes, ivBytes), CryptoStreamMode.Write))
    72.                 {
    73.                     cs.Write(key.IV, 0, key.IV.Length);
    74.                     XmlSerializer formatter = new XmlSerializer(typeof(GameVariables));
    75.                     formatter.Serialize(cs, variables);
    76.                 }
    77.  
    78.                 Debug.Log("The file " + fileName + " has been saved!");
    79.             }
    80.         }    
    81.     }
    82.  
    83.     public void DecryptAndLoad(string fileName)
    84.     {
    85.         FileStream fileStream;
    86.  
    87.         GenerateIVBytes();
    88.         GenerateKeyBytes();
    89.  
    90.         SymmetricAlgorithm key = Aes.Create();
    91.         key.Key = keyBytes;
    92.         key.IV = ivBytes;
    93.         key.Mode = CipherMode.CBC;
    94.         key.Padding = PaddingMode.PKCS7;
    95.  
    96.         if (File.Exists(path + fileName + ".xml"))
    97.         {                                                              
    98.             using (fileStream = File.Open(path + fileName + ".xml", FileMode.Open))
    99.             {
    100.                 using (CryptoStream cs = new CryptoStream(fileStream, key.CreateDecryptor(keyBytes, ivBytes), CryptoStreamMode.Read))
    101.                 {
    102.                     cs.Read(key.IV, 0, key.IV.Length);
    103.                     XmlSerializer formatter = new XmlSerializer(typeof(GameVariables));
    104.  
    105.                     variables = (GameVariables)formatter.Deserialize(cs);
    106.                 }
    107.  
    108.                 variables.date = DateTime.Now.ToString("yyyy/MMMM/dd HH:mm:ss");
    109.  
    110.                 // OPTION
    111.                 RT_language = variables.language;
    112.  
    113.                 // Player
    114.                 RT_health = variables.health;
    115.                 RT_mana = variables.mana;
    116.                 RT_exp = variables.exp;
    117.  
    118.                 Debug.Log("The file " + fileName + " has been loaded!");
    119.             }
    120.         }
    121.         else
    122.         {
    123.             Debug.Log("The file doesn't exist!");
    124.         }
    125.     }
     
    Last edited: Jun 7, 2022
  8. Pietrofonix

    Pietrofonix

    Joined:
    Aug 1, 2020
    Posts:
    54
    I know that nothing is secure and if someone wants to hack my save file, sooner or later it will make it, but the risks are more than this. I copy two comments that i've read under this video
    about why is important to have a solid save system and not, for example, the binaryformatter system.

    First comment of Nines
    "Deserializing a file with BinaryFormatter (or rather, loading a file into an application that will deserialize with BinaryFormatter) is essentially as dangerous as running an unknown .exe file. It's possible to construct serialized data that when deserialized will execute Process.Start() with user-supplied arguments, meaning that it's possible to create a save game that a user can load that will open a remote connection (like a Trojan) and allow the creator to control the victim's PC. The issue is essentially that users share save game files. There are websites dedicated to sharing save files at different completion% which users will download and use, assuming that they simply contain game data. Look at Steam forums for examples of people asking for 100% save games for achievements. Developers also receive save games in bug reports and may unwittingly debug and execute a dangerous save file. Mechanically it's not just Dispose() methods, but also deserialization callbacks, property setters and direct reflection-based avenues which can all result in arbitrary code execution. This happens because there are objects (like ObjectDataProvider, etc) in the .NET framework (and beyond) that are constructed in such a way that user-constructed delegates are invoked during the deserialization mechanism. In short, don't use BinaryFormatter. There really is no need and there are a multitude of better options. Even aside from the serious security implications it's not fast, it's not particularly efficient, it's not 'encrypted' and it's not able to support things like versioning out-of-the-box."

    Second comment of Juho Joensuu
    "The technical vector isn't the only reason why it's a serious issue, the most likely target for a hacker wouldn't be your average player but the company who made the game itself for example in the form of a faux bug report with save file, the attacker gets the company to run the malicious code inside their network straight up with whatever execution rights the program opening it had ( likely the game engine editor ). Full filesystem access to your company sources files etc in the form of your local SVN repository for example etc.."
     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,749
    I never suggested you use binary. In fact, I have exactly that blurb in my standard save notes (see below), which I've posted here hundreds of times to this same load/save question that keeps coming up.

    Load/Save steps:

    https://forum.unity.com/threads/save-system-questions.930366/#post-6087384

    An excellent discussion of loading/saving in Unity3D by Xarbrough:

    https://forum.unity.com/threads/save-system.1232301/#post-7872586

    When loading, you can never re-create a MonoBehaviour or ScriptableObject instance directly from JSON. The reason is they are hybrid C# and native engine objects, and when the JSON package calls
    new
    to make one, it cannot make the native engine portion of the object.

    Instead you must first create the MonoBehaviour using AddComponent<T>() on a GameObject instance, or use ScriptableObject.CreateInstance<T>() to make your SO, then use the appropriate JSON "populate object" call to fill in its public fields.

    If you want to use PlayerPrefs to save your game, it's always better to use a JSON-based wrapper such as this one I forked from a fellow named Brett M Johnson on github:

    https://gist.github.com/kurtdekker/7db0500da01c3eb2a7ac8040198ce7f6

    Do not use the binary formatter/serializer: it is insecure, it cannot be made secure, and it makes debugging very difficult, plus it actually will NOT prevent people from modifying your save data on their computers.

    https://docs.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide

    Remember: if you have a problem... and you decide to use XML... now you have TWO (2) problems.

    Excellent... Good luck!
     
    Pietrofonix likes this.
  10. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,527
    No :) it makes no sense to write the initialization vector data into the encrypted stream. You need the vector before you can even start decrypting the encrypted data. The point is to write the IV data unencrypted at the start of the stream so it can be read before you create your crypto stream. Also watch out what values you actually read / write. You can not read the IV bytes into key.IV as this is a property that returns a copy as far as I know. You have to assign the iv array after it has been filled with the correct data. Here are the two methods modified. I've tested it and it works fine. My "GameVariables" class only contains the date string for simplicity


    Code (CSharp):
    1.         public void EncryptAndSave(string fileName)
    2.         {
    3.             FileStream fileStream;
    4.  
    5.             GenerateIVBytes();
    6.             GenerateKeyBytes();
    7.  
    8.             FileMode mode = FileMode.Create;
    9.             if (File.Exists(path + fileName + ".xml"))
    10.                 mode = FileMode.Open;
    11.             using (fileStream = File.Open(path + fileName + ".xml", mode))
    12.             {
    13.                 fileStream.Write(ivBytes, 0, ivBytes.Length);
    14.  
    15.                 SymmetricAlgorithm key = Aes.Create();
    16.                 key.Key = keyBytes;
    17.                 key.IV = ivBytes;
    18.                 key.Mode = CipherMode.CBC;
    19.                 key.Padding = PaddingMode.PKCS7;
    20.                 using (CryptoStream cs = new CryptoStream(fileStream, key.CreateEncryptor(keyBytes, ivBytes), CryptoStreamMode.Write))
    21.                 {      
    22.                     XmlSerializer formatter = new XmlSerializer(typeof(GameVariables));
    23.                     variables.date = System.DateTime.Now.ToString("yyyy/MMMM/dd HH:mm:ss");
    24.                     formatter.Serialize(cs, variables);
    25.                 }
    26.  
    27.                 Debug.Log("The file " + fileName + " has been saved!");
    28.             }
    29.         }
    30.  
    31.         public void DecryptAndLoad(string fileName)
    32.         {
    33.             FileStream fileStream;
    34.             GenerateKeyBytes();
    35.             if (File.Exists(path + fileName + ".xml"))
    36.             {
    37.                 using (fileStream = File.Open(path + fileName + ".xml", FileMode.Open))
    38.                 {
    39.                     fileStream.Read(ivBytes,0,ivBytes.Length);
    40.                     SymmetricAlgorithm key = Aes.Create();
    41.                     key.Key = keyBytes;
    42.                     key.IV = ivBytes;
    43.                     key.Mode = CipherMode.CBC;
    44.                     key.Padding = PaddingMode.PKCS7;
    45.                     using (CryptoStream cs = new CryptoStream(fileStream, key.CreateDecryptor(keyBytes, ivBytes), CryptoStreamMode.Read))
    46.                     {  
    47.                         XmlSerializer formatter = new XmlSerializer(typeof(GameVariables));
    48.                         variables = (GameVariables)formatter.Deserialize(cs);
    49.                     }
    50.                     Debug.Log("date: " + variables.date);
    51.  
    52.                     Debug.Log("The file " + fileName + " has been loaded!");
    53.                 }
    54.             }
    55.             else
    56.             {
    57.                 Debug.Log("The file doesn't exist!");
    58.             }
    59.         }
    60.  
    Note you should not give your save file the extension xml when it's not an xml file. Any file association with xml files would not work with it anyways. Also the whole point of your encryption is to "obfuscate" the data. With that extension you're giving away an important implementation detail so a cheater would know that the data he's looking for should be xml. Use
    .sav
    or something similar.

    Also note I removed your redundant code when saving the file. The only difference between your two sections was the file mode. So it makes much more sense to just change the file mode based on if the file exists or not. Though this is actually not necessary and would even cause issues if the new data you're writing to the file is shorter than the previous one. If you open the file in write mode, it means the original content remains and you just start overwriting the content from the beginning. The whole data would be written, but as I said, if the old data was longer, you would still have old data following which would cause issues in the crypto stream. So you actually should always use FileMode.Create when you want to save / overwrite the file.
     
    Pietrofonix likes this.
  11. Pietrofonix

    Pietrofonix

    Joined:
    Aug 1, 2020
    Posts:
    54
    I cannot thank you enough for your effort. I modified the script with the one you posted and it works really well (for what i see it works the same as before, but i'm sure this has much more sense and it's also not redundant anymore). At the beginning i was using the binary formatter system, but i read that it was not recommended so i decided to change it with the Xml serialization and encryption. Thanks to you now it works but I don't know if this method is going to cause me some problems in the future given my little knowledge about it. Do you suggest to keep it or change it with a simpler method?
     
  12. Pietrofonix

    Pietrofonix

    Joined:
    Aug 1, 2020
    Posts:
    54
    Why is so problematic using xml as a save system?

    I'm really curious because I'm new with this type of systems and in the future, if i had to implement one in an important project, I would like to know as much as possible to prevent serious problems.

    Anyway, thanks a lot for the explanation and for the links you posted. I'm still a student and I have a lot of things to learn
     
    Last edited: Jun 8, 2022
  13. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,527
    Well, those warnings are specifically about the BinaryFormatter which is a security risk and even microsoft does not recommend using it anywhere. Security is not about someone messing with your save files for chearing but actual security risks for the machine it runs on. This can be an entrance point for malware.

    While I partially agree with Kurt that encryption or a binary format makes debugging a bit harder, it's not nearly as bad as he suggests ^^. When you use VS as a debugger you can inspect the deserialized data just fine. Actually most file formats you're using in Unity are binary files and you trust that the loaders work as they should. Once the system works you can essentially ignore any further issues.

    Note that you can restructure the creation of the stream so you can use pre processor defines to control if you want to use encryption or not. Of course save files that has been created in one mode would not be able to be loaded in the other. However it may help with debugging. By outsourcing the creation of the crypto stream we can actually just take out a single line of code and the saving / loading would be just plain xml:

    Code (CSharp):
    1.         public void EncryptAndSave(string fileName)
    2.         {
    3.             Stream stream;
    4.             using (stream = File.Open(path + fileName + ".xml", FileMode.Create))
    5. #if USE_ENCRYPTION
    6.             using (stream = CreateCryptoStreamW(stream))
    7. #endif
    8.             {
    9.                 XmlSerializer formatter = new XmlSerializer(typeof(GameVariables));
    10.                 variables.date = System.DateTime.Now.ToString("yyyy/MMMM/dd HH:mm:ss");
    11.                 formatter.Serialize(stream, variables);
    12.                 Debug.Log("The file " + fileName + " has been saved!");
    13.             }
    14.         }
    15.  
    16.  
    17.         public void DecryptAndLoad(string fileName)
    18.         {
    19.             if (!File.Exists(path + fileName + ".xml"))
    20.             {
    21.                 Debug.Log("The file doesn't exist!");
    22.                 return;
    23.             }
    24.             Stream stream;
    25.             using (stream = File.Open(path + fileName + ".xml", FileMode.Open))
    26. #if USE_ENCRYPTION
    27.             using (stream = CreateCryptoStreamR(stream))
    28. #endif
    29.             {
    30.                 XmlSerializer formatter = new XmlSerializer(typeof(GameVariables));
    31.                 variables = (GameVariables)formatter.Deserialize(stream);
    32.                 Debug.Log("date: " + variables.date);
    33.  
    34.                 Debug.Log("The file " + fileName + " has been loaded!");
    35.             }
    36.         }
    37.  
    38.         public Stream CreateCryptoStreamW(Stream aStream)
    39.         {
    40.             GenerateIVBytes();
    41.             GenerateKeyBytes();
    42.             aStream.Write(ivBytes, 0, ivBytes.Length);
    43.             SymmetricAlgorithm key = Aes.Create();
    44.             key.Key = keyBytes;
    45.             key.IV = ivBytes;
    46.             key.Mode = CipherMode.CBC;
    47.             key.Padding = PaddingMode.PKCS7;
    48.             return new CryptoStream(aStream, key.CreateEncryptor(), CryptoStreamMode.Write);
    49.         }
    50.         public Stream CreateCryptoStreamR(Stream aStream)
    51.         {
    52.             GenerateKeyBytes();
    53.             aStream.Read(ivBytes, 0, ivBytes.Length);
    54.             SymmetricAlgorithm key = Aes.Create();
    55.             key.Key = keyBytes;
    56.             key.IV = ivBytes;
    57.             key.Mode = CipherMode.CBC;
    58.             key.Padding = PaddingMode.PKCS7;
    59.             return new CryptoStream(aStream, key.CreateDecryptor(), CryptoStreamMode.Read);
    60.         }
    61.  
    The second using line simply wraps the original stream and replaces it with the crypto stream. The rest of the code works just the same way.

    You can enable / disable the encryption by adding this line at the top of your script file:

    Code (CSharp):
    1. //#define USE_ENCRYPTION
    When you uncomment this line, encryption would be used. If it's commented out you get plain xml files.

    Alternatively you can of course create the define in the player settings like any other define symbol.
     
    Pietrofonix likes this.
  14. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,749
    XML is awful, that's why.