第七十一 无敌密码
历史上的“无敌密码“
密码,自古以来就是保护秘密的核心手段。从古罗马的凯撒密码到二战时期的恩尼格码,从纳瓦荷语的“风语者“到中国共产党第一本密码“豪密“,密码战贯穿了整个人类战争史。在信息时代,密码学更是计算机安全、网络通信、金融交易的基石。本章将从历史上的经典密码战出发,深入讲解中国国密(商用密码)算法体系,并使用 Rust 实现完整的国密加密示例。
风语者:纳瓦荷语加密
在电影《风语者》中,美军为了防止无线电通信被日军监听、破译,征召懂得纳瓦荷语的部落士兵入伍,并使用纳瓦荷语加密情报。经过训练的部落士兵成为了“风语者“,通过语言加密传递情报。由于懂得纳瓦荷语的人极少,并且经过进一步加密后,即使是本族部落的人没有经过专门的训练也无法破译。
纳瓦荷语是一种阿萨巴斯卡语系的语言,语法结构复杂,没有文字,仅靠口耳相传。在二战之前,全世界懂纳瓦荷语的非纳瓦荷人不到30人。美军将军事术语与纳瓦荷语词汇对应起来,例如:
| 英文术语 | 纳瓦荷语 | 直译 |
|---|---|---|
| Fighter plane | Da-he-tih-hi | 蜂鸟 |
| Battleship | Lo-tso | 鲸鱼 |
| Submarine | Besh-lo | 铁鱼 |
| Tank | Chay-da-gahi-ni | 乌龟 |
| Grenade | Ni-ma-si | 土豆 |
这种密码直到战争结束也没有被破译,堪称“无敌密码“。在硫磺岛战役中,6名风语者在48小时内传递了超过800条消息,无一出错。但要想破译密码最简单的方式就是俘虏“风语者“,逼迫其协助破译密码,这在电影中有比较深刻的描述。由此可见使用极少人使用的语言(“部落语言”、“方言”)加密情报,尽管有一定的局限性,但在特定的场景下仍是十分可靠有效的方式。
豪密:中共第一本密码
豪密,是中国共产党和中国工农红军第一本无线电通讯密码的简称。由中国共产党初期领导人之一周恩来(化名:伍豪)编制。
1928年,周恩来在上海秘密编制了这套密码系统。“豪密“的命名来源于周恩来的化名“伍豪“中的“豪“字。这套密码采用了一种独创的加密方法,将汉字转换为数字编码,再进行二次加密处理。其核心特点包括:
- 双层加密:先将明文转换为数字编码,再对数字序列进行位移和替换加密
- 动态密钥:密钥定期更换,即使截获部分密文也无法推导出完整密钥
- 无规律性:加密后的密文看似随机,没有明显的统计规律
从1928年编制到1949年国民党垮台,“豪密“在长达21年的使用中从未被破译。这在中国密码史上堪称奇迹,也为中国革命的胜利提供了重要的通信保障。
恩尼格码:被盟军破译的“无敌“密码
恩尼格码(Enigma)是二战期间德国使用的最著名的加密机器。恩尼格码机利用转子(rotor)和反射器(reflector)的机械组合,可以产生天文数字般的密钥空间。
恩尼格码机的加密原理可以用数学描述如下。设 $E_k$ 为密钥 $k$ 下的加密函数,则:
$$E_k(x) = \pi_3(\pi_2(\pi_1(x \oplus s) \oplus s) \oplus s)$$
其中 $\pi_1, \pi_2, \pi_3$ 分别代表三个转子的置换,$s$ 为初始设定。每加密一个字符,转子就会步进,使得加密函数动态变化。
恩尼格码机的密钥空间约为 $10^{158}$,在当时看来几乎不可能被暴力破解。然而,英国布莱切利园的数学家阿兰·图灵(Alan Turing)领导的团队,利用德国操作员的习惯性错误和恩尼格码机的一个设计缺陷(任何字母都不会加密为自身),成功破译了恩尼格码。
盟军通过破译恩尼格码获得了大量德军情报,据估计这一成就使二战至少缩短了两年,挽救了约1400万人的生命。
中途岛海战:密码决定命运
1942年的中途岛海战是太平洋战争的转折点。美军能够取得这场决定性胜利,关键在于成功破译了日军的 JN-25 密码系统。
美国海军的密码破译小组(Station HYPO)在约瑟夫·罗奇福特(Joseph Rochefort)的领导下,通过长期的密码分析和模式识别,成功解读了日军计划攻击目标代号“AF“的含义。为了确认“AF“就是中途岛,美军故意让中途岛基地发出明文报告淡水设备故障,随后截获日军报告“AF“淡水不足的密文,从而确认了目标。
这场海战的结果:日军损失4艘航空母舰(赤城、加贺、苍龙、飞龙),而美军仅损失1艘(约克城)。密码破译的力量在这场战役中得到了淋漓尽致的展现。
紫密:美国破译日本外交密码
“紫密”(PURPLE)是二战期间日本外交部门使用的高级加密系统。美国陆军信号情报处(SIS)在威廉·弗里德曼(William Friedman)的领导下,经过长达20个月的艰苦努力,于1940年成功破译了紫密。
通过破译紫密,美国获取了大量日本外交情报,包括日本与德国、意大利之间的秘密通信。这些情报在二战的关键决策中发挥了重要作用。然而,出于保护情报来源的考虑,美国并未在所有情况下都利用这些情报,珍珠港事件前虽然截获了相关情报,但未能及时转化为有效的防御行动。
密码战的历史意义
纵观历史,密码战对战争进程的影响是深远的:
| 战争/事件 | 密码系统 | 结果 | 影响 |
|---|---|---|---|
| 二战太平洋战场 | 纳瓦荷语密码 | 未被破译 | 保障美军通信安全 |
| 二战欧洲战场 | 恩尼格码 | 被盟军破译 | 二战缩短约两年 |
| 太平洋战争 | JN-25密码 | 被美军破译 | 中途岛海战胜利 |
| 二战外交战线 | 紫密 | 被美国破译 | 获取大量外交情报 |
| 中国革命 | 豪密 | 未被破译 | 保障中共通信21年 |
密码是保护秘密的重要手段,其本身编码、破译就是十分重要的秘密。一旦密码被破译,秘密被泄露,其危害也是十分巨大的。
语言作为密码
使用人类语言作为加密手段,是一种古老但有效的思路。其原理和局限如下:
原理:
- 利用语言的稀有性:使用人数极少的语言天然具有保密性
- 利用语言的复杂性:语法结构复杂的语言增加了学习门槛
- 利用文化壁垒:不同文化背景的人难以理解特定语言的含义
局限:
- 安全性依赖于语言使用者的稀缺性,一旦敌方掌握了该语言,安全性即丧失
- 语言使用者本身就是最大的安全隐患(如俘虏风语者)
- 通信效率低,编码解码速度慢
- 不适合大规模、标准化的军事通信
- 随着语言学研究的深入,稀有语言的神秘面纱逐渐被揭开
国密(中国商用密码)算法详解
如果说计算机通用的密码算法是“世界通用语言“,那么使用范围有限的国密(商用密码)则可以成为“部落语言“。
世界上本没有路,走的人多了,也便成了路。 世界上本没有标准,用的人多了,也便成了标准。
中国商用密码(简称“国密“)是由中国国家密码管理局制定的一系列密码算法标准,旨在保障国家信息安全,减少对国外密码算法的依赖。国密算法主要包括 SM2、SM3、SM4、SM9 等。
SM2:基于ECC的非对称密码算法
SM2 是一种基于椭圆曲线密码学(Elliptic Curve Cryptography, ECC)的非对称加密算法,由国家密码管理局于2010年发布。SM2 可用于数字签名、密钥交换和数据加密。
数学原理:
SM2 基于椭圆曲线离散对数问题(Elliptic Curve Discrete Logarithm Problem, ECDLP)。给定椭圆曲线 $E$ 上的一点 $P$ 和整数 $k$,计算 $Q = kP$ 是容易的(点乘运算),但给定 $Q$ 和 $P$,求 $k$ 则是计算上不可行的。
SM2 使用的推荐椭圆曲线参数如下:
$$y^2 = x^3 + ax + b \pmod{p}$$
其中 $p$ 为256位素数,曲线阶 $n$ 也是256位素数。
SM2 与 RSA 对比:
| 特性 | SM2 (ECC 256) | RSA 2048 | RSA 3072 |
|---|---|---|---|
| 密钥长度 | 256 bit | 2048 bit | 3072 bit |
| 安全等级 | 128 bit | 112 bit | 128 bit |
| 签名速度 | 快 | 慢 | 更慢 |
| 密钥生成速度 | 快 | 慢 | 更慢 |
| 证书体积 | 小 | 大 | 更大 |
| 计算资源消耗 | 低 | 高 | 更高 |
可以看到,SM2 在256位密钥长度下就能达到与 RSA 3072 位相当的安全等级,同时密钥更短、计算更快、证书更小,非常适合移动端和物联网设备使用。
SM2 的主要应用场景:
- 数字签名与验签
- 密钥交换(类似 ECDH)
- 公钥加密(类似 ECIES)
- 电子政务、金融支付、身份认证
SM3:密码杂凑算法
SM3 是中国国家密码管理局于2010年发布的密码杂凑算法,输出256位(32字节)的杂凑值。SM3 适用于数字签名和验证、消息认证码的生成与验证、随机数的生成等。
算法原理:
SM3 采用 Merkle-Damgård 结构,处理流程如下:
- 填充:对消息进行填充,使其长度为512位的整数倍
- 迭代压缩:将填充后的消息分成512位的分组,逐组进行压缩
- 输出:最终输出256位的杂凑值
SM3 的压缩函数可以形式化表示为:
$$V^{(i+1)} = CF(V^{(i)}, B^{(i)})$$
其中 $V^{(0)}$ 为初始值(IV),$B^{(i)}$ 为第 $i$ 个消息分组,$CF$ 为压缩函数。
SM3 与 SHA-256 对比:
| 特性 | SM3 | SHA-256 |
|---|---|---|
| 输出长度 | 256 bit | 256 bit |
| 分组长度 | 512 bit | 512 bit |
| 轮数 | 64 | 64 |
| 压缩函数结构 | 类似SHA-256但有差异 | 标准SHA-256 |
| 消息扩展 | 更复杂的扩展方式 | 标准扩展 |
| 安全性 | 抗碰撞、抗原像、抗第二原像 | 抗碰撞、抗原像、抗第二原像 |
| 标准来源 | 中国GB/T 32905-2016 | NIST FIPS 180-4 |
SM3 在设计上借鉴了 SHA-256 的基本框架,但在消息扩展和压缩函数的布尔函数设计上做了改进,增强了安全性。
SM4:对称加密算法
SM4 是中国国家密码管理局于2012年发布的分组密码算法,原名 SMS4。SM4 是一个对称加密算法,密钥长度为128位,分组长度为128位。
算法原理:
SM4 采用非平衡 Feistel 结构(类似于 SPN 结构),共进行32轮迭代变换。每轮变换包含以下操作:
- 非线性变换 $\tau$:使用 S 盒进行字节替换
- 线性变换 $L$:对非线性变换的输出进行线性混合
- 轮密钥加:将线性变换的结果与轮密钥进行异或
SM4 的加密过程可以表示为:
$$X^{(i+1)} = X^{(i)} \oplus T(X^{(i)} \oplus X^{(i+1)} \oplus X^{(i+2)} \oplus X^{(i+3)} \oplus rk^{(i)})$$
其中 $T = L \circ \tau$,$rk^{(i)}$ 为第 $i$ 轮的轮密钥。
SM4 支持多种工作模式,包括 ECB、CBC、CFB、OFB、CTR 等。
SM4 与 AES 对比:
| 特性 | SM4 | AES |
|---|---|---|
| 分组长度 | 128 bit | 128 bit |
| 密钥长度 | 128 bit | 128/192/256 bit |
| 轮数 | 32 | 10/12/14 |
| 结构 | 非平衡Feistel | SPN |
| S盒大小 | 8×8 bit | 8×8 bit |
| 标准来源 | 中国GB/T 32907-2016 | NIST FIPS 197 |
| 安全性 | 抗差分、抗线性分析 | 抗差分、抗线性分析 |
SM4 在硬件实现上具有良好的性能,适合在各种嵌入式设备和智能卡上部署。
SM9:基于身份的密码算法
SM9 是中国国家密码管理局于2016年发布的基于身份的密码(Identity-Based Cryptography, IBC)算法。SM9 不需要传统的公钥证书,而是直接使用用户的身份标识(如手机号、邮箱、身份证号)作为公钥。
核心原理:
SM9 基于双线性对(Bilinear Pairing)的数学难题。设 $G_1, G_2, G_T$ 为三个循环群,$e: G_1 \times G_2 \rightarrow G_T$ 为双线性映射,则:
$$e(aP, bQ) = e(P, Q)^{ab}$$
SM9 的密钥生成中心(KGC)持有主密钥 $s$,用户使用身份标识 $ID$ 向 KGC 申请私钥。KGC 计算 $d_{ID} = s \cdot H(ID)$ 作为用户私钥,而 $Q_{ID} = H(ID)$ 作为用户公钥。
SM9 的优势:
- 无需公钥基础设施(PKI)和证书管理
- 简化密钥管理流程
- 支持数字签名、密钥封装、密钥协商
- 适合大规模物联网设备的身份认证
国密标准体系
中国商用密码标准体系由国家密码管理局制定和管理,主要标准编号如下:
| 标准编号 | 算法 | 类型 | 发布年份 |
|---|---|---|---|
| GB/T 32905-2016 | SM3 | 密码杂凑算法 | 2016 |
| GB/T 32907-2016 | SM4 | 分组密码算法 | 2016 |
| GB/T 32918-2016 | SM2 | 椭圆曲线公钥密码算法 | 2016 |
| GB/T 32915-2016 | SM2 | 椭圆曲线密码签名技术 | 2016 |
| GB/T 35276-2017 | SM2 | 签名算法使用规范 | 2017 |
| GB/T 36622-2018 | SM9 | 标识密码算法 | 2018 |
| GM/T 0001-2012 | SM2 | 密码算法使用规范 | 2012 |
| GM/T 0002-2012 | SM4 | 分组密码算法 | 2012 |
| GM/T 0004-2012 | SM3 | 密码杂凑算法 | 2012 |
| GM/T 0009-2012 | SM2 | 密码算法使用规范 | 2012 |
注:GB/T 为国家标准,GM/T 为密码行业标准。
国密在实践中的应用
国密算法在中国各行业得到了广泛应用,尤其在以下领域:
银行与金融:
- 中国人民银行要求所有银行核心系统支持国密算法
- 银行卡交易、跨行转账使用 SM2/SM4 保护
- 第三方支付(支付宝、微信支付)已全面支持国密
政务系统:
- 电子政务平台使用 SM2 进行数字签名和身份认证
- 税务系统、社保系统使用 SM4 加密敏感数据
- 政府公文传输使用 SM3 进行完整性校验
其他领域:
- 电力系统:智能电表通信加密
- 交通系统:ETC不停车收费系统
- 物联网:智能家居设备身份认证
- 电子商务:电子发票、电子合同签名
Rust 国密实战
环境准备
在 Rust 项目中使用国密算法,需要在 Cargo.toml 中添加依赖:
[dependencies]
libsm = "0.5"
rand = "0.8"
base64 = "0.21"
hex = "0.4"
SM3 杂凑算法示例
#![allow(unused)]
fn main() {
use libsm::sm3::hash::Sm3Hash;
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
fn sm3_demo() {
// 对字符串进行 SM3 哈希
let string = String::from("abc文心一言&ChatGPT");
let mut hash = Sm3Hash::new(string.as_bytes());
let digest: [u8; 32] = hash.get_hash();
// 输出 Base64 编码的哈希值
let base64_str = BASE64.encode(&digest);
println!("SM3 Hash (Base64): {}", base64_str);
// 输出十六进制编码的哈希值
let hex_str: String = digest.iter().map(|b| format!("{:02x}", b)).collect();
println!("SM3 Hash (Hex): {}", hex_str);
}
}
SM3 对输入 “abc” 的标准测试向量输出为:
66c7f0f462eeedd9d1f2d46bdc10e4e2415c90e068e1e4dda26a45273ee1c7d0
SM4 对称加密示例
#![allow(unused)]
fn main() {
use libsm::sm4::Cipher;
use libsm::sm4::Mode;
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
fn sm4_demo() {
// 128位密钥
let key: [u8; 16] = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
];
// 使用 CBC 模式
let cipher = Cipher::new(&key, Mode::Cbc);
// 生成随机 IV(初始化向量)
let iv = rand_block();
// 加密陈毅元帅的诗
let poem = String::from("断头今日意如何?创业艰难百战多。此去泉台招旧部,旌旗十万斩阎罗。");
let encrypt_bytes = cipher.encrypt(&poem.as_bytes(), &iv);
println!("SM4 Encrypted (Base64): {}", BASE64.encode(&encrypt_bytes));
// 解密
let plaintext_bytes = cipher.decrypt(&encrypt_bytes, &iv);
let poem1 = String::from_utf8(plaintext_bytes.to_vec()).unwrap();
println!("SM4 Decrypted: {}", poem1);
}
fn rand_block() -> [u8; 16] {
use rand::prelude::*;
let mut rng = rand::thread_rng();
let mut block: [u8; 16] = [0; 16];
rng.fill_bytes(&mut block[..]);
block
}
}
SM2 数字签名示例
#![allow(unused)]
fn main() {
use libsm::sm2::signature::{SigCtx, Signature};
fn sm2_demo() {
let poem2 = String::from("南国烽烟正十年,此头须向国门悬。后死诸君多努力,捷报飞来当纸钱。");
let msg = poem2.as_bytes();
// 创建签名上下文并生成密钥对
let ctx = SigCtx::new();
let (pk, sk) = ctx.new_keypair();
// 签名:速度快
let signature = ctx.sign(msg, &sk, &pk);
println!("SM2 Signature created successfully");
// 验签:速度相对较慢
let valid = ctx.verify(msg, &pk, &signature);
println!("SM2 Signature valid: {}", valid);
}
}
综合测试示例
#![allow(unused)]
fn main() {
extern crate rand as random;
use rand::prelude::*;
fn rand_block() -> [u8; 16] {
let mut rng = random::thread_rng();
let mut block: [u8; 16] = [0; 16];
rng.fill_bytes(&mut block[..]);
println!("IV:{}", hex::encode(&block));
block
}
use libsm::sm2::signature::{SigCtx, Signature};
use libsm::sm3::hash::Sm3Hash;
use libsm::sm4::Cipher;
use libsm::sm4::Mode;
#[test]
fn sm() {
// SM3 杂凑
let string = String::from("abc文心一言&ChatGPT");
let mut hash = Sm3Hash::new(string.as_bytes());
let digest: [u8; 32] = hash.get_hash();
let base64_str = base64::encode(&digest);
println!("{}", base64_str);
let key: [u8; 16] = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
0x10,
];
// SM4 对称加密
let cipher = Cipher::new(&key, Mode::Cbc);
let iv = rand_block();
let poem = String::from("断头今日意如何?创业艰难百战多。此去泉台招旧部 ,旌旗十万斩阎罗。");
let encrypt_bytes = cipher.encrypt(&poem.as_bytes(), &iv);
println!("{}", base64::encode(&encrypt_bytes));
let plaintext_bytes = cipher.decrypt(&encrypt_bytes, &iv);
let poem1 = String::from_utf8(plaintext_bytes.to_vec()).unwrap();
println!("{}", poem1);
let poem2 = String::from("南国烽烟正十年,此头须向国门悬。后死诸君多努力,捷报飞来当纸钱。");
let msg = poem2.as_bytes();
let mut sw = Stopwatch::new();
// SM2 签名速度快,验签速度慢
let ctx = SigCtx::new();
let (pk, sk) = ctx.new_keypair();
println!("elapsed_ms 0:{:?}", sw.elapsed());
sw.restart();
let signature = ctx.sign(msg, &sk, &pk);
println!("elapsed_ms 1:{:?}", sw.elapsed());
sw.restart();
let valid = ctx.verify(msg, &pk, &signature);
println!("valid:{},elapsed_ms:{:?}", valid, sw.elapsed());
}
}
OpenSSL 命令行验证
# 使用 OpenSSL 计算 SM3 哈希
openssl sm3 Rust实战配套代码.zip
# 使用 OpenSSL 进行 SM4 加密
openssl enc -e -sm4 -in /tmp/1.txt -out /tmp/2.txt
Rust 国密库推荐
| 库名 | 说明 | 特点 |
|---|---|---|
libsm | 纯 Rust 实现的国密算法库 | 支持SM2/SM3/SM4,无外部依赖,适合嵌入式 |
gm-sm2 | SM2 算法实现 | 专注于SM2,API简洁 |
rust-sm2 | SM2 签名与加密 | 支持SM2签名和加密,与标准兼容性好 |
sm3 | SM3 杂凑算法独立实现 | 轻量级,仅SM3功能 |
ring (OpenSSL后端) | 通用加密库 | 通过OpenSSL支持国密,功能全面 |
总结
| 算法 | 类型 | 密钥长度 | 输出长度 | 主要用途 | 国际对标 |
|---|---|---|---|---|---|
| SM2 | 非对称(ECC) | 256 bit | - | 数字签名、密钥交换、加密 | ECDSA/ECDH |
| SM3 | 杂凑 | - | 256 bit | 消息摘要、完整性校验 | SHA-256 |
| SM4 | 对称(分组) | 128 bit | 128 bit | 数据加密、通信保密 | AES-128 |
| SM9 | 基于身份 | - | - | 无证书认证、密钥管理 | IBC标准 |
练习题
-
历史分析:为什么纳瓦荷语密码在二战中从未被破译?这种方法在现代数字通信中还有应用价值吗?为什么?
-
密码对比:SM2 使用256位密钥就能达到 RSA 3072位的安全等级,请从数学原理角度解释为什么 ECC 可以用更短的密钥达到更高的安全等级。
-
编程实践:使用
libsm库实现一个完整的文件加密工具,要求:- 使用 SM3 计算文件的哈希值
- 使用 SM4-CBC 模式加密文件内容
- 使用 SM2 对 SM3 哈希值进行数字签名
- 将加密文件、IV、签名打包输出
-
思考题:国密算法与国密标准体系(GB/T、GM/T)之间的关系是什么?如果一个国际软件产品要进入中国市场,是否必须支持国密算法?请分析其中的技术原因和政策原因。
-
扩展阅读:研究 SM9 基于身份的密码体制,与传统的 PKI 体系(基于证书的公钥基础设施)相比,SM9 在密钥管理方面有哪些优势和劣势?
-
实战挑战:使用 Rust 实现一个简单的“国密工具箱“命令行程序,支持以下功能:
sm3 hash <file>- 计算文件SM3哈希sm4 encrypt <input> <output>- SM4加密文件sm4 decrypt <input> <output>- SM4解密文件sm2 sign <file>- SM2签名文件sm2 verify <file> <signature>- SM2验签