c#加密文本输出
本文关键字:输出 加密文本 | 更新日期: 2023-09-27 18:06:15
我创建了一些小程序,使用StreamWriter将数据导出到文本文件,然后我使用StreamReader读取它们。这工作很好,做什么我需要它做的,但我想知道是否有一种方法,我可以保存这些信息,而无需用户能够访问或修改它有意或无意。我在文本文件中的一个例子是,如果一个复选框被选中,当你勾选它时,它会输出"被选中"到文本文件,当程序重新打开时,我知道表单在关闭时的状态。我显然不想继续使用文本文件。有没有人有任何想法,我可以很容易地存储这些信息,而不需要用户修改它?非常感谢。
最简单的方法是Base-64编码/解码该文本。这是不安全的,但可以防止临时用户修改数据。
static public string EncodeTo64(string toEncode)
{
byte[] toEncodeAsBytes
= System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode);
string returnValue
= System.Convert.ToBase64String(toEncodeAsBytes);
return returnValue;
}
static public string DecodeFrom64(string encodedData)
{
byte[] encodedDataAsBytes
= System.Convert.FromBase64String(encodedData);
string returnValue =
System.Text.ASCIIEncoding.ASCII.GetString(encodedDataAsBytes);
return returnValue;
}
EDIT: Real encryption
#region Encryption
string passPhrase = "Pasword"; // can be any string
string saltValue = "sALtValue"; // can be any string
string hashAlgorithm = "SHA1"; // can be "MD5"
int passwordIterations = 7; // can be any number
string initVector = "~1B2c3D4e5F6g7H8"; // must be 16 bytes
int keySize = 256; // can be 192 or 128
private string Encrypt(string data)
{
byte[] bytes = Encoding.ASCII.GetBytes(this.initVector);
byte[] rgbSalt = Encoding.ASCII.GetBytes(this.saltValue);
byte[] buffer = Encoding.UTF8.GetBytes(data);
byte[] rgbKey = new PasswordDeriveBytes(this.passPhrase, rgbSalt, this.hashAlgorithm, this.passwordIterations).GetBytes(this.keySize / 8);
RijndaelManaged managed = new RijndaelManaged();
managed.Mode = CipherMode.CBC;
ICryptoTransform transform = managed.CreateEncryptor(rgbKey, bytes);
MemoryStream stream = new MemoryStream();
CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Write);
stream2.Write(buffer, 0, buffer.Length);
stream2.FlushFinalBlock();
byte[] inArray = stream.ToArray();
stream.Close();
stream2.Close();
return Convert.ToBase64String(inArray);
}
private string Decrypt(string data)
{
byte[] bytes = Encoding.ASCII.GetBytes(this.initVector);
byte[] rgbSalt = Encoding.ASCII.GetBytes(this.saltValue);
byte[] buffer = Convert.FromBase64String(data);
byte[] rgbKey = new PasswordDeriveBytes(this.passPhrase, rgbSalt, this.hashAlgorithm, this.passwordIterations).GetBytes(this.keySize / 8);
RijndaelManaged managed = new RijndaelManaged();
managed.Mode = CipherMode.CBC;
ICryptoTransform transform = managed.CreateDecryptor(rgbKey, bytes);
MemoryStream stream = new MemoryStream(buffer);
CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Read);
byte[] buffer5 = new byte[buffer.Length];
int count = stream2.Read(buffer5, 0, buffer5.Length);
stream.Close();
stream2.Close();
return Encoding.UTF8.GetString(buffer5, 0, count);
}
#endregion
您应该调用ProtectedData.Protect
来使用每个用户的密钥加密数据。
请注意,对于一个熟练的用户来说,解密和修改数据并不难。
你的程序在用户机器上做的任何事情,用户也可以做。
您可以向文件添加校验和或哈希-如果文件内容与校验和不一致,您就知道它被篡改了。
如果重要的是用户不能读取文件的内容,您可以加密它。
我不相信你可以制作一个不能被篡改的文件(例如,一个精明的用户可以使用十六进制编辑器并更改它)-你能做的最好的是检测这种篡改。
您可以使用Ionic zip库来压缩这些文本文件。如果有必要,你也可以使用Ionic zip的功能,如密码保护和加密。您仍然可以使用最初创建文件时使用的相同设置手动打开文件(使用压缩应用程序,例如7zip)。
如果程序可以访问信息,用户通常也可以。但是,您可以生成用户无法立即理解的数据。
我将首先创建一个类来保存您想要保存的所有状态信息,从而隔离问题。巧合的是,BinaryFormatter
类将允许您轻松地将该类保存并加载到文件中。我不知道它的结果是否"足够不可读"——如果不是,就像Leon提到的那样应用Base64编码。
虽然您可以base64编码甚至完全加密您的配置数据(使用SHA1或MD5),正如已经建议的那样,我认为良好的做法是使用处理配置数据的框架类(Configuration
在System.Configuration
命名空间下),并且它内置了加密数据的能力(通过ConfigurationSection
类的ProtectSection
方法)。
首先你应该声明并初始化一个实例:
using System.Configuration;
...
static void Main(string[] args)
{
Configuration config;
config = ConfigurationManager.OpenExeConfiguration(/*path to config file*/); //Use ConfigurationManager.OpenMachineConfiguration(/*path to config file*/) when opening machine configuration
...
之后,您需要定义一个自定义配置部分来定义您的配置(msdn示例)
一旦你完成了,你只需要初始化一个自定义配置节的实例,并使用以下代码将其添加到配置文件:
isTicked = config.Sections.Add("isTicked", customSection);
要加密您刚刚添加的部分,请使用以下代码(在VB和VB中都有进一步的示例)。. NET和c#):
config.Sections["isTicked"].SectionInformation.ProtectSection("protection provider");
"DPAPIProtectedConfigurationProvider"answers"RSAProtectedConfigurationProvider"是默认内置的。
一旦你想解密这个部分,使用下面的代码:
config.Sections["isTicked"].SectionInformation.UnprotectSection();
强调一点- 加密和解密只有在保存配置文件
后才能生效保存文件,使用以下代码:
config.Save(); //config.SaveAs("string") is also available
有关相关类和方法的进一步信息可以在msdn中找到,从上面链接的Configuration
类页面开始。
尝试此代码加密和解密您的文本!我认为这很容易,而且很结实。
public static class Crypto
{
private static readonly byte[] IVa = new byte[] { 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };
public static string Encrypt(this string text, string salt)
{
try
{
using (Aes aes = new AesManaged())
{
Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt));
aes.Key = deriveBytes.GetBytes(128 / 8);
aes.IV = aes.Key;
using (MemoryStream encryptionStream = new MemoryStream())
{
using (CryptoStream encrypt = new CryptoStream(encryptionStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
byte[] cleanText = Encoding.UTF8.GetBytes(text);
System.Diagnostics.Debug.WriteLine(String.Concat("Before encryption text data size: ", text.Length.ToString()));
System.Diagnostics.Debug.WriteLine(String.Concat("Before encryption byte data size: ", cleanText.Length.ToString()));
encrypt.Write(cleanText, 0, cleanText.Length);
encrypt.FlushFinalBlock();
}
byte[] encryptedData = encryptionStream.ToArray();
string encryptedText = Convert.ToBase64String(encryptedData);
System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted text data size: ", encryptedText.Length.ToString()));
System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted byte data size: ", encryptedData.Length.ToString()));
return encryptedText;
}
}
}
catch(Exception e)
{
return String.Empty;
}
}
public static string Decrypt(this string text, string salt)
{
try
{
using (Aes aes = new AesManaged())
{
Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt));
aes.Key = deriveBytes.GetBytes(128 / 8);
aes.IV = aes.Key;
using (MemoryStream decryptionStream = new MemoryStream())
{
using (CryptoStream decrypt = new CryptoStream(decryptionStream, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
byte[] encryptedData = Convert.FromBase64String(text);
System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted text data size: ", text.Length.ToString()));
System.Diagnostics.Debug.WriteLine(String.Concat("Encrypted byte data size: ", encryptedData.Length.ToString()));
decrypt.Write(encryptedData, 0, encryptedData.Length);
decrypt.Flush();
}
byte[] decryptedData = decryptionStream.ToArray();
string decryptedText = Encoding.UTF8.GetString(decryptedData, 0, decryptedData.Length);
System.Diagnostics.Debug.WriteLine(String.Concat("After decryption text data size: ", decryptedText.Length.ToString()));
System.Diagnostics.Debug.WriteLine(String.Concat("After decryption byte data size: ", decryptedData.Length.ToString()));
return decryptedText;
}
}
}
catch(Exception e)
{
return String.Empty;
}
}
}
只是为了添加Leon的答案的另一个实现,并遵循微软文档
这里有一个加密和解密字符串的类示例
public static class EncryptionExample
{
#region internal consts
internal const string passPhrase = "pass";
internal const string saltValue = "salt";
internal const string hashAlgorithm = "MD5";
internal const int passwordIterations = 3; // can be any number
internal const string initVector = "0123456789abcdf"; // must be 16 bytes
internal const int keySize = 64; // can be 192 or 256
#endregion
#region public static Methods
public static string Encrypt(string data)
{
string res = string.Empty;
try
{
byte[] bytes = Encoding.ASCII.GetBytes(initVector);
byte[] rgbSalt = Encoding.ASCII.GetBytes(saltValue);
byte[] buffer = Encoding.UTF8.GetBytes(data);
byte[] rgbKey = new PasswordDeriveBytes(passPhrase, rgbSalt, hashAlgorithm, passwordIterations).GetBytes(keySize / 8);
RijndaelManaged managed = new RijndaelManaged();
managed.Mode = CipherMode.CBC;
ICryptoTransform transform = managed.CreateEncryptor(rgbKey, bytes);
byte[] inArray = null;
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, transform, CryptoStreamMode.Write))
{
csEncrypt.Write(buffer, 0, buffer.Length);
csEncrypt.FlushFinalBlock();
inArray = msEncrypt.ToArray();
res = Convert.ToBase64String(inArray);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Encrypt " + ex);
}
return res;
}
public static string Decrypt(string data)
{
string res = string.Empty;
try
{
byte[] bytes = Encoding.ASCII.GetBytes(initVector);
byte[] rgbSalt = Encoding.ASCII.GetBytes(saltValue);
byte[] buffer = Convert.FromBase64String(data);
byte[] rgbKey = new PasswordDeriveBytes(passPhrase, rgbSalt, hashAlgorithm, passwordIterations).GetBytes(keySize / 8);
RijndaelManaged managed = new RijndaelManaged();
managed.Mode = CipherMode.CBC;
ICryptoTransform transform = managed.CreateDecryptor(rgbKey, bytes);
using (MemoryStream msEncrypt = new MemoryStream(buffer))
{
using (CryptoStream csDecrypt = new CryptoStream(msEncrypt, transform, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
res = srDecrypt.ReadToEnd();
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Decrypt " + ex);
}
return res;
}
}
顺便说一下,这是我在谷歌上搜索过的"盐值"的定义。
盐值如果攻击者不知道密码,并且试图用暴力攻击来猜测密码,那么他尝试的每个密码都必须用每个盐值来尝试。因此,对于一个位盐(0或1),这使得加密以这种方式破解的难度增加了一倍。
可以使用校验和来防止无意的字符串修改,正如这个答案所指出的那样。
然而,生成这样的校验和是很容易的,因为它们不是那么多广泛使用的算法。
因此,这并不能保护您免受有意修改的影响。为了防止这种情况,人们使用数字签名。这使得任何人都可以验证您的数据没有被篡改,但只有您(私有秘密的所有者)可以生成签名。下面是c#中的一个例子。
然而,正如其他人指出的那样,您需要将私钥嵌入到二进制文件中的某个地方,并且(不那么)熟练的程序员将能够检索它,即使您混淆了。net dll或在单独的本机进程中进行。
这对大多数人来说已经足够了。
如果您真的关心安全性,那么您需要迁移到云上,并在您自己的机器上执行代码。