65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 中的 Enigma 模拟器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (21投票s)

2005年4月13日

CPOL

4分钟阅读

viewsIcon

211470

downloadIcon

6597

本文展示了二战期间使用的德国 Enigma 密码机的内部工作原理,并用 C# 语言进行了实现。

Enigma emulator main image

Enigma emulator rotor configuration

引言

本文展示了如何使用 C# 实现类似 Enigma 的密码学。这种密码系统一直使用到 70 年代。它最著名的用途是在二战期间被德国军队使用。破解这个密码导致了盟军的胜利。这种密码方法仍然足够安全,可以在非关键系统中应用。如果德国人谨慎使用这种方法,也许盟军就不会发现他们的秘密了。

背景

读者可能对 Enigma 的一些基本信息感兴趣。这些链接可能会对您有所帮助。

使用代码

这段代码是一个完整的程序,展示了这种机器的内部工作原理。如果用户想了解更多关于 Enigma 机器的信息,那么他/她需要通过 Google 搜索更多信息。这段代码展示了使用现代编程语言模拟 Enigma 机器的复杂接线是多么简单。

该机器至少有三个转子,每个转子上都写有字母表。转子有一个可见部分,由字母表组成,还有一个部分有其他字母,这些字母通过电线连接到第一个字母。三个转子从右到左依次排列。转子不是静态的。

这种机器是电气和机械密码系统的结合。基本上,当机器用户按下一个键时,电流将流向第一个转子,到达与该键对应的字母(比如说 'A')。轮子的接线将导致第一个转子的输出字母具有不同的值,比如说 'D',这个脉冲随后将流向第二个转子,同样的事情也会发生在那里,以及在第三个转子中。从那里,脉冲将流向最后一个轮子 - 称为 '反射器'。这个轮子(转子)是唯一不旋转的轮子。脉冲将流向这个轮子,然后从这里返回到第三个转子,并向后传递。从第一个转子开始,电流将照亮面板下的一个 LED。这将是加密的字母。

这还不是全部。每按下一个键,第一个转子就会旋转一个位置,这样当您两次按下相同的键时,输出字母就不会相同。第二个转子将在第一个转子每旋转 28 次时旋转一个位置,第三个转子将在第二个转子每旋转 28 次时旋转一个位置。正如我之前所说,反射器是唯一的静态转子。在某些型号上,第二个和第三个转子将在前一个转子每旋转 28 次时旋转多次。

要解密数据,您只需将转子设置为与生成该输出的机器上的转子完全相同的初始位置,然后输入加密的数据。

有几个转子,它们可以互换。为了使事情更加复杂,军用版本有一个前面板,您可以在其中互连两个字母,这样每次出现一个字母时,它都会被另一个字母替换。为了展示这种复杂的内部工作原理,以下 C# 代码就足够了

//encrypt the data in the upper text box, and put the result in the lower one
//this code is taking the data in one text box 
//and puts the crypted/decrypted result
//in another one.
void Button1Click(object sender, System.EventArgs e)
{
   char[] chIn = txtInit.Text.ToUpper().ToCharArray();
   txtFinal.Text = "";
   for(int i=0;i<chIn.Length;i++){
      //we only use the upper letters
      if(chIn[i]>=65 && chIn[i]<=90){
         rr.Move();
         rr.PutDataIn(chIn[i]);
         txtFinal.AppendText(""+rr.GetDataOut());
      }
   }
}

您可以看到我们只使用了大写字母。这是因为标点符号和空格的存在会帮助解密消息。

我还创建了一个转子类,它实际上代表了 Enigma 机器的一个转子。在这个类中,我们有方法 `Move`,它将转子移动一个位置,以及 `PutDataIn`,它模拟将电信号发送到第一个转子。如您所见,我们只将数据发送到第一个转子,它会将数据进一步传递下去。

这是这个类的代码

using System;
using System.Text;
using System.Windows.Forms;

namespace Enigma
{

    public class Rotor
    {
        private string layout;
        private byte offset;
        private Rotor previous, next;
        private Label lbl;
        private char cIn = '\0', notchPos;

        public Rotor(string layout,Label lbl,char notchPos)
        {
            this.layout = layout;
            this.previous = previous;
            this.next = next;
            this.lbl = lbl;
            this.notchPos = notchPos;
            offset = 0;

        }

        public string GetLayout(){
            return layout;
        }

        public void SetNextRotor(Rotor next){
            this.next = next;
        }
        public void SetPreviousRotor(Rotor previous){
            this.previous = previous;
        }

        public char GetInverseCharAt(string ch){
            int pos = layout.IndexOf(ch);

            if(offset>pos){
                pos = 26 - (offset-pos);
            }else{
                pos = pos - offset;
            }

            if(previous!=null){
                pos = (pos+previous.GetOffset())%26;
            }

            return (char)(65+pos);
        }

        public int GetOffset(){
            return offset;
        }

        public char GetNotchPos(){
            return notchPos;
        }

        public void ResetOffset(){
            offset = 0;
        }

        public bool HasNext(){
            return next!=null;
        }

        public bool HasPrevious(){
            return previous!=null;
        }

        public void Move(){
            if(next==null){
                return;
            }
            offset++;
            if(offset==26){
                offset = 0;
            }

            if(next!=null && (offset+65) == ((notchPos-65)%26)+66){
                next.Move();
            }
            lbl.Text = ""+((char)(65+offset));
        }

        public void MoveBack(){
            if(offset==0){
                offset = 26;
            }
            offset--;

            lbl.Text = ""+((char)(65+offset));
        }

        public void PutDataIn(char s){
            cIn = s;
            char c = s;
            c = (char)(((c - 65) + offset) % 26 + 65);

            if(next!=null){
                c = layout.Substring((c-65),1).ToCharArray()[0];
                if((((c-65)+(-offset))%26 + 65)>=65){
                    c = (char)(((c-65)+(-offset))%26 + 65);
                }else{
                    c = (char)(((c-65)+(26+(-offset)))%26 + 65);
                }
                next.PutDataIn(c);

            }
        }

        public char GetDataOut(){
            char c = '\0';

            if(next!=null){
                c = next.GetDataOut();
                c = GetInverseCharAt(""+c);
            }else{ //only in the reflector case
                c = layout.Substring((cIn-65),1).ToCharArray()[0];
                c = (char)(((c - 65) + previous.offset)%26+65);

            }

            return c;
        }

    }
}

关注点

虽然这看起来很奇怪,但密码算法越古老,它就越安全。这是因为这意味着人们已经测试过它,并且他们没有找到任何漏洞。也许在以后的文章中,我将向您展示如何实现一个使用这种密码技术的即时消息程序。

历史

  • 2011 年 4 月 29 日:更新了源代码。
© . All rights reserved.