找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 33482|回复: 55

【VB】探访纳粹德军的秘密—Enigma密码机的原理与编程实现

[复制链接]
发表于 2015-1-25 20:34:57 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
    1918年,德国。一个名叫亚瑟·谢尔比乌斯的发明家与他的朋友查理·里特创办了一个名为谢尔比乌斯和里特公司。而他却不知道,他的所作所为将改变第二次世界大战。
    不久,谢尔比乌斯发明了一个机器并给它起名叫做Enigma(迷)。这台机器乍看起来好似一台打字机,按下机器键盘上的一个键,机器键盘上方的字一个母就会亮起来,同时在机器顶部的三个转轮最右边的那一个会相应转动一个位置。如果第二次再按下相同的键,另一个字母又会亮起来。
Enigma.jpg
   
    起初这台机器被用来加密商业机密,但是德国军方看上了它。于是在整个二次世界大战进程中,Enigma扮演了极其关键的重要角色——加密纳粹德军的通讯信息。然而不幸谢尔比乌斯却没能活到这一天,他于1929年5月13日骑马时摔在墙上因而撒手人寰。
1.JPG
   
    纳粹德军将Enigma看作是牢不可破的密码,当然,在1941年以前事情的确如此。随着二战深入,德军不断改进Enigma密码机,增加密码机加密的可靠性。在1941年,英国海军捕获了一条德军潜艇U-110。在这艘潜艇上英军意外得到了一台还没来得及销毁的Enigma密码机。这是一历史性的时刻,在此之后Enigma得到了破译,而有人说这导致了二战进程提前两年结束,数以千计的盟军士兵的生命得以拯救。
    破译Enigma的行动以绝密方式在英国布莱切利公园内进行,这一行动中不乏世界各地的天才,年轻的天才数学家阿兰图灵便是其中一位。他发明的机电式计算机“炸弹”为密码破译立下了汗马功劳。
225px-Alan_Turing_photo.jpg u=4137712407,69905525&fm=15&gp=0.jpg

图灵和“炸弹”

    让我们来仔细看一看这台神奇机器的内部构造:机器大致分为五个部分,分别是:键盘、接线板、转子、反射器、显示器。机器的加密过程是这样的:按下键盘上的一个键,电流从电池中导出。经过接线板,接线板的功能是将一个字母连接至另一字母。而后电流到达定子,定子将电流送至转子。在德国海军型的Enigma机上有4个转子,电流依次经过四个转子到达反射器。反射器的功能类似于接线板,将一个字母的电流信号连接至另一字母后,反射器将电流送回转子。这时电流反序依次经过刚才的4个转子,经定子到达接线板后流至显示器上的一个灯泡。灯泡被点亮,显示加密结果。同时最左边的转子下移一位。加密过程结束。具体的细节大家可以在维基百科上查阅:http://zh.wikipedia.org/wiki/%E6%81%A9%E5%B0%BC%E6%A0%BC%E7%8E%9B%E5%AF%86%E7%A0%81%E6%9C%BA
640px-Enigma_wiring_kleur.svg.png

图001

    那么接下来,老C教大家用程序实现整个过程。
    老C用的开发工具是VB6。在写代码之前,我们先要准备好一些资源。首先需要的是26个显示器点亮和未点亮时的图片: 图形.bmp 。接着是26个按键分别在按下和未按下时的图片: 按键.bmp 按键2.bmp 。然后我们需要转子转动相关图片: 未命名2.bmp 。一些附件图片(比如接线板插口): insert.bmp 。最后是几个背景: 2.JPG 3.JPG gdfsfads.JPG
    将这些图片按一定规律保存进RES文件,1xx(101-126)是显示器未点亮图片、2xx(201-226)是显示器点亮的图片、3xx(301-326)是键盘按键未按下图、4xx(401-426)键盘按键按下、501,502,601,602分别存放转子转动效果图,700存放接线板插销。
    下面我们需要按照键位画好控件数组,将Image1的Image1(1)~Image1(26)分别对应字母A~Z放置在主界面上,将Image2的Image2(1)~Image2(26)分别对应键盘字母A~Z放置在主界面上,其他一些相关对象可以再放置。
    好的,接着我们分析机器原理,将机械结构转变为我们的数据结构。首先看图001,根据上述的简单原理我们不难得出,接线板,反射器,转子三者分别可以看作为若干数组。我们使用数组定义语句:Dim RotorN(26) As Integer .为了便于理解,我们将一维数组下标看做1,而不是默认的0.这样,在线路寻址是我们可以这样使用数组:数组下标可以看做入口,数组内容可以看做出口,若一个数组内容为 RotorI(Asc("A")-64)==90,那么即可理解为,电流从A进入从Z流出(Chr(90)=="Z")。
4.JPG

这幅图清楚地反映了原理

    在处理转子的旋转方面,我们可以这么做:在转子左右两边分别定义两个数组,比如转子III,II,I,对于转子II,我们可以定义数组3-2,2-1,名称可以为 Riii_Rii(26)和Rii_Ri(26)。在旋转时,就会有如下现象:
入口--(上端)ABCDEFGHIJKLMNOPQRSTUVWXYZ(下端)

出口--(上端)ABCDEFGHIJKLMNOPQRSTUVWXYZ(下端)

原先的情况

入口--(上端)ABCDEFGHIJKLMNOPQRSTUVWXYZ(下端)

出口--(上端)ZABCDEFGHIJKLMNOPQRSTUVWXY(下端)

向下移动一位

入口--(上端)ABCDEFGHIJKLMNOPQRSTUVWXYZ(下端)

出口--(上端)BCDEFGHIJKLMNOPQRSTUVWXYZA(下端)

向上移动一位

我们在编写转动算法时,可以将数组内容预先放置在一临时数组中,然后再进行错位赋值 向上转动可以写作:roto(i) = temp(i-1)向下转动则可写为roto = temp(i+1)。转子转动时,左右两边的接口都需要跟着转动,转子II向下转动时,Riii_Rii和Rii_Ri要跟着向下转动。这里要注意的是,转子II向下转动,Riii_Rii向下转动,而转子III向下转动,Riii_Rii需要向上转动,因为转子二不动,转子三向下转动相当于转子二向上转动,转子三不动。在处理转子进位是,我们可以将进位标记写入变量,比如转子二的两个V型槽可以被写入Public RotorII_V(2)  As Integer这样一个变量。在I的转动过程中,如果满足I的位置等于变量任一值,则可调用II的向下转动过程。
以下是转子I的转动过程子程序:
Public Sub OnRotorIII(Optional flagDown As Boolean = True)
    Dim i As Integer
    Dim r(26) As Integer
   
    If flagDown = True Then
        For i = 1 To 26
            r(i) = I_Riii(i)
        Next
        I_Riii(1) = r(26)
        For i = 1 To 25
            I_Riii(i + 1) = r(i)
        Next
        
        For i = 1 To 26
            r(i) = Riii_Rii(i)
        Next
        Riii_Rii(1) = r(26)
        For i = 1 To 25
            Riii_Rii(i + 1) = r(i)
        Next
        
        For i = 1 To 26
            r(i) = RotorC(i)
        Next
        RotorC(1) = r(26)
        For i = 1 To 25
            RotorC(i + 1) = r(i)
        Next
        
        If I_Riii(26) = RotorI_V(1) Or I_Riii(26) = RotorI_V(2) Then
            If isTyping = True Then Call FrmMain.Image4_Click(2)
        End If
    Else
        For i = 1 To 26
            r(i) = I_Riii(i)
        Next
        I_Riii(26) = r(1)
        For i = 1 To 25
            I_Riii(i) = r(i + 1)
        Next
        
        For i = 1 To 26
            r(i) = Riii_Rii(i)
        Next
        Riii_Rii(26) = r(1)
        For i = 1 To 25
            Riii_Rii(i) = r(i + 1)
        Next
        
        For i = 1 To 26
            r(i) = RotorC(i)
        Next
        RotorC(26) = r(1)
        For i = 1 To 25
            RotorC(i) = r(i + 1)
        Next
    End If
   
    FrmMain.Label1(1).Caption = Format(RotorC(26) - 64, "00")
End Sub
可以看出flagDown控制了转动方向,r(26)是临时数组变量。
    处理完转子的转动,下面是加密的主函数。加密函数非常容易完成(甚至是整个程式最容易实现的部分),我们只要将传入的字符ascii码依次经过各个接线板数组、转子连接数组、转子数组、反射器数组即可。当信号经过反射器后,逆向搜寻各个数组,再将信号传入下一数组:
Public Function DisplayEncode(c As String) As Integer
    Dim i As Integer
    Dim j As Integer
   
    HistoryIn = HistoryIn & UCase(c) '记录输入历史
   
    i = Asc(UCase(c))
    i = Plugboard(i - 64)
    i = I_Riii(i - 64) 'i-3
    i = RotorIII(i - 64)
    i = Riii_Rii(i - 64) '3-2
    i = RotorII(i - 64)
    i = Rii_Ri(i - 64) '2-1
    i = RotorI(i - 64)
    i = Ri_Rx(i - 64) '1-x
    i = RotorX(i - 64)
    i = Rx_F(i - 64) 'x-f
   
    i = Reflector(i - 64)
   
    For j = 1 To 26
        If Rx_F(j) = i Then
            i = j + 64
            Exit For
        End If
    Next
    For j = 1 To 26
        If RotorX(j) = i Then
            i = j + 64
            Exit For
        End If
    Next
    For j = 1 To 26
        If Ri_Rx(j) = i Then
            i = j + 64
            Exit For
        End If
    Next
    For j = 1 To 26
        If RotorI(j) = i Then
            i = j + 64
            Exit For
        End If
    Next
    For j = 1 To 26
        If Rii_Ri(j) = i Then
            i = j + 64
            Exit For
        End If
    Next
    For j = 1 To 26
        If RotorII(j) = i Then
            i = j + 64
            Exit For
        End If
    Next
    For j = 1 To 26
        If Riii_Rii(j) = i Then
            i = j + 64
            Exit For
        End If
    Next
    For j = 1 To 26
        If RotorIII(j) = i Then
            i = j + 64
            Exit For
        End If
    Next
    For j = 1 To 26
        If I_Riii(j) = i Then
            i = j + 64
            Exit For
        End If
    Next
    i = Plugboard(i - 64)
   
    HistoryOut = HistoryOut & Chr(i) '记录输出历史
   
    DisplayEncode = i - 64
End Function
当我们按照先前说的步骤设置好资源文件后,显示器的点亮工作就变得相当简单:两行代码搞定
i = DisplayEncode(CStr(Chr(Index + 64)))
Image1(i).Picture = LoadResPicture(i + 200, 0)
    主要的工作完成以后,接下来是一些琐事,比如画一个历史记录的对话框,一个自动输入的对话框,为接线板写好相应算法之类的。老C不再赘述,具体代码可以看老C提供的源码。最后写一份所有转子,反射器数据的ini,用GetPrivateProfileString api将设置读入,也可以使用WritePrivateProfileString将数据保存写入ini文件。
    至此,一个Enigma模拟器就实现了,是不是有些小激动呢?我实现了德军二战的机密O(∩_∩)O哈哈~
捕获.JPG

此处是可执行文件(EXE)的下载: CENIGMA_EXE.zip (176.6 KB, 下载次数: 162)


Enigma Emulator 源码下载:
游客,如果您要查看本帖隐藏内容请回复


本帖被以下淘专辑推荐:

回复

使用道具 举报

发表于 2015-1-25 21:24:55 来自手机 | 显示全部楼层
有才!。。。。。。。
回复

使用道具 举报

发表于 2015-4-21 10:18:14 | 显示全部楼层
赞一个,这个东西太好了
回复 赞! 靠!

使用道具 举报

发表于 2015-9-18 17:25:15 | 显示全部楼层
说的不错,知乎很早前我也看到一篇介绍这个的文章,很受用。谢谢
回复 赞! 靠!

使用道具 举报

发表于 2015-9-21 06:05:13 来自手机 | 显示全部楼层
cc酱好六,喵
回复 赞! 靠!

使用道具 举报

 楼主| 发表于 2015-9-21 18:39:59 | 显示全部楼层
相关电影推荐:《猎杀U-571》《模仿游戏》
回复 赞! 靠!

使用道具 举报

发表于 2015-10-28 16:48:31 | 显示全部楼层
学习学习原理
回复 赞! 靠!

使用道具 举报

发表于 2015-12-27 21:42:40 | 显示全部楼层
最近有个任务就是要用程序实现enigma
回复 赞! 靠!

使用道具 举报

发表于 2016-1-8 20:07:45 | 显示全部楼层
看看,学习学习
回复 赞! 靠!

使用道具 举报

发表于 2016-3-21 17:18:00 | 显示全部楼层
要看要看要看
回复 赞! 靠!

使用道具 举报

发表于 2016-3-24 08:27:07 | 显示全部楼层
很好。。。。。。。。。。。。。。。
回复

使用道具 举报

发表于 2016-4-7 08:47:32 | 显示全部楼层
很厉害。崇拜
回复 赞! 靠!

使用道具 举报

发表于 2016-5-8 12:50:07 | 显示全部楼层
万分感谢 下来学习
回复 赞! 靠!

使用道具 举报

发表于 2016-7-11 11:33:21 | 显示全部楼层
看看
ding
wq
回复 赞! 靠!

使用道具 举报

发表于 2016-10-12 17:13:06 | 显示全部楼层
谢谢楼主,如果能用C写就更好了
回复 赞! 靠!

使用道具 举报

发表于 2016-10-12 21:58:28 | 显示全部楼层
很好的分享,楼主高人。
回复 赞! 靠!

使用道具 举报

发表于 2017-3-3 21:49:39 | 显示全部楼层
厉害!学习学习
回复 赞! 靠!

使用道具 举报

发表于 2017-3-11 22:38:19 | 显示全部楼层
什么意思
回复

使用道具 举报

发表于 2017-3-11 22:40:29 | 显示全部楼层
怎么看源码
回复 赞! 靠!

使用道具 举报

发表于 2017-3-19 13:33:06 | 显示全部楼层
感謝分享!
回复

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-21 20:45 , Processed in 0.044465 second(s), 32 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表