前言
古典密码学的核心原理为替换法和移位法。
其中替换法是将明文字母按照表单进行替换得到密文,解密时用密文根据表单进行替换获取明文。
------例如:表单 abcde----->swtrp ,明文bee,将b替换成w,e替换成p 明文就变成了密文wpp
移位法就是字母按照在字母表上的位置进行移位,最著名的就是凯撒加密
------例如 abcde 往后移动两位 cdefg
凯撒加密的破解方法为频率分析法。每种语言文字中字母的出现频率是有比例的,其中英文中出现频率最大的字母为‘e’, 频率分析法就是将密文中字母出现的频率进行统计和平时语法中字母出现频率做比较,计算字母位移差,根据唯位移差进行破解。
代码实现
凯撒加解密工具类
package com.lh.basecryptology.kaiser;
/**
* 位移法实现凯撒加密
*/
public class KaiserEnDecryptUtil {
public static void main(String[] args) {
// 定义原文
String text = "hello world";
String s = encryptKaiser(text, 3);
System.out.println("密文------" + s);
String decrypt = encryptKaiser(s, 3);
System.out.println("明文------" + decrypt);
}
/**
* 加密
*
* @param text 明文
* @param key 偏移量
* @return 密文
*/
public static String encryptKaiser(String text, int key) {
// 第一key 移动位数
StringBuilder sb = new StringBuilder();
for (char c : text.toCharArray()) {
int b = c;
b = b + key;
c = (char) b;
sb.append(c);
}
System.out.println(sb);
return sb.toString();
}
/**
* 解密
*
* @param text 密文
* @param key 偏移数
* @return 明文
*/
public static String decryptKaiser(String text, int key) {
StringBuilder sb = new StringBuilder();
for (char c : text.toCharArray()) {
int b = c;
b = b - key;
c = (char) b;
sb.append(c);
}
System.out.println(sb);
return sb.toString();
}
}
频率分析法破解凯撒加密的内容
package com.lh.basecryptology.kaiser;
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("input.txt");
//加密文件
int key = 3;
encryptFile("input.txt", "input_en.txt", key);
//读取加密后的文件
String artile = Util.file2String("input_en.txt");
//解密(会生成多个备选文件)
decryptCaesarCode(artile, "input.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 = KaiserEnDecryptUtil.encryptKaiser(artile, key);
//保存加密后的文件
Util.string2File(encryptData, destFile);
}
/**
* 破解凯撒密码
*
* @param input 数据源
* @return 返回解密后的数据
*/
private 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 = KaiserEnDecryptUtil.decryptKaiser(input, key);
String fileName = "de_" + deCount + destPath;
Util.string2File(decrypt, fileName);
}
}
/**
* 统计String里字符出现次数
*
* @param data 内容
* @return 统计结果
*/
private static List<Entry<Character, Integer>> getMaxCountChar(String data) {
Map<Character, Integer> map = new HashMap<>();
for (char c : data.toCharArray()) {
if (!map.containsKey(c)) {
map.put(c, 1);
} else {
Integer count = map.get(c);
map.put(c, count + 1);
}
}
// map转换成list便于排序
List<Entry<Character, Integer>> mapList = new ArrayList<>(map.entrySet());
// 根据字符出现次数排序
mapList.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));
return mapList;
}
}
文件处理工具类
package com.lh.basecryptology.kaiser;
import java.io.*;
public class Util {
public static void print(byte[] bytes){
StringBuilder sb = new StringBuilder();
for (byte aByte : bytes) {
sb.append(aByte).append(" ");
}
System.out.println(sb);
}
/**
* 文件转字符串
* @param path 路径
* @return 内容
* @throws IOException 异常
*/
public static String file2String(String path) throws IOException {
FileReader reader = new FileReader(new File(path));
char[] buffer = new char[1024];
int len = -1;
StringBuilder sb = new StringBuilder();
while ((len = reader.read(buffer)) != -1){
sb.append(buffer,0,len);
}
return sb.toString();
}
/**
* 内容转文件
* @param data 内容
* @param path 罗静
*/
public static void string2File(String data, String path) {
try (FileWriter writer = new FileWriter(new File(path))) {
writer.write(data);
}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");
}
}