Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第七十一 无敌密码

历史上的“无敌密码“

密码,自古以来就是保护秘密的核心手段。从古罗马的凯撒密码到二战时期的恩尼格码,从纳瓦荷语的“风语者“到中国共产党第一本密码“豪密“,密码战贯穿了整个人类战争史。在信息时代,密码学更是计算机安全、网络通信、金融交易的基石。本章将从历史上的经典密码战出发,深入讲解中国国密(商用密码)算法体系,并使用 Rust 实现完整的国密加密示例。

风语者:纳瓦荷语加密

在电影《风语者》中,美军为了防止无线电通信被日军监听、破译,征召懂得纳瓦荷语的部落士兵入伍,并使用纳瓦荷语加密情报。经过训练的部落士兵成为了“风语者“,通过语言加密传递情报。由于懂得纳瓦荷语的人极少,并且经过进一步加密后,即使是本族部落的人没有经过专门的训练也无法破译。

纳瓦荷语是一种阿萨巴斯卡语系的语言,语法结构复杂,没有文字,仅靠口耳相传。在二战之前,全世界懂纳瓦荷语的非纳瓦荷人不到30人。美军将军事术语与纳瓦荷语词汇对应起来,例如:

英文术语纳瓦荷语直译
Fighter planeDa-he-tih-hi蜂鸟
BattleshipLo-tso鲸鱼
SubmarineBesh-lo铁鱼
TankChay-da-gahi-ni乌龟
GrenadeNi-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 2048RSA 3072
密钥长度256 bit2048 bit3072 bit
安全等级128 bit112 bit128 bit
签名速度更慢
密钥生成速度更慢
证书体积更大
计算资源消耗更高

可以看到,SM2 在256位密钥长度下就能达到与 RSA 3072 位相当的安全等级,同时密钥更短、计算更快、证书更小,非常适合移动端和物联网设备使用。

SM2 的主要应用场景:

  • 数字签名与验签
  • 密钥交换(类似 ECDH)
  • 公钥加密(类似 ECIES)
  • 电子政务、金融支付、身份认证

SM3:密码杂凑算法

SM3 是中国国家密码管理局于2010年发布的密码杂凑算法,输出256位(32字节)的杂凑值。SM3 适用于数字签名和验证、消息认证码的生成与验证、随机数的生成等。

算法原理:

SM3 采用 Merkle-Damgård 结构,处理流程如下:

  1. 填充:对消息进行填充,使其长度为512位的整数倍
  2. 迭代压缩:将填充后的消息分成512位的分组,逐组进行压缩
  3. 输出:最终输出256位的杂凑值

SM3 的压缩函数可以形式化表示为:

$$V^{(i+1)} = CF(V^{(i)}, B^{(i)})$$

其中 $V^{(0)}$ 为初始值(IV),$B^{(i)}$ 为第 $i$ 个消息分组,$CF$ 为压缩函数。

SM3 与 SHA-256 对比:

特性SM3SHA-256
输出长度256 bit256 bit
分组长度512 bit512 bit
轮数6464
压缩函数结构类似SHA-256但有差异标准SHA-256
消息扩展更复杂的扩展方式标准扩展
安全性抗碰撞、抗原像、抗第二原像抗碰撞、抗原像、抗第二原像
标准来源中国GB/T 32905-2016NIST FIPS 180-4

SM3 在设计上借鉴了 SHA-256 的基本框架,但在消息扩展和压缩函数的布尔函数设计上做了改进,增强了安全性。

SM4:对称加密算法

SM4 是中国国家密码管理局于2012年发布的分组密码算法,原名 SMS4。SM4 是一个对称加密算法,密钥长度为128位,分组长度为128位。

算法原理:

SM4 采用非平衡 Feistel 结构(类似于 SPN 结构),共进行32轮迭代变换。每轮变换包含以下操作:

  1. 非线性变换 $\tau$:使用 S 盒进行字节替换
  2. 线性变换 $L$:对非线性变换的输出进行线性混合
  3. 轮密钥加:将线性变换的结果与轮密钥进行异或

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 对比:

特性SM4AES
分组长度128 bit128 bit
密钥长度128 bit128/192/256 bit
轮数3210/12/14
结构非平衡FeistelSPN
S盒大小8×8 bit8×8 bit
标准来源中国GB/T 32907-2016NIST 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-2016SM3密码杂凑算法2016
GB/T 32907-2016SM4分组密码算法2016
GB/T 32918-2016SM2椭圆曲线公钥密码算法2016
GB/T 32915-2016SM2椭圆曲线密码签名技术2016
GB/T 35276-2017SM2签名算法使用规范2017
GB/T 36622-2018SM9标识密码算法2018
GM/T 0001-2012SM2密码算法使用规范2012
GM/T 0002-2012SM4分组密码算法2012
GM/T 0004-2012SM3密码杂凑算法2012
GM/T 0009-2012SM2密码算法使用规范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-sm2SM2 算法实现专注于SM2,API简洁
rust-sm2SM2 签名与加密支持SM2签名和加密,与标准兼容性好
sm3SM3 杂凑算法独立实现轻量级,仅SM3功能
ring (OpenSSL后端)通用加密库通过OpenSSL支持国密,功能全面

总结

算法类型密钥长度输出长度主要用途国际对标
SM2非对称(ECC)256 bit-数字签名、密钥交换、加密ECDSA/ECDH
SM3杂凑-256 bit消息摘要、完整性校验SHA-256
SM4对称(分组)128 bit128 bit数据加密、通信保密AES-128
SM9基于身份--无证书认证、密钥管理IBC标准

练习题

  1. 历史分析:为什么纳瓦荷语密码在二战中从未被破译?这种方法在现代数字通信中还有应用价值吗?为什么?

  2. 密码对比:SM2 使用256位密钥就能达到 RSA 3072位的安全等级,请从数学原理角度解释为什么 ECC 可以用更短的密钥达到更高的安全等级。

  3. 编程实践:使用 libsm 库实现一个完整的文件加密工具,要求:

    • 使用 SM3 计算文件的哈希值
    • 使用 SM4-CBC 模式加密文件内容
    • 使用 SM2 对 SM3 哈希值进行数字签名
    • 将加密文件、IV、签名打包输出
  4. 思考题:国密算法与国密标准体系(GB/T、GM/T)之间的关系是什么?如果一个国际软件产品要进入中国市场,是否必须支持国密算法?请分析其中的技术原因和政策原因。

  5. 扩展阅读:研究 SM9 基于身份的密码体制,与传统的 PKI 体系(基于证书的公钥基础设施)相比,SM9 在密钥管理方面有哪些优势和劣势?

  6. 实战挑战:使用 Rust 实现一个简单的“国密工具箱“命令行程序,支持以下功能:

    • sm3 hash <file> - 计算文件SM3哈希
    • sm4 encrypt <input> <output> - SM4加密文件
    • sm4 decrypt <input> <output> - SM4解密文件
    • sm2 sign <file> - SM2签名文件
    • sm2 verify <file> <signature> - SM2验签