概述
恺撒密码作为一种最为古老的对称加密体制,在古罗马的时候都已经很流行,它的基本思想是:通过把字母移动一定的位数来实现加密和解密。例如,密匙是把明文字母的位数向后移动三位,那么明文字母B就变成了密文的E,依次类推,X将变成A,Y变成B,Z变成C,由此可见,位数就是恺撒密码加密和解密的密钥。这个加密方法是以恺撒的名字命名的,当年恺撒曾用此方法与其将军们进行联系。
恺撒密码通常被作为其他更复杂的加密方法中的一个步骤,例如维吉尼亚密码。恺撒密码还在现代的ROT13系统中被应用。但是和所有的利用字母表进行替换的加密技术一样,恺撒密码非常容易被破解,而且在实际应用中也无法保证通信安全。
这种密码替换通常叫做恺撒移位密码,或简单的说,恺撒密码。
举例说明
恺撒密码的替换方法是通过排列明文和密文字母表,密文字母表示通过将明文字母表向左或向右移动一个固定数目的位置。例如,当偏移量是左移3的时候(解密时的密钥就是3):
明文字母表 | A B C D E F G H I J K L M N O P Q R S T U V W X Y Z |
密文字母表 | D E F G H I J K LM N O P Q R S T U V W X Y Z A B C |
使用时,加密者查找明文字母表中需要加密的消息中的每一个字母所在位置,并且写下密文字母表中对应的字母。需要解密的人则根据事先已知的密钥反过来操作,得到原来的明文。例如:
明文 | THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG |
密文 | WKHTXLFNEURZQIRAMXPSVRYHUWKHODCBGRJ |
破译密码
即使使用唯密文攻击,恺撒密码也是一种非常容易破解的加密方式,有以下两种情况需要考虑:
一、攻击者知道密码中使用了某个简单的替换加密方式,但是不确定是恺撒密码。 这种情况下,攻击者可以通过使用诸如频率分析或者样式单词分析的方法,马上就能从分析结果中看出规律,得出加密者使用的是恺撒密码。
二、攻击者知道使用了恺撒密码,但是不知道其偏移量。由于使用恺撒密码进行加密的语言一般都是字母文字系统,因此密码中可能是使用的偏移量也是有限的,例如使用26个字母的英语,它的偏移量最多就是25(偏移量26等同于偏移量0,即明文;偏移量超过26,等同于偏移量1-25)。因此可以通过穷举法,很轻易地进行破解。其中一种方法是在表格中写下密文中的某个小片段使用所有可能的偏移量解密后的内容——称为候选明文,然后分析表格中的候选明文是否具有实际含义,得出正确的偏移量,解密整个密文。例如,被选择出的密文片段是"EXXEGOEXSRGI",从下表中的候选明文,我们可以很快看出其正确的偏移量是4。也可以通过在每一个密文单词的每一个字母下面,纵向写下整个字母表其他字母,然后可以通过分析,得出其中的某一行便是明文。
偏移 量 | 候选明文 |
0 | e x x e g o ex s r g i |
1 | d w w df n d wr q f h |
2 | c v v c e m c v q p e g |
3 | b u u b d l b u p o d f |
4 | a t t a c k a t o n c e |
5 | z s s z b j z s n mb d |
… | … |
24 | g z z g i q g z u t i k |
25 | f y y f h p f y t s h j |
Java实现代码如下
(1) 加密包含特殊字符
package cryptology; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Scanner; /** * 凯撒加密器 * @author优客志 */ public class Main { char ciphertext[]; // 密文 int key; char plaintext[]; // 明文 StringBuffer plaintextStr = new StringBuffer(""); StringBuffer ciphertextStr = new StringBuffer(""); final int max = 500; // 最大字符 public static void main(String[] args) { Main m = new Main(); m.setKey(); m.getPlaintext(); m.encryption(); m.deciphering(); m.display(); } /** * 设置密钥,返回偏移值 * @return */ int setKey() { System.out.println("***************** 欢迎使用凯撒加密器 *********************"); System.out.println("请输入一个Caesar数字密钥:"); while (true) { Scanner sc = new Scanner(System.in); try { key = sc.nextInt() % 26; // %26的意义是获取密钥的偏移值 return key; } catch (Exception e) { System.out.println("ERROR__请重新输入整数密钥..."); } } } /** * 获得明文 */ void getPlaintext() { plaintext = new char[max]; for (int j = 0; j < max; j++) { plaintext[j] = '★'; // 设置临时变量将数组填充,因明文中可存在' '空,所以需要填充判断 } int i = 0; char ch = ' '; BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); System.out.println("请输入明文"); try { ch = (char) bf.read(); // 获得字符 while (ch != '\r' && ch != '\n') { // 回车 plaintext[i] = ch; i++; try { ch = (char) bf.read(); } catch (Exception e) { e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); } } /** * 加密 */ void encryption() { ciphertext = new char[max]; for (int j = 0; j < max; j++) { ciphertext[j] = '★'; // 设置临时变量将数组填充,因明文中可存在' '空,所以需要填充判断 } for (int i = 0; i < plaintext.length; i++) { if (plaintext[i] != '★') { int temp = plaintext[i] + key; // 偏移后的ASCII码 ciphertext[i]=(char)temp; // 加密符号 ciphertextStr.append(ciphertext[i]); // 拼接字符串 } else { break; } } } /** * 解密 */ void deciphering() { char c = ' '; for (int i = 0; i < ciphertext.length; i++) { if (ciphertext[i] != '★') { int temp = ciphertext[i] - key; c = (char) temp; plaintextStr.append(c); // 拼接解密字符串 } else { break; } } } /** * 显示对比结果 */ void display() { System.out.println("密文明文对比"); System.out.println("密文:" + ciphertextStr); System.out.println("明文:" + plaintextStr); } }
(2)不包含特殊字符,只允许含有英文( 空格、逗号、小数点、感叹号)
package cryptology; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Scanner; /** * 凯撒加密器 * @author 优客志 */ public class Mainer { char ciphertext[]; // 密文 int key; char plaintext[]; // 明文 StringBuffer plaintextStr = new StringBuffer(""); StringBuffer ciphertextStr = new StringBuffer(""); final int max = 500; // 最大字符 public static void main(String[] args) { Mainer m = new Mainer(); m.setKey(); m.getPlaintext(); m.encryption(); m.deciphering(); m.display(); } /** * 设置密钥,返回偏移值 * * @return */ int setKey() { System.out.println("***************** 欢迎使用凯撒加密器 *********************"); System.out.println("请输入一个Caesar数字密钥:"); while (true) { Scanner sc = new Scanner(System.in); try { key = sc.nextInt() % 26; // %26的意义是获取密钥的偏移值 return key; } catch (Exception e) { System.out.println("ERROR__请重新输入整数密钥..."); } } } /** * 获得明文 */ void getPlaintext() { plaintext = new char[max]; for (int j = 0; j < max; j++) { plaintext[j] = '★'; // 设置临时变量将数组填充,因明文中可存在' '空,所以需要填充判断 } int i = 0; char ch = ' '; BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); System.out.println("请输入明文"); try { ch=(char) bf.read();//获得字符 while(ch!='\r'&&ch!='\n'){ if(ch>='a'&&ch<='z'||ch>='A'&&ch<='Z'||ch==' '||ch==','||ch=='.'||ch=='!'){ plaintext[i]=ch; i++; } else{ System.out.println("输入字符不支持!!"); break; } try{ ch=(char) bf.read(); } catch(Exception e1){ } } } catch (Exception e) { e.printStackTrace(); } } /** * 加密 */ void encryption() { ciphertext = new char[max]; for (int j = 0; j < max; j++) { ciphertext[j] = '★'; // 设置临时变量将数组填充,因明文中可存在' '空,所以需要填充判断 } int temp = 0; for (int i = 0; i < plaintext.length; i++) { if (plaintext[i] != '★') { temp = plaintext[i] + key; if (plaintext[i] == ' ' || plaintext[i] == ',' || plaintext[i] == '.' || plaintext[i] == '!') { ciphertext[i] = (char) (plaintext[i]); } // ASCII码 A=65,Z=90; a=97 z=122; if (plaintext[i] >= 'a' && plaintext[i] <= 'z') { if (temp > 122) ciphertext[i] = (char) (97 + temp - 123); else { ciphertext[i] = (char) temp; } } if (plaintext[i] >= 'A' && plaintext[i] <= 'Z') { if ((plaintext[i] + key) > 90) ciphertext[i] = (char) (65 + temp - 91); else { ciphertext[i] = (char) temp; } } ciphertextStr.append(ciphertext[i]); } else { break; } } } void deciphering() {// 解密 char c = ' '; int temp = 0; for (int i = 0; i < ciphertext.length; i++) { temp = ciphertext[i] - key; if (ciphertext[i] != '★') { if (plaintext[i] == ' ' || plaintext[i] == ',' || plaintext[i] == '.' || plaintext[i] == '!') { c = (char) (ciphertext[i]); } if (ciphertext[i] >= 97 && ciphertext[i] <= 122) { c = (char) (temp); if (temp < 97) { c = (char) (26 + temp); } } if (ciphertext[i] >= 65 && ciphertext[i] <= 90) { c = (char) (temp); if (temp < 65) { c = (char) (26 + temp); } } plaintextStr.append(c); } else { break; } } } /** * 显示对比结果 */ void display() { System.out.println("密文明文对比"); System.out.println("密文:" + ciphertextStr); System.out.println("明文:" + plaintextStr); } }
(3)凯撒算法还可以利用公式来计算 如下
// k(偏移值),b[i](明文字符) %26等同于穷举法 char ch = (char) ((b[i] - 'a' + k) % 26 + 'a'); // 加密 char ch = (char) ((b[i] - 'a' + 26 - k) % 26 + 'a'); // 解密
package cryptology; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Scanner; /** * 凯撒算法——公式篇 (只可以输入字母),相信大家一眼就能看懂 * @author 优客志 */ public class Kaisa { StringBuffer eStr = new StringBuffer(""); // 加密字符串 StringBuffer dStr = new StringBuffer(""); // 解密字符串 public static void main(String[] args) { System.out.print("请输入密钥:"); Scanner s = new Scanner(System.in); int key = s.nextInt() % 26; // %26的意义是获取密钥的偏移值 Kaisa ks = new Kaisa(); ks.E(key); // 加密 ks.D(key); // 解密 } /** * 加密 公式 */ void E(int k) { try { System.out.println("请输入一段明文:"); char b[]; BufferedReader br2 = new BufferedReader(new InputStreamReader(System.in)); String str2 = br2.readLine(); b = str2.toCharArray(); char ch = ' '; for (int i = 0; i < str2.length(); i++) { if (b[i] >= 'a' && b[i] <= 'z') { ch = (char) ((b[i] - 'a' + k) % 26 + 'a'); } if(b[i] >= 'A' && b[i] <= 'Z'){ ch = (char) ((b[i] - 'A' + k) % 26 + 'A'); } eStr.append(ch); // 拼接字符串 } System.out.println("密文为:"+eStr+" 密匙为:" + k); } catch (IOException e) { System.out.println(e.getMessage()); } } /** * 解密 公式 */ void D(int k) { try { char b[]; b = eStr.toString().toCharArray(); char ch =' '; for (int i = 0; i < eStr.length(); i++) { if (b[i] >= 'a' && b[i] <= 'z') { ch = (char) ((b[i] - 'a' + 26 - k) % 26 + 'a'); } if(b[i] >= 'A' && b[i] <= 'Z'){ ch = (char) ((b[i] - 'A' + 26 - k) % 26 + 'A'); } dStr.append(ch); // 拼接字符串 } System.out.println("明文:"+dStr); } catch (Exception e) { System.out.println(e.getMessage()); } } }