2. 准备知识
//字符转换成ASCII 码数值 char charA = "a"; int intA = charA; //char 强转为int 即得到对应的ASCII 码值,"a"的值为97//ASCII 码值转成charint intA = 97;//97 对应的ASCII 码"a"char charA = (char) intA; //int 值强转为char 即得到对应的ASCII 字符,即"a"
3. 凯撒密码的简单代码实现
/** * 加密 * @param input 数据源(需要加密的数据) * @param key 秘钥,即偏移量 * @return 返回加密后的数据 */public static String encrypt(String input, int key) {//得到字符串里的每一个字符char[] array = input.toCharArray();for (int i = 0; i < array.length; ++i) {//字符转换成ASCII 码值int ascii = array[i];//字符偏移,例如a->bascii = ascii + key;//ASCII 码值转换为charchar newChar = (char) ascii;//替换原有字符array[i] = newChar;//以上4 行代码可以简写为一行//array[i] = (char) (array[i] + key);}//字符数组转换成Stringreturn new String(array);}/** * 解密 * @param input 数据源(被加密后的数据) * @param key 秘钥,即偏移量 * @return 返回解密后的数据 */public static String decrypt(String input, int key) {//得到字符串里的每一个字符char[] array = input.toCharArray();for (int i = 0; i < array.length; ++i) {//字符转换成ASCII 码值int ascii = array[i];//恢复字符偏移,例如b->aascii = ascii - key;//ASCII 码值转换为charchar newChar = (char) ascii;//替换原有字符array[i] = newChar;//以上4 行代码可以简写为一行//array[i] = (char) (array[i] - key);}//字符数组转换成Stringreturn new String(array);}代码输出结果:
5. 破解流程
统计密文里出现次数最多的字符,例如出现次数最多的字符是是"h"。
计算字符"h"到"e"的偏移量,值为3,则表示原文偏移了3 个位置。
将密文所有字符恢复偏移3 个位置。
注意点:统计密文里出现次数最多的字符时,需多统计几个备选,因为最多的可能是空格或者其他字符,例如下图出现次数最多的字符"#"是空格加密后的字符,"h"才是"e"偏移后的值。
解密时要多几次尝试,因为不一定出现次数最多的字符就是我们想要的目标字符,如下图,第二次解密的结果才是正确的。
/** * 频率分析法破解凯撒密码 */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("article1_en.txt");//加密文件//int key = 3;//encryptFile("article1.txt", "article1_en.txt", key);//读取加密后的文件String artile = file2String("article1_en.txt");//解密(会生成多个备选文件)decryptCaesarCode(artile, "article1_de.txt");}public static void printCharCount(String path) throws IOException{String data = 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 = file2String(srcFile);//加密文件String encryptData = MyEncrypt.encrypt(artile, key);//保存加密后的文件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 + "个备选文件" + " ");String decrypt = MyEncrypt.decrypt(input, key);String fileName = "de_" + deCount + destPath;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<Map.Entry<Character,Integer>>(map.entrySet());//根据字符出现次数排序Collections.sort(mapList, new Comparator<Entry<Character, Integer>>(){@Overridepublic int compare(Entry<Character, Integer> o1,Entry<Character, Integer> o2) {return o2.getValue().compareTo(o1.getValue());}});return mapList;}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();}}}}}
int i = 97; String bit = Integer.toBinaryString(i); //输出:97 对应的二进制数据为: 1100001 System.out.println(i + "对应的二进制数据为: " + bit);Byte 与Bit 区别
//byte 的取值范围:-128 到127System.out.println(Byte.MIN_VALUE + "到" + Byte.MAX_VALUE);即10000000 到01111111 之间,一个字节占8 个比特位
任何字符串都可以转换为字节数组
String data = "1234abcd";byte[] bytes = data.getBytes();//内容为:49 50 51 52 97 98 99 100上面数据49 50 51 52 97 98 99 100 对应的二进制数据(即比特位为):