C# 简单不安全的双向数据“混淆”?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/165808/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Simple insecure two-way data "obfuscation"?
提问by Matt Dawdy
I'm looking for very simple obfuscation (like encrypt and decrypt but not necessarily secure) functionality for some data. It's not mission critical. I need something to keep honest people honest, but something a little stronger than ROT13or Base64.
我正在为某些数据寻找非常简单的混淆(如加密和解密但不一定安全)功能。这不是关键任务。我需要一些东西来让诚实的人保持诚实,但比ROT13或Base64强一些。
I'd prefer something that is already included in the .NETframework 2.0, so I don't have to worry about any external dependencies.
我更喜欢.NETframework 2.0 中已经包含的东西,所以我不必担心任何外部依赖。
I really don't want to have to mess around with public/private keys, etc. I don't know much about encryption, but I do know enough to know that anything I wrote would be less than worthless... In fact, I'd probably screw up the math and make it trivial to crack.
我真的不想搞乱公钥/私钥等。我对加密知之甚少,但我确实知道我写的任何东西都不会一文不值......事实上,我可能会搞砸数学并使破解变得微不足道。
采纳答案by Mark Brittingham
Other answers here work fine, but AES is a more secure and up-to-date encryption algorithm. This is a class that I obtained a few years ago to perform AES encryption that I have modified over time to be more friendly for web applications (e,g. I've built Encrypt/Decrypt methods that work with URL-friendly string). It also has the methods that work with byte arrays.
这里的其他答案工作正常,但 AES 是一种更安全和最新的加密算法。这是我几年前获得的一个类,用于执行 AES 加密,我随着时间的推移对其进行了修改,以便对 Web 应用程序更加友好(例如,我已经构建了使用 URL 友好字符串的 Encrypt/Decrypt 方法)。它还具有处理字节数组的方法。
NOTE: you should use different values in the Key (32 bytes) and Vector (16 bytes) arrays! You wouldn't want someone to figure out your keys by just assuming that you used this code as-is! All you have to do is change some of the numbers (must be <= 255) in the Key and Vector arrays (I left one invalid value in the Vector array to make sure you do this...). You can use https://www.random.org/bytes/to generate a new set easily:
注意:您应该在 Key(32 字节)和 Vector(16 字节)数组中使用不同的值!您不希望有人通过假设您按原样使用此代码来找出您的密钥!您所要做的就是更改 Key 和 Vector 数组中的一些数字(必须<= 255)(我在 Vector 数组中保留了一个无效值以确保您这样做...)。您可以使用https://www.random.org/bytes/轻松生成新集合:
Using it is easy: just instantiate the class and then call (usually) EncryptToString(string StringToEncrypt) and DecryptString(string StringToDecrypt) as methods. It couldn't be any easier (or more secure) once you have this class in place.
使用它很容易:只需实例化类,然后(通常)调用 EncryptToString(string StringToEncrypt) 和 DecryptString(string StringToDecrypt) 作为方法。一旦你有了这个类,就再容易(或更安全)了。
using System;
using System.Data;
using System.Security.Cryptography;
using System.IO;
public class SimpleAES
{
// Change these keys
private byte[] Key = __Replace_Me__({ 123, 217, 19, 11, 24, 26, 85, 45, 114, 184, 27, 162, 37, 112, 222, 209, 241, 24, 175, 144, 173, 53, 196, 29, 24, 26, 17, 218, 131, 236, 53, 209 });
// a hardcoded IV should not be used for production AES-CBC code
// IVs should be unpredictable per ciphertext
private byte[] Vector = __Replace_Me__({ 146, 64, 191, 111, 23, 3, 113, 119, 231, 121, 2521, 112, 79, 32, 114, 156 });
private ICryptoTransform EncryptorTransform, DecryptorTransform;
private System.Text.UTF8Encoding UTFEncoder;
public SimpleAES()
{
//This is our encryption method
RijndaelManaged rm = new RijndaelManaged();
//Create an encryptor and a decryptor using our encryption method, key, and vector.
EncryptorTransform = rm.CreateEncryptor(this.Key, this.Vector);
DecryptorTransform = rm.CreateDecryptor(this.Key, this.Vector);
//Used to translate bytes to text and vice versa
UTFEncoder = new System.Text.UTF8Encoding();
}
/// -------------- Two Utility Methods (not used but may be useful) -----------
/// Generates an encryption key.
static public byte[] GenerateEncryptionKey()
{
//Generate a Key.
RijndaelManaged rm = new RijndaelManaged();
rm.GenerateKey();
return rm.Key;
}
/// Generates a unique encryption vector
static public byte[] GenerateEncryptionVector()
{
//Generate a Vector
RijndaelManaged rm = new RijndaelManaged();
rm.GenerateIV();
return rm.IV;
}
/// ----------- The commonly used methods ------------------------------
/// Encrypt some text and return a string suitable for passing in a URL.
public string EncryptToString(string TextValue)
{
return ByteArrToString(Encrypt(TextValue));
}
/// Encrypt some text and return an encrypted byte array.
public byte[] Encrypt(string TextValue)
{
//Translates our text value into a byte array.
Byte[] bytes = UTFEncoder.GetBytes(TextValue);
//Used to stream the data in and out of the CryptoStream.
MemoryStream memoryStream = new MemoryStream();
/*
* We will have to write the unencrypted bytes to the stream,
* then read the encrypted result back from the stream.
*/
#region Write the decrypted value to the encryption stream
CryptoStream cs = new CryptoStream(memoryStream, EncryptorTransform, CryptoStreamMode.Write);
cs.Write(bytes, 0, bytes.Length);
cs.FlushFinalBlock();
#endregion
#region Read encrypted value back out of the stream
memoryStream.Position = 0;
byte[] encrypted = new byte[memoryStream.Length];
memoryStream.Read(encrypted, 0, encrypted.Length);
#endregion
//Clean up.
cs.Close();
memoryStream.Close();
return encrypted;
}
/// The other side: Decryption methods
public string DecryptString(string EncryptedString)
{
return Decrypt(StrToByteArray(EncryptedString));
}
/// Decryption when working with byte arrays.
public string Decrypt(byte[] EncryptedValue)
{
#region Write the encrypted value to the decryption stream
MemoryStream encryptedStream = new MemoryStream();
CryptoStream decryptStream = new CryptoStream(encryptedStream, DecryptorTransform, CryptoStreamMode.Write);
decryptStream.Write(EncryptedValue, 0, EncryptedValue.Length);
decryptStream.FlushFinalBlock();
#endregion
#region Read the decrypted value from the stream.
encryptedStream.Position = 0;
Byte[] decryptedBytes = new Byte[encryptedStream.Length];
encryptedStream.Read(decryptedBytes, 0, decryptedBytes.Length);
encryptedStream.Close();
#endregion
return UTFEncoder.GetString(decryptedBytes);
}
/// Convert a string to a byte array. NOTE: Normally we'd create a Byte Array from a string using an ASCII encoding (like so).
// System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
// return encoding.GetBytes(str);
// However, this results in character values that cannot be passed in a URL. So, instead, I just
// lay out all of the byte values in a long string of numbers (three per - must pad numbers less than 100).
public byte[] StrToByteArray(string str)
{
if (str.Length == 0)
throw new Exception("Invalid string value in StrToByteArray");
byte val;
byte[] byteArr = new byte[str.Length / 3];
int i = 0;
int j = 0;
do
{
val = byte.Parse(str.Substring(i, 3));
byteArr[j++] = val;
i += 3;
}
while (i < str.Length);
return byteArr;
}
// Same comment as above. Normally the conversion would use an ASCII encoding in the other direction:
// System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
// return enc.GetString(byteArr);
public string ByteArrToString(byte[] byteArr)
{
byte val;
string tempStr = "";
for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
{
val = byteArr[i];
if (val < (byte)10)
tempStr += "00" + val.ToString();
else if (val < (byte)100)
tempStr += "0" + val.ToString();
else
tempStr += val.ToString();
}
return tempStr;
}
}
回答by Mitch Wheat
The namespace System.Security.Cryptography
contains the TripleDESCryptoServiceProvider
and RijndaelManaged
classes
命名空间System.Security.Cryptography
包含TripleDESCryptoServiceProvider
和RijndaelManaged
类
Don't forget to add a reference to the System.Security
assembly.
不要忘记添加对System.Security
程序集的引用。
回答by ZeroBugBounce
Yes, add the System.Security
assembly, import the System.Security.Cryptography
namespace. Here's a simple example of a symmetric (DES) algorithm encryption:
是的,添加System.Security
程序集,导入System.Security.Cryptography
命名空间。这是对称 (DES) 算法加密的一个简单示例:
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.GenerateKey();
byte[] key = des.Key; // save this!
ICryptoTransform encryptor = des.CreateEncryptor();
// encrypt
byte[] enc = encryptor.TransformFinalBlock(new byte[] { 1, 2, 3, 4 }, 0, 4);
ICryptoTransform decryptor = des.CreateDecryptor();
// decrypt
byte[] originalAgain = decryptor.TransformFinalBlock(enc, 0, enc.Length);
Debug.Assert(originalAgain[0] == 1);
回答by paxdiablo
If you just want simple encryption (i.e., possible for a determined cracker to break, but locking out most casual users), just pick two passphrases of equal length, say:
如果您只想要简单的加密(即,确定的破解者可能会破解,但锁定大多数临时用户),只需选择两个长度相同的密码,例如:
deoxyribonucleicacid
while (x>0) { x-- };
and xor your data with both of them (looping the passphrases if necessary)(a). For example:
并将您的数据与它们两者进行异或(如有必要,循环密码)(a)。例如:
1111-2222-3333-4444-5555-6666-7777
deoxyribonucleicaciddeoxyribonucle
while (x>0) { x-- };while (x>0) {
Someone searching your binary may well think the DNA string is a key, but they're unlikely to think the C code is anything other than uninitialized memory saved with your binary.
搜索您的二进制文件的人很可能认为 DNA 字符串是一个键,但他们不太可能认为 C 代码是与您的二进制文件一起保存的未初始化内存以外的任何东西。
(a)Keep in mind this is verysimple encryption and, by some definitions, may not be considered encryption at all (since the intent of encryption is to preventunauthorised access rather than just make it more difficult). Although, of course, even the strongest encryption is insecure when someone's standing over the key-holders with a steel pipe.
(a)请记住,这是非常简单的加密,根据某些定义,它可能根本不被视为加密(因为加密的目的是防止未经授权的访问,而不仅仅是让它变得更加困难)。当然,当有人拿着钢管站在钥匙架上时,即使是最强大的加密也是不安全的。
As stated in the first sentence, this is a means to make it difficult enough for the casual attacker that they'll move on. It's similar to preventing burglaries on your home - you don't need to make it impregnable, you just need to make it less pregnable than the house next door :-)
正如第一句话中所述,这是一种使临时攻击者难以继续前进的手段。这类似于防止您家中的入室盗窃 - 您不需要让它坚不可摧,您只需要让它比隔壁的房子更不容易被破坏:-)
回答by Harald Scheirich
回答by Joe
Encryption is easy: as others have pointed out, there are classes in the System.Security.Cryptography namespace that do all the work for you. Use them rather than any home-grown solution.
加密很容易:正如其他人指出的那样,System.Security.Cryptography 命名空间中有一些类可以为您完成所有工作。使用它们而不是任何本土解决方案。
But decryption is easy too. The issue you have is not the encryption algorithm, but protecting access to the key used for decryption.
但是解密也很容易。您遇到的问题不是加密算法,而是保护对用于解密的密钥的访问。
I would use one of the following solutions:
我会使用以下解决方案之一:
DPAPI using the ProtectedData class with CurrentUser scope. This is easy as you don't need to worry about a key. Data can only be decrypted by the same user, so no good for sharing data between users or machines.
DPAPI using the ProtectedData class with LocalMachine scope. Good for e.g. protecting configuration data on a single secure server. But anyone who can log into the machine can encrypt it, so no good unless the server is secure.
Any symmetric algorithm. I typically use the static SymmetricAlgorithm.Create() method if I don't care what algorithm is used (in fact it's Rijndael by default). In this case you need to protect your key somehow. E.g. you can obfuscate it in some way and hide it in your code. But be aware that anyone who is smart enough to decompile your code will likely be able to find the key.
DPAPI 使用具有 CurrentUser 范围的 ProtectedData 类。这很容易,因为您无需担心密钥。数据只能由同一用户解密,因此不利于用户或机器之间的数据共享。
DPAPI 使用具有 LocalMachine 范围的 ProtectedData 类。适用于例如保护单个安全服务器上的配置数据。但是任何可以登录机器的人都可以加密它,所以除非服务器是安全的,否则没有好处。
任何对称算法。如果我不在乎使用什么算法,我通常会使用静态 SymmetricAlgorithm.Create() 方法(实际上它默认是 Rijndael)。在这种情况下,您需要以某种方式保护您的密钥。例如,您可以以某种方式混淆它并将其隐藏在您的代码中。但请注意,任何聪明到可以反编译您的代码的人都可能会找到密钥。
回答by stalepretzel
[EDIT] Years later, I've come back to say: don't do this!See What's wrong with XOR encryption?for details.
[编辑] 多年后,我回来说:不要这样做!请参阅异或加密有什么问题?详情。
A very simple, easy two-way encrytpion is XOR encryption.
一个非常简单、容易的双向加密是 XOR 加密。
- Come up with a password. Let's have it be
mypass
. - Convert the password into binary (according to ASCII). The password becomes 01101101 01111001 01110000 01100001 01110011 01110011.
- Take the message you want to encode. Convert that into binary, also.
- Look at the length of the message. If the message length is 400 bytes, turn the password into a 400 byte string by repeating it over and over again. It would become 01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011... (or
mypassmypassmypass...
) - XOR the message with the long password.
- Send the result.
- Another time, XOR the encrypted message with the same password (
mypassmypassmypass...
). - There's your message!
- 想出一个密码。就这样吧
mypass
。 - 将密码转换为二进制(根据 ASCII)。密码变为 01101101 01111001 01110000 01100001 01110011 01110011。
- 获取您要编码的消息。也将其转换为二进制。
- 查看消息的长度。如果消息长度为 400 字节,则通过一遍又一遍地重复将密码变成 400 字节的字符串。这将成为01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011 ...(或
mypassmypassmypass...
) - 使用长密码对消息进行异或。
- 发送结果。
- 另一次,使用相同的密码 (
mypassmypassmypass...
)对加密的消息进行异或。 - 有你的消息!
回答by Achilleterzo
I changed this:
我改变了这个:
public string ByteArrToString(byte[] byteArr)
{
byte val;
string tempStr = "";
for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
{
val = byteArr[i];
if (val < (byte)10)
tempStr += "00" + val.ToString();
else if (val < (byte)100)
tempStr += "0" + val.ToString();
else
tempStr += val.ToString();
}
return tempStr;
}
to this:
对此:
public string ByteArrToString(byte[] byteArr)
{
string temp = "";
foreach (byte b in byteArr)
temp += b.ToString().PadLeft(3, '0');
return temp;
}
回答by Mud
I cleaned up SimpleAES (above) for my use. Fixed convoluted encrypt/decrypt methods; separated methods for encoding byte buffers, strings, and URL-friendly strings; made use of existing libraries for URL encoding.
我清理了 SimpleAES(上图)供我使用。修复了复杂的加密/解密方法;用于编码字节缓冲区、字符串和 URL 友好字符串的分离方法;使用现有的库进行 URL 编码。
The code is small, simpler, faster and the output is more concise. For instance, [email protected]
produces:
代码更小、更简单、更快,输出更简洁。例如,[email protected]
产生:
SimpleAES: "096114178117140150104121138042115022037019164188092040214235183167012211175176167001017163166152"
SimplerAES: "YHKydYyWaHmKKnMWJROkvFwo1uu3pwzTr7CnARGjppg%3d"
Code:
代码:
public class SimplerAES
{
private static byte[] key = __Replace_Me__({ 123, 217, 19, 11, 24, 26, 85, 45, 114, 184, 27, 162, 37, 112, 222, 209, 241, 24, 175, 144, 173, 53, 196, 29, 24, 26, 17, 218, 131, 236, 53, 209 });
// a hardcoded IV should not be used for production AES-CBC code
// IVs should be unpredictable per ciphertext
private static byte[] vector = __Replace_Me_({ 146, 64, 191, 111, 23, 3, 113, 119, 231, 121, 221, 112, 79, 32, 114, 156 });
private ICryptoTransform encryptor, decryptor;
private UTF8Encoding encoder;
public SimplerAES()
{
RijndaelManaged rm = new RijndaelManaged();
encryptor = rm.CreateEncryptor(key, vector);
decryptor = rm.CreateDecryptor(key, vector);
encoder = new UTF8Encoding();
}
public string Encrypt(string unencrypted)
{
return Convert.ToBase64String(Encrypt(encoder.GetBytes(unencrypted)));
}
public string Decrypt(string encrypted)
{
return encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
}
public byte[] Encrypt(byte[] buffer)
{
return Transform(buffer, encryptor);
}
public byte[] Decrypt(byte[] buffer)
{
return Transform(buffer, decryptor);
}
protected byte[] Transform(byte[] buffer, ICryptoTransform transform)
{
MemoryStream stream = new MemoryStream();
using (CryptoStream cs = new CryptoStream(stream, transform, CryptoStreamMode.Write))
{
cs.Write(buffer, 0, buffer.Length);
}
return stream.ToArray();
}
}
回答by Simon
A variant of Marks (excellent) answer
标记的变体(优秀)答案
- Add "using"s
- Make the class IDisposable
- Remove the URL encoding code to make the example simpler.
- Add a simple test fixture to demonstrate usage
- 添加“使用”
- 使类 IDisposable
- 删除 URL 编码代码以使示例更简单。
- 添加一个简单的测试夹具来演示使用
Hope this helps
希望这可以帮助
[TestFixture]
public class RijndaelHelperTests
{
[Test]
public void UseCase()
{
//These two values should not be hard coded in your code.
byte[] key = {251, 9, 67, 117, 237, 158, 138, 150, 255, 97, 103, 128, 183, 65, 76, 161, 7, 79, 244, 225, 146, 180, 51, 123, 118, 167, 45, 10, 184, 181, 202, 190};
byte[] vector = {214, 11, 221, 108, 210, 71, 14, 15, 151, 57, 241, 174, 177, 142, 115, 137};
using (var rijndaelHelper = new RijndaelHelper(key, vector))
{
var encrypt = rijndaelHelper.Encrypt("StringToEncrypt");
var decrypt = rijndaelHelper.Decrypt(encrypt);
Assert.AreEqual("StringToEncrypt", decrypt);
}
}
}
public class RijndaelHelper : IDisposable
{
Rijndael rijndael;
UTF8Encoding encoding;
public RijndaelHelper(byte[] key, byte[] vector)
{
encoding = new UTF8Encoding();
rijndael = Rijndael.Create();
rijndael.Key = key;
rijndael.IV = vector;
}
public byte[] Encrypt(string valueToEncrypt)
{
var bytes = encoding.GetBytes(valueToEncrypt);
using (var encryptor = rijndael.CreateEncryptor())
using (var stream = new MemoryStream())
using (var crypto = new CryptoStream(stream, encryptor, CryptoStreamMode.Write))
{
crypto.Write(bytes, 0, bytes.Length);
crypto.FlushFinalBlock();
stream.Position = 0;
var encrypted = new byte[stream.Length];
stream.Read(encrypted, 0, encrypted.Length);
return encrypted;
}
}
public string Decrypt(byte[] encryptedValue)
{
using (var decryptor = rijndael.CreateDecryptor())
using (var stream = new MemoryStream())
using (var crypto = new CryptoStream(stream, decryptor, CryptoStreamMode.Write))
{
crypto.Write(encryptedValue, 0, encryptedValue.Length);
crypto.FlushFinalBlock();
stream.Position = 0;
var decryptedBytes = new Byte[stream.Length];
stream.Read(decryptedBytes, 0, decryptedBytes.Length);
return encoding.GetString(decryptedBytes);
}
}
public void Dispose()
{
if (rijndael != null)
{
rijndael.Dispose();
}
}
}