恩尼格码的发明和破解

恩尼格码是二战德军所采用的电子加密机械

 

其基本组成可以分为三部分:

键盘,转子和显示器

键盘一共具有26个键,类似于今天的计算机键盘(显示起见,省略为6个)

转子实际上有3个,缩略起见只画了1个

显示器在最右边,当键盘上的键被按下时,字母被加密后所对应的小灯就会在显示器上亮起来

 

即按下a灯,B灯会亮,这就意味着a被加密成了B

同样的,b被加密成了A,c被加密成了D,d被加密成了F,e被加密成了E,f被加密成了C。

这种加密方式称为“简单替换算法”

 

“简单替换”在现实中很容易通过频率统计法破解,这就用到了转子。

转子的作用在于键盘上的一个键被按下时,相应的密文会在显示器上显示,然后转子的方向就会自动的转动一个字母的位置

 

例如,当连续的按下3个b时:

第一次键入b时,信号通过转子中的连线使得A灯亮了起来,然后转子转动一格;

第二次键入b时,它所对应的字母就变成了C;

同样的,第三次键入b时,灯E会变亮。

 

这里看到了恩尼格码的关键:这并不是一个简单替换算法,同一个字母b在明文的不同位置时,可以被不同的字母所替换,而密文中不同位置的同一个字母,可以代表明文中的不同字母。这就使得频率分析法没有用武之地了。这种加密方式被称为“复式替换密码

 

但是如果连续的键入6个键(实际有26个键),转子就会整整转一圈,回到原始的方向上,这时编码就和最初重复了。于是在实际的恩尼格码中,一共有3个转子(二战后期甚至有4个转子)

所以要在26*26*26=17576后才会重复原来的编码。

 

自此基础上,设计者巧妙地在三个转子的一端加上了反射器

 

如图所示,键盘和显示器中的相同字母被连到了一起。反射器和转子一样,把一个字母连在另一个字母上,但是并不转动。乍一看这个固定的反射器并没有作用,并不增加可以使用的编码数目,但是如果将它和解码联系起来就会发现这种设计的别具匠心之处:

当一个键被按下时,信号并不从键盘直接传到显示器,而是首先通过三个转子连成的一条线路,然后通过反射器回到3个转子,再通过另一条线路到达显示器上。例如,b键被按下时,D灯亮。

如果此时按下的不是b键而是d键,则信号会按照相反的方向,到达B灯。

也就是说,反射器虽然没有增加可能的不重复方向,但是使得编码过程和解码过程完全一致

 

在实际情况下:发信人首先要调整三个转子的方向,使其处于17576中的一个方向(其实转子的初始方向即为密钥,这是约定好的),然后依次键入明文,发送出去。解密方只需要使用一台相同的恩尼格码机,按照约定,将转子的方向调整至和发信方相同,依次键入得到的密文,就可以得到明文--这就是反射器的作用

我们也可以得到,反射器的一个副作用就是一个字母永远也不能被加密成它自己,因为反射器中的1个字母总是被连接到另外1个字母。

 

也就是说,转子的初始方向决定了整个密文的加密方式

虽然17576很大,但是也足够进行暴力破解。当然设计者也可以再多增加转子,但是我们发现,每增加一个转子,也只是使得转子的初始方向增加了26倍(即乘26),同样增加了转子也就增加了恩尼格码的体积和成本

这时设计者使得三个转子做得可以拆卸下来互相交换,这样一来初始方向的可能性变成了原来的六倍

即将3个转子编号为1、2、3,那么它们可以被放成,123-132-213-231-312-321一共六种不同的位置,也就是说,现在收发信息的双方除了要约定好转子的初始方向,还必须约定好六种排列中的一种。

不过增加6种转子的排列方式也并不能有效地回避暴力破解问题。

下一步设计者在键盘和第一转子之间增加了一个连接板,这块连接板允许使用者用一根连线把某个字母和另一个字母连起来,这样这个字母的信号在进入转子之前就会变成另一个字母的信号,这样的连线一共有6根。

这样就可以使得6对字母的信号互换,其他没有连线的字母保持不变。当然连线状况也需要约定好

那么由转子自身的初始方向,转子之间的位置,以及连接板连线的状况就组成了所有可能的密钥:

三个转子不同的方向为26*26*26=17576种;

三个转子之间的相对位置为6种;

连接板上两两交换6对字母的可能性很大,由排列组合可得为100391791500种;

于是一共具有17576*6*100391791500,大约1亿亿种可能性。

只要约定好上面的密钥,收发双方之间就可以简单的加密和解密,但是如果不知道密钥,破解出来基本没有可能。

 

我们总结一下可以发现,连线班对可能性的贡献最大,但是由于连线班本身是一个“简单替换”,非常容易被频率分析;转子的提供的可能虽然不多,但是其“复式替换”系统的设计,使得整个系统再也无法受到频率分析。这二者优势互补,增加了可能性,也使得暴力破解望而却步。(这是我认为恩尼格码机最出色,也是最有魅力的设计所在)。

 

“加密系统的保密性只应建立在对密钥的保密上,而不应该取决于加密算法的保密”。恩尼格码的设计使得,即使敌人获取了一台恩尼格码机,但是如果不知道密钥(即转子的初始方向,转子的相对位置以及连线板的连接情况),破解是不现实的。

 

在战争期间,如果每天只有一个密钥,那么难免被对方截获大量的以同一个密钥加密的信息,这对保密工作有害无益。尽管不知道对于恩尼格码机是否可以采用类似的方法,德国人还是留了个心眼。他们决定在当天密钥调整好的恩尼格码机上不直接加密要发送的明文,相反的,首先发送的是一个新的密钥

例如,在连线板的连接顺序和转子的顺序不改变的情况下,随机选择三个字母,输入两次,比如PEHPEH,加密为比如KIVNSE(注意复式加密的会被加密成不同的形式,第一次KIV,第二次NSE),然后发信方把KIVNSE放在电文的最前面,接着重新调整三个转子的初始方向到PEH,才开始继续加密。

这种方法的每一份电报都有属于自己的三个表示初始方向的密钥。输入两遍是为了纠错。

 

一直到图灵破解恩尼格码时,对于其的破译都采用的是雷杰夫斯基的方法,即利用每条密文最开始重复的密钥。如果德国人发觉这一点并取消,那么英国密码分析专家的破译手段就会毫无用处。图灵的任务就是找到另一种不必利用重复密钥的破译方法。

在发现德国人总是在早上6点发出天气预报,其中八成含有“天气”,也就是“Wetter”这个词,根据此前德国人天气预报的死板形式,图灵甚至能够准确的知道这个词具体在密文的哪个位置,这就使得图灵想到使用“候选单词”这个方法来破译恩尼格码电文。

如果在一篇密文中,图灵知道了Wetter这个词被加密成了ETJWPX,那么剩下的工作就是找到将Wetter加密成ETJWPX的初始位置,如果暴力破解,将会遇到1590亿种的组合。但是雷杰夫斯基的天才思想告诉图灵,必须将转子方向变化造成的问题和连接板交换字母造成的问题分开来考虑

假设图灵已经猜到了Wetter这个词被加密成了ETJWPX,那么存在一个字母循环圈:

 

图灵并不清楚在密文中出现这个候选单词时的转子状态,但是假设他猜对了这个候选单词,把这个候选单词起始时的转子方向记为S,那么在此时恩尼格码把w加密成了E;然后转子转到了下一个状态,假设为S+1,恩尼格码把e加密成T;在S+2方向上一个不属于这个循环的字母被加密了,暂且不去管它;接下来在S+3方向,恩尼格码把t加密成W。

图灵的绝妙思想是利用3台恩尼格码机将连线板效应消除!

也就是说将第一台恩尼格码显示器上的E和第二台恩尼格码显示器上的e连起来,又把第二台上的T和第三台上的t连起来,最后把第三台上的W和第一台上的w连起来(注意恩尼格码本身并没有区分大小写,只用来区分明文密文)

假设连接板上有关的交换字母的连线是这样的:

E<——>L1

T<——>L2

W<——>L3

现在假设字母w被输入第一台恩尼格码机,它先通过连接板变成了L3,然后通过三个转子经过反射器,再通过三个转子返回连接板;因为我们根据候选单词知道w此时会被加密成E,所以没有经过接线板前它一定是和E对应的L1;L1经过接线板变成E后,直接成了第二台恩尼格码机的输入。提醒一下,第二台恩尼格码机的转子方向是S+1,所以根据候选单词知道e此时会被加密成T,我们来看看具体是怎么回事。从第一台恩尼格码机来的e通过连接板变成了L1,再通过转子和反射器回来变成了连接板上和字母T对应的L2;通过连接板后变成了T,然后这个T又变成第三台恩尼格码机上的输入t。第三台恩尼格码机的转子方向是S+3,这个传送过来的t会被加密成E,具体的情况和上面第一第二台上的类似。我们发现现在三台恩尼格码机的线路组成了一个闭合回路,如果在里面加上一个灯泡,它就会亮起来。

这是我们发现,无论连接板上的连线究竟如何(也就是说无论L1,L2,L3是什么),只要转子的方向正确,这个回路就会闭合(小灯泡就会亮)。当然也需要找到连接板上的连线,但是这是一个简单替换,比如键入ETJWPX出来了tewwer,就说明w和t交换了,键入其他部分可以猜出其他字母的交换情况。

同时其他的问题也可以通过一定的技巧解决。

比如说即使找到了候选单词,也不一定能确定其在一整句话的位置。

但是根据反射器的构造我们发现,一个字母从来也不会被加密成它本身,可以用一句明文去逐渐的对应密文,找到有相同对应的情况就排除,其他的可以交给破译工具去尝试。

转载于:https://www.cnblogs.com/lzhdcyy/p/6556116.html

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
尼格机是一种简单的替换密,它使用一个密钥表来将明文字符替换为密文字符。Java可以很容易地实现这个密机的加密和解密过程。以下是一个示例程序: ```java import java.util.HashMap; import java.util.Map; public class EnigmaCipher { private Map<Character, Character> keyTable; public EnigmaCipher(String key) { keyTable = new HashMap<>(); String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for (int i = 0; i < alphabet.length(); i++) { char c = alphabet.charAt(i); char k = key.charAt(i); keyTable.put(c, k); } } public String encrypt(String plaintext) { StringBuilder ciphertext = new StringBuilder(); for (int i = 0; i < plaintext.length(); i++) { char c = plaintext.charAt(i); if (Character.isUpperCase(c)) { c = keyTable.get(c); } ciphertext.append(c); } return ciphertext.toString(); } public String decrypt(String ciphertext) { StringBuilder plaintext = new StringBuilder(); for (int i = 0; i < ciphertext.length(); i++) { char c = ciphertext.charAt(i); if (Character.isUpperCase(c)) { for (Map.Entry<Character, Character> entry : keyTable.entrySet()) { if (entry.getValue() == c) { c = entry.getKey(); break; } } } plaintext.append(c); } return plaintext.toString(); } public static void main(String[] args) { String key = "QWERTYUIOPASDFGHJKLZXCVBNM"; EnigmaCipher cipher = new EnigmaCipher(key); String plaintext = "HELLO WORLD"; String ciphertext = cipher.encrypt(plaintext); String decryptedText = cipher.decrypt(ciphertext); System.out.println("Plaintext: " + plaintext); System.out.println("Ciphertext: " + ciphertext); System.out.println("Decrypted text: " + decryptedText); } } ``` 在这个示例程序中,我们创建了一个 `EnigmaCipher` 类,它接受一个密钥表作为构造函数的参数。`encrypt()` 方法接受明文字符串并返回密文字符串,`decrypt()` 方法接受密文字符串并返回明文字符串。我们还提供了一个 `main()` 方法来演示如何使用这个类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值