简体   繁体   English

我试图解密C#中的自动密钥密码,但我一直得到一个未处理的异常

[英]I am trying to decrypt the autokey cipher in C#, but I keep getting an unhandled exception

I know how to fix the error, but I don't know where to place the line of code to make it successfully run. 我知道如何修复错误,但我不知道在哪里放置代码行以使其成功运行。 The place that I am having issues with is where I try and create the passletter character. 我遇到问题的地方是我尝试创建密码字符的地方。

static string AutokeyDecrypt(string encryptmess, string pass)
    {
        //sets both secret message and password to arrays so they can be shifted
        char[] passkey = pass.ToCharArray();
        char[] decryptmess = encryptmess.ToCharArray();
        char newletter = ' ';

        for (int i = 0; i < decryptmess.Length; i++)
        {
            char passletter = (char)(passkey[i] + newletter); //This is the line on which I am having issues. I need to concatenate the key (passletter) with each newletter that I decode.
            char messletter = decryptmess[i];

            //shifts the letters in the message back to original using the first letter of the concatenated key
            int shift = passletter - ' '; // passletter - (space character)
            newletter = (char)(messletter - shift); // Add shift to message letter

            //loops through the ASCII table
            if (newletter > '~')
            {
                newletter = (char)(newletter - 94);
            }
            else if (newletter < ' ')
            {
                newletter = (char)(newletter + 94);
            }
            decryptmess[i] = newletter;
        }
        return new string(decryptmess);
    }

The problem is almost certainly that you are trying to access an index in the passkey array that is beyond the end of the array, which results in an IndexOutOfRange exception. 几乎可以肯定,您正在尝试访问超出数组末尾的passkey数组中的索引,这会导致IndexOutOfRange异常。

Specifically: 特别:

char passletter = (char)(passkey[i] + newletter);

Let's say that you have a password that is 10 characters in length. 假设您的密码长度为10个字符。 The valid indices for the passkey array are 0 to 9. At the 11th character of the message ( i == 10 ) your code attempts to read from index 10, which is invalid. passkey数组的有效索引是0到9.在消息的第11个字符( i == 10 )中,您的代码尝试从索引10读取,这是无效的。

The standard way to handle this is to use the modulus operator % to wrap the indexing through the valid values: 处理此问题的标准方法是使用模数运算符%来通过有效值包装索引:

char passletter = (char)(passkey[i % passkey.Length] + newletter);

At i = 10 (for a 10 character array) this will return the first character (index 0) from the array. i = 10 (对于10个字符的数组),这将返回数组中的第一个字符(索引0)。


Beyond the indexing error, there are some other issues that you should address. 除了索引错误之外,还有一些其他问题需要解决。

At the top of your code you do this: 在代码的顶部,您可以执行以下操作:

char[] passkey = new char[pass.Length];
passkey = pass.ToCharArray();

This creates an empty array, then immediately replaces it with a new array that is created from the pass string. 这将创建一个空数组,然后立即将其替换为从pass字符串创建的数组。 The initial array is wasted space that will be disposed of by the garbage collector and isn't necessary here. 初始数组是浪费的空间,将由垃圾收集器处理,这里不需要。

Long story short, replace the above code (and the same pattern immediately after it for decryptmess ) with: 长话短说,用以下代码替换上面的代码(以及紧随其后用于decryptmess的相同模式):

char[] passkey = pass.ToCharArray();

This is a very common pattern for people coming to C# from C or C++ where you often have to allocate arrays and then fill them and most code assumes that you will be giving it an array to fill rather than expecting it to allocate and return. 对于从C或C ++进入C#的人来说,这是一种非常常见的模式,你经常需要分配数组,然后填充它们,大多数代码假定你将给它一个数组来填充而不是期望它分配和返回。 There are reasons why you might want to do either, mostly around resource management that we don't tend to use in C#. 您有可能想要这样做的原因,主要是我们不习惯在C#中使用的资源管理。

In C# it is more normal for arrays to be created by methods like ToArray() , ToCharArray() , etc. There can be some performance implications here, but that's how it goes. 在C#中,通过ToArray()ToCharArray()等方法创建数组更为正常。这里可能会有一些性能影响,但就是这样。 When you call a method that returns an array or other object it is not necessary for you to pre-allocate the array or object. 当您调用返回数组或其他对象的方法时,您不必预先分配数组或对象。

The other answer didn't factor in the AutoKey cypher, so here's a better one. 另一个答案没有考虑AutoKey cypher,所以这里有一个更好的。

Using the cypher description and examples here I think it's clear that your code isn't going to implement the decryption correctly. 使用这里的密码描述和示例我认为很明显您的代码不会正确实现解密。

The encryption algorithm uses a key stream that is composed of the message text appended to the password, and the decryption process rebuilds the key stream as it goes. 加密算法使用由附加到密码的消息文本组成的密钥流,并且解密过程在密钥流进行时重建密钥流。

I've chosen to implement based on a flexible alphabet that looks like this: 我选择基于灵活的字母表来实现,如下所示:

char[] alphabet = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".ToCharArray();

This includes all ASCII printable characters from 32 ( 这包括32的所有ASCII可打印字符( ) to 127 ( ~ ) in numeric order, but could be any order and/or useful subset of the range. )数字顺序为127( ~ ),但可以是范围的任何顺序和/或有用子集。 The alphabet is the first defining characteristic of your specific variant of the cypher. 字母表是您的特定密码变体的第一个定义特征。 From your code it appears that the alphabet above matches what you're expecting. 从您的代码中可以看出,上面的字母符合您的期望。

Each encrypted character is calculated from the characters in the key stream and message at the location by looking up their indices in the array, adding those indices and locating the encrypted character in the alphabet: 每个加密字符都是根据密钥流中的字符和位置上的消息计算出来的,方法是在数组中查找它们的索引,添加这些索引并在字母表中找到加密字符:

string Encrypt(string plain, string pass) 
{
    char[] message = plain.ToCharArray();
    char[] keystream = (pass + plain).ToCharArray();

    for (int i = 0; i < message.Length; i++)
    {
        int keyidx = Array.IndexOf(alphabet, keystream[i]);
        int msgidx = Array.IndexOf(alphabet, message[i]);

        message[i] = alphabet[(alphabet.Length + keyidx + msgidx) % alphabet.Length];
    }
    return new string(message);
}

NB: the line that sets message[i] uses a modulo operation to constrain the result to the array bounds. 注意:设置message[i]使用模运算将结果约束到数组边界。 Since keyidx + msgidx could both be a negative and the % operator doesn't wrap this ( -1 % 10 == -1 , etc) I've added alphabet.Length to ensure only positive indices. 由于keyidx + msgidx都可能是负数而%运算符不包含这个( -1 % 10 == -1等)我添加了alphabet.Length以确保只有正数指数。

Decryption is basically the same except that the output of the decryption loop is used to build the content of the key stream as it goes. 解密基本相同,只是解密循环的输出用于构建密钥流的内容。 The key stream is initialized in the same way - concatenate password and message - but is updated as the decryption progresses. 密钥流以相同的方式初始化 - 连接密码和消息 - 但随着解密的进行而更新。

string Decrypt(string encrypted, string pass)
{
    char[] message = encrypted.ToCharArray();
    char[] keystream = (pass + encrypted).ToCharArray();

    for (int i = 0; i < message.Length; i++)
    {
        int keyidx = Array.IndexOf(alphabet, keystream[i]);
        int msgidx = Array.IndexOf(alphabet, message[i]);

        message[i] = alphabet[(alphabet.Length + msgidx - keyidx) % alphabet.Length];
        keystream[i + pass.Length] = message[i];
    }
    return new string(message);
}

Constructing the keystream buffer in this way ensures that we always have sufficient space to update, even when we reach the end of the process and no longer need the last few characters. 以这种方式构造keystream缓冲区可确保我们始终有足够的空间来更新,即使我们到达流程的最后并且不再需要最后几个字符。

Of course the code above assumes that your inputs are going to be well behaved and not include anything outside of the alphabet values. 当然,上面的代码假定您的输入表现良好,并且不包括字母表值之外的任何内容。 Any value in the password or the message that doesn't match one of the values in the alphabet will be treated as identical to the last character in the alphabet since Array.IndexOf returns -1 for any failed search. 密码中的任何值或与字母表中的某个值不匹配的消息都将被视为与字母表中的最后一个字符相同,因为Array.IndexOf对于任何失败的搜索Array.IndexOf返回-1。 So if your input string includes any Unicode or high ASCII characters they will be rendered as ~ in the output. 因此,如果您的输入字符串包含任何Unicode或高位ASCII字符,它们将在输出中呈现为~

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 尝试打开.ini文件,但我在C#中不断收到错误 - Trying to open a .ini file but i keep getting an error in C# VS Express 2013 C#使用“ DA.Fill(DT);”,并且不断收到未处理的异常没有为一个或多个必需参数提供值 - VS Express 2013 C# using “DA.Fill(DT);” and I keep getting An unhandled exception No value given for one or more required parameters 为什么在保存数据库条目时会收到​​“未处理的异常”? - Why am I getting an "unhandled exception" when saving a database entry? 为什么我在尝试从正在写入的文件中读取时遇到未处理的异常:System.IO.IOException? - Why am I getting a Unhandled Exception: System.IO.IOException when trying to read from a file being written to? 我正在忙于编写C#IBM MQ Client应用程序,并不断收到“异常:&#39;IBM.WMQ.Nmqi.UnmanagedNmqiMQ&#39;的类型初始化程序引发了异常。” - I am busy writing a C# IBM MQ Client app and keep getting “Exception: The type initializer for 'IBM.WMQ.Nmqi.UnmanagedNmqiMQ' threw an exception.” 我正在尝试简单的C#程序? - I am trying to simple c# program? 为什么我得到一个FormatException是未处理的错误? - Why am I getting a FormatException was unhandled error? 为什么在这里收到“未处理IndexOutOfRangeException”? - Why am I getting an “IndexOutOfRangeException was unhandled” here? 为什么我尝试使用C#写入日志文件时获得“不支持URI格式”异常? - Why am I obtaining “URI formats are not supported” exception trying to write into a log file using C#? 我不断收到异常特性“内插字符串”无法使用,因为它不是 C# 4.0 语言的一部分? - I keep getting exception Feature `interpolated strings' cannot be used because it is not part of the C# 4.0 language?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM