传送门
数据安全系列6:从SM4国密算法谈到Bouncy Castle
历史上的密码
前面的几个章节,始终围绕的都是信息安全面临的几个主要问题和与之对应的解决方案:
在这个过程中也接触到了许多密码算法:
上面所列的脑图中并没有详尽所有的算法,但对于日常应用程序开发来说其实已经足够了。也没有涉及到信息安全的方方面面,比如对公钥认证的证书没有探讨、Diffie-Hellman (DH)密钥协商协议、以及前瞻性的量子密码等。
之所以称量子加密为未来密码的原因,因为把上面所探讨的密码都称现代密码学的算法。而在很久以前历史上也有许多著名的密码算法,称为古典密码!
这一节开始就会陆续了解一下在历史密码学(不知道学术上是否有这个定义)上的一些经典加密算法,首先从凯撒密码开始吧!
凯撒密码
没错,这里的凯撒就是指你想象中的那个男人:
盖乌斯·尤利乌斯·恺撒(Gaius Julius Caesar,公元前100年7月12日/13日---公元前44年3月15日),罗马共和国末期军事统帅 、政治家、军事家、历史学家 ,罗马帝国的奠基者。
而凯撒密码这个加密方法就是以罗马共和时期恺撒的名字命名的,相传当年恺撒曾用此方法与其将军们进行联系。
什么是凯撒密码
先看下百度百科对它的定义:
在密码学中,恺撒密码(英语:Caesar cipher),或称恺撒加密、恺撒变换、变换加密,是一种最简单且最广为人知的加密技术。它是一种替换加密的技术,明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。
大概意思就是通过将明文中所使用的字母表按照一定的字数"平移"来进行加密。例如限定范围为使用26个英文字母。当偏移量是3的时候,所有的字母A将被替换成D,B变成E,示意图如下:


加密
现在就刚才的例子来仔细分析一下加密过程。明文是"HELLO WORLD",偏移量是3。
- 解析第一个明文字母 "H",根据对照表可以得到密文字母"K"
- 解析第二个明文字母 "E",根据对照表可以得到密文字母"H"
- 解析第三个明文字母 "L",根据对照表可以得到密文字母"O"
- 解析第四个明文字母 "L",根据对照表可以得到密文字母"O"
- 解析第五个明文字母 "O",根据对照表可以得到密文字母"R"
- 解析第六个明文空格 ,默认不加密,还是空格
- 解析第七个明文字母 "W",根据对照表可以得到密文字母"Z"
- 解析第八个明文字母 "O",根据对照表可以得到密文字母"R"
- 解析第九个明文字母 "R",根据对照表可以得到密文字母"U"
- 解析第十个明文字母 "L",根据对照表可以得到密文字母"O"
- 解析最后一个明文字母 "D",根据对照表可以得到密文字母"G"
因为这个算法比较直观,可以通过代码来验证一下:
java
package com.tw.tsm.cipher.util;
public class CaesarCipher {
// 英文字母表长度
private static final int ALPHABET_SIZE = 26;
/**
* 凯撒加密
*
* @param plaintext 明文
* @param shift 偏移量(可以为负数)
* @return 密文
*/
public static String encrypt(String plaintext, int shift) {
return shift(plaintext, shift);
}
/**
* 核心平移逻辑
* 只处理英文字母,其他字符(数字、空格、标点)保持不变
*/
private static String shift(String text, int shift) {
// 使用 JDK 8+ 的 Math.floorMod,自动处理负数
shift = Math.floorMod(shift, ALPHABET_SIZE);
StringBuilder result = new StringBuilder();
for (char c : text.toCharArray()) {
if (c >= 'A' && c <= 'Z') {
// 大写字母
char base = 'A';
char shifted = (char) ((c - base + shift) % ALPHABET_SIZE + base);
result.append(shifted);
} else if (c >= 'a' && c <= 'z') {
// 小写字母
char base = 'a';
char shifted = (char) ((c - base + shift) % ALPHABET_SIZE + base);
result.append(shifted);
} else {
// 非字母字符保持不变
result.append(c);
}
}
return result.toString();
}
public static void main(String[] args) {
String plaintext = "HELLO, WORLD! Attack at Dawn. XYZ";
int shift = 3;
System.out.println("=== 凯撒加密演示 (偏移量 = " + shift + ") ===");
System.out.println("明文: " + plaintext);
String ciphertext = encrypt(plaintext, shift);
System.out.println("密文: " + ciphertext);
// 再演示一下单个字母的映射
System.out.println("\n=== 字母映射 (A-Z, 偏移量 3) ===");
for (char c = 'A'; c <= 'Z'; c++) {
System.out.print(c + "->" + encrypt(String.valueOf(c), shift) + " ");
if ((c - 'A' + 1) % 13 == 0) {
System.out.println();
}
}
}
}
运行一下加密程序,输出结果如下:

解密
解密就是加密的逆向过程,最简单的办法就是拿密文"KHOOR ZRUOG"根据对照表进行反向翻译:
- 解析第一个密文字母 "K",根据对照表可以得到明文字母"H"
- 解析第二个密文字母 "H",根据对照表可以得到明文字母"E"
- 解析第三个密文字母 "O",根据对照表可以得到明文字母"L"
- 解析第四个密文字母 "O",根据对照表可以得到明文字母"L"
- 解析第五个密文字母 "R",根据对照表可以得到明文字母"O"
- 解析第六个密 文空格 ,默认不加密,还是空格
- 解析第七个密文字母 "Z",根据对照表可以得到明文字母"W"
- 解析第八个密文字母 "R",根据对照表可以得到明文字母"O"
- 解析第九个密文字母 "U",根据对照表可以得到明文字母"R"
- 解析第十个密文字母 "O",根据对照表可以得到明文字母"L"
- 解析最后一个密文字母 "G",根据对照表可以得到明文字母"D"
可以通过代码来验证一下:
java
/**
* 凯撒解密
*
* @param ciphertext 密文
* @param shift 加密时使用的偏移量
* @return 明文
*/
public static String decrypt(String ciphertext, int shift) {
return shift(ciphertext, -shift);
}
public static void main(String[] args) {
String plaintext = "HELLO, WORLD! Attack at Dawn. XYZ";
int shift = 3;
System.out.println("=== 凯撒加密演示 (偏移量 = " + shift + ") ===");
System.out.println("明文: " + plaintext);
String ciphertext = encrypt(plaintext, shift);
System.out.println("密文: " + ciphertext);
String decrypted = decrypt(ciphertext, shift);
System.out.println("解密: " + decrypted);
System.out.println("加解密是否成功: " + plaintext.equals(decrypted));
// 再演示一下单个字母的映射
System.out.println("\n=== 字母映射 (A-Z, 偏移量 3) ===");
for (char c = 'A'; c <= 'Z'; c++) {
System.out.print(c + "->" + encrypt(String.valueOf(c), shift) + " ");
if ((c - 'A' + 1) % 13 == 0) {
System.out.println();
}
}
}
运行一下加密程序,输出结果如下:

算法
通过上面的代码实地来感受了一下凯撒密码的过程加密、解密之后,再来看这个算法可能就不会显得那么突兀了:
数学原理:
加密:Ek(x) = (x + k) mod 26
解密:Dk(y) = (y - k) mod 26
原理示意
偏移量为 3 时,字母表整体向后平移 3 位:

例如:
-
A→D -
B→E -
X→A(越过 Z 后回绕) -
Y→B -
Z→C
暴力破解
对于凯撒密码来说,脆弱性是它最致命的弱点。所谓"脆弱性"是指很容易被破解,凯撒密码之所以是古典密码,就在于它对几乎所有经典攻击手段都不设防。比如看看下面这个例子,使用最朴素的暴力破解手段:
java
public class CaesarBruteForce {
private static final int ALPHABET_SIZE = 26;
public static String decrypt(String ciphertext, int shift) {
shift = Math.floorMod(shift, ALPHABET_SIZE);
StringBuilder sb = new StringBuilder();
for (char c : ciphertext.toCharArray()) {
if (c >= 'A' && c <= 'Z') {
sb.append((char) ((c - 'A' - shift + 26) % 26 + 'A'));
} else if (c >= 'a' && c <= 'z') {
sb.append((char) ((c - 'a' - shift + 26) % 26 + 'a'));
} else {
sb.append(c);
}
}
return sb.toString();
}
public static void main(String[] args) {
String ciphertext = "Khoor, Zruog! Dwwdfn dw Gdzq. ABC";
System.out.println("密文: " + ciphertext);
System.out.println("=== 枚举所有 26 种偏移量 ===\n");
for (int shift = 0; shift < ALPHABET_SIZE; shift++) {
String plain = decrypt(ciphertext, shift);
System.out.printf("shift=%2d : %s%n", shift, plain);
}
}
}
运行一下程序,输出结果如下:

这就是最原始的暴力破解方式------枚举 + 人工筛选。
凯撒密码的密钥空间只有 25 种有效偏移量(偏移 0 等于不加密,1~25 才是有效的),所以暴力破解的思路极其简单:
枚举所有 25 种偏移量,把每种偏移下的解密结果都打印出来,人工或程序识别哪个是真正的明文。
不需要任何密码学知识,一把梭即可。
结束语
严格来说,现代基本不再使用凯撒密码作为一种**独立的、用于保障机密性的加密手段。**因为就算攻击者不知道系统使用了凯撒密码,也会有通过使用诸如频率分析或者样式单词分析的方法,就能从分析结果中看出规律,得出加密者使用的是恺撒密码进而暴力破解。
刚才说独立的、用于保障机密性的加密手段的原因是,可以用它和其它加密算法进行混合使用,比如将根密钥让凯撒加密之后再进行加密存储,起个混淆作用。
但是这不妨碍我们去了解它、学习它,甚至使用它,让自己构建一个完整的信息安全-密码体系。