我正在为某些数据寻找非常简单的混淆(如加密和解密但不一定是安全的)功能.这不是关键任务.我需要一些东西来保持诚实的人诚实,但比ROT13或Base64强一点.
我更喜欢已经包含在.NET framework 2.0中的东西,所以我不必担心任何外部依赖.
我真的不想乱用公钥/私钥等等.我对加密知之甚少,但我知道我写的任何东西都不会毫无价值......事实上,我可能搞砸了数学并且让它变得微不足道.
这里的其他答案工作正常,但AES是一种更安全和最新的加密算法.这是我几年前为了执行AES加密而获得的类,我已经对其进行了一段时间的修改,以便对Web应用程序更加友好(例如,我已经构建了使用URL友好字符串的加密/解密方法).它还具有与字节数组一起使用的方法.
注意:您应该在Key(32字节)和Vector(16字节)数组中使用不同的值!您不希望有人通过假设您按原样使用此代码来弄清楚您的密钥!您所要做的就是更改Key和Vector数组中的一些数字(必须<= 255)(我在Vector数组中保留了一个无效值以确保您执行此操作...).您可以使用https://www.random.org/bytes/轻松生成新集:
生成 Key
生成 Vector
使用它很简单:只需实例化类,然后调用(通常)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; } }
我清理了SimpleAES(上面)供我使用.固定的复杂加密/解密方法; 用于编码字节缓冲区,字符串和URL友好字符串的分离方法; 利用现有的库进行URL编码.
代码更小,更简单,更快速,输出更简洁.例如,johnsmith@gmail.com
产生:
SimpleAES: "096114178117140150104121138042115022037019164188092040214235183167012211175176167001017163166152" SimplerAES: "YHKydYyWaHmKKnMWJROkvFwo1uu3pwzTr7CnARGjppg%3d"
码:
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(); } }
是的,添加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);
只是想我会补充一点,我通过添加一个随机的IV来改进Mud的SimplerAES,这个随机的IV被传回加密的字符串.这改进了加密,因为加密相同的字符串每次都会导致不同的输出.
public class StringEncryption
{
private readonly Random random;
private readonly byte[] key;
private readonly RijndaelManaged rm;
private readonly UTF8Encoding encoder;
public StringEncryption()
{
this.random = new Random();
this.rm = new RijndaelManaged();
this.encoder = new UTF8Encoding();
this.key = Convert.FromBase64String("Your+Secret+Static+Encryption+Key+Goes+Here=");
}
public string Encrypt(string unencrypted)
{
var vector = new byte[16];
this.random.NextBytes(vector);
var cryptogram = vector.Concat(this.Encrypt(this.encoder.GetBytes(unencrypted), vector));
return Convert.ToBase64String(cryptogram.ToArray());
}
public string Decrypt(string encrypted)
{
var cryptogram = Convert.FromBase64String(encrypted);
if (cryptogram.Length < 17)
{
throw new ArgumentException("Not a valid encrypted string", "encrypted");
}
var vector = cryptogram.Take(16).ToArray();
var buffer = cryptogram.Skip(16).ToArray();
return this.encoder.GetString(this.Decrypt(buffer, vector));
}
private byte[] Encrypt(byte[] buffer, byte[] vector)
{
var encryptor = this.rm.CreateEncryptor(this.key, vector);
return this.Transform(buffer, encryptor);
}
private byte[] Decrypt(byte[] buffer, byte[] vector)
{
var decryptor = this.rm.CreateDecryptor(this.key, vector);
return this.Transform(buffer, decryptor);
}
private byte[] Transform(byte[] buffer, ICryptoTransform transform)
{
var stream = new MemoryStream();
using (var cs = new CryptoStream(stream, transform, CryptoStreamMode.Write))
{
cs.Write(buffer, 0, buffer.Length);
}
return stream.ToArray();
}
}
和奖金单元测试
[Test]
public void EncryptDecrypt()
{
// Arrange
var subject = new StringEncryption();
var originalString = "Testing123!£$";
// Act
var encryptedString1 = subject.Encrypt(originalString);
var encryptedString2 = subject.Encrypt(originalString);
var decryptedString1 = subject.Decrypt(encryptedString1);
var decryptedString2 = subject.Decrypt(encryptedString2);
// Assert
Assert.AreEqual(originalString, decryptedString1, "Decrypted string should match original string");
Assert.AreEqual(originalString, decryptedString2, "Decrypted string should match original string");
Assert.AreNotEqual(originalString, encryptedString1, "Encrypted string should not match original string");
Assert.AreNotEqual(encryptedString1, encryptedString2, "String should never be encrypted the same twice");
}
Marks(优秀)答案的变体
添加"使用"s
使类IDisposable
删除URL编码代码以使示例更简单.
添加一个简单的测试夹具来演示用法
希望这可以帮助
[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(); } } }
[编辑]多年以后,我回来说:不要这样做! 看看XOR加密有什么问题?详情.
一个非常简单,容易的双向加密是XOR加密.
拿出密码.我们拥有它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...
)
使用长密码对消息进行异或.
发送结果.
另一次,XOR加密的消息具有相同的密码(mypassmypassmypass...
).
有你的消息!
我从几个答案和评论中结合了我发现的最好的东西.
随机初始化向量前置加密文本(@jbtule)
使用TransformFinalBlock()而不是MemoryStream(@RenniePet)
没有预先填写的密钥,以避免任何人复制和粘贴灾难
正确处置和使用模式
码:
////// Simple encryption/decryption using a random initialization vector /// and prepending it to the crypto text. /// ///Based on multiple answers in http://stackoverflow.com/questions/165808/simple-two-way-encryption-for-c-sharp public class SimpleAes : IDisposable { ////// Initialization vector length in bytes. /// private const int IvBytes = 16; ////// Must be exactly 16, 24 or 32 bytes long. /// private static readonly byte[] Key = Convert.FromBase64String("FILL ME WITH 24 (2 pad chars), 32 OR 44 (1 pad char) RANDOM CHARS"); // Base64 has a blowup of four-thirds (33%) private readonly UTF8Encoding _encoder; private readonly ICryptoTransform _encryptor; private readonly RijndaelManaged _rijndael; public SimpleAes() { _rijndael = new RijndaelManaged {Key = Key}; _rijndael.GenerateIV(); _encryptor = _rijndael.CreateEncryptor(); _encoder = new UTF8Encoding(); } public string Decrypt(string encrypted) { return _encoder.GetString(Decrypt(Convert.FromBase64String(encrypted))); } public void Dispose() { _rijndael.Dispose(); _encryptor.Dispose(); } public string Encrypt(string unencrypted) { return Convert.ToBase64String(Encrypt(_encoder.GetBytes(unencrypted))); } private byte[] Decrypt(byte[] buffer) { // IV is prepended to cryptotext byte[] iv = buffer.Take(IvBytes).ToArray(); using (ICryptoTransform decryptor = _rijndael.CreateDecryptor(_rijndael.Key, iv)) { return decryptor.TransformFinalBlock(buffer, IvBytes, buffer.Length - IvBytes); } } private byte[] Encrypt(byte[] buffer) { // Prepend cryptotext with IV byte [] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length); return _rijndael.IV.Concat(inputBuffer).ToArray(); } }
更新2015-07-18:通过@bpsilver和@Evereq的注释修复了私有Encrypt()方法中的错误.IV被意外加密,现在以Decrypt()预期的明文前缀.
如果你只是想要简单的加密(即,确定的破解者可能会破坏,但锁定大多数临时用户),只需选择两个相等长度的密码,例如:
deoxyribonucleicacid while (x>0) { x-- };
和xor你的数据(必要时循环密码)(a).例如:
1111-2222-3333-4444-5555-6666-7777 deoxyribonucleicaciddeoxyribonucle while (x>0) { x-- };while (x>0) {
搜索你的二进制文件的人可能会认为DNA字符串是一个关键字,但他们不太可能认为C代码不是用二进制文件保存的未初始化内存.
(a)请记住,这是非常简单的加密,并且根据某些定义,可能根本不被视为加密(因为加密的目的是为了防止未经授权的访问,而不仅仅是使其更加困难).当然,当有人用钢管架住钥匙扣时,即使最强的加密也是不安全的.
如第一句中所述,这是一种让偶然攻击者难以继续前进的手段.它类似于防止你家的盗窃 - 你不需要让它变得坚不可摧,你只需要让它比隔壁的房子更难以预防:-)
加密很容易:正如其他人指出的那样,System.Security.Cryptography命名空间中有一些类可以为您完成所有工作。使用它们而不是任何本地解决方案。
但是解密也很容易。您遇到的问题不是加密算法,而是保护对用于解密的密钥的访问。
我将使用以下解决方案之一:
DPAPI将ProtectedData类与CurrentUser范围一起使用。这很容易,因为您无需担心密钥。数据只能由同一用户解密,因此不利于用户或计算机之间共享数据。
DPAPI将ProtectedData类与LocalMachine范围一起使用。有益于例如保护单个安全服务器上的配置数据。但是任何可以登录计算机的人都可以对其进行加密,因此,除非服务器是安全的,否则效果不佳。
任何对称算法。如果我不在乎使用哪种算法,我通常会使用静态SymmetricAlgorithm.Create()方法(事实上,默认情况下是Rijndael)。在这种情况下,您需要以某种方式保护您的钥匙。例如,您可以通过某种方式对其进行混淆,并将其隐藏在代码中。但是请注意,只要有足够的能力来反编译您的代码的人都可以找到密钥。
我想发布我的解决方案,因为上述解决方案都不像我的那么简单.让我知道你的想法:
// This will return an encrypted string based on the unencrypted parameter public static string Encrypt(this string DecryptedValue) { HttpServerUtility.UrlTokenEncode(MachineKey.Protect(Encoding.UTF8.GetBytes(DecryptedValue.Trim()))); } // This will return an unencrypted string based on the parameter public static string Decrypt(this string EncryptedValue) { Encoding.UTF8.GetString(MachineKey.Unprotect(HttpServerUtility.UrlTokenDecode(EncryptedValue))); }
这假定用于加密该值的服务器的MachineKey与用于解密该值的服务器的MachineKey相同.如果需要,您可以在Web.config中指定静态MachineKey,以便您的应用程序可以解密/加密数据,无论数据在何处运行(例如开发与生产服务器).您可以按照这些说明生成静态机器密钥.