密码那些事儿|(十)“钥匙”打开维吉尼亚的锁

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了密码那些事儿|(十)“钥匙”打开维吉尼亚的锁相关的知识,希望对你有一定的参考价值。

参考技术A 在说维吉尼亚加密法的破解方法之前,有必要来回顾一下它的加密原理。

维吉尼亚加密法是由26套密码组成的表,我们默认要用多套密码给原文加密的时候,具体操作中密文的每个字母由哪套密码来加密,是由钥匙规定的。钥匙最初都是一个正常的单词,原文很长,钥匙最初很短,为了让原文和钥匙一一对应,就反复使用钥匙。比如钥匙是boy,只有3个字母,我们可以boyboyboy这样一直循环下去,和原文中每个字母一一对应。

我们先来看一个例子,在这个例子里,原文是“the sun and the man in the moon”,钥匙是KING,用维吉尼亚加密法加密之后,密码文是一串看起来没什么规律的字母。我们把钥匙、原文和密文的内容依次记录下来。

原文的内容中,有3个定冠词the,变成密文后,the变成了两种样子,第一种是DPR,第二和第三种是BUK。第一个我们不管,关键点就在于——

第二个和第三个竟然加密成了相同的密文。

为什么会出现这种情况,这是巧合吗?

不是的。我们可以看钥匙单词KING,它由4个字母组成。我们再看密文中,后两个代表the的BUK,间隔了8个字母,间隔距离正好是钥匙长度的2倍。也就是说,正好在KING这个钥匙循环到整数倍的时候,如果也正好赶上出现了同样的原文,那巧合就出现了——原文就会被加密成相同的密文。

根据这个规律,我们就能确定钥匙的长度。

比如有这样一段密文:

DYDUXRMH TV NQD QN DYDUXRMH ARTJGW NQD

其中,两个 DYDUXRMH 的出现相隔了15个字母。因此,可以假定钥匙的长度是15的约数,即长度为15、5或3。而两个 NQD 则相距20个字母,意味着钥匙长度应为20、10、5、4或2。取两者的交集,则可以基本确定钥匙长度为5。

这一步,就是破解维吉尼亚加密法的关键一步。

接下来,我们已经知道钥匙的长度是5了,那就意味着在原文中第1、第6、第11、第16……,这些字母单独挑出来放在一组叫作A组。A组可是由维吉尼亚密码表中,同一行移位的字母加密得到的结果。我们再把第2、第7、第12、第17……,这些字母挑出来放在一起称作B组,它们又是用另一行移位字母加密得到的。

我们把这些按组别归纳起来:

A组:第1、第6、第11、第16……

B组:第2、第7、第12、第17……

C组:第3、第8、第13、第18……

D组:第4、第9、第14、第19……

F组:第5、第10、第15、第20……

这就相当于,将原来的密文分解成了五组新的密文,每一组都是由维尼尼亚加密法中的单独一行加密而成,也即单套密码加密。

单套密码加密怎么破解?我们之前介绍过的——频率分析法。

所以,我们来总结一下维吉尼亚加密法的步骤:

第一步,是从密文中找出拼写完全相同的字母串;

第二步,计算出钥匙的长度;

第三步,将密文分析成若干组(与钥匙长度对应);

第四步,分别对每组密文用频率分析法破解。

在20世纪之前,人们一直以为这套方法是普鲁士少校卡西斯基在1863年发明的,所以一直以来这套破解法叫作“卡西斯基试验法”。但是后来更多的资料公布,发现剑桥大学的英国科学家巴贝奇在9年前就已经写下了解法,这位巴贝奇也是后世认为的计算机创造者之一。

发明者受委屈,这既是密码学领域的特点,又是密码学研究者躲不过的委屈。

不论是剑桥大学的巴贝奇,还是普鲁士军官卡西斯基,虽然他们破解了维吉尼亚密码法,但他们在世的时候,始终都不知道自己其实已经在密码学上引起了一场革命。

往期文章:

密码那些事儿|(九)维吉尼亚登场

密码那些事儿|(八)玛丽女王被密码改变的人生

密码那些事儿|(七)以频率之矛,攻移位之盾

密码那些事儿|(六)中外古时候的移位加密

密码那些事儿|(五)换个位置,面目全非

密码那些事儿|(四)隐藏的消息

密码那些事儿|(三)“风语者”——从未被破解的密码

密码那些事儿|(二)密码学发展的七个阶段

密码那些事儿|(一)无所不在的密码

本人是官方授权会员推广专员,点击 会员专属通道 成为会员,您将会获得钻奖励及诸多权益!

《钻奖励调整公告》

维吉尼亚加密

【中文标题】维吉尼亚加密【英文标题】:Vigenère encryption 【发布时间】:2015-03-01 07:22:59 【问题描述】:

我一直有一个关于凯撒密码和维吉尼亚密码的项目。我已经想出了凯撒密码,但我正在努力使用维吉尼亚密码。

我的加密代码分为两部分:encrypt()encrypt1()encrypt() 方法提取每个字符并将其传递给 encrypt1(),基本上所有加密都在此处进行。我在这里做了代码,但它没有给我任何好的结果。

例如,如果new VigenereCipher("SLIME").encrypt("GREEN") 其中"SLIME" 是密钥密码,"GREEN" 是应加密为YcMQR 的字符串。但我的代码给出了?&|

认为字母对象涵盖了所有内容 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz 1234567890!@#$%^&*()_+-=[]\\|;:'\",./?<>"

有人可以帮我吗?

public class VigenereCipher extends SymmetricCipher 

    protected String password;
    protected int passwordPos;

    public VigenereCipher(String password, Alphabet alphabet)
    
        super(alphabet);
        this.password = password;
    
    public VigenereCipher(String password)
    
        super(Alphabet.DEFAULT);
        this.password = password    ;
    

    public String getPassword()
    
        return this.password;
    



    public String encrypt(String s)
    
        passwordPos = 0;
        String encrypted = "";
        for(int i = 0; i < s.length(); i++)
        
            char c = s.charAt(i);
            encrypted += encrypt1(c);

        


        return encrypted;
    

    protected char encrypt1(char c)
    
        //Alphabet temp = new Alphabet(s);
        int index = 0;
        char result = 0;

        index = alphabet.indexOf(c); //Found index of a character 

        if(index != -1)
        
            int keyIndex = alphabet.get(passwordPos++ % password.length());
            result = alphabet.get((keyIndex + index) % alphabet.length());
        
        else 
            throw new NotInAlphabetException(c, alphabet);
        return result;
    

    public String decrypt(String c)
    
        return c;
    

    protected char decrypt1(char c)
    
        return c;
    

    public String toString()
    
        return "Vigenere Cipher (password =\'"+this.password+"\')"; 
    






public abstract class SymmetricCipher extends Cipher 

    protected Alphabet alphabet;

    public SymmetricCipher (Alphabet alphabet)
    
        this.alphabet = alphabet;
    

    public int wrapInt(int i)
    
        int index = 0;
        if (i >= alphabet.length())
            index = Math.abs(i) % alphabet.length();
        else if (i < 0)
        
            int temp = Math.abs(i) % alphabet.length();
            index = alphabet.length() - temp;
        
        else 
            index = i;

        return index;
    

    public int rotate(int index, int shift)
    
        int result = 0;

        if (shift > 0)
        
            result = (index + shift) % alphabet.length();
        

        else if (shift < 0)
        
            if(index < Math.abs(shift))
            
                int temp = Math.abs(index + shift);
                result = alphabet.length() - temp;

            
            else 
                result = index + shift ;
           


        return result;
    

    public Alphabet getAlphabet()
    
        return this.alphabet;
    

    public String encrypt(String s) 
    
        String string = "";
        char c = 0;
        char encrypted = 0;
        for (int i = 0; i < s.length(); i++)
        
            c = s.charAt(i);
            encrypted = encrypt1(c);
            string += encrypted;
        
        return string;


    

    public String decrypt(String s) throws NotInAlphabetException
    
        String string = "";
        char c = 0;
        char encrypted = 0;
        for (int i = 0; i < s.length(); i++)
        
            c = s.charAt(i);
            encrypted = encrypt1(c);
            string += encrypted;
        
        return string;


    

    protected abstract char encrypt1(char c);

    protected abstract char decrypt1(char c);






public class Alphabet 


    private String symbols;
    public static final Alphabet DEFAULT = new Alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz 1234567890!@#$%^&*()_+-=[]\\|;:'\",./?<>");

    public Alphabet(String symbols)
    
        this.symbols = symbols;
    

    public int indexOf(char c) 
    
        Alphabet temp = new Alphabet(symbols);
        for(int i = 0; i < symbols.length(); i++)
        
            if(c == symbols.charAt(i))
                return symbols.indexOf(c) ;
        

        throw new NotInAlphabetException (c, temp); 
    

    public char get(int i) 
    
        Alphabet temp = new Alphabet(symbols);
        char c = 0;
        if (i > this.symbols.length())
            throw new NotInAlphabetException (c, temp);
        else 
            return symbols.charAt(i);
    

    public int length()
    
        return symbols.length();
    

    public String getSymbols()
    
        return symbols;
    

    public String toString()
    
        return "Alphabet("+this.symbols+")";
    

    public boolean equals(Object other)
    
        if(other instanceof Alphabet)
        
            Alphabet temp = (Alphabet) other;
            return this.symbols.equals(temp.symbols);

        
        else 
            return false;
    

【问题讨论】:

赞成不厌其烦地正确拼写。谢谢。 【参考方案1】:

问题出在encrypt1函数的这一行:

int keyIndex = alphabet.get(passwordPos++ % password.length());

在这里,您尝试在密码 中查找密钥,但实际上是从字母 中获取的。 您想要的是从密码中找到相关字符(当您到达末尾时循环),然后找出该字符在您的字母表中的索引,这样您就可以获取平面文本并将其移动( index) 字母表中的许多字符。

为此,您可以使用以下代码:

char pwChar = password.charAt(passwordPos++ % password.length());
int keyIndex = alphabet.indexOf(pwChar);

当我更改这一行并以SLIME 作为键并以GREEN 作为明文运行代码时,我得到了YcMQR 的结果,这就是你所说的你所期望的。

【讨论】:

嘿@ErwinBolwidt,我已经添加了其余的课程。你假设 int keyIndex = alphabet.get(passwordPos++ % password.length());是错误输出的来源吗? 是的,我假设这就是原因。现在尝试运行您的代码。 是的 - 当我用我第二次提到的两行替换我提到的第一行时,输出是 YcMQR,这是你所说的你所期望的。 谢谢@ErwinBolwidt,我还没有尝试过代码,但是当我逐行阅读时,它似乎在逻辑上是正确的 很抱歉打扰你,对于同一个类的decrypt1()我这样做了:char passwordChar = password.charAt(passwordPos++ % password.length()); int keyIndex = alphabet.indexOf(passwordChar);结果 = 字母表.get((index - keyIndex) % 字母表.length());但是如何环绕字符呢?

以上是关于密码那些事儿|(十)“钥匙”打开维吉尼亚的锁的主要内容,如果未能解决你的问题,请参考以下文章

维吉尼亚密码 C++

维吉尼亚密码:加密强悍,却为何没人用?

维吉尼亚密码(Vigenère cipher)

《密码学》维吉尼亚密码。

凯撒简单替换维吉尼亚密码 CRC 破解

维吉尼亚加密