密码学浅尝辄止

Posted 程序dunk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了密码学浅尝辄止相关的知识,希望对你有一定的参考价值。

个人博客欢迎访问

总结不易,如果对你有帮助,请点赞关注支持一下
微信搜索程序dunk,关注公众号,获取博客源码、数据结构与算法笔记、面试笔试题

本文加密算法源码

文章目录

密码学浅尝辄止

密码学主要研究编制密码 和 破译密码的学科

密码学的主要目的:研究如何隐藏信息并且把信息传递出去的一个学科

密码学的基本概念

密码在我们的生活中有着重要的作用,那么密码究竟来自何方,为何会产生呢?

密码学是网络安全、信息安全、区块链等产品的基础,常见的非对称加密、对称加密、散列函数等,都属于密码学范畴。

密码学有数千年的历史,从最开始的替换法到如今的非对称加密算法,经历了古典密码学,近代密码学和现代密码学三个阶段。密码学不仅仅是数学家们的智慧,更是如今网络空间安全的重要基础。

古典密码学

在古代的战争中,多见使用隐藏信息的方式保护重要的通信资料。比如先把需要保护的信息用化学药水写到纸上,药水干后,纸上看不出任何的信息,需要使用另外的化学药水涂抹后才可以阅读纸上的信息。

这些方法都是在保护重要的信息不被他人获取,但藏信息的方式比较容易被他人识破,例如增加哨兵的排查力度,就会发现其中的猫腻,因而随后发展出了较难破解的古典密码学。

替换法

替换法很好理解,就是用固定的信息将原文替换成无法直接阅读的密文信息。例如将 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 。

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

恺撒加密

中国古代加密

公元683年,唐中宗即位。随后,武则天废唐中宗,立第四子李旦为皇帝,但朝政大事均由她自己专断。裴炎、徐敬业和骆宾王等人对此非常不满。徐敬业聚兵十万,在江苏扬州起兵。裴炎做内应,欲以拆字手段为其传递秘密信息。后因有人告密,裴炎被捕,未发出的密信落到武则天手中。这封密信上只有“青鹅”二字,群臣对此大惑不解。武则天破解了“青鹅”的秘密:“青”字拆开来就是“十二月”,而“鹅”字拆开来就是“我自与”。密信的意思是让徐敬业、骆宾王等率兵于十二月进发,裴炎在内部接应。“青鹅”破译后,裴炎被杀。接着,武则天派兵击败了徐敬业和骆宾王。

外国加密

在密码学中,恺撒密码是一种最简单且最广为人知的加密技术。

凯撒密码最早由古罗马军事统帅盖乌斯·尤利乌斯·凯撒在军队中用来传递加密信息,故称凯撒密码。这是一种位移加密方式,只对26个字母进行位移替换加密,规则简单,容易破解。下面是位移1次的对比:

将明文字母表向后移动1位,A变成了B,B变成了C……,Z变成了A。

同理,若将明文字母表向后移动3位:则A变成了D,B变成了E……,Z变成了C。

字母表最多可以移动25位。凯撒密码的明文字母表向后或向前移动都是可以的,通常表述为向后移动,如果要向前移动1位,则等同于向后移动25位,位移选择为25即可。

它是一种替换加密的技术,明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。

例如,当偏移量是3的时候,所有的字母A将被替换成D,B变成E,以此类推。

这个加密方法是以恺撒的名字命名的,当年恺撒曾用此方法与其将军们进行联系。恺撒密码通常被作为其他更复杂的加密方法中的一个步骤。简单来说就是当秘钥为n,其中一个待加密字符ch,加密之后的字符为ch+n,当ch+n超过’z’时,回到’a’计数。

/**
 * @author :zsy
 * @date :Created 2021/6/16 23:36
 * @description:凯撒加密
 */
public class KaiSerDemo 
    public static void main(String[] args) 
        String encryptData = encryptKaiser("Hello World", 3);
        System.out.println("加密后:" + encryptData);
        String decryptDate = decryptKaiSer(encryptData, 3);
        System.out.println("解密后:" + decryptDate);
    

    public static String encryptKaiser(String orignal, int key) 
        StringBuilder builder = new StringBuilder();
        char[] chars = orignal.toCharArray();
        for (char c : chars) 
            //转换为ascii码值
            int asciiCode = c;
            //偏移数据
            asciiCode += key;
            //转换回编码
            char newChar = (char)asciiCode;
            builder.append(newChar);
        
        return builder.toString();
    

    public static String decryptKaiSer(String encryptData, int key) 
        StringBuilder builder = new StringBuilder();
        char[] chars = encryptData.toCharArray();
        for (char c : chars) 
            //转换为ascii码值
            int asciiCode = c;
            //偏移数据
            asciiCode -= key;
            //转换回编码
            char newChar = (char)asciiCode;
            builder.append(newChar);
        
        return builder.toString();
    

频度分析法破解恺撒加密

密码棒

公元前5世纪的时候,斯巴达人利用一根木棒,缠绕上皮革或者羊皮纸,在上面横向写下信息,解下这条皮带。展开来看,这长串字母没有任何意义。信差可以将这条皮带当成腰带,系在腰上。

然后收件人将这条皮带缠绕在相同的木棒上,就能恢复信息了。

前404年,一位遍体鳞伤的信差来到斯巴达将领利桑德面前,这趟波斯之旅只有他和四位同伴幸存,利桑德接下腰带,缠绕到他的密码棒上,得知波斯的发那巴祖斯准备侵袭他,多亏密码棒利桑德才能够预先防范,击退敌军。

频率分析解密法

加密者选择将组成信息的字母替代成别的字母,比如说将a写成1,这样就不能被解密者直接拿到信息了。

这难不倒解密者,以英文字母为例,为了确定每个英文字母的出现频率,分析一篇或者数篇普通的英文文章,英文字母出现频率最高的是e,接下来是t,然后是a……,然后检查要破解的密文,也将每个字母出现的频率整理出来,假设密文中出现频率最高的字母是j,那么就可能是e的替身,如果密码文中出现频率次高的但是P,那么可能是t的替身,以此类推便就能解开加密信息的内容。这就是频率分析法。

  • 将明文字母的出现频率与密文字母的频率相比较的过程
  • 通过分析每个符号出现的频率而轻易地破译代换式密码
  • 在每种语言中,冗长的文章中的字母表现出一种可对之进行分辨的频率。
  • e是英语中最常用的字母,其出现频率为八分之一
创建工具类负责文件的写入和读取
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("UTF-8");
		

破解密码
/**
 * 频率分析法破解凯撒密码
 */
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.txt");
		
		//加密文件
		int key = 3;
		//encryptFile("article.txt", "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<Entry<Character, Integer>> mapList = getMaxCountChar(data);
		for (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 = KaiserDemo.encrypt(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 = KaiserDemo.decrypt(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<Entry<Character, Integer>> mapList = new ArrayList<Entry<Character,Integer>>(map.entrySet());
		//根据字符出现次数排序
		Collections.sort(mapList, new Comparator<Entry<Character, Integer>>()
			@Override
			public int compare(Entry<Character, Integer> o1,
					Entry<Character, Integer> o2) 
				return o2.getValue().compareTo(o1.getValue());
			
		);
		return mapList;
	


字符'#'出现989次
猜测key = -66, 解密生成第1个备选文件

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

字符'd'出现344次
猜测key = -1, 解密生成第3个备选文件

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

古典密码破解方式

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

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

频率分析

频率分析在数学、物理学和信号处理中是一种分解函数、波形、或者信号的频率组成,以获取频谱的方法。在密码学中,频率分析是指研究字母或者字母组合在文本中出现的频率。应用频率分析可以破解古典密码。

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

近代密码学

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

恩尼格玛机

恩尼格玛密码机,在密码学史中是一种用于加密与解密文件的密码机。确切地说,恩尼格玛是一系列相似的转子机械的统称,它包括了一系列不同的型号。恩尼格玛在1920年代早期开始被用于商业,也被一些国家的军队与政府采用过,在这些国家中,最著名的是第二次世界大战时的纳粹德国。恩尼格玛密码机的大部分设置都会在一段时间(一般为一天)以后被更换。

恩尼格玛机是二战时期纳粹德国使用的加密机器,后被英国破译,参与破译的人员有被称为计算机科学之父、人工智能之父的图灵。恩尼格玛机使用的加密方式本质上还是移位和替代,只不过因为密码表种类极多,破解难度高,同时加密解密机器化,使用便捷,因而在二战时期得以使用。

现代密码学

HTTPS的通信过程

https通信是建立在SSL连接层之上的请求和响应,客户端将加密组件发送到服务器端,服务端进行匹配后将数字证书等信息发送到客户端,客户端进行证书验证,验证通过后使用非对称加密对数据的密钥进行协商,协商后得到对称的加密密钥,然后使用对称算法进行TCP链接,然后与客户端进行三次握手后,进行数据传输,传输完成后,四次挥手,断开链接,通信结束。

  • 客户端和服务器端通过TCP建立来连接,发送https请求
  • 服务器响应请求,并将数字证书发送给客户端,数字证书包括公共密钥、域名、申请证书的公司
  • 客户端收到服务器端的数字证书之后,会验证数字证书的合法性。
  • 如果公钥合格,那么客户端会生成client key,一个用于进行对称加密的密钥,并用服务器的公钥对客户端密钥进行非对称加密。
  • 客户端会再次发起请求,将加密之后的客户端密钥发送给服务器
  • 服务器接受加密文后,会用私钥对其进行非对称解密,得到客户端秘钥,并使用客户端密钥进行对称加密,生成密钥文并发送
  • 客户端收到密文,并使用客户端密钥进行解密获得数据

常见的加密方式

对称加密

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

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

  • 采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密。
  • 示例
    • 我们现在有一个原文3要发送给B
    • 设置密钥为108, 3 * 108 = 324, 将324作为密文发送给B
    • B拿到密文324后, 使用324/108 = 3 得到原文
  • 常见加密算法
    • DES : Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
    • AES : Advanced Encryption Standard, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
  • 特点
    • 加密速度快, 可以加密大文件
    • 密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露
    • 加密后编码表找不到对应字符, 出现乱码
    • 一般结合Base64使用

DES加密解密

加密的秘钥必须为8个字节,原文是8字节的整数倍

/**
 * @author :zsy
 * @date :Created 2021/6/17 9:51
 * @description:Des加密
 */
public class DesDemo 
    public static void main(String[] args) throws Exception 
        String input = "计算机网络";
        //加密密钥
        String key = "12345678";
        //加密模式
        String transformation = "DES";
        //加密算法
        String algorithm = "DES";
        String encryptDES = encryptDES(input, key, transformation, algorithm);
        System.out.println("加密后:" + encryptDES);
        String decryptDES = decryptDES(encryptDES, key, transformation , algorithm);
        System.out.println("解密后:" + decryptDES);
    

    private static String encryptDES(String input, String key, String transformation, String algorithm) throws Exception 
        //获取加密对象
        Cipher cipher = Cipher.getInstance(transformation);
        //加密规则
        SecretKeySpec sks = new SecretKeySpec(key.getBytes(), algorithm);
        //初始化加密模式和算法
        cipher.init(Cipher.ENCRYPT_MODE, sks);
        //加密
        byte[] bytes = cipher.doFinal(input.getBytes());
        String encode = Base64.encode(bytes);
        return encode;
    

    private static String decryptDES(Stri

以上是关于密码学浅尝辄止的主要内容,如果未能解决你的问题,请参考以下文章

密码学浅尝辄止

简单的单字母替换密码

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

密码由字母、数字、下划线、组成密码长度为6_16位 、

密码6到16位数字和字母组合

密码由 6-16 位数字、字母或符号组成,至少包含 2 种字符。 保存