首页 > 解决方案 > ITI 是否存在相当于“Memset”或“SecureZeroMemory”的 C#

问题描述

我正在创建一个 Unity 游戏,所以我需要一些安全性来保护我的保存文件和其他敏感数据。为此,我创建了一些使用内置 AES 和 RSA 加密方法的函数。但是,它们应该具有某种“内存清除”功能,可以自动将不再使用的任何内存归零。问题是,我在 c# 中找不到任何等价物。我能找到的最接近的是

[DllImport("KERNEL32.DLL", EntryPoint = "RtlZeroMemory")]
public unsafe static extern bool ZeroMemory(byte* destination, int length);

但我不知道如何使用它(以前从未使用过指针)。任何帮助将非常感激。

编辑:添加代码

请注意,我正在从我的 Main() 程序中调用这些函数

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using Encryption_test;

class OurCodeWorld
{
    public static int _BlockSize = 128,
        _KeySize = 256,
        _Iterations = 50000;
    public static PaddingMode _PaddingMode = PaddingMode.PKCS7;
    public static CipherMode _CipherMode = CipherMode.CBC;
    //  Call this function to remove the key from memory after use for security
    [DllImport("KERNEL32.DLL", EntryPoint = "RtlZeroMemory")]
    public unsafe static extern bool ZeroMemory(byte* destination, int length);
    /// <summary>
    /// Creates a random salt that will be used to encrypt your file. This method is required on FileEncrypt.
    /// </summary>
    /// <returns></returns>
    public static byte[] GenerateRandomSalt(int size = 32)
    {
        byte[] data = new byte[size];

        using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
        {
            for (int i = 0; i < size/6; i++)
            {
                // Fill the buffer with the generated data
                rng.GetBytes(data);
            }
        }
        return data;
    }

    /// <summary>
    /// Encrypts a file from its path and a plain password.
    /// </summary>
    /// <param name="inputFile">The file to encrypt</param>
    /// <param name="password">The key used to encrypt the file</param>
    public static void FileEncrypt(string inputFile, string password)
    {
        //http://stackoverflow.com/questions/27645527/aes-encryption-on-large-files

        //generate random salt
        byte[] salt = GenerateRandomSalt();

        //create output file name
        FileStream fsCrypt = new FileStream(inputFile + Program.encryptedExtension, FileMode.Create);

        //convert password string to byte arrray
        byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);

        //Set Rijndael symmetric encryption algorithm
        RijndaelManaged AES = new RijndaelManaged
        {
            KeySize = _KeySize,
            BlockSize = _BlockSize,
            Padding = _PaddingMode
        };

        //http://stackoverflow.com/questions/2659214/why-do-i-need-to-use-the-rfc2898derivebytes-class-in-net-instead-of-directly
        //"What it does is repeatedly hash the user password along with the salt." High iteration counts.
        var key = new Rfc2898DeriveBytes(passwordBytes, salt, _Iterations);
        AES.Key = key.GetBytes(AES.KeySize / 8);
        AES.IV = key.GetBytes(AES.BlockSize / 8);

        //Cipher modes: http://security.stackexchange.com/questions/52665/which-is-the-best-cipher-mode-and-padding-mode-for-aes-encryption
        AES.Mode = _CipherMode;

        // write salt to the begining of the output file, so in this case can be random every time
        fsCrypt.Write(salt, 0, salt.Length);

        CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);

        FileStream fsIn = new FileStream(inputFile, FileMode.Open);

        //create a buffer (1mb) so only this amount will allocate in the memory and not the whole file
        byte[] buffer = new byte[1048576];
        int read;

        try
        {
            while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
            {
                //Application.DoEvents(); // -> for responsive GUI, using Task will be better!
                cs.Write(buffer, 0, read);
            }

            // Close up
            fsIn.Close();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: " + ex.Message);
        }
        finally
        {
            cs.Close();
            fsCrypt.Close();
        }
    }

    /// <summary>
    /// Decrypts an encrypted file with the FileEncrypt method through its path and the plain password.
    /// </summary>
    /// <param name="inputFile"></param>
    /// <param name="outputFile"></param>
    /// <param name="password"></param>
    public static void FileDecrypt(string inputFile, string outputFile, string password, int saltLength = 32)
    {
        byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);
        byte[] salt = new byte[saltLength];

        FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);
        fsCrypt.Read(salt, 0, salt.Length);

        RijndaelManaged AES = new RijndaelManaged
        {
            KeySize = _KeySize,
            BlockSize = _BlockSize
        };
        var key = new Rfc2898DeriveBytes(passwordBytes, salt, _Iterations);
        AES.Key = key.GetBytes(AES.KeySize / 8);
        AES.IV = key.GetBytes(AES.BlockSize / 8);
        AES.Padding = _PaddingMode;
        AES.Mode = _CipherMode;

        CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateDecryptor(), CryptoStreamMode.Read);

        FileStream fsOut = new FileStream(outputFile, FileMode.Create);

        int read;
        byte[] buffer = new byte[1048576];

        try
        {
            while ((read = cs.Read(buffer, 0, buffer.Length)) > 0)
            {
                //Application.DoEvents();
                fsOut.Write(buffer, 0, read);
            }
        }
        catch (CryptographicException ex_CryptographicException)
        {
            Console.WriteLine("CryptographicException error: " + ex_CryptographicException.Message);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: " + ex.Message);
        }

        try
        {
            cs.Close();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error by closing CryptoStream: " + ex.Message);
        }
        finally
        {
            fsOut.Close();
            fsCrypt.Close();
        }
    }
}

标签: c#securitydll

解决方案


我在这里回答我自己的问题,请随时添加。 ZeroMemory不是 C# 方法,它是存储在 windows dll 中的非托管(可能是 C/C++)方法,在低级代码领域Kernel32.dll被调用。RtlZeroMemory这就是extern修饰符和DllImport属性的作用;他们告诉 .Net 我想从中获取一个方法.dll并使用它。

从技术上讲,有一个解决方案 - 只需使用这样的指针(但我不确定这与原始指针有多相似):

public static unsafe void ZeroMemory(byte* ptr, int length)
{
    //Loop to cover all the bytes
    for (int i = 0; i < length; i++)
        //Set the byte at the index of (start + i) to 0
        *(ptr + i) = 0;
}

我的问题最初源于对我在做什么缺乏了解,所以我对所有事情都感到困惑(比如如何使用该方法),这就是我发布问题的原因。我试图将密钥归零,以便无法从内存中读取它,这应该可以通过使用上面的代码来实现。


推荐阅读