Unity 加密ASE 游戏数据

///

///

DES 和 ASE 加密数据

在游戏可以打开、使用或保存文件且玩家也可以访问它们的任何情况下,用户、系统或进程总是有可能更改文件。在 PC 和 Mac 平台上,玩家也有可能直接跳过编辑文件并直接在 RAM 本身中更改值,从而破坏任何保护文件的尝试。根据玩家可用的资源和时间,他们还可以尝试反编译游戏的某些部分,并找出任何密钥在代码中的存储位置,或者所有内容在其内部数据结构中的排列方式。

虽然一些编译过程可以帮助保护游戏的运行内存,并且有一些方法可以更好地保护文件,例如加密它们,但根本问题仍然是信任问题之一。如果作为开发人员,您可以相信您的玩家不会更改游戏文件,那么读取和写入文本文件的更简单的解决方案可能是最佳选择。如果您预计某些用户可能会尝试更改保存文件之类的值,那么对敏感数据使用加密之类的方法可能是一个不错的选择。如果您觉得您根本无法信任您的玩家,请记住,确保完全安全的唯一完美解决方案是让任何玩家都无法玩您的游戏。所有数字交互都带有一定程度的信息风险。

现有游戏数据文件操作的问题

使用 Unity 时处理游戏数据的所有常见解决方案都存在玩家可能更改保存数据的文件的风险。这适用于使用BinaryFormatterJsonUtility,其中游戏对象被序列化为更简单的格式以保存数据,并且使用PlayerPrefs也是如此。通过使用本地系统上的本地文件,他们都会冒着玩家或其他用户、系统或进程意外或故意更改数据文件的风险。

解决部分这些问题的一种方法是使用加密。这使得希望用恶意值覆盖文件的系统、进程和用户更难理解这些文件。但是,它不能防止意外损坏。由于多种原因,文件仍有可能损坏。

使用加密还给开发人员带来了更大的复杂性。作为使用数据的一部分,在写入文件之前必须对其进行加密,在使用这些值之前必须对其进行解密。这引入了额外的处理时间,并且需要更多代码作为保存和加载操作的一部分。

对称和非对称加密

与加密一起使用的术语“对称”和“非对称”回答了“谁是可信的?”的问题。在对称加密中,至少有两方拥有用于创建加密数据的“密钥”,并且可以使用它来读取和写入数据。使用“密钥”可以让他们访问。这与非对称加密不同,非对称加密也称为公钥加密。在这种情况下,有两个密钥用于生成加密数据:公钥和私钥。公钥是“已知的”,可用于生成加密数据。私钥与公钥一起使用时,可用于解密数据。

一般来说,非对称加密远比对称加密强。但是,相对于数据大小,它也更复杂且更慢。用非常笼统的说法:

  • 对称:更快,但不太安全。
  • 不对称:速度较慢,但​​更安全。

通常,由于非对称加密依赖于已知的公钥,因此它对游戏数据没有那么有用。它可以用于涉及服务器并访问公钥的情况,但大多数游戏安装到本地设备而不访问公钥。这使得对称加密成为可能需要加密的大多数用例的“好”选择,但在使用游戏时无法更广泛地共享公钥。

DES(数据加密标准)和 AES(高级加密标准)

首字母缩略词 DES 和 AES 指的是“标准”,但也是遵循它们所基于的标准的算法。由于对 DES 的一些已知攻击,许多开发人员使用 AES 而不是 DES。但是,DES 仍然可以用于许多情况。

虽然 C# 支持DESAES,但 AES 将在本文中用作示例代码的一部分。

了解 AES

Aes类是命名空间System.Security.Cryptography的一部分。

<span style="background-color:#eeeeee"><span style="color:#111111"><code>Aes aes = Aes.Create();</code></span></span>

与许多其他类不同,Aes 有一个特殊的Create()方法,用于创建新对象并设置一些初始值

<span style="background-color:#eeeeee"><span style="color:#111111"><code>// Create new AES instance.
Aes iAes = Aes.Create();

// Save the generated key
byte[] savedKey = iAes.Key;

// Save the generated IV
byte[] inputIV = iAes.IV;</code></span></span>

因为Aes对数据本身起作用,所以它的所有操作都对字节数组起作用,读取或写入没有特定数据类型的数据。

什么是密钥和IV?

AES 加密通过使用两条数据来工作:密钥和 IV。第一个是用作加密和解密过程的一部分的密钥。谁拥有密钥,谁就可以访问数据。第二个,IV,是初始化向量。这告诉算法在加密数据时从哪里“开始”。基于 key 和 IV 值的不同组合,得到的加密输出会有所不同。

因为 IV 不是受保护的数据,它通常会与文件本身一起存储或作为文件本身的一部分存储。只要这对中的至少一部分(通常是密钥本身)是私有的,各方就可以加密和解密数据。

在 C# 中,除非显式覆盖,否则使用Aes类的Create()方法将生成新的随机键和 IV 值。出于解密的目的,两者都应保存。正如此示例代码所示,密钥可以作为应用程序的一部分保存,IV 可以作为加密游戏数据文件的一部分保存。

流包装流包装流

C# 使用流进行所有输入和输出操作。这还包括使用加密和解密作为称为CryptoStream的特殊类的一部分。此流通常可以“包装”其他流类,如FileStream,提供一种在数据从一个流传递到下一个流时加密或解密数据的方法。通常,为了帮助处理流本身的过程,使用第三个流类StreamReaderStreamWriter

拥有三个不同的流的效果是每个“包装”另一个。数据被读取或写入StreamReaderStreamWriter,后者读取或写入数据到CryptoStream,根据要求加密或解密数据,然后最后使用FileStream读取或写入数据到文件。

多流的代码片段

<span style="background-color:#eeeeee"><span style="color:#111111"><code>// Update Unity persistent path
string saveFile = Application.persistentDataPath + "/gamedata.json";

// Create FileStream for writing
FileStream dataStream = new FileStream(saveFile, FileMode.Create);

// Create (wrap) the FileStream in a CryptoStream for writing
CryptoStream iStream = new CryptoStream(
                dataStream,
                iAes.CreateEncryptor(iAes.Key, iAes.IV),
                CryptoStreamMode.Write);

// Create (wrap) the CryptoStream in a StreamWriter
StreamWriter sWriter = new StreamWriter(iStream);

// Write to the innermost stream (which will encrypt).
sWriter.Write("Hello World!");

// Close innermost.
sWriter.Close();

// Close crytostream
iStream.Close();

// Close FileStream.
dataStream.Close();</code></span></span>

对于三个不同的流,它们应该以特定的顺序使用:

  • 写作:FileStream –> CryptoStream –> StreamWriter。
  • 阅读:FileStream –> CryptoStream –> StreamReader

使用GameDataGameDataManager

根据面向对象编程中封装的概念,相似的值应该保持为一个单元。这意味着有一个类GameData保存要用作处理应用程序的一部分的值,以及一个单独的类GameDataManager来处理数据操作,“管理”游戏数据。

System.Serializable

C# 使用属性的概念让运行时知道有关某些数据的额外信息。在 Unity 中,属性SerializableSystem.Serializable向 Unity 中使用的 C# 运行时发出信号,某些代码(通常是类)可以“序列化”(从运行时数据转换为另一种格式)。它经常用作其他数据保存技术的一部分,例如使用JsonUtility类。

<span style="background-color:#eeeeee"><span style="color:#111111"><code>[System.Serializable]
public class GameData
{
    // Public lives
    public int Lives;

    // Public highScore
    public int HighScore;
}</code></span></span>

在上面的例子中,GameData类有两个字段,LivesHighScore。因为这些是public,它们可以被其他代码访问。

Unity 中的文件在哪里?

当 Unity 项目运行时,它提供了一个名为 Application.persistentDataPath的字段,该字段 设置了当前的“持久”目录。这是在项目启动时设置的,并且只能用作 Unity 中初始化系统的一部分,作为消息的一部分,例如 脚本组件 中 的Awake() 或 Start()方法。

JSON 文件示例

<span style="background-color:#eeeeee"><span style="color:#111111"><code>void Awake()
{
  // Update the path once the persistent path exists.
  saveFile = Application.persistentDataPath + "/gamedata.json";
}</code></span></span>

Application.persistentDataPath字段  仅包含一个目录。要使用文件,需要额外的文件名,例如上面使用“/”和文件名的示例。

写作和阅读

使用Aes会增加额外的复杂性,即需要保存用于创建加密数据的IV以便读取它。由于这不是私有数据,因此可以将其添加到文件的开头,然后从同一位置读取。

编写IV的代码片段

<span style="background-color:#eeeeee"><span style="color:#111111"><code>// Create new AES instance.
Aes iAes = Aes.Create();


// Create a FileStream
dataStream = new FileStream(saveFile, FileMode.Create);

// Save the generated IV
byte[] inputIV = iAes.IV;
// Write it to the data stream
dataStream.Write(inputIV, 0, inputIV.Length);</code></span></span>

阅读四的代码片段

<span style="background-color:#eeeeee"><span style="color:#111111"><code>// Create a FileStream
dataStream = new FileStream(saveFile, FileMode.Open);

// Create new AES instance.
Aes oAes = Aes.Create();

// Crete an array of correct size
byte[] outputIV = new byte[oAes.IV.Length];
// Read its length
dataStream.Read(outputIV, 0, outputIV.Length);</code></span></span>

使用JsonUtility

Aes类的使用提供了一种加密或解密数据的方法。使用流时,可以使用流的“层”来读取或写入数据。但是,这些操作都没有提供将文件中的数据序列化和反序列化为游戏数据的方法。为此,可以使用JsonUtility类。

由于JsonUtility类适用于String值,因此非常适合使用现有的StreamReaderStreamWriter类来读取和写入值。

更新了 GameDataManager代码

<span style="background-color:#eeeeee"><span style="color:#111111"><code>// Add System.IO to work with files!
using System.IO;
// Add System.Security.Crytography to use Encryption!
using System.Security.Cryptography;
using UnityEngine;

public class GameDataManager : MonoBehaviour
{
    // Create a field for the save file.
    string saveFile;

    // Create a GameData field.
    public GameData gameData = new GameData();

    // FileStream used for reading and writing files.
    FileStream dataStream;

    // Key for reading and writing encrypted data.
    byte[] savedKey;

    void Awake()
    {
        // Update the path once the persistent path exists.
        saveFile = Application.persistentDataPath + "/gamedata.json";
    }

    public void readFile()
    {
        // Does the file exist?
        if (File.Exists(saveFile))
        {
            // Create FileStream for opening files.
            dataStream = new FileStream(saveFile, FileMode.Open);

            // Create new AES instance.
            Aes oAes = Aes.Create();

            // Create an array of correct size based on AES IV.
            byte[] outputIV = new byte[oAes.IV.Length];
            
            // Read the IV from the file.
            dataStream.Read(outputIV, 0, outputIV.Length);

            // Create CryptoStream, wrapping FileStream
            CryptoStream oStream = new CryptoStream(
                   dataStream,
                   oAes.CreateDecryptor(savedKey, outputIV),
                   CryptoStreamMode.Read);

            // Create a StreamReader, wrapping CryptoStream
            StreamReader reader = new StreamReader(oStream);
            
            // Read the entire file into a String value.
            string text = reader.ReadToEnd();

            // Deserialize the JSON data 
            //  into a pattern matching the GameData class.
            gameData = JsonUtility.FromJson<GameData>(text);
        }
    }

    public void writeFile()
    {
        // Create new AES instance.
        Aes iAes = Aes.Create();

        // Update the internal key.
        savedKey = iAes.Key;

        // Create a FileStream for creating files.
        dataStream = new FileStream(saveFile, FileMode.Create);

        // Save the new generated IV.
        byte[] inputIV = iAes.IV;
        
        // Write the IV to the FileStream unencrypted.
        dataStream.Write(inputIV, 0, inputIV.Length);

        // Create CryptoStream, wrapping FileStream.
        CryptoStream iStream = new CryptoStream(
                dataStream,
                iAes.CreateEncryptor(iAes.Key, iAes.IV),
                CryptoStreamMode.Write);

        // Create StreamWriter, wrapping CryptoStream.
        StreamWriter sWriter = new StreamWriter(iStream);

        // Serialize the object into JSON and save string.
        string jsonString = JsonUtility.ToJson(gameData);

        // Write to the innermost stream (which will encrypt).
        sWriter.Write(jsonString);

        // Close StreamWriter.
        sWriter.Close();

        // Close CryptoStream.
        iStream.Close();

        // Close FileStream.
        dataStream.Close();
    }
}
</code></span></span>

在新版本的GameDataManager代码中,JsonUtility类及其方法用于将GameData类及其字段序列化为String格式。这还与现有的流“包装”用法相结合,用于解密和反序列化,用于读取文件,以及序列化和加密,用于写入文件。

我们如何处理密钥?

本文中共享的现有示例代码将使用 AES 加密和解密游戏数据文件。它还将处理所有流,并根据需要对类中的数据进行序列化或反序列化。但是,还有一个问题:如何处理密钥?在当前代码中,密钥由Create()方法生成并保存。只要代码正在运行,该值就会存在于应用程序中。它可以轻松地将该值用于所有与加密相关的任务。但是,一旦应用程序关闭,任何读取文件的尝试都将失败。密钥不会跨会话保存。

这是一个信任问题。这篇文章的起始问题仍然存在。任何加密使用的核心问题都围绕着信任问题。如果可以信任播放器不会更改文件或可以仔细检查其值的使用,则可能不需要加密。如果游戏数据应该受到玩家的保护,加密数据可能是一个有用的步骤。然而,与最初的担忧一样,任何对称加密的使用都意味着各方需要一个密钥来访问数据。它必须保存在某个地方。

在PlayerPrefs中保存对称键

PlayerPrefs类允许访问 Unity 为 player 保存的“首选项”数据。这通常是分辨率、可访问性或其他设置等数据。它也可以用作保存由Aes类生成的密钥的地方。

将密钥(使用 Base64 字符串和 Byte[] 转换)保存为“密钥”示例

<span style="background-color:#eeeeee"><span style="color:#111111"><code>// Add System.IO to work with files!
using System.IO;
// Add System.Security.Crytography to use Encryption!
using System.Security.Cryptography;
using UnityEngine;

public class GameDataManager : MonoBehaviour
{
    // Create a field for the save file.
    string saveFile;

    // Create a GameData field.
    public GameData gameData = new GameData();

    // FileStream used for reading and writing files.
    FileStream dataStream;

    void Awake()
    {
        // Update the path once the persistent path exists.
        saveFile = Application.persistentDataPath + "/gamedata.json";
    }

    public void readFile()
    {
        // Does the file exist AND does the "key" preference exist?
        if (File.Exists(saveFile) && PlayerPrefs.HasKey("key"))
        {
            // Update key based on PlayerPrefs
            // (Convert the String into a Base64 byte[] array.)
            byte[] savedKey = System.Convert.FromBase64String(PlayerPrefs.GetString("key") );

            // Create FileStream for opening files.
            dataStream = new FileStream(saveFile, FileMode.Open);

            // Create new AES instance.
            Aes oAes = Aes.Create();

            // Create an array of correct size based on AES IV.
            byte[] outputIV = new byte[oAes.IV.Length];
            
            // Read the IV from the file.
            dataStream.Read(outputIV, 0, outputIV.Length);

            // Create CryptoStream, wrapping FileStream
            CryptoStream oStream = new CryptoStream(
                   dataStream,
                   oAes.CreateDecryptor(savedKey, outputIV),
                   CryptoStreamMode.Read);

            // Create a StreamReader, wrapping CryptoStream
            StreamReader reader = new StreamReader(oStream);
            
            // Read the entire file into a String value.
            string text = reader.ReadToEnd();

            // Deserialize the JSON data 
            //  into a pattern matching the GameData class.
            gameData = JsonUtility.FromJson<GameData>(text);
        }
    }

    public void writeFile()
    {
        // Create new AES instance.
        Aes iAes = Aes.Create();

        // Update the internal key.
        byte[] savedKey = iAes.Key;

        // Convert the byte[] into a Base64 String.
        string key = System.Convert.ToBase64String(savedKey);

        // Update the PlayerPrefs
        PlayerPrefs.SetString("key", key);

        // Create a FileStream for creating files.
        dataStream = new FileStream(saveFile, FileMode.Create);

        // Save the new generated IV.
        byte[] inputIV = iAes.IV;
        
        // Write the IV to the FileStream unencrypted.
        dataStream.Write(inputIV, 0, inputIV.Length);

        // Create CryptoStream, wrapping FileStream.
        CryptoStream iStream = new CryptoStream(
                dataStream,
                iAes.CreateEncryptor(iAes.Key, iAes.IV),
                CryptoStreamMode.Write);

        // Create StreamWriter, wrapping CryptoStream.
        StreamWriter sWriter = new StreamWriter(iStream);

        // Serialize the object into JSON and save string.
        string jsonString = JsonUtility.ToJson(gameData);

        // Write to the innermost stream (which will encrypt).
        sWriter.Write(jsonString);

        // Close StreamWriter.
        sWriter.Close();

        // Close CryptoStream.
        iStream.Close();

        // Close FileStream.
        dataStream.Close();
    }
}
</code></span></span>

在上面的示例中,每次使用writeFile()方法时,它都会生成一个新的 AES 密钥并将其保存在PlayerPrefs中(覆盖之前的密钥)。作为副作用,每个新的保存操作都会更改使用的密钥,并且只要该密钥作为PlayerPrefs的一部分存在,就允许代码读取加密文件。

使用硬编码的密钥

也可以在GameDataManager类中“硬编码”秘密,对所有操作使用相同的密钥作为私有字段保存在类本身中。

硬编码的密钥

<span style="background-color:#eeeeee"><span style="color:#111111"><code>// Add System.IO to work with files!
using System.IO;
// Add System.Security.Crytography to use Encryption!
using System.Security.Cryptography;
using UnityEngine;

public class GameDataManager : MonoBehaviour
{
    // Create a field for the save file.
    string saveFile;

    // Create a GameData field.
    public GameData gameData = new GameData();

    // FileStream used for reading and writing files.
    FileStream dataStream;

    // Key for reading and writing encrypted data.
    // (This is a "hardcoded" secret key. )
    byte[] savedKey = { 0x16, 0x15, 0x16, 0x15, 0x16, 0x15, 0x16, 0x15, 0x16, 0x15, 0x16, 0x15, 0x16, 0x15, 0x16, 0x15 };

    void Awake()
    {
        // Update the path once the persistent path exists.
        saveFile = Application.persistentDataPath + "/gamedata.json";
    }

    public void readFile()
    {
        // Does the file exist?
        if (File.Exists(saveFile))
        {
            // Create FileStream for opening files.
            dataStream = new FileStream(saveFile, FileMode.Open);

            // Create new AES instance.
            Aes oAes = Aes.Create();

            // Create an array of correct size based on AES IV.
            byte[] outputIV = new byte[oAes.IV.Length];

            // Read the IV from the file.
            dataStream.Read(outputIV, 0, outputIV.Length);

            // Create CryptoStream, wrapping FileStream
            CryptoStream oStream = new CryptoStream(
                   dataStream,
                   oAes.CreateDecryptor(savedKey, outputIV),
                   CryptoStreamMode.Read);

            // Create a StreamReader, wrapping CryptoStream
            StreamReader reader = new StreamReader(oStream);

            // Read the entire file into a String value.
            string text = reader.ReadToEnd();

            // Deserialize the JSON data 
            //  into a pattern matching the GameData class.
            gameData = JsonUtility.FromJson<GameData>(text);
        }
    }

    public void writeFile()
    {
        // Create new AES instance.
        Aes iAes = Aes.Create();

        // Create a FileStream for creating files.
        dataStream = new FileStream(saveFile, FileMode.Create);

        // Save the new generated IV.
        byte[] inputIV = iAes.IV;

        // Write the IV to the FileStream unencrypted.
        dataStream.Write(inputIV, 0, inputIV.Length);

        // Create CryptoStream, wrapping FileStream.
        CryptoStream iStream = new CryptoStream(
                dataStream,
                iAes.CreateEncryptor(savedKey, iAes.IV),
                CryptoStreamMode.Write);

        // Create StreamWriter, wrapping CryptoStream.
        StreamWriter sWriter = new StreamWriter(iStream);

        // Serialize the object into JSON and save string.
        string jsonString = JsonUtility.ToJson(gameData);

        // Write to the innermost stream (which will encrypt).
        sWriter.Write(jsonString);

        // Close StreamWriter.
        sWriter.Close();

        // Close CryptoStream.
        iStream.Close();

        // Close FileStream.
        dataStream.Close();
    }
}</code></span></span>

在上面的代码中,secretKey字段用于加密和解密过程,将密钥作为类本身的一部分引用。

///

What is Encryption?

- In cryptography, encryption is the process of encoding information. This process converts the original representation of the information, known as plaintext, into an alternative form known as ciphertext. Ideally, only authorized parties can decipher a ciphertext back to plaintext and access the original information.

What is Decryption?

- The conversion of encrypted data into its original form is called Decryption. It is generally a reverse process of encryption. It decodes the encrypted information so that an authorized user can only decrypt the data because decryption requires a secret key or password.

There are many techniques and algorithms for data Encryption and Decryption in Unity(C#).

Here Describe 4 most popular algorithms to Encrypt or Decrypt process in C#.

1) AES

2) DES

3) MD5

4) XOR

Download Unity Demo Project: 

GitHub - sudhirkotila/Data-Security-Unity-Encryption-Decryption-Demo: There are many techniques and algorithms for data Encryption and Decryption in Unity(C#). Here Describe 4 most popular algorithms to Encrypt or Decrypt process in C#. 1) AES 2) DES 3) MD5 4) XOR

1) AES:

AES – Advanced Encryption Standard.

- This is more popular with a strong security level encryption-decryption data algorithm.

- The Advanced Encryption Standard, also known by its original name Rijndael is a specification for the encryption of electronic data established by the U.S. National Institute of Standards and Technology in 2001.

-  It is found at least six times faster than triple DES.

Example:

  
  using System;
  using System.Text;
  using System.Security.Cryptography;   
  string key = "A60A5770FE5E7AB200BA9CFC94E4E8B0"//set any string of 32 chars
  string iv = "1234567887654321"//set any string of 16 chars
 
  //AES - Encription 
  public string AESEncryption(string inputData)
    {
        AesCryptoServiceProvider AEScryptoProvider = new AesCryptoServiceProvider();
        AEScryptoProvider.BlockSize = 128;
        AEScryptoProvider.KeySize = 256;
        AEScryptoProvider.Key = ASCIIEncoding.ASCII.GetBytes(key);
        AEScryptoProvider.IV = ASCIIEncoding.ASCII.GetBytes(iv);
        AEScryptoProvider.Mode = CipherMode.CBC;
        AEScryptoProvider.Padding = PaddingMode.PKCS7;
 
        byte[] txtByteData = ASCIIEncoding.ASCII.GetBytes(inputData);
        ICryptoTransform trnsfrm = AEScryptoProvider.CreateEncryptor(AEScryptoProvider.KeyAEScryptoProvider.IV);
 
        byte[] result = trnsfrm.TransformFinalBlock(txtByteData0txtByteData.Length);
        return Convert.ToBase64String(result);
    }
 
    //AES -  Decryption
    public string AESDecryption(string inputData)
    {
        AesCryptoServiceProvider AEScryptoProvider = new AesCryptoServiceProvider();
        AEScryptoProvider.BlockSize = 128;
        AEScryptoProvider.KeySize = 256;
        AEScryptoProvider.Key = ASCIIEncoding.ASCII.GetBytes(key);
        AEScryptoProvider.IV = ASCIIEncoding.ASCII.GetBytes(iv);
        AEScryptoProvider.Mode = CipherMode.CBC;
        AEScryptoProvider.Padding = PaddingMode.PKCS7;
 
        byte[] txtByteData = Convert.FromBase64String(inputData);
        ICryptoTransform trnsfrm = AEScryptoProvider.CreateDecryptor();
 
        byte[] result = trnsfrm.TransformFinalBlock(txtByteData0txtByteData.Length);
        return ASCIIEncoding.ASCII.GetString(result);
    }
 

Input: This is AES Demo

Output: Pwsx645kDk6jTLZOOLjcYzRn//JwVdPu9zqH07rdUEY= 

2) DES:

- DES – Data Encryption Standard

- The Data Encryption Standard (DES) is a symmetric-key block cipher published by the National Institute of Standards and Technology (NIST).

- DES is a symmetric-key algorithm for the encryption of digital data. Although its short key length of 56 bits makes it too insecure for applications, it has been highly influential in the advancement of cryptography.

For more info: https://en.wikipedia.org/wiki/Data_Encryption_Standard

Example:

   using System;
   using System.Text;
   using System.Security.Cryptography; 
   using System.IO;
   string DESKey = "ABCDEFGH"//set any string of 8 chars
 
    //DES - Encription 
    public string DESEncryption(string inputData)
    {
        byte[] txtByteData = ASCIIEncoding.ASCII.GetBytes(inputData);
        byte[] keyByteData = ASCIIEncoding.ASCII.GetBytes(DESKey);
 
        DESCryptoServiceProvider DEScryptoProvider = new DESCryptoServiceProvider();
        ICryptoTransform trnsfrm = DEScryptoProvider.CreateEncryptor(keyByteDatakeyByteData);
        CryptoStreamMode mode = CryptoStreamMode.Write;
 
        //Set up Stream & Write Encript data
        MemoryStream mStream = new MemoryStream();
        CryptoStream cStream = new CryptoStream(mStreamtrnsfrmmode);
        cStream.Write(txtByteData0txtByteData.Length);
        cStream.FlushFinalBlock();
 
        //Read Ecncrypted Data From Memory Stream
        byte[] result = new byte[mStream.Length];
        mStream.Position = 0;
        mStream.Read(result0result.Length);
 
        return Convert.ToBase64String(result);
    }
 
    //DES -  Decryption
    public string DESDecryption(string inputData)
    {
        byte[] txtByteData = Convert.FromBase64String(inputData);
        byte[] keyByteData = ASCIIEncoding.ASCII.GetBytes(DESKey);
 
        DESCryptoServiceProvider DEScryptoProvider = new DESCryptoServiceProvider();
        ICryptoTransform trnsfrm = DEScryptoProvider.CreateDecryptor(keyByteDatakeyByteData);
        CryptoStreamMode mode = CryptoStreamMode.Write;
 
        //Set up Stream & Write Encript data
        MemoryStream mStream = new MemoryStream();
        CryptoStream cStream = new CryptoStream(mStreamtrnsfrmmode);
        cStream.Write(txtByteData0txtByteData.Length);
        cStream.FlushFinalBlock();
 
        //Read Ecncrypted Data From Memory Stream
        byte[] result = new byte[mStream.Length];
        mStream.Position = 0;
        mStream.Read(result0result.Length);
 
        return ASCIIEncoding.ASCII.GetString(result);
    }

 Input: This is DES Demo

Output: sUCnq2SAMpO7racUGDO85ovr9i5cFI4h

3) MD5:

The MD5 message-digest algorithm is a widely used hash function producing a 128-bit hash value.

- Although MD5 was initially designed to be used as a cryptographic hash function, it has been found to suffer from extensive vulnerabilities

For more info: https://en.wikipedia.org/wiki/MD5

Example:

    using System; 
    using System.Text;
    using System.Security.Cryptography; 
    //MD5 - Encryption 
    public string MD5Encryption(string inputData)
    {
        string hasKey = "Password@SudhirKotila12:34"//You can use any string here as haskey
        byte[] bData = UTF8Encoding.UTF8.GetBytes(inputData);
 
        MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
        TripleDESCryptoServiceProvider tripalDES = new TripleDESCryptoServiceProvider();
 
        tripalDES.Key = md5.ComputeHash(UTF8Encoding.UTF8.GetBytes(hasKey));
        tripalDES.Mode = CipherMode.ECB;
 
        ICryptoTransform trnsfrm = tripalDES.CreateEncryptor();
        byte[] result = trnsfrm.TransformFinalBlock(bData0bData.Length);
 
        return Convert.ToBase64String(result);
    }
 
    //MD5 -  Decryption
    public string MD5Decryption(string inputData)
    {
        string hasKey = "Password@SudhirKotila12:34"//You can use any string here as haskey
        byte[] bData = Convert.FromBase64String(inputData);
 
        MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
        TripleDESCryptoServiceProvider tripalDES = new TripleDESCryptoServiceProvider();
 
        tripalDES.Key = md5.ComputeHash(UTF8Encoding.UTF8.GetBytes(hasKey));
        tripalDES.Mode = CipherMode.ECB;
 
        ICryptoTransform trnsfrm = tripalDES.CreateDecryptor();
        byte[] result = trnsfrm.TransformFinalBlock(bData0bData.Length);
 
        return UTF8Encoding.UTF8.GetString(result);
    }

Input: This is MD5 Demo

Output: VYnMMSoeuy3jLwl3d6otyX33NBd7NibG

4) XOR:

- XOR Encryption is an encryption method used to encrypt data and is hard to crack by brute-force method, i.e generating encryption keys to match with the correct one

- This operation is sometimes called modulus 2 addition (or subtraction) process.

- The Logic is, a string of text can be encrypted by applying the bitwise XOR operator to every character using a given key. To decrypt the output, merely reapplying the XOR function with the key will remove the cipher.

For More info: https://en.wikipedia.org/wiki/XOR_cipher

Example:

    using System.Text;
    //XOR - Encription & Decryption
    public string XOREncryptDecrypt(string inputData)
    {
        StringBuilder outSB = new StringBuilder(inputData.Length);
        for (int i = 0i < inputData.Lengthi++)
        {     
            //Here 1234 is key for Encrypt/Decrypt, You can use any int number
            char ch = (char(inputData[i] ^ 1234); 
            outSB.Append(ch);
        }
        return outSB.ToString();
    }

 Input: This is XOR Demo

Output :  ҆ҺһҡӲһҡӲҊҝҀӲҖҷҿҽ

Download Unity Demo Project: https://drive.google.com/drive/folders/1oedqWl7skJO88UeQnZVnik7_l-i6vGhn?usp=sharing

///

在 Unity (C#) 中创建了一个用于“正确”加密的库

在Unity(C#)中,作为公用密钥加密的代表的AES加密(准确的说是Rijndael加密而不是AES加密)和作为公钥加密的代表的RSA加密可以很容易地实现和保护(解密)。我们发布了一个使它变得困难的库)。
如果你想要UnityCipher 统一
包,你可以从版本中下载,所以请从这里下载。使用UnityCipher Releases
Unity Package Manager (UPM)Packages/manifest.json下载时,描述以下内容。

包/清单.json
{ "dependencies": { "net.taptappun.taku.kobayashi.unitycipher": "https://github.com/TakuKobayashi/UnityCipher.git?path=/Assets/UnityCipher", ... } } 

或者你可以通过添加- WindowPackageManager->来安装它。Add package from git URL
https://github.com/TakuKobayashi/UnityCipher.git?path=/Assets/UnityCipher

参考

如何使用

下面有一个示例以了解详细用法UnityCipher/Examples/,请在此处查看。

此外,如果您使用库中的方法,您可以using UnityCipher通过添加来使用它。

使用AES 加密时

加密

可以通过调用如下方法对其进行加密。

string encrypted = RijndaelEncryption.Encrypt(planeText, passwordText); 

通过这样做,您可以获得加密的 ( encrypted)。您还可以指定方法的第一个参数 ha,它将
接收加密的. (参考以下)EncryptplaneTextbyte[]byte[]

byte[] encrypted = RijndaelEncryption.Encrypt(planeBinary, passwordText); 

解密

可以通过调用下面的方法来解密。

string planeText = RijndaelEncryption.Decrypt(encryptedText, passwordText); 

如果你能成功解密加密的那一个,你就可以得到解密的那一个(planeText)。您还可以将encrypted 指定为
Decrypt方法的第一个参数,在这种情况下,如果成功,您将收到解密的。(见下文)encryptedTextbyte[]byte[]

byte[] planeBinary = RijndaelEncryption.Decrypt(encryptedBinary, passwordText); 

使用RSA 加密时

创建公钥/私钥对

首先,通过调用以下方法创建公钥和私钥。

KeyValuePair<string, string> publicAndPrivateKeyValuePair = RSAEncryption.GenrateKeyPair(int keySize); 

此时,生成参数中设置的keySize中设置的值大小的公钥和私钥。可以指定的 keySize 值是 512 到 16384 之间的 8 位单位的数字(8 的倍数)。
(实际上,您可以创建一个 384 或更大的 keySize 值。)
有关详细信息,请参见此处。
RSACryptoServiceProvider.KeySize 属性
这里指定的值越大,生成的公私钥越长,越安全,但同时加解密所需的时间也会增加。它还增加了创建密钥所需的时间。
生成的公钥和私钥可以通过KeyValuePair<string, string>publicAndPrivateKeyValuePair)的形式获得,其中Key是公钥,Value是私钥。

加密

可以通过调用如下方法对其进行加密。

string encrypted = RSAEncryption.Encrypt(planeText, publicKey); 

encrypted您可以通过在第二个参数中指定上面生成的公钥 (publicKey)来获取RSA 加密的密文 ( )。
Encrypt您还可以指定is 的第一个参数,planeText在这种情况下您将收到byte[]一个加密版本。byte[](见下文)

byte[] encrypted = RSAEncryption.Encrypt(planeBinary, publicKey); 

解密

可以通过调用如下方法对其进行加密。

string planeText = RSAEncryption.Decrypt(encryptedText, privateKey); 

通过在第二个参数中指定上面生成的公钥(privateKey),planeText如果可以成功解密,您可以获得解密的一个( )。
Decrypt, 的第一个参数planeText可以指定byte[],也可以byte[]指定,在这种情况下,如果可以解密成功,它将接收解密byte[]。(见下文)

byte[] planeBinary = RSAEncryption.Decrypt(encryptedBinary, privateKey); 

特征

作为这个库的一个特点

  • 它可以简单地使用。
  • 它是加密的,具有很强的安全性。

它具有以上特点。

什么是强安全?

UnityCipher如果使用公用密钥加密,每次加密都会生成不同的密文,即使使用相同的明文和相同的密码。此外,从下面的“规格”中,不会观察到由于加密导致的某些模式。(因为每次加密都会生成 Salt 和 IV)

由于RSA 密码使用的是标准密码,所以这次我将省略细节。

共同故事

在实现AES密码学时,需要设置四种元素:明文、密码、Salt和IV(初始向量) 。将 Salt 和 IV 设置为常量是很常见的。然而,AES加密的原始特点是“即使明文相同,密码相同,每次加密时密文都会发生变化”
但是,如果IV 和 salt 是常数,则此功能将根本不起作用。(即相同的明文和相同的密码,每次都会产生相同的密文。)
在实际操作中,很多情况下使用Salt和IV这样的常量。

规格

在这个库中,每次在 AES 加密中执行加密时都会生成 Salt 和 IV。byte[]我们生成Salt + IV + 密文的串联,byte[]以便我们可以得到它。(点击这里查看详细信息)
这使得“每次加密时都更改密文,即使它具有相同的明文和相同的密码”成为可能。

此外,由于上述规范已经提到,如果您想更安全地操作,您还可以通过制定自己的规则来应对,例如更改顺序或更改位规则。

//

Unity Editor Encryption Scheme Based on Mono

Keywords: Unity Windows github encoding

Introduction

This blog is written to game developers who need to protect their intellectual property rights. The encryption method of this article is relatively simple and easy to understand, but the effect is enough to deter most of the small white stealers.

Windows Encryption Chapter

Unity2017-based

I. preparation

1.Mono source code (download the source code according to your Unity version): https://github.com/Unity-Technologies/mono

2. Visual Studio 2010 (Mono version 2017 needs to be compiled with this VS version)

3. encryption algorithm (arbitrarily, based on C encoding).

Two. Text

1. Modify the load source code (the first few parts are relatively simple, no more details, directly into the topic)

  • Principle: C# is able to load DLL(Assembly.LoadFile) dynamically. We mainly do some articles here.
    • After reading mono source code, we can know
    • Mon_image_open_from_data_with_name:
MonoImage *
mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name)
{
	MonoCLIImageInfo *iinfo;
	MonoImage *image;
	char *datac;

	if (!data || !data_len) {
		if (status)
			*status = MONO_IMAGE_IMAGE_INVALID;
		return NULL;
	}

	// The data here is the data after reading the DLL
	// Here we can decrypt our encrypted DLL by filtering names.

	datac = data;
	if (need_copy) {
		datac = g_try_malloc (data_len);
		if (!datac) {
			if (status)
				*status = MONO_IMAGE_ERROR_ERRNO;
			return NULL;
		}
		memcpy (datac, data, data_len);
	}

	image = g_new0 (MonoImage, 1);
	image->raw_data = datac;
	image->raw_data_len = data_len;
	image->raw_data_allocated = need_copy;
	image->name = (name == NULL) ? g_strdup_printf ("data-%p", datac) : g_strdup(name);
	iinfo = g_new0 (MonoCLIImageInfo, 1);
	image->image_info = iinfo;
	image->ref_only = refonly;
	image->ref_count = 1;

	image = do_mono_image_load (image, status, TRUE, TRUE);
	if (image == NULL)
		return NULL;

	return register_image (image);
}
  • Read mono_assembly_open_full with DLL code assembly.c
MonoAssembly *
mono_assembly_open_full (const char *filename, MonoImageOpenStatus *status, gboolean refonly)
{
	MonoImage *image;
	MonoAssembly *ass;
	MonoImageOpenStatus def_status;
	gchar *fname;
	gchar *new_fname;
	
	g_return_val_if_fail (filename != NULL, NULL);

	if (!status)
		status = &def_status;
	*status = MONO_IMAGE_OK;

	if (strncmp (filename, "file://", 7) == 0) {
		GError *error = NULL;
		gchar *uri = (gchar *) filename;
		gchar *tmpuri;

		/*
		 * MS allows file://c:/... and fails on file://localhost/c:/... 
		 * They also throw an IndexOutOfRangeException if "file://"
		 */
		if (uri [7] != '/')
			uri = g_strdup_printf ("file:///%s", uri + 7);
	
		tmpuri = uri;
		uri = mono_escape_uri_string (tmpuri);
		fname = g_filename_from_uri (uri, NULL, &error);
		g_free (uri);

		if (tmpuri != filename)
			g_free (tmpuri);

		if (error != NULL) {
			g_warning ("%s\n", error->message);
			g_error_free (error);
			fname = g_strdup (filename);
		}
	} else {
		fname = g_strdup (filename);
	}

	mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
			"Assembly Loader probing location: '%s'.", fname);

	new_fname = NULL;
	if (!mono_assembly_is_in_gac (fname))
		new_fname = mono_make_shadow_copy (fname);
	if (new_fname && new_fname != fname) {
		g_free (fname);
		fname = new_fname;
		mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
			    "Assembly Loader shadow-copied assembly to: '%s'.", fname);
	}
	
	image = NULL;
	// The data read in will be analyzed here.
	// When bundles are encrypted, the original DLL data format is changed
	// So we need to deal with it here so that the read DLL data is not filtered.
	
	FILE * pFile;
	long lSize;
	char * buffer;
	size_t result;
	char*name;
	name = g_path_get_basename(filename); // Get the DLL name
	if (strstr(name, "MyXXX.dll" != NULL) // Determine whether the required decryption conditions are met (here is the name of the DLL)
	{
		pFile = fopen(filename, "rb");
		fseek(pFile, 0, SEEK_END);
		lSize = ftell(pFile);
		rewind(pFile);
		buffer = (char*) malloc (sizeof(char)*lSize);
		result=fread(buffer, 1, lSize, pFile);
		fclose(pFile);
		image = mono_image_open_from_data_with_name(buffer, lSize, FALSE, status, refonly, name);
		free(buffer);
	}
	else if (bundles != NULL)
		image = mono_assembly_open_from_bundle (fname, status, refonly);

	if (!image)
		image = mono_image_open_full (fname, status, refonly);

	if (!image){
		if (*status == MONO_IMAGE_OK)
			*status = MONO_IMAGE_ERROR_ERRNO;
		g_free (fname);
		return NULL;
	}

	if (image->assembly) {
		/* Already loaded by another appdomain */
		mono_assembly_invoke_load_hook (image->assembly);
		mono_image_close (image);
		g_free (fname);
		return image->assembly;
	}

	ass = mono_assembly_load_from_full (image, fname, status, refonly);

	if (ass) {
		mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY,
				"Assembly Loader loaded assembly from location: '%s'.", filename);
		if (!refonly)
			mono_config_for_assembly (ass->image);
	}

	/* Clear the reference added by mono_image_open */
	mono_image_close (image);
	
	g_free (fname);

	return ass;
}

Later, VS2010 is used for compilation and substitution to take effect.
After 2018, the method is different. After I update it again, the principle is basically the same.

///

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

警醒与鞭策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值