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


426

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

我更喜欢.NET Framework 2.0中已经包含的内容,因此我不必担心任何外部依赖关系。

我真的不想弄乱公共/私有密钥等。我对加密不了解很多,但是我知道的足够多,知道我写的任何东西都会变得一文不值...实际上,我可能会搞砸数学,使其变得微不足道。


3
嗨,马克-没问题。我不得不接受richdiet的回答,我感到很难过,因为我确实使用了他的解决方案,而且效果很好。但是,我一直回到这里阅读其他答案,而您的答案确实更好。当有更好的答案时,没有理由告诉人们使用一些可行的方法,但这并不是做某事的好方法。
马特·道迪

3
节省您的时间,并使用HttpServerUtility.UrlTokenEn / Decode从字节数组来回转换为url友好字符串。
Praesagus

32
+1(不尝试推出自己的巧妙设计)。您可能对加密不是很了解,但是您所知道的事实使您比我认识的大多数对加密不太了解但认为仍然可以创建自己的解决方案的开发人员领先。
黛娜2010年

6
注意:此问题中的许多答案仅是未经身份验证的加密。这意味着攻击者可以在不通知应用程序的情况下更改数据。它还会导致其他严重漏洞(例如,由于填充oracle而没有密钥进行解密)。TL; DR:如果您对此不满意,请不要使用给出的答案中的代码,否则请不要理解我刚才所说的内容。
usr

36
没有一个单一的答案描述安全加密。加密时使用jbtule的答案,然后解密一个字符串
CodesInChaos

Answers:


471

此处的其他答案很好用,但是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;
    }
}

53
@AndyMcKenna-这样做是有意的,因此您可以更改数组中的值,如Mark在第二段中所述。
Pauk)

42
您不应该这样使用IV。对于给定的两个消息,不应使用相同的密钥和相同的IV对它们进行加密。对于每条消息,IV应当是随机的,并添加到加密流中,并在解密之前将其读出。crypto.stackexchange.com/a/82/1934
jbtule 2012年

30
对每条消息使用随机IV并不是陌生的或新颖的,只是重要的并且是算法设计的一部分。对每条消息使用可预测的IV是一个常见的加密错误,不需要永久存在。
jbtule 2012年

14
还请注意,使用CBC作为其模式的后果是,您很容易受到填充oracle攻击的攻击。使用经过身份验证的加密,并且尽可能不要自己实施加密
Stephen Touset 2013年

57
安全警告:请勿使用此代码尽管已被接受,但以上评论中提到了一些严重的安全问题,而作者却继续忽略了8年。
jbtule

176

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

2
解码时,我必须用+替换空格才能在Chrome中使用QueryString:(new SimplerAES())。Decrypt(Request.QueryString [“ myParam”]。Replace('','+'));
现场直播

20
永远不要使用恒定的初始化向量,有关原因的更多信息,请参见:crypto.stackexchange.com/questions/66/…。取而代之的是,为每种加密生成一个新的IV,并将其附加到加密文本上,这要好得多而且不难。
汤姆·希德

2
请注意,此解决方案中的EncryptToUrl方法的输出(或通常使用UrlEncoded base 64字符串)在IIS 7下默认用作URL路径(而不是查询字符串)的一部分时将无法正常工作,例如由于IIS 7安全设置而导致的ASP.NET MVC路由。有关更多信息,请参见:stackoverflow.com/a/2014121/12484
Jon Schneider 2014年

5
@TomHeard如何使用上面的代码来做到这一点?
MKII

26
安全警告:请勿使用此代码,请参阅@TomHeard的评论
jbtule

36

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

5
这是一种不错的紧凑型双向加密。唯一的警告是DES已不再被视为最新的安全性。现在,该标题将授予我下面讨论的AES算法。
Mark Brittingham

@richdiet。对不起,我不接受你的回答。另一个答案是37票以上,因为它的最新性。感谢您的回答,因为它仍然是一个不错的选择。
马特·道迪

14
@MarkBrittingham:没有块链功能,初始化向量和适当填充的任何块密码都是不安全的。使用DES是此方案中最不重要的问题。
休伯特·卡里奥

2
那么,密钥在哪里使用?
亚历克斯

22
安全警告:请勿使用此代码,请参见@HubertKario的注释
jbtule

28

只是以为我会补充说,我通过添加在加密字符串内部传递回的随机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");
}

11
1)不要System.Random用作RNG。2)这完全抵制了选择密文攻击(特别是padding-
oracle

21
安全警告:请勿使用此代码,请参见上述@CodesInChaos的评论
jbtule

@jbtule请不要误导每个不想加密的人,也不要对攻击保持警惕,-如果要提出建议,请不要订购。
Virbhadrasinh

@Virbhadrasinh对我而言没有任何误导,实际上恰恰相反。如果您要使用AES,正确使用它是非常重要的,错误地使用它并说没关系,我没有将它用于重要的事情,这是被误导的。
jbtule

1
@Corey不大喊大叫,并且已按照最佳实践来处理堆栈溢出答案中的安全性问题。如果需要链接,则将其发布在问题注释中。但我也会把它放在这里stackoverflow.com/a/10366194/637783
jbtule

12

分数的变体(出色)答案

  • 添加“使用”
  • 使班级ID成为可能
  • 删除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();
        }
    }
}

好答案。在Dispose方法有一件事你需要转换的Rijndael到了IDisposable或者你会被调用Dispose得到保护级别的错误
约翰ClearZ

8
永远不要使用恒定的初始化向量,有关原因的更多信息,请参见:crypto.stackexchange.com/questions/66/…。取而代之的是,为每种加密生成一个新的IV,并将其附加到加密文本上,这要好得多而且不难。
汤姆·希德

5
@Chalky在加密时,您可以使用Rijndael类为您生成一个随机IV(msdn.microsoft.com/en-us/library/…),进行加密,然后使用IV属性从Rijndael实例中获取IV 。然后,您可以将其前置(或追加,只要您的解密从同侧抓取即可)将其添加到您的加密文本中。解密时,您然后从接收到的数据中提取IV(IV属性的大小与BlockSize属性的值除以8相同),然后在解密之前将其传递给解密实例。
Tom Heard 2014年

2
@Chalky请注意,IV不必是秘密的,只需要对于发送的每个消息都唯一。
Tom Heard

20
安全警告:请勿使用此代码参见上述@TomHeard的评论
jbtule

8

[编辑]多年后,我再说一遍:不要这样做! 请参阅XOR加密有什么问题?有关详细信息。

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

  1. 拿出密码。让我们拥有它mypass
  2. 将密码转换为二进制(根据ASCII)。密码变为01101101 01111001 01110000 01100001 01110011 01110011。
  3. 获取您要编码的消息。也将其转换为二进制。
  4. 查看消息的长度。如果消息长度为400字节,请一遍又一遍地将密码变成400字节的字符串。它会变成01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011 01101101 01111001 01110000 01100001 01110011 01110011 ...(或mypassmypassmypass...
  5. 将消息与长密码异或。
  6. 发送结果。
  7. 再次,对具有相同密码(mypassmypassmypass...)的加密邮件进行XOR 。
  8. 有您的留言!

10
@Ryan并非每种情况都需要加密安全的哈希或Rijndael密码。“简单2方式加密”实际上可能意味着simple,这表明xor甚至ROT13。

1
@Ryan:具有静态加密密钥,没有初始化向量和块链接功能的AES只是XOR加密的奇特名称,您只是在使用真正奇特的KDF ...
Hubert Kario 2012年

17
安全警告:请勿对重复密钥使用此代码 XOR加密。
jbtule

7

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

  • 随机初始化向量位于加密文本之前(@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年7月18日更新:通过@bpsilver和@Evereq的注释修复了私有Encrypt()方法中的错误。IV被意外加密,现在像Decrypt()期望的那样以明文形式添加。


您应该使用IV加密整个inputBuffer,否则将丢失要加密的字符串的前16个字符。因此,您的代码应为:return _encryptor.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
bpsilver 2014年

2
在这种情况下:byte [] inputBuffer = _encryptor.TransformFinalBlock(buffer, 0, buffer.Length); return _rijndael.IV.Concat(inputBuffer).ToArray();
bpsilver 2014年

1
那会和当前的实现做同样的事情,不是吗?
angularsen 2014年

1
好吧,“不,请用16、24或32个字符填充我”,不是在进行 base 64解码之前。并且密钥应该是随机的。真的是随机的。
Maarten Bodewes 2015年

1
我注意到@bpsilver是正确的,如果没有他的修复,提供的代码将无法正常工作:crypto方法将在没有 IV的情况下返回加密的数据(它首先将IV添加到inputbuffer中,然后在没有它的情况下进行加密并返回数据)。因此,如果可能的话,只需用他的代码更新答案。(注意:我只测试带有byte []参数的方法,而不测试字符串)。谢谢!
Evereq

6

如果您只想进行简单的加密(例如,确定的破解者可能会破解,但将大多数临时用户拒之门外),则只需选择两个长度相等的密码,例如:

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

然后将它们与它们的数据进行异或处理(如有必要,循环密码)(a)。例如:

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

搜寻您的二进制文件的人可能会认为DNA字符串是关键,但是除了二进制文件中保存的未初始化内存之外,他们不太可能认为C代码是什么。


(a)请记住,这是非常简单的加密,根据某些定义,它可能根本不被认为是加密(因为加密的目的是为了防止未经授权的访问,而不只是使其变得更加困难)。当然,即使有人用钢管站在密钥持有人的手中,即使最强的加密也是不安全的。

如第一句话所述,这是使临时攻击者难以继续前进的一种手段。这类似于防止家庭盗窃-您不需要使其牢不可破,只需使其不如隔壁的房子容易识别:-)


3
有趣的主意。我不确定我会以二进制形式“相信”源代码-但是如何改编想法以将错误消息用作密码短语呢?
乔恩·斯基特

1
我更喜欢使用应用程序中已经存在的一些明文字符串的md5哈希(错误消息等)。
Treb

2
为什么它们需要相等的长度?如果它们的长度不同,实际上似乎更好。这样,有效的XOR操作数的长度就是LCM(length1,length2),而不仅仅是length1(= length2)。如果长度是相对素数,则哪个当然成为length1 * length2。
Fantius 2011年

15
安全警告:请勿使用此代码只需对加密数据有一些常识,重复密钥XOR就很容易破解。
jbtule

3
@jbtule,如果您读过这个问题,您将意识到根本不需要更安全的加密。特别是对“简单加密”,“不是关键任务”和“诚实的人诚实”的提法。您还应该阅读我的第一段,其中明确指出了这一事实不会将确定的攻击者拒之门外。
paxdiablo'5

5

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

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

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

  • DPAPI将ProtectedData类与CurrentUser范围一起使用。这很容易,因为您无需担心密钥。数据只能由同一用户解密,因此不利于用户或计算机之间共享数据。

  • DPAPI将ProtectedData类与LocalMachine范围一起使用。有益于例如保护单个安全服务器上的配置数据。但是任何可以登录计算机的人都可以对其进行加密,因此,除非服务器是安全的,否则效果不佳。

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


5

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

 // 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,以便您的应用程序可以解密/加密数据,而不管它在何处运行(例如,开发服务器与生产服务器)。您可以按照以下说明生成静态机器密钥


请注意,此方法只能用于ASP.NET应用程序。
Feru

2

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

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


8
并不是我不赞成投票,而是为什么投票时问题的年龄很重要?
user247702 2014年

2

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

1

我改变了这个

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

1

此示例使用内置的.Net密码库,显示了如何使用高级加密标准(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;

        }
    }
}


0

我一直在使用Mark Brittingham接受的答案,它对我有很大帮助。最近,我不得不将加密的文本发送到另一个组织,这就是一些问题的出处。OP不需要这些选项,但是由于这是一个很普遍的问题,因此我将发布我的修改内容(Encrypt以及Decrypt此处借用的功能):

  1. 每个消息的IV不同-在获取十六进制之前,将IV字节连接到密码字节。当然,这是一个惯例,需要传达给接收密文的各方。
  2. 允许两个构造函数-一个用于默认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;
    }
}

在此处输入图片说明


-2

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

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

ROT ... 1?真?OP甚至叫出ROT13作为他的例子并不想这样做。
user812786
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.