Frage

I've made a program that encodes a text with a random key. When the key is used, the result from the decoder is completely wrong even if I use just one letter to encode

This is the function used to encrypt/decrypt and the function that generates a random key with a determined length:

string XOR_String(string text, string key)
{
    var result = new StringBuilder();

    for (int c = 0; c < text.Length; c++)
        result.Append((char)((uint)text[c] ^ (uint)key[c % key.Length]));

    return result.ToString();
}

private static string RandomString(int Size)
{
    Random random = new Random();
    string input = "abcdefghijklmnopqrstuvwxyz0123456789";
    var chars = Enumerable.Range(0, Size)
                           .Select(x => input[random.Next(0, input.Length)]);
    return new string(chars.ToArray());
}

Decrypt:

private void button1_Click(object sender, EventArgs e)
{
  openFileDialog1.FileName = "data.txt";
  openFileDialog1.Title = "Open file";
  openFileDialog1.InitialDirectory = System.Environment.GetFolderPath(Environment.SpecialFolder.Personal);

  if (openFileDialog1.ShowDialog() == DialogResult.OK)
  {
      file = openFileDialog1;

      fs = new System.IO.FileStream(file.FileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
      reader = new System.IO.StreamReader(fs);
      file_contents = reader.ReadToEnd();

      if (textBox1.Text != "")
      {
          data = XOR_String(file_contents, textBox1.Text);
          reader.Close();
      }
  }
}

Encrypt:

private void button2_Click(object sender, EventArgs e)
{
    System.IO.FileInfo file_info = new System.IO.FileInfo(file.FileName);
    long file_info_length = file_info.Length;
    int file_length = checked((int)file_info_length);
    String key = RandomString(file_length);
    textBox1.Text = key;
    data = XOR_String(file_contents, key);
    System.IO.File.WriteAllText(file.FileName, data);
    reader.Close();
}
War es hilfreich?

Lösung

You do realize that strings in the C#/.Net/CLR world are UTF-16 encoded Unicode, right?

http://en.wikipedia.org/wiki/UTF-16

Only those characters at code points in the Basic Multilingual Plance (ranges U+0000– U+D7FF and U+E000–U+FFFF) are represented as a single 16-bit character.

Your random XORing of characters results in random stuff that won't round trip: it will either be invalid UTF-16 or a surrogate pair.

That's your problem.

If you want to encode things like this, try this:

  • Use the encoding of choice to create a byte[] from your string to be encoded.
  • XOR that.
  • Convert the resulting byte[] ciphertext into a Base64-encoded string using Convert.ToBase64String().

To decode it, reverse the process:

  • Convert the Base64-encoded string into a byte[] using Convert.FromBase64String().
  • XOR that
  • Convert the resulting plaintext byte[] back into a C#/.Net string using the same encoding you used in the first place.

Edited To Note:

Just for fun...

  • A test case:

    MyNotVerySecureCrypto crypto = new MyNotVerySecureCrypto("cat" ) ;
    
    string plainText  = "The quick brown fox jumped over the lazy dog." ;
    string cipherText = crypto.Encrypt(plainText) ;
    string plainText1 = crypto.Decrypt(cipherText) ;
    
    Debug.Assert(plainText.Equals(plainText1,StringComparison.Ordinal));
    
  • To test this code:

    public class MyNotVerySecureCrypto
    {
      private byte[]   Key      { get ; set ; }
      private Encoding Encoding { get ; set ; }
    
      public MyNotVerySecureCrypto( string key , Encoding encoding )
      {
        this.Encoding = encoding ;
        this.Key      = Encoding.GetBytes(key).Where( b => b != 0 ).ToArray() ;
        return ;
      }
      public MyNotVerySecureCrypto( string key ) : this ( key , Encoding.UTF8 )
      {
        return ;
      }
    
      public string Encrypt( string plainText )
      {
        int i = 0 ;
        byte[] octets = Encoding
                        .GetBytes(plainText)
                        .Select( b => (byte) (b^Key[(++i)%Key.Length]) )
                        .ToArray()
                        ;
        string cipherText = Convert.ToBase64String(octets) ;
        return cipherText ;
      }
    
      public string Decrypt( string cipherText )
      {
        int i = 0 ;
        byte[] octets = Convert
                        .FromBase64String(cipherText)
                        .Select( b => (byte) (b^Key[(++i)%Key.Length]) )
                        .ToArray()
                        ;
        string plainText = Encoding.GetString( octets ) ;
        return plainText ;
      }
    
    }
    

Andere Tipps

Are you sure each character inside your data is exactly one byte long? Only ASCII characters are one byte long, UTF characters can occupy more than one bytes. In that case you'll run into trouble. You can use following to read ASCII strings

StreamReader reader = new StreamReader (fileStream, new ASCIIEncoding());

Convert the text into Base64 before encrypting it with XOR.

Using this method especially helps when exchanging XORed data between PHP & C#.

    public static byte[] EncryptOrDecrypt(byte[] text, byte[] key)
    {
        byte[] xor = new byte[text.Length];
        for (int i = 0; i < text.Length; i++)
        {
            xor[i] = (byte)(text[i] ^ key[i % key.Length]);
        }
        return xor;
    }

    static void Main(string[] args){
        string input;
        byte[] inputBytes;

        string inputKey;
        byte[] key;

        do
        {
            input = System.Console.ReadLine();
            inputBytes = Encoding.Unicode.GetBytes(input);

            inputKey = System.Console.ReadLine();
            key = Encoding.Unicode.GetBytes(inputKey);

            //byte[] key = { 0, 0 }; if key is 0, encryption will not happen

            byte[] encryptedBytes = EncryptOrDecrypt(inputBytes, key);
            string encryptedStr = Encoding.Unicode.GetString(encryptedBytes);

            byte[] decryptedBytes = EncryptOrDecrypt(encryptedBytes, key);
            string decryptedStr = Encoding.Unicode.GetString(decryptedBytes);

            System.Console.WriteLine("Encrypted string:");
            System.Console.WriteLine(encryptedStr);
            System.Console.WriteLine("Decrypted string:");
            System.Console.WriteLine(decryptedStr);

        } while (input != "-1" && inputKey != "-1");
        //test:
        //pavle
        //23
        //Encrypted string:
        //BRD_W
        //Decrypted string:
        //pavle
    }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top