C#的简单不安全双向"混淆"

时间:2020-03-06 15:03:46  来源:igfitidea点击:

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

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

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

解决方案

名称空间" System.Security.Cryptography"包含" TripleDESCryptoServiceProvider"和" RijndaelManaged"类。

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

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

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

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

并使用它们两者对数据进行异或者处理(如有必要,循环使用密码短语)(a)。例如:

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

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

(a)请记住,这是非常简单的加密,并且根据某些定义,它可能根本不被视为加密(因为加密的目的是为了防止未经授权的访问,而不仅仅是增加难度)。当然,当有人用钢管站立在密钥持有者的手中时,即使最强的加密也是不安全的。

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

我知道我们说过我们并不关心它的安全性,但是如果我们选择DES,则最好还是使用AES,它是最新的加密方法。

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

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

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

  • DPAPI将ProtectedData类与CurrentUser范围一起使用。这很容易,因为我们无需担心密钥。数据只能由同一用户解密,因此不利于在用户或者计算机之间共享数据。
  • DPAPI将ProtectedData类与LocalMachine范围一起使用。适用于保护单个安全服务器上的配置数据。但是任何可以登录计算机的人都可以对其进行加密,因此,除非服务器是安全的,否则效果不佳。
  • 任何对称算法。如果我不在乎使用哪种算法,我通常会使用静态SymmetricAlgorithm.Create()方法(事实上,默认情况下是Rijndael)。在这种情况下,我们需要以某种方式保护钥匙。例如。我们可以通过某种方式对其进行混淆,并将其隐藏在代码中。但是请注意,只要有足够的能力来反编译代码的人都可以找到密钥。

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

[编辑]多年后,我再说一遍:不要这样做!请参阅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 ...)
  • 将消息与长密码异或者。
  • 发送结果。
  • 还有一次,使用相同的密码(mypassmypassmypass ...)对加密的消息进行XOR。
  • 有留言!