密码学发展史

古典密码学

古代就已经开始使用密码,目的:就是希望保护信息。

替换法

替换法很好理解,就是用固定的信息将原文替换成无法直接阅读的密文信息。例如将 b 替换成 w ,e 替换成p ,这样bee单词就变换成了wpp,不知道替换规则的人就无法阅读出原文的含义。

替换法有单表替换和多表替换两种形式。单表替换即只有一张原文密文对照表单,发送者和接收者用这张表单来加密解密。在上述例子中,表单即为:a b c d e - s w t r p

多表替换即有多张原文密文对照表单,不同字母可以用不同表单的内容替换。

例如约定好表单为:表单 1:abcde-swtrp 、表单2:abcde-chfhk 、表单 3:abcde-jftou

规定第一个字母用第三张表单,第二个字母用第一张表单,第三个字母用第二张表单,这时bee单词就变成了(312)fpk ,破解难度更高,其中 312 又叫做密钥,密钥可以事先约定好,也可以在传输过程中标记出来。

移位法

移位法就是将原文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后得出密文,典型的移位法应用有 “ 恺撒密码 ”。

例如约定好向后移动2位(abcde - cdefg),这样 bee 单词就变换成了dgg

同理替换法,移位法也可以采用多表移位的方式,典型的多表案例是“维尼吉亚密码”(又译维热纳尔密码),属于多表密码的一种形式。

基于ASCII码的凯撒密码Java实现

public class CaesarCode {
    public static void main(String[] args) {
        String orignal = "Hello world";
        // 往右边偏移三位
        int key = 3;
        // 选中我即将抽取的代码,按快捷键Ctrl + Alt + M 
        String encryptCaesar =  encryptCaesar(orignal,key);
        System.out.println("加密:" + encryptCaesar);
        String decryptCaesar =  decryptCaesar(encryptCaesar,key);
        System.out.println("解密:" + decryptCaesar);
    }
    /**
     * 使用凯撒加密方式解密数据
     *
     * @param encryptedData :密文
     * @param key           :密钥
     * @return : 源数据
     */
    public static String decryptCaesar(String encryptedData, int key) {
        // 将字符串转为字符数组
        char[] chars = encryptedData.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char aChar : chars) {
            // 获取字符的ASCII编码
            int asciiCode = aChar;
            // 偏移数据
            asciiCode -= key;
            // 将偏移后的数据转为字符
            char result = (char) asciiCode;
            // 拼接数据
            sb.append(result);
        }
        return sb.toString();
    }
    /**
     * 使用凯撒加密方式加密数据
     *
     * @param orignal :原文
     * @param key     :密钥
     * @return :加密后的数据
     */
    public static String encryptCaesar(String orignal, int key) {
        // 将字符串转为字符数组
        char[] chars = orignal.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char aChar : chars) {
            // 获取字符的ascii编码
            int asciiCode = aChar;
            // 偏移数据
            asciiCode += key;
            // 将偏移后的数据转为字符
            char result = (char) asciiCode;
            // 拼接数据
            sb.append(result);
        }
        return sb.toString();
    }
}

附录拉丁字母共23个字母

https://www.aulafacil.com/cursos/latin/i/el-alfabeto-latino-l24462

El alfabeto latino lo forman las siguientes letras:

A B C D E F G H I J K L M N O P Q R S T V X Y Z

A b c d e f g h i j k l m n o p q r s t u x y z

El alfabeto latino tiene 23 letras.

Tiene una única letra para la U y la V.

Hoy se diferencia la U de la V en la mayoría de los textos latinos, pero pueden encontrarse ediciones dónde no se diferencian estas dos letras (en este curso sí las diferenciaremos).

Las letras KY y Z se utilizaban poco.

古典密码破解方式及Java实现

古典密码虽然很简单,但是在密码史上是使用的最久的加密方式,直到“概率论”的数学方法被发现,古典密码就被破解了。

英文单词中字母出现的频率是不同的,e以12.702%的百分比占比最高,z 只占到0.074%,感兴趣的可以去百科查字母频率详细统计数据。如果密文数量足够大,仅仅采用频度分析法就可以破解单表的替换法或移位法。

多表的替换法或移位法虽然难度高一些,但如果数据量足够大的话,也是可以破解的。以维尼吉亚密码算法为例,破解方法就是先找出密文中完全相同的字母串,猜测密钥长度,得到密钥长度后再把同组的密文放在一起,使用频率分析法破解。

提示:直接运行下面的代码不一定会成功,这取决于文件路径设置是否正确。

FrequencyAnalysis.java

package code;

import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;

/**
 * 频率分析法破解凯撒密码
 */
public class FrequencyAnalysis {
    //英文里出现次数最多的字符
    private static final char MAGIC_CHAR = 'e';
    //破解生成的最大文件数
    private static final int DE_MAX_FILE = 4;

    public static void main(String[] args) throws Exception {
        //测试1,统计字符个数
        printCharCount("article_en.txt");

        //加密文件
//		int key = 3;
//		encryptFile("src/code/article.txt", "src/code/article_en.txt", key);

        //读取加密后的文件
        String artile = Util.file2String("article_en.txt");
        //解密(会生成多个备选文件)
        decryptCaesarCode(artile, "article_de.txt");
    }

    public static void printCharCount(String path) throws IOException {
        String data = Util.file2String(path);
        List<Map.Entry<Character, Integer>> mapList = getMaxCountChar(data);
        for (Map.Entry<Character, Integer> entry : mapList) {
            //输出前几位的统计信息
            System.out.println("字符'" + entry.getKey() + "'出现" + entry.getValue() + "次");
        }
    }

    public static void encryptFile(String srcFile, String destFile, int key) throws IOException {
        String artile = Util.file2String(srcFile);
        //加密文件
        String encryptData = CaesarCode.encryptCaesar(artile, key);
        //保存加密后的文件
        Util.string2File(encryptData, destFile);
    }

    /**
     * 破解凯撒密码
     * @param input 数据源
     * @return 返回解密后的数据
     */
    public static void decryptCaesarCode(String input, String destPath) {
        int deCount = 0;//当前解密生成的备选文件数
        //获取出现频率最高的字符信息(出现次数越多越靠前)
        List<Entry<Character, Integer>> mapList = getMaxCountChar(input);
        for (Entry<Character, Integer> entry : mapList) {
            //限制解密文件备选数
            if (deCount >= DE_MAX_FILE) {
                break;
            }

            //输出前几位的统计信息
            System.out.println("字符'" + entry.getKey() + "'出现" + entry.getValue() + "次");

            ++deCount;
            //出现次数最高的字符跟MAGIC_CHAR的偏移量即为秘钥
            int key = entry.getKey() - MAGIC_CHAR;
            System.out.println("猜测key = " + key + ", 解密生成第" + deCount + "个备选文件" + "\n");
            String decrypt = CaesarCode.decryptCaesar(input, key);

            String fileName = "de_" + deCount + destPath;
            Util.string2File(decrypt, fileName);
        }
    }

    //统计String里出现最多的字符
    public static List<Entry<Character, Integer>> getMaxCountChar(String data) {
        Map<Character, Integer> map = new HashMap<Character, Integer>();
        char[] array = data.toCharArray();
        for (char c : array) {
            if(!map.containsKey(c)) {
                map.put(c, 1);
            }else{
                Integer count = map.get(c);
                map.put(c, count + 1);
            }
        }

        //输出统计信息
		/*for (Entry<Character, Integer> entry : map.entrySet()) {
			System.out.println(entry.getKey() + "出现" + entry.getValue() +  "次");
		}*/

        //获取获取最大值
        int maxCount = 0;
        for (Entry<Character, Integer> entry : map.entrySet()) {
            //不统计空格
            if (/*entry.getKey() != ' ' && */entry.getValue() > maxCount) {
                maxCount = entry.getValue();
            }
        }

        //map转换成list便于排序
        List<Map.Entry<Character, Integer>> mapList = new ArrayList<Map.Entry<Character,Integer>>(map.entrySet());
        //根据字符出现次数排序
        Collections.sort(mapList, new Comparator<Map.Entry<Character, Integer>>(){

            public int compare(Map.Entry<Character, Integer> o1,
                               Entry<Character, Integer> o2) {
                return o2.getValue().compareTo(o1.getValue());
            }
        });
        return mapList;
    }
}

CaesarCode.java

package code;

public class CaesarCode {
    public static void main(String[] args) {
        String orignal = "Hello world";
        // 往右边偏移三位
        int key = 3;
        // 选中我即将抽取的代码,按快捷键Ctrl + Alt + M
        String encryptCaesar =  encryptCaesar(orignal,key);
        System.out.println("加密:" + encryptCaesar);
        String decryptCaesar =  decryptCaesar(encryptCaesar,key);
        System.out.println("解密:" + decryptCaesar);
    }
    /**
     * 使用凯撒加密方式解密数据
     *
     * @param encryptedData :密文
     * @param key           :密钥
     * @return : 源数据
     */
    public static String decryptCaesar(String encryptedData, int key) {
        // 将字符串转为字符数组
        char[] chars = encryptedData.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char aChar : chars) {
            // 获取字符的ASCII编码
            int asciiCode = aChar;
            // 偏移数据
            asciiCode -= key;
            // 将偏移后的数据转为字符
            char result = (char) asciiCode;
            // 拼接数据
            sb.append(result);
        }
        return sb.toString();
    }
    /**
     * 使用凯撒加密方式加密数据
     *
     * @param orignal :原文
     * @param key     :密钥
     * @return :加密后的数据
     */
    public static String encryptCaesar(String orignal, int key) {
        // 将字符串转为字符数组
        char[] chars = orignal.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char aChar : chars) {
            // 获取字符的ascii编码
            int asciiCode = aChar;
            // 偏移数据
            asciiCode += key;
            // 将偏移后的数据转为字符
            char result = (char) asciiCode;
            // 拼接数据
            sb.append(result);
        }
        return sb.toString();
    }
}

Util.java

package code;

import java.io.*;
import java.nio.charset.StandardCharsets;

public class Util {

    public static void print(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            sb.append(bytes[i]).append(" ");
        }
        System.out.println(sb);
    }

    public static String file2String(String path) throws IOException {
        FileReader reader = new FileReader(new File(path));
        char[] buffer = new char[1024];
        int len = -1;
        StringBuffer sb = new StringBuffer();
        while ((len = reader.read(buffer)) != -1) {
            sb.append(buffer, 0, len);
        }
        return sb.toString();
    }

    public static void string2File(String data, String path){
        FileWriter writer = null;
        try {
            writer = new FileWriter(new File(path));
            writer.write(data);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static String inputStream2String(InputStream in) throws IOException {
        int len = -1;
        byte[] buffer = new byte[1024];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while((len = in.read(buffer)) != -1) {
            baos.write(buffer, 0, len);
        }
        baos.close();

        return baos.toString(StandardCharsets.UTF_8);
    }
}


article.txt

Chief Justice Roberts, President Carter, PresidentClinton, President Bush, President Obama, fellow Americans and people of the world, thank you.  
  
We, the citizens of America, are now joined in a great national effort to rebuild our country and restore its promise for all of our people.  
  
Together, we will determine the course of America and the world for many, many years to come. We will face challenges. We will confront hardships. But we will get the job done.  
  
Every four years we gather on these steps to carry out the orderly and peaceful transfer of power.  
  
And we are grateful to President Obama and first ladyMichelle Obama for their gracious aid throughout this transition.  
  
They have been magnificent.  
  
Thank you.  
  
Today's ceremony, however, has a very special meaning because today we are not merely transferring power from one administration toanother or from one party to another, but we are transferring power fromWashington, D.C., and giving it back to you, the people.  
  
For too long, a small group in our nation's capital has reaped the rewards of government while the people have bore the cost.Washington flourished, but the people did not share in its wealth. Politiciansprospered but the jobs left and the factories closed.  
  
The establishment protected itself, but not the citizens of our country. Their victories have not been your victories. Their triumphs havenot been your triumphs. And while they celebrated in our nation's capital,there was little to celebrate for struggling families all across our land.  
  
That all changes starting right here and right now, because this moment is your moment.  
  
It belongs to you.  
  
It belongs to everyone gathered here today and everyone watching all across America.  
  
This is your day.  
  
This is your celebration.  
  
And this, the United States of America, is your country.  
  
What truly matters is not which party controls our government, but whether our government is controlled by the people.  
  
January 20th, 2017, will be remembered as the day the people became the rulers of this nation again.  
  
The forgotten men and women of our country will be forgotten no longer. Everyone is listening to you now. You came by the tens of millionsto become part of a historic movement, the likes of which the world has neverseen before.  
  
At the center of this movement is a crucial conviction that a nation exists to serve its citizens. Americans want great schools for theirchildren, safe neighborhoods for their families and good jobs for themselves.  
  
These are just and reasonable demands of righteous people and a righteous public.  
  
But for too many of our citizens, a different reality exists.  
  
Mothers and children trapped in poverty in our inner cities,rusted out factories scattered like tombstones across the landscape of ournation.  
  
An education system flush with cash but which leaves our young and beautiful students deprived of all knowledge.  
  
And the crime and the gangs and the drugs that have stolen too many lives and robbed our country of so much unrealized potential. ThisAmerican carnage stops right here and stops right now.  
  
We are one nation, and their pain is our pain.  
  
Their dreams are our dreams, and their success will be our success. We share one heart, one home and one glorious destiny.  
  
The oath of office I take today is an oath of allegiance to all Americans.  
  
For many decades we've enriched foreign industry at the expense of American industry, subsidized the armies of other countries whileallowing for the very sad depletion of our military.  
  
We've defended other nations' borders while refusing to defend our own. And we've spent trillions and trillions of dollars overseaswhile America's infrastructure has fallen into disrepair and decay.  
  
We've made other countries rich while the wealth, strength and confidence of our country has dissipated over the horizon.  
  
One by one, the factories shuttered and left our shores with not even a thought about the millions and millions of American workers thatwere left behind.  
  
The wealth of our middle class has been ripped from their homes and then redistributed all across the world. But that is the past, andnow we are looking only to the future.  
  
We assembled here today are issuing a new decree to be heard in every city, in every foreign capital and in every hall of power. From thisday forward, a new vision will govern our land.  
  
From this day forward, it's going to be only America first,America first. Every decision on trade, on taxes, on immigration, on foreignaffairs will be made to benefit American workers and American families. We mustprotect our borders from the ravages of other countries making our product,stealing our companies and destroying our jobs.  
  
Protection will lead to great prosperity and strength. I will fight for you with every breath in my body, and I will never ever let youdown.  
  
America will start winning again, winning like never before.  
  
We will bring back our jobs.  
  
We will bring back our borders.  
  
We will bring back our wealth, and we will bring back our dreams.  
  
We will build new roads and highways and bridges and airports and tunnels and railways all across our wonderful nation.  
  
We will get our people off of welfare and back to work,rebuilding our country with American hands and American labor.  
  
We will follow two simple rules: Buy American and hireAmerican.  
  
We will seek friendship and goodwill with the nations of the world, but we do so with the understanding that it is the right of all nationsto put their own interests first.  
  
We do not seek to impose our way of life on anyone, but rather to let it shine as an example.  
  
We will shine for everyone to follow.  
  
We will re-enforce old alliances and form new ones and unite the civilized world against radical Islamic terrorism, which we will eradicatecompletely from the face of the earth.  
  
At the bedrock of our politics will be a total allegiance to the United States of America, and through our loyalty to our country we willrediscover our loyalty to each other.  
  
When you open your heart to patriotism, there is no room for prejudice.  
  
The Bible tells us how good and pleasant it is when God's people live together in unity. We must speak our minds openly, debate ourdisagreements honestly, but always pursue solidarity. When America is united,America is totally unstoppable. There should be no fear. We are protected andwe will always be protected. We will be protected by the great men and women ofour military and law enforcement. And most importantly, we will be protected byGod.  
  
Finally, we must think big and dream even bigger. InAmerica, we understand that a nation is only living as long as it is striving.We will no longer accept politicians who are all talk and no action, constantlycomplaining but never doing anything about it.  
  
The time for empty talk is over. Now arrives the hour of action.  
  
Do not allow anyone to tell you that it cannot be done. No challenge can match the heart and fight and spirit of America. We will notfail. Our country will thrive and prosper again.  
  
We stand at the birth of a new millennium, ready to unlock the mysteries of space, to free the earth from the miseries of disease, and toharness the energies, industries and technologies of tomorrow.  
  
A new national pride will stir ourselves, lift our sights and heal our divisions. It's time to remember that old wisdom our soldiers willnever forget, that whether we are black or brown or white, we all bleed thesame red blood of patriots.  
  
We all enjoy the same glorious freedoms and we all salute the same great American flag.  
  
And whether a child is born in the urban sprawl of Detroit or the windswept plains of Nebraska, they look up at the same night sky, theyfill their heart with the same dreams and they are infused with the breath oflife by the same almighty creator.  
  
So to all Americans in every city near and far, small and large, from mountain to mountain, from ocean to ocean, hear these words: Youwill never be ignored again. Your voice, your hopes and your dreams will defineour American destiny. And your courage and goodness and love will forever guideus along the way.  
  
  
Together we will make America strong again, we will makeAmerica wealthy again, we will make America proud again, we will make America safe again.  
  
And, yes, together we will make America great again.  
  
Thank you.  
  
God bless you.  
  
And God bless America.

破译结果:

字符'#'出现1343次
字符'h'出现817次
字符'r'出现560次
字符'w'出现539次
字符'u'出现510次
字符'd'出现500次
...
字符'#'出现1343次
猜测key = -66, 解密生成第1个备选文件

字符'h'出现817次
猜测key = 3, 解密生成第2个备选文件

字符'r'出现560次
猜测key = 13, 解密生成第3个备选文件

字符'w'出现539次
猜测key = 18, 解密生成第4个备选文件

运行结果 # 出现次数最多, 我们知道在英文当中 e 出现的频率是最高的,我们假设现在 # 号,就是 e ,变形而来的 ,我们可以对照 ascii 编码表 ,我们的凯撒加密当中位移是加了一个 key ,所以我们 猜测 两个值直接相差 -66 ,我们现在就以 -66 进行解密 生成一个文件,我们查看第一个文件发现,根本读不懂,所以解密失败,我们在猜测 h 是 e ,h 和 e 之间相差3 ,所以我们在去看第二个解密文件,发现我们可以读懂,解密成功。

现代密码学

恩尼格玛机

核心原理:替换法,移位法

古典密码的安全性受到了威胁,外加使用便利性较低,到了工业化时代,近现代密码被广泛应用。

恩尼格玛机是二战时期纳粹德国使用的加密机器,后被英国破译,参与破译的人员有被称为计算机科学之父、人工智能之父的图灵。

恩尼格玛机使用的加密方式本质上还是移位和替代,只不过因为密码表种类极多,破解难度高,同时加密解密机器化,使用便捷,因而在二战时期得以使用。

ASCII 编码

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统,并等同于国际标准ISO/IEC 646。

字符串转换成ascii码 Java实现

public class AsciiDemo {
    public static void main(String[] args) {
//        char a = 'A';
//        int b = a;
//        System.out.println(b);
        String a = "AaZ";
        // 获取ascii码,需要把字符串转成字符  65 97 90
        char[] chars = a.toCharArray();
        for (char c : chars) {
            int asciiCode = c;
            System.out.print(asciiCode);
        }
    }
}

散列函数

散列函数,也见杂凑函数、摘要函数或哈希函数,可将任意长度的消息经过运算,变成固定长度数值,常见的有MD5、SHA-1、SHA256、SHA512,多应用在文件校验,数字签名中。

MD5 可以将任意长度的原文生成一个128位(16字节)的哈希值

SHA-1可以将任意长度的原文生成一个160位(20字节)的哈希值

对称密码

常见的对称密码加密方式是DES、AES。

对称密码应用了相同的加密密钥和解密密钥。对称密码分为:序列密码(流密码),分组密码(块密码)两种。流密码是对信息流中的每一个元素(一个字母或一个比特)作为基本的处理单元进行加密,块密码是先对信息流分块,再对每一块分别加密。

例如原文为1234567890,流加密即先对1进行加密,再对2进行加密,再对3进行加密……最后拼接成密文;块加密先分成不同的块,如1234成块,5678成块,90XX(XX为补位数字)成块,再分别对不同块进行加密,最后拼接成密文。前文提到的古典密码学加密方法,都属于流加密。

非对称密码

对称密码的密钥安全极其重要,加密者和解密者需要提前协商密钥,并各自确保密钥的安全性,一但密钥泄露,即使算法是安全的也无法保障原文信息的私密性。

在实际的使用中,远程的提前协商密钥不容易实现,即使协商好,在远程传输过程中也容易被他人获取,因此非对称密钥此时就凸显出了优势。

非对称密码有两支密钥,公钥(public key)和私钥(private key),加密和解密运算使用的密钥不同。用公钥对原文进行加密后,需要由私钥进行解密;用私钥对原文进行加密后(此时一般称为签名),需要由公钥进行解密(此时一般称为验签)。公钥可以公开的,大家使用公钥对信息进行加密,再发送给私钥的持有者,私钥持有者使用私钥对信息进行解密,获得信息原文。因为私钥只有单一人持有,因此不用担心被他人解密获取信息原文。

如何设置密码才比较安全

  • 密码不要太常见,不要使用类似于123456式的常用密码。
  • 各应用软件密码建议不同,避免出现一个应用数据库被脱库,全部应用密码崩塌,
  • 可在设置密码时增加注册时间、注册地点、应用特性等方法。例如tianjin123456,表示在天津注册的该应用。