ChatGPT解决这个技术问题 Extra ChatGPT

简单不安全的双向数据“混淆”?

我正在为某些数据寻找非常简单的混淆(如加密和解密但不一定安全)功能。这不是关键任务。我需要一些东西来让诚实的人保持诚实,但比 ROT13Base64 更强大一些。

我更喜欢 .NET 框架 2.0 中已经包含的东西,所以我不必担心任何外部依赖项。

我真的不想弄乱公钥/私钥等。我对加密知之甚少,但我确实知道我写的任何东西都不是毫无价值的......事实上,我可能会搞砸数学并使破解变得微不足道。

嗨,马克——没问题。我不得不接受来自richdiet 的回答,我感到很遗憾,因为我确实使用了他的解决方案并且效果很好。但是,我一直回到这里阅读其他答案,而您的答案确实更好。没有理由告诉人们使用某些东西,虽然它有效,但当有更好的答案可用时,它并不是做某事的好方法。
节省时间并使用 HttpServerUtility.UrlTokenEn/Decode 从字节数组来回转换为 url 友好的字符串。
+1 不尝试推出自己的巧妙设计。您可能对加密知之甚少,但您知道这一事实使您比我遇到的大多数对加密知之甚少但认为他们无论如何都可以创建自己的解决方案的开发人员领先光年。
注意:此问题中的许多答案仅是未经身份验证的加密。这意味着攻击者可以在应用程序没有注意到的情况下更改数据。它也会导致其他严重的漏洞(例如由于填充 oracle 而没有密钥的解密)。 TL;DR:如果您对此不满意,或者不理解我刚才所说的话,请不要使用给出的答案中的代码。
此问题没有一个答案描述了安全加密。 请改用 jbtule 在 Encrypt and decrypt a string 的答案。

j
jbtule

这里的其他答案工作正常,但 AES 是一种更安全和最新的加密算法。这是我几年前获得的一个用于执行 AES 加密的类,我随着时间的推移对其进行了修改,以便对 Web 应用程序更友好(例如,我已经构建了适用于 URL 友好字符串的 Encrypt/Decrypt 方法)。它还具有处理字节数组的方法。

注意:您应该在 Key(32 字节)和 Vector(16 字节)数组中使用不同的值!您不希望有人通过假设您按原样使用此代码来找出您的密钥!您所要做的就是更改 Key 和 Vector 数组中的一些数字(必须 <= 255)(我在 Vector 数组中留下了一个无效值以确保您这样做......)。您可以使用 https://www.random.org/bytes/ 轻松生成新集合:

生成密钥

生成向量

使用它很简单:只需实例化类,然后(通常)调用 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;
    }
}

@AndyMcKenna - 这样做是故意的,以便您更改数组中的值,正如 Mark 在第二段中指出的那样。
你不应该像这样使用 IV。对于给定的两条消息,它们不应该使用相同的密钥和相同的 IV 进行加密。对于每条消息,IV 应该是随机的,添加到加密流中,并在解密之前读出。 crypto.stackexchange.com/a/82/1934
对每条消息使用随机 IV 并不陌生或新奇,只是算法设计的重要部分。为每条消息使用可预测的 IV 是一个常见的加密错误,不需要长期存在。
另请注意,使用 CBC 作为其模式的后果是您很可能容易受到 padding oracle attacks 的攻击。使用经过身份验证的加密,并且尽可能不要自己实施加密
安全警告:请勿使用此代码 尽管是公认的答案,但上述评论中提到了严重的安全问题,作者一直忽略了 8 年。
j
jbtule

我清理了 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();
    }
}

解码时,我必须将空格替换为 + 才能与 Chrome 中的 QueryString 一起使用: (new SimplerAES()).Decrypt(Request.QueryString["myParam"].Replace(' ', '+'));
永远不要使用常量初始化向量,有关原因的详细信息,请参阅:crypto.stackexchange.com/questions/66/…。相反,为每个加密生成一个新的 IV 并将其附加到密文中,这样会好得多,而且不难。
请注意,当用作 URL 路径(不是查询字符串)的一部分时,此解决方案中 EncryptToUrl 方法的输出(或一般使用 UrlEncoded base 64 字符串的任何使用)在 IIS 7 下默认不起作用,如由于 IIS 7 安全设置,一个 ASP.NET MVC 路由。有关更多信息,请参阅:stackoverflow.com/a/2014121/12484
@TomHeard 使用上面的代码如何做呢?
安全警告:请勿使用此代码 请参阅@TomHeard 的评论
S
Soner Gönül

是的,添加 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);

这是一个不错的、紧凑的双向加密。唯一需要注意的是,DES 不再被认为是最先进的安全性。该标题现在属于我在下面讨论的 AES 算法。
@richdiet。很抱歉我没有接受你的回答。另一个答案是 37+ 票,因为它是最新的。感谢您的回答,因为它仍然是一个很好的答案。
@MarkBrittingham:任何没有块链接功能、初始化向量和适当填充的块密码都是不安全的。使用 DES 是该方案中最不重要的问题。
那么密钥在哪里使用呢?
安全警告:请勿使用此代码 请参阅 @HubertKario 的评论
A
Andy C

只是想补充一点,我通过添加一个在加密字符串中传回的随机 IV 来改进 Mud 的 SimplerAES。这改进了加密,因为加密相同的字符串每次都会导致不同的输出。

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");
}

1) 不要将 System.Random 用作 RNG。 2)这完全破坏了选择密文攻击(特别是填充预言)
安全警告:请勿使用此代码,请参阅@CodesInChaos 的上述评论
@jbtule 请不要误导每个不想复杂加密的人,以及对攻击不警惕的人,--如果您想提出建议,请不要订购。
@jbtule 如果此代码容易受到某些攻击 - 确实如此 - 那么也许不是仅仅对我们大喊不要使用它,而是可以链接到一些正确的代码。只是说。
@Corey 没有大喊大叫,并且遵循了处理堆栈溢出答案中的安全问题的最佳实践。如果您需要链接,请在问题评论中发布。但我也会把它放在这里stackoverflow.com/a/10366194/637783
S
Simon

分数(优秀)答案的变体

添加“使用”

使类 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();
        }
    }
}

永远不要使用常量初始化向量,有关原因的更多信息,请参阅:crypto.stackexchange.com/questions/66/…。相反,为每个加密生成一个新的 IV 并将其附加到密文中,这样会好得多,而且不难。
@Chalky 在加密时,您使用 Rijndael 类为您生成一个随机 IV(msdn.microsoft.com/en-us/library/…),进行加密,然后使用 IV 属性从 Rijndael 实例中获取 IV。然后,您将其添加(或附加,只要您的解密从同一侧获取它)就可以将其添加到您的加密文本中。在解密时,您然后从收到的数据中提取 IV(IV 属性的大小与 BlockSize 属性除以 8 相同),然后在解密之前将其传递给您的解密实例。
@Chalky 请注意,IV 不需要保密,它只需要对于发送的每条消息都是唯一的。
安全警告:请勿使用此代码参见@TomHeard 的上述评论
@jbtule这个问题的字面意思是“简单的不安全的双向数据”混淆“?为什么你还要评论每一个答案安全警告:不要使用这个代码-_-?如果不是完全烦人的话,有点多余和有点重复。因为根据问题的标题,它并不安全也不复杂......
A
Artjom B.

我结合了我从几个答案和评论中找到的最好的东西。

加密文本前面的随机初始化向量 (@jbtule)

使用 TransformFinalBlock() 而不是 MemoryStream (@RenniePet)

没有预先填写的密钥,以避免任何人复制和粘贴灾难

正确配置和使用模式

代码:

/// <summary>
/// Simple encryption/decryption using a random initialization vector
/// and prepending it to the crypto text.
/// </summary>
/// <remarks>Based on multiple answers in http://stackoverflow.com/questions/165808/simple-two-way-encryption-for-c-sharp </remarks>
public class SimpleAes : IDisposable
{
    /// <summary>
    ///     Initialization vector length in bytes.
    /// </summary>
    private const int IvBytes = 16;

    /// <summary>
    ///     Must be exactly 16, 24 or 32 bytes long.
    /// </summary>
    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() 的预期以明文形式添加。


您应该在前面加上 IV 加密整个 inputBuffer,否则要加密的字符串的前 16 个字符会丢失。因此您的代码应为:return _encryptor.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
在这种情况下:byte [] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length); return _rijndael.IV.Concat(inputBuffer).ToArray();
这将与当前的实现做同样的事情,不是吗?
“用 16、24 或 32 个字符填充我”好吧,不,不是在 base 64 解码之前。并且密钥应该是随机的。真的很随意。
我注意到@bpsilver 是对的,如果没有他的修复,提供的代码将无法工作:加密方法返回没有 IV 的加密数据(它首先将 IV 添加到输入缓冲区,但接下来加密并返回没有它的数据)。因此,如果可能的话,只需用他的代码更新答案。 (注意:我只测试带有 byte[] 参数的方法,而不是字符串)。谢谢!
A
Ashkan S

在 System.Security.Cryptography 中使用 TripleDESCryptoServiceProvider :

public static class CryptoHelper
{
    private const string Key = "MyHashString";
    private static TripleDESCryptoServiceProvider GetCryproProvider()
    {
        var md5 = new MD5CryptoServiceProvider();
        var key = md5.ComputeHash(Encoding.UTF8.GetBytes(Key));
        return new TripleDESCryptoServiceProvider() { Key = key, Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 };
    }

    public static string Encrypt(string plainString)
    {
        var data = Encoding.UTF8.GetBytes(plainString);
        var tripleDes = GetCryproProvider();
        var transform = tripleDes.CreateEncryptor();
        var resultsByteArray = transform.TransformFinalBlock(data, 0, data.Length);
        return Convert.ToBase64String(resultsByteArray);
    }

    public static string Decrypt(string encryptedString)
    {
        var data = Convert.FromBase64String(encryptedString);
        var tripleDes = GetCryproProvider();
        var transform = tripleDes.CreateDecryptor();
        var resultsByteArray = transform.TransformFinalBlock(data, 0, data.Length);
        return Encoding.UTF8.GetString(resultsByteArray);
    }
}

我喜欢这个解决方案,同时将类中的键作为属性保留是不好的。所以我在这里为关心它的人做一个要点。 gist.github.com/shukebeta/780df627a3de55c848a800cfb242a276
C
Community

[编辑] 多年后,我回来说:不要这样做!有关详细信息,请参阅 What's wrong with XOR encryption?

一个非常简单、容易的双向加密是 XOR 加密。

想出一个密码。让它成为我的通行证。将密码转换为二进制(根据 ASCII)。密码变为 01101101 01111001 01110000 01100001 01110011 01110011 取你要编码的消息。也将其转换为二进制。查看消息的长度。如果消息长度为 400 字节,则通过一遍又一遍地重复将密码变成 400 字节的字符串。 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.发送结果。另一次,使用相同密码 (mypassmypassmypass...) 对加密消息进行异或。你的消息来了!


@Ryan 并非每种情况都需要加密安全哈希或 Rijndael 密码。 “简单的 2 路加密”实际上可能意味着简单,这意味着 xor 甚至 ROT13。
@Ryan:带有静态加密密钥、没有初始化向量和块链接函数的 AES 只是 XOR 加密的花哨名称,你只是在使用真正花哨的 KDF ......
安全警告:请勿使用此代码 使用重复密钥的 XOR 加密很容易被破解。
@jbtule 该问题专门要求使用 insecure 算法。 XOR 加密因其简单性而完美匹配。我认为这是一个有效的答案 - 只需选择一个足够长的随机密码。
J
Joe

加密很容易:正如其他人指出的那样,System.Security.Cryptography 命名空间中有一些类可以为您完成所有工作。使用它们而不是任何本土解决方案。

但解密也很容易。您遇到的问题不是加密算法,而是保护对用于解密的密钥的访问。

我会使用以下解决方案之一:

DPAPI 使用具有 CurrentUser 范围的 ProtectedData 类。这很容易,因为您无需担心密钥。数据只能由同一用户解密,因此不利于在用户或机器之间共享数据。

DPAPI 使用带有 LocalMachine 范围的 ProtectedData 类。例如,适用于保护单个安全服务器上的配置数据。但是任何可以登录机器的人都可以对其进行加密,所以除非服务器是安全的,否则没有任何好处。

任何对称算法。如果我不在乎使用什么算法(实际上默认情况下它是 Rijndael),我通常使用静态 SymmetricAlgorithm.Create() 方法。在这种情况下,您需要以某种方式保护您的密钥。例如,您可以以某种方式对其进行混淆并将其隐藏在您的代码中。但请注意,任何聪明到可以反编译您的代码的人都可能找到密钥。


W
William

我想发布我的解决方案,因为上述解决方案都不像我的那么简单。让我知道你的想法:

 // 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 与用于解密该值的相同。如果需要,您可以在 Web.config 中指定静态 MachineKey,以便您的应用程序可以解密/加密数据,而不管它在哪里运行(例如,开发服务器与生产服务器)。您可以generate a static machine key following these instructions


注意这种方法只能用于 ASP.NET 应用程序。
p
paxdiablo

如果您只想要简单的加密(即,一个确定的破解者可能会破解,但会锁定大多数临时用户),只需选择两个长度相等的密码,例如:

deoxyribonucleicacid
while (x>0) { x-- };

并用它们对您的数据进行异或运算(必要时循环密码)(a)。例如:

1111-2222-3333-4444-5555-6666-7777
deoxyribonucleicaciddeoxyribonucle
while (x>0) { x-- };while (x>0) { 

搜索您的二进制文件的人可能会认为 DNA 字符串是一个键,但他们不太可能认为 C 代码是与您的二进制文件一起保存的未初始化内存之外的任何内容。

(a) 请记住,这是非常简单的加密,并且根据某些定义,可能根本不被视为加密(因为加密的目的是防止未经授权的访问,而不仅仅是使其更加困难)。当然,当有人拿着钢管站在钥匙持有人面前时,即使是最强大的加密也不安全。

如第一句话所述,这是一种使临时攻击者难以继续前进的方法。这类似于防止家中的入室盗窃 - 你不需要让它坚不可摧,你只需要让它比隔壁的房子更不易受攻击:-)


有趣的想法。我不确定我是否会“相信”二进制文件中的源代码——但是如何调整这个想法以使用错误消息作为密码?
我更喜欢使用应用程序中已经存在的一些明文字符串的 md5 哈希(错误消息左右)。
为什么它们需要等长?如果它们的长度不同,实际上似乎更好。这样,您的有效 XOR 操作数的长度是 LCM(length1, length2),而不仅仅是 length1 (=length2)。如果长度是相对素数,那当然变成 length1 * length2 。
@jbtule,如果您阅读了这个问题,您会意识到根本不需要更安全的加密。特别提到“简单加密”、“不是关键任务”和“让诚实的人保持诚实”。您还应该阅读我的第一段,其中明确指出它不会锁定坚定的攻击者。
@paxdiablo 您可能有兴趣参与此元讨论:meta.stackoverflow.com/q/348946/637783
M
Mitch Wheat

命名空间 System.Security.Cryptography 包含 TripleDESCryptoServiceProviderRijndaelManaged

不要忘记添加对 System.Security 程序集的引用。


不是我投了反对票,而是为什么投票时问题的年龄很重要?
C
Community

我更改了 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;
}

对此:

    public string ByteArrToString(byte[] byteArr)
    {
        string temp = "";
        foreach (byte b in byteArr)
            temp += b.ToString().PadLeft(3, '0');
        return temp;
    }

M
Matt

使用内置的 .Net Cryptography 库,此示例展示了如何使用高级加密标准 (AES)。

using System;
using System.IO;
using System.Security.Cryptography;

namespace Aes_Example
{
    class AesExample
    {
        public static void Main()
        {
            try
            {

                string original = "Here is some data to encrypt!";

                // Create a new instance of the Aes
                // class.  This generates a new key and initialization 
                // vector (IV).
                using (Aes myAes = Aes.Create())
                {

                    // Encrypt the string to an array of bytes.
                    byte[] encrypted = EncryptStringToBytes_Aes(original, myAes.Key, myAes.IV);

                    // Decrypt the bytes to a string.
                    string roundtrip = DecryptStringFromBytes_Aes(encrypted, myAes.Key, myAes.IV);

                    //Display the original data and the decrypted data.
                    Console.WriteLine("Original:   {0}", original);
                    Console.WriteLine("Round Trip: {0}", roundtrip);
                }

            }
            catch (Exception e)
            {
                Console.WriteLine("Error: {0}", e.Message);
            }
        }
        static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key,byte[] IV)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("Key");
            byte[] encrypted;
            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {

                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }


            // Return the encrypted bytes from the memory stream.
            return encrypted;

        }

        static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("Key");

            // Declare the string used to hold
            // the decrypted text.
            string plaintext = null;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {

                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }

            }

            return plaintext;

        }
    }
}

P
Peter Mortensen

我知道您说过您不关心它的安全性,但如果您选择 DES,您不妨选择 AES,它是更新的加密方法。


C
Community

我一直在使用 Mark Brittingham 接受的答案,它对我有很大帮助。最近我不得不将加密文本发送到不同的组织,这就是出现一些问题的地方。 OP 不需要这些选项,但由于这是一个热门问题,我发布了我的修改(从 here 借用的 EncryptDecrypt 函数):

每条消息都有不同的 IV - 在获取十六进制之前将 IV 字节连接到密码字节。当然,这是一个约定,需要传达给接收密文的各方。允许两个构造函数 - 一个用于默认 RijndaelManaged 值,另一个用于指定属性值(基于加密和解密方之间的相互协议)

这是课程(最后是测试样本):

/// <summary>
/// Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx
/// Uses UTF8 Encoding
///  http://security.stackexchange.com/a/90850
/// </summary>
public class AnotherAES : IDisposable
{
    private RijndaelManaged rijn;

    /// <summary>
    /// Initialize algo with key, block size, key size, padding mode and cipher mode to be known.
    /// </summary>
    /// <param name="key">ASCII key to be used for encryption or decryption</param>
    /// <param name="blockSize">block size to use for AES algorithm. 128, 192 or 256 bits</param>
    /// <param name="keySize">key length to use for AES algorithm. 128, 192, or 256 bits</param>
    /// <param name="paddingMode"></param>
    /// <param name="cipherMode"></param>
    public AnotherAES(string key, int blockSize, int keySize, PaddingMode paddingMode, CipherMode cipherMode)
    {
        rijn = new RijndaelManaged();
        rijn.Key = Encoding.UTF8.GetBytes(key);
        rijn.BlockSize = blockSize;
        rijn.KeySize = keySize;
        rijn.Padding = paddingMode;
        rijn.Mode = cipherMode;
    }

    /// <summary>
    /// Initialize algo just with key
    /// Defaults for RijndaelManaged class: 
    /// Block Size: 256 bits (32 bytes)
    /// Key Size: 128 bits (16 bytes)
    /// Padding Mode: PKCS7
    /// Cipher Mode: CBC
    /// </summary>
    /// <param name="key"></param>
    public AnotherAES(string key)
    {
        rijn = new RijndaelManaged();
        byte[] keyArray = Encoding.UTF8.GetBytes(key);
        rijn.Key = keyArray;
    }

    /// <summary>
    /// Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx
    /// Encrypt a string using RijndaelManaged encryptor.
    /// </summary>
    /// <param name="plainText">string to be encrypted</param>
    /// <param name="IV">initialization vector to be used by crypto algorithm</param>
    /// <returns></returns>
    public byte[] Encrypt(string plainText, byte[] IV)
    {
        if (rijn == null)
            throw new ArgumentNullException("Provider not initialized");

        // Check arguments.
        if (plainText == null || plainText.Length <= 0)
            throw new ArgumentNullException("plainText cannot be null or empty");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV cannot be null or empty");
        byte[] encrypted;

        // Create a decrytor to perform the stream transform.
        using (ICryptoTransform encryptor = rijn.CreateEncryptor(rijn.Key, IV))
        {
            // Create the streams used for encryption.
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        //Write all data to the stream.
                        swEncrypt.Write(plainText);
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
        }
        // Return the encrypted bytes from the memory stream.
        return encrypted;
    }//end EncryptStringToBytes

    /// <summary>
    /// Based on https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged(v=vs.110).aspx
    /// </summary>
    /// <param name="cipherText">bytes to be decrypted back to plaintext</param>
    /// <param name="IV">initialization vector used to encrypt the bytes</param>
    /// <returns></returns>
    public string Decrypt(byte[] cipherText, byte[] IV)
    {
        if (rijn == null)
            throw new ArgumentNullException("Provider not initialized");

        // Check arguments.
        if (cipherText == null || cipherText.Length <= 0)
            throw new ArgumentNullException("cipherText cannot be null or empty");
        if (IV == null || IV.Length <= 0)
            throw new ArgumentNullException("IV cannot be null or empty");

        // Declare the string used to hold the decrypted text.
        string plaintext = null;

        // Create a decrytor to perform the stream transform.
        using (ICryptoTransform decryptor = rijn.CreateDecryptor(rijn.Key, IV))
        {
            // Create the streams used for decryption.
            using (MemoryStream msDecrypt = new MemoryStream(cipherText))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {
                        // Read the decrypted bytes from the decrypting stream and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }
        }

        return plaintext;
    }//end DecryptStringFromBytes

    /// <summary>
    /// Generates a unique encryption vector using RijndaelManaged.GenerateIV() method
    /// </summary>
    /// <returns></returns>
    public byte[] GenerateEncryptionVector()
    {
        if (rijn == null)
            throw new ArgumentNullException("Provider not initialized");

        //Generate a Vector
        rijn.GenerateIV();
        return rijn.IV;
    }//end GenerateEncryptionVector


    /// <summary>
    /// Based on https://stackoverflow.com/a/1344255
    /// Generate a unique string given number of bytes required.
    /// This string can be used as IV. IV byte size should be equal to cipher-block byte size. 
    /// Allows seeing IV in plaintext so it can be passed along a url or some message.
    /// </summary>
    /// <param name="numBytes"></param>
    /// <returns></returns>
    public static string GetUniqueString(int numBytes)
    {
        char[] chars = new char[62];
        chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".ToCharArray();
        byte[] data = new byte[1];
        using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
        {
            data = new byte[numBytes];
            crypto.GetBytes(data);
        }
        StringBuilder result = new StringBuilder(numBytes);
        foreach (byte b in data)
        {
            result.Append(chars[b % (chars.Length)]);
        }
        return result.ToString();
    }//end GetUniqueKey()

    /// <summary>
    /// Converts a string to byte array. Useful when converting back hex string which was originally formed from bytes.
    /// </summary>
    /// <param name="hex"></param>
    /// <returns></returns>
    public static byte[] StringToByteArray(String hex)
    {
        int NumberChars = hex.Length;
        byte[] bytes = new byte[NumberChars / 2];
        for (int i = 0; i < NumberChars; i += 2)
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        return bytes;
    }//end StringToByteArray

    /// <summary>
    /// Dispose RijndaelManaged object initialized in the constructor
    /// </summary>
    public void Dispose()
    {
        if (rijn != null)
            rijn.Dispose();
    }//end Dispose()
}//end class

和..

这是测试样本:

class Program
{
    string key;
    static void Main(string[] args)
    {
        Program p = new Program();

        //get 16 byte key (just demo - typically you will have a predetermined key)
        p.key = AnotherAES.GetUniqueString(16);

        string plainText = "Hello World!";

        //encrypt
        string hex = p.Encrypt(plainText);

        //decrypt
        string roundTrip = p.Decrypt(hex);

        Console.WriteLine("Round Trip: {0}", roundTrip);
    }

    string Encrypt(string plainText)
    {
        Console.WriteLine("\nSending (encrypt side)...");
        Console.WriteLine("Plain Text: {0}", plainText);
        Console.WriteLine("Key: {0}", key);
        string hex = string.Empty;
        string ivString = AnotherAES.GetUniqueString(16);
        Console.WriteLine("IV: {0}", ivString);
        using (AnotherAES aes = new AnotherAES(key))
        {
            //encrypting side
            byte[] IV = Encoding.UTF8.GetBytes(ivString);

            //get encrypted bytes (IV bytes prepended to cipher bytes)
            byte[] encryptedBytes = aes.Encrypt(plainText, IV);
            byte[] encryptedBytesWithIV = IV.Concat(encryptedBytes).ToArray();

            //get hex string to send with url
            //this hex has both IV and ciphertext
            hex = BitConverter.ToString(encryptedBytesWithIV).Replace("-", "");
            Console.WriteLine("sending hex: {0}", hex);
        }

        return hex;
    }

    string Decrypt(string hex)
    {
        Console.WriteLine("\nReceiving (decrypt side)...");
        Console.WriteLine("received hex: {0}", hex);
        string roundTrip = string.Empty;
        Console.WriteLine("Key " + key);
        using (AnotherAES aes = new AnotherAES(key))
        {
            //get bytes from url
            byte[] encryptedBytesWithIV = AnotherAES.StringToByteArray(hex);

            byte[] IV = encryptedBytesWithIV.Take(16).ToArray();

            Console.WriteLine("IV: {0}", System.Text.Encoding.Default.GetString(IV));

            byte[] cipher = encryptedBytesWithIV.Skip(16).ToArray();

            roundTrip = aes.Decrypt(cipher, IV);
        }
        return roundTrip;
    }
}

https://i.stack.imgur.com/BFsDZ.png


T
Thunder

我认为这是世界上最简单的一个!

string encrypted = "Text".Aggregate("", (c, a) => c + (char) (a + 2));

测试

 Console.WriteLine(("Hello").Aggregate("", (c, a) => c + (char) (a + 1)));
            //Output is Ifmmp
 Console.WriteLine(("Ifmmp").Aggregate("", (c, a) => c + (char)(a - 1)));
            //Output is Hello

腐烂... 1?真的吗? OP 甚至将 ROT13 作为他不想做的事情的一个例子。