12

I wrote this code in C# to encrypt a string with a key:

private static int Bin2Dec(string num)
{
    int _num = 0;

    for (int i = 0; i < num.Length; i++)
        _num += (int)Math.Pow(2, num.Length - i - 1) * int.Parse(num[i].ToString());

    return _num;
}

private static string Dec2Bin(int num)
{
    if (num < 2) return num.ToString();

    return Dec2Bin(num / 2) + (num % 2).ToString();
}

public static string StrXor(string str, string key)
{
    string _str = "";
    string _key = "";
    string _xorStr = "";
    string _temp = "";

    for (int i = 0; i < str.Length; i++)
    {
        _temp = Dec2Bin(str[i]);    

        for (int j = 0; j < 8 - _temp.Length + 1; j++)
            _temp = '0' + _temp;

        _str += _temp;
    }

    for (int i = 0; i < key.Length; i++)
    {
        _temp = Dec2Bin(key[i]);

        for (int j = 0; j < 8 - _temp.Length + 1; j++)
            _temp = '0' + _temp;

        _key += _temp;
    }    

    while (_key.Length < _str.Length) _key += _key;

    if (_key.Length > _str.Length) _key = _key.Substring(0, _str.Length);

    for (int i = 0; i < _str.Length; i++)
        if (_str[i] == _key[i]) { _xorStr += '0'; } else { _xorStr += '1'; }

    _str = "";

    for (int i = 0; i < _xorStr.Length; i += 8)
    {
        char _chr = (char)0;
        _chr = (char)Bin2Dec(_xorStr.Substring(i, 8)); //ERROR : (Index and length must refer to a location within the string. Parameter name: length)
        _str += _chr;
    }

    return _str;
}

The problem is that I always get error when I want to decrypt an encryted text with this code:

string enc_text = ENCRYPT.XORENC("abc","a"); // enc_text = " ♥☻"
string dec_text = ENCRYPT.XORENC(enc_text,"a"); // ArgumentOutOfRangeException

Any clues?

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
fardjad
  • 20,031
  • 6
  • 53
  • 68
  • 1
    All I can say is eh? :) Perhaps it is an educational exercise but you don't need to convert the characters into strings, xor them manually then convert them back to a string. As proven by your Dec2Bin and Bin2Dec functions char can be converted to and from ints with casts so just take char from both strings, apply the '^' xor operator and put into new string. – tyranid Mar 28 '10 at 11:25
  • It would help if you specify what error you're getting :) – Michael Stum Mar 28 '10 at 11:25
  • Also, you may want to use StringBuilders instead of Strings. Strings are immutable (they cannot be changed), so things like _str += _temp; generates a new string every time, which makes this method unnecessarily heavy/expensive. Use a StringBuilder and .Append(temp). – Michael Stum Mar 28 '10 at 11:28
  • @Michael Stum: it turns out that StringBuilder can actually be slower, or about the same as using concatenation. Only for large strings is StringBuilder better. – siride Jan 30 '11 at 19:57
  • @siride It can be slower, but that's mainly for trivial concatenations (i think about 3-4 strings). In his case, the loop is non-deterministic as it depends on the input string length, so using a SB is a safer bet (and if course initialize it with a sane buffer size like str.Length) – Michael Stum Feb 01 '11 at 22:54

3 Answers3

52

If you have a character, a char, you can convert it to an integer, an int.

And then you can use the ^ operator to perform XOR on it. You don't appear to be using that operator at the moment, which might be the source of your problem.

string EncryptOrDecrypt(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();
}

That kind of thing. Here's a longer version with comments that does the same thing in steps, to make it easier to learn from:

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

    for (int c = 0; c < text.Length; c++)
    {
        // take next character from string
        char character = text[c];

        // cast to a uint
        uint charCode = (uint)character;

        // figure out which character to take from the key
        int keyPosition = c % key.Length; // use modulo to "wrap round"

        // take the key character
        char keyChar = key[keyPosition];

        // cast it to a uint also
        uint keyCode = (uint)keyChar;

        // perform XOR on the two character codes
        uint combinedCode = charCode ^ keyCode;

        // cast back to a char
        char combinedChar = (char)combinedCode;

        // add to the result
        result.Append(combineChar);
    }

    return result.ToString();
}

The short version is the same but with the intermediate variables removed, substituting expressions directly into where they're used.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • Is it possible to make this method only return alpha numeric values? It works but produces weird characters. And I need them to be human readable for my use case. – Dbloom Oct 14 '20 at 22:56
  • 1
    @Dbloom to narrow the range of characters used without losing information, you need to perform a second conversion to an encoding such as hex or base64. Note that these won’t be at all “human readable” but they will be safe to send in text formats, clipboard operations etc. – Daniel Earwicker Oct 15 '20 at 21:09
2
// Code
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;
}

// Test
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
}
0

Here is some simple code to encrypt and decrypt

class CEncryption
{
    public static string Encrypt(string strIn, string strKey)
    {
        string sbOut = String.Empty;
        for (int i = 0; i < strIn.Length; i++)
        {
            sbOut += String.Format("{0:00}", strIn[i] ^ strKey[i % strKey.Length]);
        }

        return sbOut;
    }

    public static string Decrypt(string strIn, string strKey)
    {
        string sbOut = String.Empty;
        for (int i = 0; i < strIn.Length; i += 2)
        {
            byte code = Convert.ToByte(strIn.Substring(i, 2));
            sbOut += (char)(code ^ strKey[(i/2) % strKey.Length]);
        }

        return sbOut;
    }
 }
Bert F
  • 85,407
  • 12
  • 106
  • 123