现代密码学的数学地基:从模运算到离散对数

现代密码学的数学地基:从模运算到离散对数

    • 前言
    • [1. 模运算:密码学的运算基石](#1. 模运算:密码学的运算基石)
      • [1.1 什么是模运算](#1.1 什么是模运算)
      • [1.2 模运算的基本性质](#1.2 模运算的基本性质)
      • [1.3 模幂运算与快速幂](#1.3 模幂运算与快速幂)
      • [1.4 为什么密码学偏爱模运算](#1.4 为什么密码学偏爱模运算)
    • [2. 欧几里得算法与逆元](#2. 欧几里得算法与逆元)
      • [2.1 欧几里得算法求最大公约数](#2.1 欧几里得算法求最大公约数)
      • [2.2 扩展欧几里得算法](#2.2 扩展欧几里得算法)
      • [2.3 模逆元的存在条件与求解](#2.3 模逆元的存在条件与求解)
    • [3. 三大数论定理](#3. 三大数论定理)
      • [3.1 费马小定理](#3.1 费马小定理)
      • [3.2 欧拉定理与欧拉函数](#3.2 欧拉定理与欧拉函数)
      • [3.3 中国剩余定理(CRT)](#3.3 中国剩余定理(CRT))
    • [4. 离散对数问题](#4. 离散对数问题)
      • [4.1 什么是离散对数](#4.1 什么是离散对数)
      • [4.2 离散对数的"难解性"](#4.2 离散对数的"难解性")
      • [4.3 离散对数在密码学中的应用](#4.3 离散对数在密码学中的应用)
    • [5. 抽象代数基础](#5. 抽象代数基础)
      • [5.1 群、环、域的概念](#5.1 群、环、域的概念)
      • [5.2 有限域 GF(p)](#5.2 有限域 GF(p))
      • [5.3 有限域 GF(2ⁿ)](#5.3 有限域 GF(2ⁿ))
      • [5.4 有限域在密码学中的作用](#5.4 有限域在密码学中的作用)

前言

很多开发者熟悉密码学库的API调用,却对底层原理感到陌生------模运算为何能构建加密体系?离散对数为什么是安全性的基石?

本文作为系列开篇,将从程序员的视角出发,用实例而非纯公式,拆解现代密码学的核心数学工具:模运算与欧几里得算法、费马小定理与中国剩余定理、离散对数问题,以及群环域的基本概念。


1. 模运算:密码学的运算基石

1.1 什么是模运算

模运算(Modular Arithmetic),简单说就是**"取余数"的运算**。

你小学就学过:7 除以 3,商是 2,余数是 1。用模运算的写法就是:

7 mod 3 = 1

翻译成程序员的话,就是 7 % 3 == 1

但模运算远不止"求余"这么简单------它本质上是把无限的整数映射到一个有限的集合里。比如模 12 的世界里,所有整数都会被压缩到 0~11 这 12 个数里:

base 复制代码
13 mod 12 = 1
25 mod 12 = 1
-1 mod 12 = 11

你可以把它想象成一个钟表:时针从 12 点走到 13 点,表盘上显示的还是 1 点。这就是模 12 的直观理解。

💡 核心直觉:模运算 = 有限范围内的算术。无论数字多大,做完 mod 之后都会被"折叠"回一个固定大小的空间里。


1.2 模运算的基本性质

模运算之所以能构建加密体系,关键在于它的代数性质非常好------加法、减法、乘法都可以"先算再取模",也可以"取模再算",结果一样。

加法性质:

( a + b )   m o d   m = ( a   m o d   m ) + ( b   m o d   m )   m o d   m (a + b) \bmod m = (a \\bmod m) + (b \\bmod m) \bmod m (a+b)modm=(amodm)+(bmodm)modm

base 复制代码
(17 + 23) mod 7 = 40 mod 7 = 5
// 等价于
(17 mod 7 + 23 mod 7) mod 7 = (3 + 2) mod 7 = 5

乘法性质:

( a × b )   m o d   m = ( a   m o d   m ) × ( b   m o d   m )   m o d   m (a \times b) \bmod m = (a \\bmod m) \\times (b \\bmod m) \bmod m (a×b)modm=(amodm)×(bmodm)modm

base 复制代码
(17 × 23) mod 7 = 391 mod 7 = 6
// 等价于
(17 mod 7 × 23 mod 7) mod 7 = (3 × 2) mod 7 = 6

减法性质:

( a − b )   m o d   m = ( a   m o d   m ) − ( b   m o d   m ) + m   m o d   m (a - b) \bmod m = (a \\bmod m) - (b \\bmod m) + m \bmod m (a−b)modm=(amodm)−(bmodm)+mmodm

⚠️ 注意减法可能出现负数,所以要加 m 再取模,保证结果非负。

这些性质意味着什么?数字再大,你都可以随时取模缩小,不影响最终结果。 这对密码学至关重要------因为加密算法要处理的数字动辄几百上千位,不靠模运算随时"瘦身",计算机根本算不动。


1.3 模幂运算与快速幂

模幂运算就是 "先求幂,再取模"

a b   m o d   m a^b \bmod m abmodm

比如 3 5   m o d   7 3^5 \bmod 7 35mod7:

base 复制代码
3^5 = 243
243 mod 7 = 5

但如果指数很大呢?比如 3 1000000   m o d   7 3^{1000000} \bmod 7 31000000mod7,直接算 3 1000000 3^{1000000} 31000000 会得到一个几十万位的数,根本存不下。

这时候就要用快速幂算法(Fast Exponentiation)------利用指数的二进制分解,把时间复杂度从 O(n) 降到 O(log n)。

核心思想:指数折半,底数平方

比如计算 3 13   m o d   7 3^{13} \bmod 7 313mod7:

  • 13 的二进制是 1101,即 13 = 8 + 4 + 1 13 = 8 + 4 + 1 13=8+4+1
  • 所以 3 13 = 3 8 × 3 4 × 3 1 3^{13} = 3^8 \times 3^4 \times 3^1 313=38×34×31
  • 每一步都可以通过前一步平方得到: 3 2 , 3 4 , 3 8 3^2, 3^4, 3^8 32,34,38...
python 复制代码
def fast_pow(base, exponent, mod):
    result = 1
    base = base % mod
    while exponent > 0:
        if exponent & 1:
            result = (result * base) % mod
        base = (base * base) % mod
        exponent = exponent >> 1
    return result

print(fast_pow(3, 13, 7))      # 输出 5
print(fast_pow(3, 1000000, 7)) # 瞬间出结果

💡 快速幂是密码学的"基础设施"。RSA、Diffie-Hellman 等算法的核心运算都是模幂,没有快速幂,这些算法根本跑不起来。


1.4 为什么密码学偏爱模运算

讲到这里,你可能会问:不就是取个余吗,凭什么能撑起整个密码学?

答案在于三个关键词:有限性、单向性、代数结构

① 有限性:把无限压缩进有限空间

普通的整数加法/乘法,数字会越算越大。但在模运算的世界里,无论怎么算,结果永远落在 0 到 m-1 之间。存储空间可控、运算效率稳定、还能定义"逆运算"。

② 单向性:正向容易反向难

来看一个问题:已知 3 x   m o d   7 = 5 3^x \bmod 7 = 5 3xmod7=5,求 x 是多少?

你得一个个试: 3 1 = 3 3^1=3 31=3、 3 2 = 2 3^2=2 32=2、 3 3 = 6 3^3=6 33=6、 3 4 = 4 3^4=4 34=4、 3 5 = 5 3^5=5 35=5... 直到试出 x = 5。

数字小的时候还好,但如果模数是一个 2048 位的大质数呢?

  • 正向计算 :给你 a 和 x,算 a x   m o d   p a^x \bmod p axmodp → 用快速幂,毫秒级出结果
  • 反向求解 :给你 a 和 a x   m o d   p a^x \bmod p axmodp,求 x → 目前没有高效算法,暴力破解要算到宇宙毁灭

这就是离散对数问题(下一章会详细讲),也是现代公钥密码学的安全性根基。

③ 代数结构:模运算构成"群"

模 m 的整数集合,配上加法运算,构成一个数学上的"群";模 m 的乘法(只看与 m 互质的数),也构成一个"群"。

群是什么?简单说就是满足封闭性、结合律、有单位元、有逆元的代数结构。这些性质是构建加密算法的数学前提------没有逆元,解密就做不到;没有封闭性,运算就不完整。

🔑 一句话总结:模运算把"无限的整数"关进"有限的笼子"里,还保留了漂亮的代数性质,让"正向容易、反向困难"成为可能------这就是密码学需要的全部。


2. 欧几里得算法与逆元

2.1 欧几里得算法求最大公约数

最大公约数(Greatest Common Divisor,简称 GCD),指的是两个整数共有约数中最大的那个。比如 g c d ( 12 , 18 ) = 6 gcd(12, 18) = 6 gcd(12,18)=6,因为 12 和 18 的公约数有 1、2、3、6,最大的是 6。

求 GCD 是数论中最基础的问题之一,也是密码学中很多算法的基石------RSA 的密钥生成、模逆元求解、中国剩余定理,全都离不开它。

朴素做法:从大到小枚举

最容易想到的方法是:从两个数中较小的那个开始,往下一个个试,直到找到能同时整除两个数的数。

python 复制代码
def gcd_naive(a, b):
    n = min(a, b)
    for i in range(n, 0, -1):
        if a % i == 0 and b % i == 0:
            return i
    return 1

但这个方法的时间复杂度是 O ( n ) O(n) O(n),如果 a 和 b 是密码学中常用的几百位大数,这种方法根本跑不完。


欧几里得算法:辗转相除法

公元前 300 年,欧几里得在《几何原本》中提出了一个极其优雅的算法,核心只有一句话,

两个整数 a,b 的最大公约数,等于 b 与 a 模 b 的最大公约数,写成公式:

g c d ( a , b ) = g c d ( b , a   m o d   b ) gcd(a, b) = gcd(b, a \bmod b) gcd(a,b)=gcd(b,amodb)

反复用这个等式递推,直到 b 变成 0,此时 a 就是最大公约数。

完整手算示例

我们来求 g c d ( 1071 , 462 ) gcd(1071, 462) gcd(1071,462),一步步递推:

复制代码
第 1 步:计算 gcd(1071, 462)
  用 1071 除以 462,商 2,余 147
  即 1071 mod 462 = 147
  根据 gcd(a, b) = gcd(b, a mod b),递推为 gcd(462, 147)

第 2 步:计算 gcd(462, 147)
  用 462 除以 147,商 3,余 21
  即 462 mod 147 = 21
  递推为 gcd(147, 21)

第 3 步:计算 gcd(147, 21)
  用 147 除以 21,商 7,余 0
  即 147 mod 21 = 0
  递推为 gcd(21, 0)

第 4 步:计算 gcd(21, 0)
  当第二个数为 0 时,第一个数就是最大公约数
  所以 gcd(21, 0) = 21

最终答案:g c d ( 1071 , 462 ) = 21 gcd(1071, 462) = 21 gcd(1071,462)=21

四步就出结果了,比枚举法快得多。


为什么这个等式成立?

这是整个算法的核心,我们来推导一下。

要证明: g c d ( a , b ) = g c d ( b , a   m o d   b ) gcd(a, b) = gcd(b, a \bmod b) gcd(a,b)=gcd(b,amodb)

设 d = g c d ( a , b ) d = gcd(a, b) d=gcd(a,b),即 d d d 是 a a a 和 b b b 的最大公约数。这意味着 d d d 整除 a a a,也整除 b b b。

根据带余除法,对任意整数 a a a、正整数 b b b,一定存在唯一一对整数 q , r q, r q,r,使得:

a = q × b + r , 0 ≤ r < b a = q \times b + r,\quad 0 \le r < b a=q×b+r,0≤r<b

式中:

q q q 叫做 ,代表 a a a 里最多能容纳多少个完整的 b b b;

r r r 就是余数,等价于 a   m o d   b a \bmod b amodb。

所以 r = a − q × b r = a - q \times b r=a−q×b。

现在看 d d d 和 r r r 的关系:

  • d d d 整除 a a a, d d d 也整除 b b b,因此 d d d 一定整除 q × b q \times b q×b(整数倍不改变整除关系)
  • 两个 d d d 的倍数相减,结果仍是 d d d 的倍数
  • 因此 d d d 整除 r r r,即 d d d 也是 b b b 和 r r r 的公约数

反过来,设 d ′ = g c d ( b , r ) d' = gcd(b, r) d′=gcd(b,r):

  • d ′ d' d′ 整除 b b b,也整除 r r r
  • a = q × b + r a = q \times b + r a=q×b+r,两个 d ′ d' d′ 的倍数相加,结果仍是 d ′ d' d′ 的倍数
  • 所以 d ′ d' d′ 整除 a a a,即 d ′ d' d′ 也是 a a a 和 b b b 的公约数

a , b a,b a,b 的全体公约数 和 b , r b,r b,r 的全体公约数完全一致,二者最大公约数必然相等。因此 g c d ( a , b ) = g c d ( b , r ) gcd(a, b) = gcd(b, r) gcd(a,b)=gcd(b,r)。


Python 实现

迭代版本,最常用也最稳健:

python 复制代码
def gcd(a, b):
    while b != 0:
        a, b = b, a % b
    return a

print(gcd(1071, 462))  # 输出 21
print(gcd(48, 18))     # 输出 6

递归版本,代码更简洁但大数下可能有栈溢出风险:

python 复制代码
def gcd_recursive(a, b):
    if b == 0:
        return a
    return gcd_recursive(b, a % b)

时间复杂度分析

欧几里得算法的时间复杂度是 O ( log ⁡ min ⁡ ( a , b ) ) O(\log \min(a, b)) O(logmin(a,b))。

为什么这么快?因为每一步之后,数字至少会缩小到原来的 2/3(实际上更快,接近黄金比例)。这意味着即使 a 和 b 是 1000 位的大数,也只需要几千次迭代------这在密码学中完全可以接受。

对比一下:

  • 枚举法: O ( n ) O(n) O(n),1000 位数要算 10 1000 10^{1000} 101000 次,宇宙毁灭也算不完
  • 欧几里得: O ( log ⁡ n ) O(\log n) O(logn),1000 位数大约算几千次,毫秒级完成

💡 欧几里得算法是已知最古老的算法之一,距今已有 2300 多年历史。它的优雅和高效,让它成为了整个数论大厦的第一块砖。


2.2 扩展欧几里得算法

欧几里得算法只能求出 GCD,但在密码学中,我们经常需要更多的信息------比如找到两个整数 x 和 y,使得:

a × x + b × y = g c d ( a , b ) a \times x + b \times y = gcd(a, b) a×x+b×y=gcd(a,b)

这就是贝祖定理(Bézout's Identity) :对于任意整数 a 和 b,一定存在整数 x 和 y,使得 a x + b y = g c d ( a , b ) ax + by = gcd(a, b) ax+by=gcd(a,b)。

扩展欧几里得算法(Extended Euclidean Algorithm) 就是用来求这组 x 和 y 的。


算法原理

扩展欧几里得算法在普通欧几里得算法的基础上,多维护了两组系数。它的核心思路是"回代":

  1. 当 b = 0 b = 0 b=0 时, g c d ( a , 0 ) = a gcd(a, 0) = a gcd(a,0)=a,此时 x = 1 x = 1 x=1, y = 0 y = 0 y=0

    • 验证: a × 1 + 0 × 0 = a a \times 1 + 0 \times 0 = a a×1+0×0=a ✓
  2. 当 b ≠ 0 b \neq 0 b=0 时,先递归求 g c d ( b , a   m o d   b ) gcd(b, a \bmod b) gcd(b,amodb),得到一组解 ( x ′ , y ′ ) (x', y') (x′,y′),满足:

    • b × x ′ + ( a   m o d   b ) × y ′ = g c d ( a , b ) b \times x' + (a \bmod b) \times y' = gcd(a, b) b×x′+(amodb)×y′=gcd(a,b)
  3. 把 a   m o d   b a \bmod b amodb 展开: a   m o d   b = a − ⌊ a / b ⌋ × b a \bmod b = a - \lfloor a / b \rfloor \times b amodb=a−⌊a/b⌋×b

    • 代入上式: b × x ′ + ( a − ⌊ a / b ⌋ × b ) × y ′ = g c d ( a , b ) b \times x' + (a - \lfloor a / b \rfloor \times b) \times y' = gcd(a, b) b×x′+(a−⌊a/b⌋×b)×y′=gcd(a,b)
  4. 整理一下,把 a 的项和 b 的项分开:

    • a × y ′ + b × ( x ′ − ⌊ a / b ⌋ × y ′ ) = g c d ( a , b ) a \times y' + b \times (x' - \lfloor a / b \rfloor \times y') = gcd(a, b) a×y′+b×(x′−⌊a/b⌋×y′)=gcd(a,b)
  5. 对比 a x + b y = g c d ( a , b ) ax + by = gcd(a, b) ax+by=gcd(a,b),得到递推公式:

    • x = y ′ x = y' x=y′
    • y = x ′ − ⌊ a / b ⌋ × y ′ y = x' - \lfloor a / b \rfloor \times y' y=x′−⌊a/b⌋×y′

完整手算示例

我们来求 1071 x + 462 y = 21 1071x + 462y = 21 1071x+462y=21 的一组整数解。

第一步,先正向跑一遍普通欧几里得,把每一步的商记下来:

复制代码
第 1 层:gcd(1071, 462)
  1071 ÷ 462 = 2 余 147
  商 q₁ = 2,余数 r₁ = 147

第 2 层:gcd(462, 147)
  462 ÷ 147 = 3 余 21
  商 q₂ = 3,余数 r₂ = 21

第 3 层:gcd(147, 21)
  147 ÷ 21 = 7 余 0
  商 q₃ = 7,余数 r₃ = 0

第 4 层:gcd(21, 0) = 21
  到达最底层,此时 x = 1, y = 0
  即 21 × 1 + 0 × 0 = 21

第二步,从最底层开始往回代,逐层推出 x 和 y:

复制代码
回代第 3 层(对应 gcd(147, 21)):
  上一层的结果:21 × 1 + 0 × 0 = 21
  我们要的形式:147 × x + 21 × y = 21
  从第 3 层的等式:147 = 7 × 21 + 0,即 0 = 147 - 7 × 21
  把 0 代入上式:
    21 × 1 + (147 - 7 × 21) × 0 = 21
    147 × 0 + 21 × 1 = 21
  得到 x = 0,y = 1

回代第 2 层(对应 gcd(462, 147)):
  上一层的结果:147 × 0 + 21 × 1 = 21
  我们要的形式:462 × x + 147 × y = 21
  从第 2 层的等式:462 = 3 × 147 + 21,即 21 = 462 - 3 × 147
  把 21 代入上式:
    147 × 0 + (462 - 3 × 147) × 1 = 21
    462 × 1 + 147 × (-3) = 21
  得到 x = 1,y = -3

回代第 1 层(对应 gcd(1071, 462)):
  上一层的结果:462 × 1 + 147 × (-3) = 21
  我们要的形式:1071 × x + 462 × y = 21
  从第 1 层的等式:1071 = 2 × 462 + 147,即 147 = 1071 - 2 × 462
  把 147 代入上式:
    462 × 1 + (1071 - 2 × 462) × (-3) = 21
    462 × 1 + 1071 × (-3) + 462 × 6 = 21
    1071 × (-3) + 462 × 7 = 21
  得到 x = -3,y = 7

最终答案:x = − 3 x = -3 x=−3, y = 7 y = 7 y=7

验证: 1071 × ( − 3 ) + 462 × 7 = − 3213 + 3234 = 21 1071 \times (-3) + 462 \times 7 = -3213 + 3234 = 21 1071×(−3)+462×7=−3213+3234=21 ✓

这里x,y到底是怎么定下来(其实就是展开)我们拿 147 × 0 + ( 462 − 3 × 147 ) × 1 = 21 147×0+(462−3×147)×1=21 147×0+(462−3×147)×1=21举例

原式:

147 × 0 + ( 462 − 3 × 147 ) × 1 = 21 147 \times 0 + (462 - 3 \times 147) \times 1 = 21 147×0+(462−3×147)×1=21

  1. 把括号乘开
    147 × 0 + 462 × 1 − 3 × 147 × 1 = 21 147 \times 0 + 462 \times 1 - 3 \times 147 \times 1 = 21 147×0+462×1−3×147×1=21
  2. 化简每一项
    147 × 0 = 0 147 \times 0 = 0 147×0=0, 3 × 147 × 1 = 3 × 147 3 \times 147 \times 1 = 3 \times 147 3×147×1=3×147,式子变为:
    0 + 462 × 1 − 3 × 147 = 21 0 + 462 \times 1 - 3 \times 147 = 21 0+462×1−3×147=21
  3. 调换两项顺序,写成标准 A x + B y Ax+By Ax+By 形式
    462 × 1 + 147 × ( − 3 ) = 21 462 \times 1 + 147 \times (-3) = 21 462×1+147×(−3)=21
    对比格式 462 ⋅ x + 147 ⋅ y = 21 462 \cdot x + 147 \cdot y = 21 462⋅x+147⋅y=21
    对应得到:
    x = 1 , y = − 3 x=1,\ y=-3 x=1, y=−3

递归实现

python 复制代码
def ext_gcd(a, b):
    if b == 0:
        return a, 1, 0
    g, x1, y1 = ext_gcd(b, a % b)
    x = y1
    y = x1 - (a // b) * y1
    return g, x, y

g, x, y = ext_gcd(1071, 462)
print(f"gcd={g}, x={x}, y={y}")  # 输出 gcd=21, x=-3, y=7

迭代实现

递归虽然简洁,但对于极大的数可能有栈溢出风险。迭代版本更稳健,也是实际库函数中常用的实现:

python 复制代码
def ext_gcd_iter(a, b):
    old_r, r = a, b
    old_s, s = 1, 0
    old_t, t = 0, 1

    while r != 0:
        quotient = old_r // r
        # 更新余数
        old_r, r = r, old_r - quotient * r
        # 更新 a 的系数
        old_s, s = s, old_s - quotient * s
        # 更新 b 的系数
        old_t, t = t, old_t - quotient * t

    return old_r, old_s, old_t

g, x, y = ext_gcd_iter(1071, 462)
print(f"gcd={g}, x={x}, y={y}")  # 同样输出 gcd=21, x=-3, y=7

迭代版本维护了三组变量,每一步都用商来更新。它的本质和递归是一样的,只是把递归的"栈"变成了显式的变量更新。


解的不唯一性

贝祖定理的解不是唯一的。如果 ( x 0 , y 0 ) (x_0, y_0) (x0,y0) 是一组解,那么所有解可以表示为:

x = x 0 + k × b g c d ( a , b ) x = x_0 + k \times \frac{b}{gcd(a, b)} x=x0+k×gcd(a,b)b

y = y 0 − k × a g c d ( a , b ) y = y_0 - k \times \frac{a}{gcd(a, b)} y=y0−k×gcd(a,b)a

其中 k 是任意整数。

验证一下,还是用刚才的例子: x 0 = − 3 , y 0 = 7 , g c d = 21 x_0 = -3, y_0 = 7, gcd = 21 x0=−3,y0=7,gcd=21

取 k = 1 k = 1 k=1:

  • x = − 3 + 1 × ( 462 / 21 ) = − 3 + 22 = 19 x = -3 + 1 \times (462 / 21) = -3 + 22 = 19 x=−3+1×(462/21)=−3+22=19
  • y = 7 − 1 × ( 1071 / 21 ) = 7 − 51 = − 44 y = 7 - 1 \times (1071 / 21) = 7 - 51 = -44 y=7−1×(1071/21)=7−51=−44
  • 验证: 1071 × 19 + 462 × ( − 44 ) = 20349 − 20328 = 21 1071 \times 19 + 462 \times (-44) = 20349 - 20328 = 21 1071×19+462×(−44)=20349−20328=21 ✓

💡 扩展欧几里得算法是求模逆元的标准方法,而模逆元是 RSA、ElGamal 等公钥密码算法的核心运算。可以说,没有扩展欧几里得,就没有现代公钥密码学。


2.3 模逆元的存在条件与求解

什么是模逆元?

在普通算术中,一个数 a 的逆元就是它的倒数 1 / a 1/a 1/a,因为 a × ( 1 / a ) = 1 a \times (1/a) = 1 a×(1/a)=1。

在模运算中,逆元的概念类似:如果存在一个整数 x,使得

a × x ≡ 1 ( m o d m ) a \times x \equiv 1 \pmod{m} a×x≡1(modm)

那么 x 就叫做 a 在模 m 下的乘法逆元 ,记作 a − 1   m o d   m a^{-1} \bmod m a−1modm。

举个例子: 3 × 5 = 15 3 \times 5 = 15 3×5=15, 15   m o d   7 = 1 15 \bmod 7 = 1 15mod7=1,所以 3 在模 7 下的逆元是 5。

补充说明:在区间 0 ≤ x < m 0 \le x < m 0≤x<m 内,满足同余式的解有且仅有一个;全体整数中存在无穷多等价解,但它们对 m m m 取模后结果完全相同,密码学运算统一使用区间内唯一的最小非负解。

逆元存在的条件

有了逆元,模运算中的"除法"就可以转化为"乘以逆元",就像普通算术中除以 a 等于乘以 1 / a 1/a 1/a 一样。

不是所有数在任意模数下都有逆元。

a 在模 m 下有逆元,当且仅当 g c d ( a , m ) = 1 gcd(a, m) = 1 gcd(a,m)=1,即 a 和 m 互质。


为什么?我们来证明一下。

必要性(有逆元 → 互质)

假设 a 在模 m 下有逆元 x,即 a x ≡ 1 ( m o d m ) ax \equiv 1 \pmod{m} ax≡1(modm)。

这意味着 a x − 1 = k m ax - 1 = km ax−1=km(k 是某个整数)。

整理一下: a x − k m = 1 ax - km = 1 ax−km=1。

根据贝祖定理,方程 a x + m y = 1 ax + my = 1 ax+my=1 有整数解的充要条件是 g c d ( a , m ) gcd(a, m) gcd(a,m) 整除 1。

而能整除 1 的正整数只有 1,所以 g c d ( a , m ) = 1 gcd(a, m) = 1 gcd(a,m)=1。

充分性(互质 → 有逆元)

反过来,如果 g c d ( a , m ) = 1 gcd(a, m) = 1 gcd(a,m)=1,根据贝祖定理,存在整数 x 和 y 使得 a x + m y = 1 ax + my = 1 ax+my=1。

两边 mod m,my 这一项就没了,得到 a x ≡ 1 ( m o d m ) ax \equiv 1 \pmod{m} ax≡1(modm)。

所以 x 就是 a 的逆元。

举几个例子:

  • 3 和 7 互质( g c d = 1 gcd=1 gcd=1)→ 3 在模 7 下有逆元 ✓
  • 6 和 9 不互质( g c d = 3 gcd=3 gcd=3)→ 6 在模 9 下没有逆元 ✗
    • 验证一下:6×1=6, 6×2=12≡3, 6×3=18≡0, 6×4=24≡6... 确实永远得不到 1

方法一:扩展欧几里得算法求逆元

这是最通用的方法,适用于任何互质的 a 和 m。

思路:求 a 的逆元,就是找 x 使得 a x ≡ 1 ( m o d m ) ax \equiv 1 \pmod{m} ax≡1(modm),也就是找 x 和 y 使得 a x + m y = 1 ax + my = 1 ax+my=1。

这正好是扩展欧几里得算法能解决的问题------当 g c d ( a , m ) = 1 gcd(a, m) = 1 gcd(a,m)=1 时,扩展欧几里得返回的 x 就是逆元。

完整手算示例

求 7 在模 26 下的逆元(这是仿射密码中常用的)。

即解方程: 7 x ≡ 1 ( m o d 26 ) 7x \equiv 1 \pmod{26} 7x≡1(mod26),也就是找 x 使得 7 x + 26 y = 1 7x + 26y = 1 7x+26y=1。

第一步,正向跑欧几里得:

复制代码
第 1 层:gcd(26, 7)
  26 ÷ 7 = 3 余 5
  商 q₁ = 3,余数 5

第 2 层:gcd(7, 5)
  7 ÷ 5 = 1 余 2
  商 q₂ = 1,余数 2

第 3 层:gcd(5, 2)
  5 ÷ 2 = 2 余 1
  商 q₃ = 2,余数 1

第 4 层:gcd(2, 1)
  2 ÷ 1 = 2 余 0
  商 q₄ = 2,余数 0

第 5 层:gcd(1, 0) = 1
  到达最底层,x = 1, y = 0
  即 1 × 1 + 0 × 0 = 1

第二步,往回代:

复制代码
回代第 4 层(gcd(2, 1)):
  上一层:1 × 1 + 0 × 0 = 1
  由 2 = 2 × 1 + 0,得 0 = 2 - 2 × 1
  代入:1 × 1 + (2 - 2 × 1) × 0 = 1
  整理:2 × 0 + 1 × 1 = 1
  x = 0, y = 1

回代第 3 层(gcd(5, 2)):
  上一层:2 × 0 + 1 × 1 = 1
  由 5 = 2 × 2 + 1,得 1 = 5 - 2 × 2
  代入:2 × 0 + (5 - 2 × 2) × 1 = 1
  整理:5 × 1 + 2 × (-2) = 1
  x = 1, y = -2

回代第 2 层(gcd(7, 5)):
  上一层:5 × 1 + 2 × (-2) = 1
  由 7 = 1 × 5 + 2,得 2 = 7 - 1 × 5
  代入:5 × 1 + (7 - 1 × 5) × (-2) = 1
  展开:5 × 1 + 7 × (-2) + 5 × 2 = 1
  整理:7 × (-2) + 5 × 3 = 1
  x = -2, y = 3

回代第 1 层(gcd(26, 7)):
  上一层:7 × (-2) + 5 × 3 = 1
  由 26 = 3 × 7 + 5,得 5 = 26 - 3 × 7
  代入:7 × (-2) + (26 - 3 × 7) × 3 = 1
  展开:7 × (-2) + 26 × 3 - 7 × 9 = 1
  整理:26 × 3 + 7 × (-11) = 1
  x = 3, y = -11

所以 7 × ( − 11 ) ≡ 1 ( m o d 26 ) 7 \times (-11) \equiv 1 \pmod{26} 7×(−11)≡1(mod26)。

逆元通常取正的, − 11   m o d   26 = 15 -11 \bmod 26 = 15 −11mod26=15。

最终答案:7 在模 26 下的逆元是 15

验证: 7 × 15 = 105 7 \times 15 = 105 7×15=105, 105   m o d   26 = 105 − 4 × 26 = 105 − 104 = 1 105 \bmod 26 = 105 - 4 \times 26 = 105 - 104 = 1 105mod26=105−4×26=105−104=1 ✓

Python 实现

python 复制代码
def mod_inverse(a, m):
    g, x, _ = ext_gcd(a, m)
    if g != 1:
        return None  # 逆元不存在
    return x % m  # 保证结果为正

print(mod_inverse(7, 26))  # 输出 15
print(mod_inverse(3, 7))   # 输出 5
print(mod_inverse(6, 9))   # 输出 None(不互质,无逆元)

Python 3.8 及以上版本还有更简便的写法:pow(a, -1, m),内部就是用扩展欧几里得实现的。


方法二:费马小定理求逆元(关于费马小定理后面会讲的到时候可以回过来看)

当模数 m 是质数的时候,有一个更简单的方法------费马小定理。

费马小定理说:如果 p 是质数,且 a 不是 p 的倍数,那么 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod{p} ap−1≡1(modp)。

两边同时乘以 a − 1 a^{-1} a−1,得:

a p − 2 ≡ a − 1 ( m o d p ) a^{p-2} \equiv a^{-1} \pmod{p} ap−2≡a−1(modp)

所以 a 的逆元就是 a p − 2   m o d   p a^{p-2} \bmod p ap−2modp,用快速幂就能算。

例子:求 3 在模 7 下的逆元

  • 3 7 − 2 = 3 5 = 243 3^{7-2} = 3^5 = 243 37−2=35=243
  • 243   m o d   7 = 243 − 34 × 7 = 243 − 238 = 5 243 \bmod 7 = 243 - 34 \times 7 = 243 - 238 = 5 243mod7=243−34×7=243−238=5
  • 所以逆元是 5 ✓
python 复制代码
def mod_inverse_fermat(a, p):
    # p 必须是质数
    return pow(a, p - 2, p)

print(mod_inverse_fermat(3, 7))  # 输出 5

两种方法的对比

方法 适用条件 时间复杂度 备注
扩展欧几里得 a 和 m 互质即可 O ( log ⁡ m ) O(\log m) O(logm) 通用,常数小
费马小定理 m 必须是质数 O ( log ⁡ m ) O(\log m) O(logm) 写起来简单,模数为质数时常用

虽然时间复杂度都是 O ( log ⁡ m ) O(\log m) O(logm),但扩展欧几里得的常数更小,实际运行更快。不过费马小定理写起来更简洁,在模数确定是质数的时候(比如很多密码学协议中)很常用。

逆元在密码学中的应用

模逆元是密码学中无处不在的基础运算:

  • RSA 算法 :私钥 d 就是公钥 e 在模 φ ( n ) \varphi(n) φ(n) 下的逆元,即 d ≡ e − 1 ( m o d φ ( n ) ) d \equiv e^{-1} \pmod{\varphi(n)} d≡e−1(modφ(n))
  • 仿射密码 :加密用 a x + b ax + b ax+b,解密时需要乘以 a 的逆元才能还原
  • 椭圆曲线密码:点的运算中需要求有限域元素的逆元
  • 数字签名:DSA、ECDSA 等签名算法的签名和验证过程都用到逆元
  • 中国剩余定理 :构造解的时候需要求 M i M_i Mi 的逆元

🔑 一句话总结:模逆元让模运算有了"除法",而扩展欧几里得算法是求逆元的标准工具------这对组合是整个公钥密码学的数学底座。


3. 三大数论定理

3.1 费马小定理

费马小定理是数论中最著名的定理之一,也是密码学中最常用的工具之一。它的表述极其简洁,但威力巨大。

定理内容

如果 p p p 是一个质数,且整数 a a a 不是 p p p 的倍数,那么:

a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod{p} ap−1≡1(modp)

换句话说, a a a 的 p − 1 p-1 p−1 次方除以 p p p,余数一定是 1 1 1。

先看几个例子,建立直觉

例 1: p = 5 p = 5 p=5(质数), a = 2 a = 2 a=2

  • 2 4 = 16 2^4 = 16 24=16
  • 16   m o d   5 = 1 16 \bmod 5 = 1 16mod5=1 ✓

例 2: p = 7 p = 7 p=7(质数), a = 3 a = 3 a=3

  • 3 6 = 729 3^6 = 729 36=729
  • 729   m o d   7 = 1 729 \bmod 7 = 1 729mod7=1 ✓( 7 × 104 = 728 7 \times 104 = 728 7×104=728, 729 − 728 = 1 729 - 728 = 1 729−728=1)

例 3: p = 7 p = 7 p=7(质数), a = 7 a = 7 a=7

  • 注意: a a a 是 p p p 的倍数,定理不适用
  • 7 6   m o d   7 = 0 7^6 \bmod 7 = 0 76mod7=0,不是 1 1 1

一个直观的理解方式

我们用 p = 5 p = 5 p=5, a = 2 a = 2 a=2 来演示:

考虑 1 , 2 , 3 , 4 1, 2, 3, 4 1,2,3,4 这四个数( 1 1 1 到 p − 1 p-1 p−1),把它们都乘以 a = 2 a = 2 a=2,再   m o d   5 \bmod 5 mod5:

复制代码
1 × 2 = 2 mod 5 = 2
2 × 2 = 4 mod 5 = 4
3 × 2 = 6 mod 5 = 1
4 × 2 = 8 mod 5 = 3

得到的结果是 { 2 , 4 , 1 , 3 } \{2, 4, 1, 3\} {2,4,1,3}------其实就是 { 1 , 2 , 3 , 4 } \{1, 2, 3, 4\} {1,2,3,4} 换了个顺序!

这不是巧合。因为 a a a 和 p p p 互质,乘以 a a a 再   m o d   p \bmod p modp 是一个一一映射:原来不同的数,乘完之后还是不同的。

所以两边相乘,结果应该相等:

1 × 2 × 3 × 4 ≡ ( 2 × 1 ) × ( 2 × 2 ) × ( 2 × 3 ) × ( 2 × 4 ) ( m o d 5 ) 1 \times 2 \times 3 \times 4 \equiv (2\times1) \times (2\times2) \times (2\times3) \times (2\times4) \pmod{5} 1×2×3×4≡(2×1)×(2×2)×(2×3)×(2×4)(mod5)

左边 = 4 ! = 24 4! = 24 4!=24

右边 = 2 4 × ( 1 × 2 × 3 × 4 ) = 2 4 × 4 ! 2^4 \times (1\times2\times3\times4) = 2^4 \times 4! 24×(1×2×3×4)=24×4!

所以 4 ! ≡ 2 4 × 4 ! ( m o d 5 ) 4! \equiv 2^4 \times 4! \pmod{5} 4!≡24×4!(mod5)

两边都除以 4 ! 4! 4!(因为 4 ! 4! 4! 和 5 5 5 互质,可以除),得到:

1 ≡ 2 4 ( m o d 5 ) 1 \equiv 2^4 \pmod{5} 1≡24(mod5)

这就是费马小定理!


完整的证明思路

  1. 考虑集合 S = { 1 , 2 , 3 , . . . , p − 1 } S = \{1, 2, 3, ..., p-1\} S={1,2,3,...,p−1}
  2. 把每个元素乘以 a a a,得到集合 a S = { a , 2 a , 3 a , . . . , ( p − 1 ) a } aS = \{a, 2a, 3a, ..., (p-1)a\} aS={a,2a,3a,...,(p−1)a},全部   m o d   p \bmod p modp
  3. 因为 a a a 和 p p p 互质, a S aS aS 中的元素两两不同(如果 i a ≡ j a ( m o d p ) ia \equiv ja \pmod{p} ia≡ja(modp),两边乘 a a a 的逆元得 i ≡ j i \equiv j i≡j)
  4. 所以 a S aS aS 其实就是 S S S 的一个排列,元素完全一样,只是顺序不同
  5. 因此两边乘积相等: ∏ S ≡ ∏ ( a S ) ( m o d p ) \prod S \equiv \prod(aS) \pmod{p} ∏S≡∏(aS)(modp)
  6. 左边 = ( p − 1 ) ! (p-1)! (p−1)!,右边 = a p − 1 × ( p − 1 ) ! a^{p-1} \times (p-1)! ap−1×(p−1)!
  7. 两边约掉 ( p − 1 ) ! (p-1)! (p−1)!(和 p p p 互质,可以约),得到 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod{p} ap−1≡1(modp)

费马小定理的应用

应用 1:快速求模逆元

这是费马小定理在密码学中最直接的应用。

由 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod{p} ap−1≡1(modp),两边乘 a − 1 a^{-1} a−1,得:

a p − 2 ≡ a − 1 ( m o d p ) a^{p-2} \equiv a^{-1} \pmod{p} ap−2≡a−1(modp)

所以 a a a 的逆元就是 a p − 2   m o d   p a^{p-2} \bmod p ap−2modp,用快速幂一算就出来了。

python 复制代码
def mod_inverse_fermat(a, p):
    # p 必须是质数
    return pow(a, p - 2, p)

# 例:求 3 在模 7 下的逆元
inv = mod_inverse_fermat(3, 7)
print(f"3 的逆元是 {inv}")  # 输出 5
# 验证:3 × 5 = 15 ≡ 1 mod 7 ✓

应用 2:素性检测(费马测试)

费马小定理反过来用,可以用来检测一个数是不是质数。

思路:如果 n n n 是质数,那么对于任意 a a a(不是 n n n 的倍数),都应该有 a n − 1 ≡ 1 ( m o d n ) a^{n-1} \equiv 1 \pmod{n} an−1≡1(modn)。

反过来,如果存在某个 a a a 使得 a n − 1 ≢ 1 ( m o d n ) a^{n-1} \not\equiv 1 \pmod{n} an−1≡1(modn),那 n n n 一定不是质数。

python 复制代码
def fermat_test(n, a):
    # 检测 n 是否可能是质数,a 是测试底数
    if n <= 1:
        return False
    if n == 2:
        return True
    if pow(a, n - 1, n) != 1:
        return False  # 一定不是质数
    return True  # 可能是质数

print(fermat_test(561, 2))  # 输出 True(但 561 其实是合数!)

⚠️ 注意:费马测试是"概率性"的。通过测试不代表一定是质数,只能说"很可能是"。存在一些合数(叫做卡迈克尔数,比如 561 561 561)能骗过所有与它互质的底数的费马测试。不过在密码学实际应用中,结合多个底数测试,出错概率可以降到几乎为零。
💡 费马小定理是欧拉定理的特例。下一节我们会看到,当模数不是质数时,欧拉定理给出了更一般的结论。


3.2 欧拉定理与欧拉函数

费马小定理只适用于质数模数,但密码学中经常需要处理合数模数(比如 RSA 中的 n = p q n = pq n=pq)。这时候就需要欧拉定理------费马小定理的推广版本。

欧拉函数 φ ( n ) \varphi(n) φ(n)

在讲欧拉定理之前,先认识一个重要的函数:欧拉函数 φ ( n ) \varphi(n) φ(n)(读作"phi")。

φ ( n ) \varphi(n) φ(n) 的定义很简单:

φ ( n ) \varphi(n) φ(n) 表示 1 1 1 到 n n n 之间,与 n n n 互质的正整数的个数。

举几个例子:

例 1: n = 6 n = 6 n=6

  • 1 1 1 到 6 6 6 中,与 6 6 6 互质的数有: 1 , 5 1, 5 1,5
  • φ ( 6 ) = 2 \varphi(6) = 2 φ(6)=2

例 2: n = 7 n = 7 n=7(质数)

  • 1 1 1 到 7 7 7 中,除了 7 7 7 本身,都和 7 7 7 互质
  • φ ( 7 ) = 6 \varphi(7) = 6 φ(7)=6

例 3: n = 1 n = 1 n=1

  • 只有 1 1 1 这一个数, 1 1 1 和 1 1 1 互质( g c d ( 1 , 1 ) = 1 gcd(1,1)=1 gcd(1,1)=1)
  • φ ( 1 ) = 1 \varphi(1) = 1 φ(1)=1

例 4: n = 12 n = 12 n=12

  • 与 12 12 12 互质的数: 1 , 5 , 7 , 11 1, 5, 7, 11 1,5,7,11
  • φ ( 12 ) = 4 \varphi(12) = 4 φ(12)=4

欧拉函数的性质

性质 1 :如果 p p p 是质数,那么 φ ( p ) = p − 1 \varphi(p) = p - 1 φ(p)=p−1

这个很直观------质数和所有比它小的正整数都互质。

性质 2 :如果 p p p 是质数, k k k 是正整数,那么 φ ( p k ) = p k − p k − 1 = p k − 1 ( p − 1 ) \varphi(p^k) = p^k - p^{k-1} = p^{k-1}(p-1) φ(pk)=pk−pk−1=pk−1(p−1)

推导: 1 1 1 到 p k p^k pk 中,不与 p k p^k pk 互质的数就是 p p p 的倍数,共有 p k − 1 p^{k-1} pk−1 个( p , 2 p , 3 p , . . . , p k − 1 × p p, 2p, 3p, ..., p^{k-1}\times p p,2p,3p,...,pk−1×p)。所以互质的数量就是总数减去不互质的: p k − p k − 1 p^k - p^{k-1} pk−pk−1。

例: φ ( 8 ) = φ ( 2 3 ) = 2 3 − 2 2 = 8 − 4 = 4 \varphi(8) = \varphi(2^3) = 2^3 - 2^2 = 8 - 4 = 4 φ(8)=φ(23)=23−22=8−4=4

验证: 1 , 3 , 5 , 7 1, 3, 5, 7 1,3,5,7 与 8 8 8 互质,确实是 4 4 4 个 ✓

性质 3 :欧拉函数是积性函数。如果 m m m 和 n n n 互质,那么 φ ( m n ) = φ ( m ) × φ ( n ) \varphi(mn) = \varphi(m) \times \varphi(n) φ(mn)=φ(m)×φ(n)

这是欧拉函数最重要的性质,也是计算 φ ( n ) \varphi(n) φ(n) 的关键。

有了这三个性质,我们就能计算任意 n n n 的欧拉函数了------只要先把 n n n 分解质因数,再分别算每个质因数幂的 φ \varphi φ,最后乘起来。


计算欧拉函数的完整例子

例:计算 φ ( 36 ) \varphi(36) φ(36)

第一步:分解质因数

  • 36 = 4 × 9 = 2 2 × 3 2 36 = 4 \times 9 = 2^2 \times 3^2 36=4×9=22×32

第二步:分别计算

  • φ ( 2 2 ) = 2 2 − 2 1 = 4 − 2 = 2 \varphi(2^2) = 2^2 - 2^1 = 4 - 2 = 2 φ(22)=22−21=4−2=2
  • φ ( 3 2 ) = 3 2 − 3 1 = 9 − 3 = 6 \varphi(3^2) = 3^2 - 3^1 = 9 - 3 = 6 φ(32)=32−31=9−3=6

第三步:相乘(因为 2 2 2 和 3 3 3 互质)

  • φ ( 36 ) = φ ( 2 2 ) × φ ( 3 2 ) = 2 × 6 = 12 \varphi(36) = \varphi(2^2) \times \varphi(3^2) = 2 \times 6 = 12 φ(36)=φ(22)×φ(32)=2×6=12

验证: 1 1 1 到 36 36 36 中与 36 36 36 互质的数有 1 , 5 , 7 , 11 , 13 , 17 , 19 , 23 , 25 , 29 , 31 , 35 1, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35 1,5,7,11,13,17,19,23,25,29,31,35,共 12 12 12 个 ✓


Python 实现欧拉函数

python 复制代码
def euler_phi(n):
    result = n
    p = 2
    while p * p <= n:
        if n % p == 0:
            while n % p == 0:
                n = n // p
            result -= result // p
        p += 1
    if n > 1:
        result -= result // n
    return result

print(euler_phi(36))   # 输出 12
print(euler_phi(7))    # 输出 6
print(euler_phi(1))    # 输出 1

这个算法的思路是:遍历每个质因数 p p p,每找到一个,就把结果乘以 ( p − 1 ) / p (p-1)/p (p−1)/p,也就是 result -= result // p


欧拉定理

有了欧拉函数,欧拉定理就很好表述了:

如果 a a a 和 n n n 互质,那么:

a φ ( n ) ≡ 1 ( m o d n ) a^{\varphi(n)} \equiv 1 \pmod{n} aφ(n)≡1(modn)

你看,和费马小定理长得很像吧?把费马小定理中的 p − 1 p-1 p−1 换成 φ ( n ) \varphi(n) φ(n) 就是欧拉定理了。

当 n n n 是质数时, φ ( n ) = n − 1 \varphi(n) = n-1 φ(n)=n−1,欧拉定理就退化成了费马小定理。所以说,费马小定理是欧拉定理的特例

例子验证

例 1: n = 6 n = 6 n=6, φ ( 6 ) = 2 \varphi(6) = 2 φ(6)=2,取 a = 5 a = 5 a=5(与 6 6 6 互质)

  • 5 2 = 25 5^2 = 25 52=25
  • 25   m o d   6 = 1 25 \bmod 6 = 1 25mod6=1 ✓

例 2: n = 12 n = 12 n=12, φ ( 12 ) = 4 \varphi(12) = 4 φ(12)=4,取 a = 5 a = 5 a=5(与 12 12 12 互质)

  • 5 4 = 625 5^4 = 625 54=625
  • 625   m o d   12 = 1 625 \bmod 12 = 1 625mod12=1 ✓( 12 × 52 = 624 12 \times 52 = 624 12×52=624, 625 − 624 = 1 625 - 624 = 1 625−624=1)

例 3: n = 15 n = 15 n=15, φ ( 15 ) = φ ( 3 ) × φ ( 5 ) = 2 × 4 = 8 \varphi(15) = \varphi(3) \times \varphi(5) = 2 \times 4 = 8 φ(15)=φ(3)×φ(5)=2×4=8,取 a = 2 a = 2 a=2

  • 2 8 = 256 2^8 = 256 28=256
  • 256   m o d   15 = 1 256 \bmod 15 = 1 256mod15=1 ✓( 15 × 17 = 255 15 \times 17 = 255 15×17=255, 256 − 255 = 1 256 - 255 = 1 256−255=1)

欧拉定理的直观理解

和费马小定理的证明思路几乎一样:

  1. 考虑所有与 n n n 互质且小于 n n n 的正整数,共有 φ ( n ) \varphi(n) φ(n) 个,记为集合 S S S
  2. 把 S S S 中每个元素乘以 a a a( a a a 与 n n n 互质),再   m o d   n \bmod n modn,得到集合 a S aS aS
  3. 因为 a a a 和 n n n 互质, a S aS aS 中的元素仍然都与 n n n 互质,且两两不同
  4. 所以 a S aS aS 就是 S S S 的一个排列
  5. 两边乘积相等,约掉公共部分,就得到 a φ ( n ) ≡ 1 ( m o d n ) a^{\varphi(n)} \equiv 1 \pmod{n} aφ(n)≡1(modn)

欧拉定理在密码学中的应用

应用 1:求合数模下的逆元

和费马小定理类似,由欧拉定理可以推出:如果 a a a 和 n n n 互质,那么 a a a 的逆元是 a φ ( n ) − 1   m o d   n a^{\varphi(n)-1} \bmod n aφ(n)−1modn。

不过实际中求逆元还是用扩展欧几里得更多,因为不需要算 φ ( n ) \varphi(n) φ(n)(算 φ ( n ) \varphi(n) φ(n) 需要分解质因数,而分解大数恰恰是密码学的"难题")。

应用 2:RSA 算法的理论基础

RSA 加密和解密为什么能正确还原?核心就是欧拉定理。

RSA 中, n = p q n = pq n=pq( p p p 和 q q q 是大质数), φ ( n ) = ( p − 1 ) ( q − 1 ) \varphi(n) = (p-1)(q-1) φ(n)=(p−1)(q−1)。

公钥 e e e 和私钥 d d d 满足 e d ≡ 1 ( m o d φ ( n ) ) ed \equiv 1 \pmod{\varphi(n)} ed≡1(modφ(n)),即 e d = k φ ( n ) + 1 ed = k\varphi(n) + 1 ed=kφ(n)+1。

加密: c = m e   m o d   n c = m^e \bmod n c=memodn

解密: c d = m e d = m k φ ( n ) + 1 = ( m φ ( n ) ) k × m ≡ 1 k × m = m ( m o d n ) c^d = m^{ed} = m^{k\varphi(n)+1} = (m^{\varphi(n)})^k \times m \equiv 1^k \times m = m \pmod{n} cd=med=mkφ(n)+1=(mφ(n))k×m≡1k×m=m(modn)

看,解密后正好还原出明文 m m m!这就是 RSA 正确性的数学证明,核心就是欧拉定理。

💡 欧拉定理是费马小定理的推广,也是 RSA 等公钥密码算法的理论基石。理解了欧拉定理,你就理解了公钥密码学为什么能工作。


3.3 中国剩余定理(CRT)

中国剩余定理(Chinese Remainder Theorem,简称 CRT),顾名思义,是中国古代数学家的伟大发现。最早出现在《孙子算经》中,所以也叫"孙子定理"。

问题起源:韩信点兵

《孙子算经》里有这么一道题:

今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?

翻译一下:有一堆东西,不知道多少个。三个三个数,剩 2 2 2 个;五个五个数,剩 3 3 3 个;七个七个数,剩 2 2 2 个。问这堆东西有多少个?

用数学语言说,就是解这个方程组:

{ x ≡ 2 ( m o d 3 ) x ≡ 3 ( m o d 5 ) x ≡ 2 ( m o d 7 ) \begin{cases} x \equiv 2 \pmod{3} \\ x \equiv 3 \pmod{5} \\ x \equiv 2 \pmod{7} \end{cases} ⎩ ⎨ ⎧x≡2(mod3)x≡3(mod5)x≡2(mod7)

中国剩余定理就是用来解这种"一元同余方程组"的。

定理内容

设 m 1 , m 2 , . . . , m k m_1, m_2, ..., m_k m1,m2,...,mk 是两两互质的正整数,那么对于任意整数 a 1 , a 2 , . . . , a k a_1, a_2, ..., a_k a1,a2,...,ak,同余方程组:

{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) ⋮ x ≡ a k ( m o d m k ) \begin{cases} x \equiv a_1 \pmod{m_1} \\ x \equiv a_2 \pmod{m_2} \\ \quad \vdots \\ x \equiv a_k \pmod{m_k} \end{cases} ⎩ ⎨ ⎧x≡a1(modm1)x≡a2(modm2)⋮x≡ak(modmk)

在模 M = m 1 m 2 . . . m k M = m_1m_2...m_k M=m1m2...mk 下有唯一解。

注意两个关键点:

  1. 模数必须两两互质(任意两个模数的 g c d gcd gcd 都是 1 1 1)
  2. 解在模 M M M 下是唯一的------也就是说,最小正整数解只有一个,其他解都是它加上 M M M 的倍数

怎么求解?构造法

中国剩余定理不仅告诉我们"解存在且唯一",还给出了构造解的方法。

设 M = m 1 m 2 . . . m k M = m_1m_2...m_k M=m1m2...mk,对每个 i i i,设 M i = M / m i M_i = M / m_i Mi=M/mi(也就是除了 m i m_i mi 之外所有模数的乘积)。

因为所有模数两两互质,所以 M i M_i Mi 和 m i m_i mi 互质,因此 M i M_i Mi 在模 m i m_i mi 下有逆元,记为 t i t_i ti,即 M i × t i ≡ 1 ( m o d m i ) M_i \times t_i \equiv 1 \pmod{m_i} Mi×ti≡1(modmi)

那么方程组的解就是:

x ≡ a 1 M 1 t 1 + a 2 M 2 t 2 + . . . + a k M k t k ( m o d M ) x \equiv a_1M_1t_1 + a_2M_2t_2 + ... + a_kM_kt_k \pmod{M} x≡a1M1t1+a2M2t2+...+akMktk(modM)

为什么这个构造是对的?我们来验证一下:

看第 i i i 个方程 x ≡ a i ( m o d m i ) x \equiv a_i \pmod{m_i} x≡ai(modmi):

  • 对于第 i i i 项 a i M i t i a_iM_it_i aiMiti: M i M_i Mi 包含了除 m i m_i mi 外的所有模数,所以 M i   m o d   m i M_i \bmod m_i Mimodmi 就是 M i M_i Mi 本身(因为不含 m i m_i mi 这个因子),而 t i t_i ti 是 M i M_i Mi 的逆元,所以 M i t i ≡ 1 ( m o d m i ) M_it_i \equiv 1 \pmod{m_i} Miti≡1(modmi),因此 a i M i t i ≡ a i ( m o d m i ) a_iM_it_i \equiv a_i \pmod{m_i} aiMiti≡ai(modmi)
  • 对于其他项 a j M j t j a_jM_jt_j ajMjtj( j ≠ i j \neq i j=i): M j M_j Mj 包含了 m i m_i mi 这个因子(因为 M j M_j Mj 是除了 m j m_j mj 外所有模数的乘积,而 j ≠ i j \neq i j=i),所以 M j ≡ 0 ( m o d m i ) M_j \equiv 0 \pmod{m_i} Mj≡0(modmi),因此整个这一项 ≡ 0 ( m o d m i ) \equiv 0 \pmod{m_i} ≡0(modmi)

所以把所有项加起来, x ≡ a i + 0 + 0 + . . . + 0 ≡ a i ( m o d m i ) x \equiv a_i + 0 + 0 + ... + 0 \equiv a_i \pmod{m_i} x≡ai+0+0+...+0≡ai(modmi),正好满足第 i i i 个方程!


手算完整例子:韩信点兵

我们来解《孙子算经》中的那道题:

{ x ≡ 2 ( m o d 3 ) x ≡ 3 ( m o d 5 ) x ≡ 2 ( m o d 7 ) \begin{cases} x \equiv 2 \pmod{3} \\ x \equiv 3 \pmod{5} \\ x \equiv 2 \pmod{7} \end{cases} ⎩ ⎨ ⎧x≡2(mod3)x≡3(mod5)x≡2(mod7)

第一步:计算 M M M

  • M = 3 × 5 × 7 = 105 M = 3 \times 5 \times 7 = 105 M=3×5×7=105

第二步:计算每个 M i M_i Mi

  • M 1 = 105 / 3 = 35 M_1 = 105 / 3 = 35 M1=105/3=35
  • M 2 = 105 / 5 = 21 M_2 = 105 / 5 = 21 M2=105/5=21
  • M 3 = 105 / 7 = 15 M_3 = 105 / 7 = 15 M3=105/7=15

第三步:求每个 M i M_i Mi 在模 m i m_i mi 下的逆元 t i t_i ti

  • 求 35 35 35 在模 3 3 3 下的逆元: 35   m o d   3 = 2 35 \bmod 3 = 2 35mod3=2,找 t 1 t_1 t1 使得 2 t 1 ≡ 1 ( m o d 3 ) 2t_1 \equiv 1 \pmod{3} 2t1≡1(mod3)
    • 试一下: 2 × 2 = 4 ≡ 1 ( m o d 3 ) 2 \times 2 = 4 \equiv 1 \pmod{3} 2×2=4≡1(mod3),所以 t 1 = 2 t_1 = 2 t1=2
  • 求 21 21 21 在模 5 5 5 下的逆元: 21   m o d   5 = 1 21 \bmod 5 = 1 21mod5=1, 1 1 1 的逆元就是 1 1 1,所以 t 2 = 1 t_2 = 1 t2=1
  • 求 15 15 15 在模 7 7 7 下的逆元: 15   m o d   7 = 1 15 \bmod 7 = 1 15mod7=1,所以 t 3 = 1 t_3 = 1 t3=1

第四步:代入公式

  • x ≡ a 1 M 1 t 1 + a 2 M 2 t 2 + a 3 M 3 t 3 ( m o d 105 ) x \equiv a_1M_1t_1 + a_2M_2t_2 + a_3M_3t_3 \pmod{105} x≡a1M1t1+a2M2t2+a3M3t3(mod105)
  • x ≡ 2 × 35 × 2 + 3 × 21 × 1 + 2 × 15 × 1 x \equiv 2\times35\times2 + 3\times21\times1 + 2\times15\times1 x≡2×35×2+3×21×1+2×15×1
  • x ≡ 140 + 63 + 30 x \equiv 140 + 63 + 30 x≡140+63+30
  • x ≡ 233 ( m o d 105 ) x \equiv 233 \pmod{105} x≡233(mod105)
  • 233   m o d   105 = 233 − 2 × 105 = 233 − 210 = 23 233 \bmod 105 = 233 - 2\times105 = 233 - 210 = 23 233mod105=233−2×105=233−210=23

所以最小正整数解是 23 23 23。

验证一下:

  • 23 ÷ 3 = 7 23 \div 3 = 7 23÷3=7 余 2 2 2 ✓
  • 23 ÷ 5 = 4 23 \div 5 = 4 23÷5=4 余 3 3 3 ✓
  • 23 ÷ 7 = 3 23 \div 7 = 3 23÷7=3 余 2 2 2 ✓

完美!古代人叫这个"韩信点兵,多多益善",还编了首口诀:"三人同行七十稀,五树梅花廿一枝,七子团圆正半月,除百零五便得知。" 其实就是 70 = 35 × 2 70 = 35\times2 70=35×2, 21 = 21 × 1 21 = 21\times1 21=21×1, 15 = 15 × 1 15 = 15\times1 15=15×1, 105 105 105 是 M M M。


再练一个例子

解方程组:

{ x ≡ 1 ( m o d 4 ) x ≡ 2 ( m o d 5 ) x ≡ 3 ( m o d 7 ) \begin{cases} x \equiv 1 \pmod{4} \\ x \equiv 2 \pmod{5} \\ x \equiv 3 \pmod{7} \end{cases} ⎩ ⎨ ⎧x≡1(mod4)x≡2(mod5)x≡3(mod7)

第一步: M = 4 × 5 × 7 = 140 M = 4 \times 5 \times 7 = 140 M=4×5×7=140

第二步: M 1 = 35 , M 2 = 28 , M 3 = 20 M_1 = 35,\ M_2 = 28,\ M_3 = 20 M1=35, M2=28, M3=20

第三步:求逆元

  • 35   m o d   4 = 3 35 \bmod 4 = 3 35mod4=3,找 3 3 3 的逆元   m o d   4 \bmod 4 mod4: 3 × 3 = 9 ≡ 1 ( m o d 4 ) 3\times3 = 9 \equiv 1 \pmod{4} 3×3=9≡1(mod4), t 1 = 3 t_1 = 3 t1=3
  • 28   m o d   5 = 3 28 \bmod 5 = 3 28mod5=3,找 3 3 3 的逆元   m o d   5 \bmod 5 mod5: 3 × 2 = 6 ≡ 1 ( m o d 5 ) 3\times2 = 6 \equiv 1 \pmod{5} 3×2=6≡1(mod5), t 2 = 2 t_2 = 2 t2=2
  • 20   m o d   7 = 6 20 \bmod 7 = 6 20mod7=6,找 6 6 6 的逆元   m o d   7 \bmod 7 mod7: 6 × 6 = 36 ≡ 1 ( m o d 7 ) 6\times6 = 36 \equiv 1 \pmod{7} 6×6=36≡1(mod7), t 3 = 6 t_3 = 6 t3=6

第四步: x ≡ 1 × 35 × 3 + 2 × 28 × 2 + 3 × 20 × 6 = 105 + 112 + 360 = 577 x \equiv 1\times35\times3 + 2\times28\times2 + 3\times20\times6 = 105 + 112 + 360 = 577 x≡1×35×3+2×28×2+3×20×6=105+112+360=577

  • 577   m o d   140 = 577 − 4 × 140 = 577 − 560 = 17 577 \bmod 140 = 577 - 4\times140 = 577 - 560 = 17 577mod140=577−4×140=577−560=17

验证:

  • 17   m o d   4 = 1 17 \bmod 4 = 1 17mod4=1 ✓
  • 17   m o d   5 = 2 17 \bmod 5 = 2 17mod5=2 ✓
  • 17   m o d   7 = 3 17 \bmod 7 = 3 17mod7=3 ✓

Python 实现

python 复制代码
def crt(remainders, moduli):
    """
    中国剩余定理求解
    remainders: 余数列表 [a1, a2, ..., ak]
    moduli: 模数列表 [m1, m2, ..., mk],要求两两互质
    返回最小正整数解
    """
    M = 1
    for m in moduli:
        M *= m

    x = 0
    for a, m in zip(remainders, moduli):
        Mi = M // m
        # 求 Mi 在模 m 下的逆元
        ti = pow(Mi, -1, m)  # Python 3.8+ 支持 pow 直接求逆元
        x += a * Mi * ti

    return x % M

# 测试:韩信点兵
rem = [2, 3, 2]
mod = [3, 5, 7]
result = crt(rem, mod)
print(f"解为 x ≡ {result} (mod 105)")  # 输出 23

注:Python 3.8 及以上版本,pow(a, -1, m) 可以直接求 a a a 在模 m m m 下的逆元(内部用的是扩展欧几里得)。如果逆元不存在会抛异常。


中国剩余定理在密码学中的应用

应用 1:RSA 解密加速

RSA 解密需要算 c d   m o d   n c^d \bmod n cdmodn,其中 n = p q n = pq n=pq。直接算的话,模数是 n n n,数字很大。

用中国剩余定理可以拆开算:

  • 先算 m 1 = c d   m o d   p m_1 = c^d \bmod p m1=cdmodp
  • 再算 m 2 = c d   m o d   q m_2 = c^d \bmod q m2=cdmodq
  • 最后用 CRT 合并出 m   m o d   n m \bmod n mmodn

因为 p p p 和 q q q 比 n n n 小很多(大约是 n n n 的平方根位数),所以模幂运算快很多。实际中用 CRT 可以让 RSA 解密快大约 4 4 4 倍。

应用 2:秘密共享

可以把一个秘密数 s s s 拆分成多个"份额",每个份额是 s   m o d   m i s \bmod m_i smodmi( m i m_i mi 是不同的模数)。只要拿到足够多的份额(使得模数乘积大于 s s s),就能用 CRT 还原出 s s s。这就是一种简单的秘密共享方案。

应用 3:加速模运算

很多密码学运算都可以用 CRT 拆成小模数的运算,再合并结果,从而大幅提升效率。

💡 中国剩余定理告诉我们:在两两互质的模数下,一个数和它在各个模数下的余数是"一一对应"的。这意味着我们可以把"大模数的运算"拆成"多个小模数的运算",算完再拼回去------这是密码学中常用的加速技巧。


4. 离散对数问题

4.1 什么是离散对数

从普通对数说起

你肯定学过普通对数:如果 a x = b a^x = b ax=b,那么 x = log ⁡ a b x = \log_a b x=logab。

比如 2 3 = 8 2^3 = 8 23=8,所以 log ⁡ 2 8 = 3 \log_2 8 = 3 log28=3。

在实数范围内,求对数是件很容易的事------计算器按一下就出来了。但如果把运算放在模运算的世界里,事情就完全不一样了。


离散对数的定义

给定一个质数 p p p,一个整数 g g g(叫做"生成元"或"底数"),以及另一个整数 h h h,离散对数问题就是:

找到整数 x x x,使得 g x ≡ h ( m o d p ) g^x \equiv h \pmod{p} gx≡h(modp)

这个 x x x 就叫做 h h h 在模 p p p 下以 g g g 为底的离散对数 ,记作 x = log ⁡ g h ( m o d p ) x = \log_g h \pmod{p} x=loggh(modp)。

听起来和普通对数差不多?但加上了"模 p p p"之后,难度天差地别。


举个小例子建立直觉

取 p = 7 p = 7 p=7(质数), g = 3 g = 3 g=3。

我们来算一下 3 的各次幂模 7 的结果:

x x x 3 x 3^x 3x 3 x   m o d   7 3^x \bmod 7 3xmod7
0 1 1
1 3 3
2 9 2
3 27 6
4 81 4
5 243 5
6 729 1

看到了吗?从 x = 0 x=0 x=0 到 x = 5 x=5 x=5,结果遍历了 1 到 6 所有非零余数,到 x = 6 x=6 x=6 又回到了 1,开始循环。

现在问你: 3 x ≡ 5 ( m o d 7 ) 3^x \equiv 5 \pmod{7} 3x≡5(mod7),求 x x x 是多少?

查表就知道, x = 5 x=5 x=5。这就是一个离散对数问题。

数字小的时候,你可以一个个试出来。但如果 p p p 是一个 2048 位的大质数呢?


正向 vs 反向

离散对数问题之所以重要,是因为它的两个方向难度完全不对等:

  • 正向计算(模幂) :给你 g g g 和 x x x,算 g x   m o d   p g^x \bmod p gxmodp → 用快速幂, O ( log ⁡ x ) O(\log x) O(logx) 时间,毫秒级完成
  • 反向求解(离散对数) :给你 g g g 和 h = g x   m o d   p h = g^x \bmod p h=gxmodp,求 x x x → 目前没有高效算法,对足够大的 p p p 来说, practically impossible

这种"正向容易、反向困难"的函数,在密码学中叫做单向函数(One-way Function),而离散对数问题就是最重要的单向函数之一。

💡 一句话理解:离散对数就是"模运算世界里的对数",但和实数对数不同,离散对数非常难算------这正是密码学需要的。


4.2 离散对数的"难解性"

离散对数到底有多难?为什么说它是"难解"的?我们来具体看看。

暴力求解有多慢?

最朴素的方法就是"试":从 x = 0 x=0 x=0 开始,一个个算 g x   m o d   p g^x \bmod p gxmodp,直到等于 h h h 为止。

最坏情况下,你要试 p − 1 p-1 p−1 次才能找到答案。时间复杂度是 O ( p ) O(p) O(p)。

如果 p p p 是一个 2048 位的质数, p p p 大约是 2 2048 2^{2048} 22048,也就是大约 10 616 10^{616} 10616。

10 616 10^{616} 10616 次运算是什么概念?

  • 假设你的电脑每秒能算 10 12 10^{12} 1012 次(一万亿次)
  • 一年大约 3 × 10 7 3 \times 10^7 3×107 秒
  • 一年能算 3 × 10 19 3 \times 10^{19} 3×1019 次
  • 算完需要大约 3 × 10 596 3 \times 10^{596} 3×10596 年

而宇宙的年龄才 10 10 10^{10} 1010 年左右......暴力破解等于不可能。


有没有更快的算法?

当然,暴力法是最慢的。实际中有一些更快的算法,比如:

  • Baby-step Giant-step(小步大步法) :时间复杂度 O ( p ) O(\sqrt{p}) O(p )
  • Pohlig-Hellman 算法 :当 p − 1 p-1 p−1 的质因数都很小时很快,但对精心选择的 p p p 无效
  • Index Calculus(指标演算法):目前对一般情况最快的算法,亚指数时间,但仍然远慢于多项式时间

听起来 O ( p ) O(\sqrt{p}) O(p ) 比 O ( p ) O(p) O(p) 快多了?我们来算一下:

还是 2048 位的 p p p, p ≈ 2 1024 ≈ 10 308 \sqrt{p} \approx 2^{1024} \approx 10^{308} p ≈21024≈10308。

10 308 10^{308} 10308 次运算,还是要算到天荒地老。

⚠️ 注意:这里说的"难解"是指对经典计算机而言。如果有了足够大的量子计算机,Shor 算法可以在多项式时间内求解离散对数问题------这也是为什么密码学界在研究后量子密码。


为什么模幂这么快,离散对数这么慢?

因为模幂运算有"分治"的结构:你可以把指数折半,底数平方,一步步缩小问题规模(就是快速幂的思路)。

但离散对数没有这种"分治"结构------你没法把"找 x x x 使得 g x = h g^x = h gx=h"这个问题拆成两个更小的同类问题。至少目前人类还没找到这样的方法。

这就像:

  • 把一块玻璃打碎很容易(正向)
  • 把碎玻璃拼回原样很难(反向)

离散对数问题就是数学世界里的"打碎玻璃"。


生成元的条件

前面的例子中, g = 3 g=3 g=3 在模 7 下能遍历 1 到 6 所有非零余数。这样的 g g g 叫做本原根 (Primitive Root)或生成元

不是所有数都能当生成元。比如 p = 7 p=7 p=7,取 g = 2 g=2 g=2:

  • 2 1 ≡ 2 , 2 2 ≡ 4 , 2 3 ≡ 1 , 2 4 ≡ 2... 2^1 \equiv 2,\ 2^2 \equiv 4,\ 2^3 \equiv 1,\ 2^4 \equiv 2... 21≡2, 22≡4, 23≡1, 24≡2...
  • 只能遍历 {1, 2, 4},只有 3 个元素,循环周期是 3

生成元的循环周期是 p − 1 p-1 p−1,也就是最长的可能周期。密码学中通常用生成元,这样离散对数的搜索空间才最大。

💡 离散对数的"难解性"是现代公钥密码学的安全性基石之一。Diffie-Hellman、ElGamal、DSA、ECC......这些算法的安全性全部建立在"离散对数很难算"这个假设上。


4.3 离散对数在密码学中的应用

离散对数问题不是纯粹的数学玩具------它是公钥密码学的两大基石之一(另一个是大整数分解)。现代互联网的很多安全机制,背后都有离散对数的身影。

这里先简单列几个核心应用,后面讲具体密码算法时再展开。

应用 1:Diffie-Hellman 密钥交换

公钥密码学的开山之作。两个人在完全不安全的信道上,仅凭互相交换几个公开数字,就能协商出只有他俩知道的共享密钥。

核心逻辑:双方各自保密一个私钥,把"底数的私钥次方"发给对方,然后用对方的公钥再做一次幂运算,两边得到相同的结果。攻击者截获了中间传输的所有数据,却因为算不出离散对数而还原不了密钥。

应用 2:ElGamal 加密算法

基于离散对数的公钥加密方案。用公钥加密,用私钥解密,安全性建立在"知道公钥却求不出私钥"的离散对数难题上。

应用 3:数字签名算法(DSA)

美国国家标准的数字签名算法,用于身份认证和防抵赖。签名者用私钥签名,验证者用公钥验证,同样基于离散对数问题。

应用 4:椭圆曲线密码学(ECC)

本质上也是离散对数问题,只不过运算场景从"模 p 乘法群"换成了"椭圆曲线点群"。

ECC 的优势是效率更高:相同安全级别下,ECC 的密钥比 RSA 短得多。比如 256 位 ECC 的安全性,大约相当于 3072 位 RSA。现在的 HTTPS、区块链、移动设备加密,很多都用 ECC。


核心思想

所有这些应用,本质上都在利用同一件事:

正向运算(模幂 / 点乘)很快,反向运算(求离散对数)很难。

把秘密藏在"指数"里,公开"幂"的结果------攻击者拿到公开值,却没法反推出指数,因为离散对数太难算了。

这就是基于离散对数的密码学的核心逻辑。具体算法的细节,我们后面讲到对应章节再深入。

💡 一句话总结:离散对数撑起了半壁公钥密码学,从密钥交换到数字签名,从 HTTPS 到区块链,背后都是这个数学难题在守护安全。


5. 抽象代数基础

5.1 群、环、域的概念

为什么要学抽象代数?

你可能会问:我学密码学,为什么要搞懂群环域这些抽象的东西?

答案很简单:密码学的运算都是在某个代数结构里进行的。模运算有模运算的规则,椭圆曲线有椭圆曲线的规则------这些规则不是随便定的,它们都满足某种代数结构的性质。

理解了群、环、域,你就能从更高的视角看清楚:为什么这些运算能构成加密体系?为什么逆元一定存在?为什么解密一定能还原?

用程序员的话说:群、环、域就像是接口------它们定义了一套运算规则和性质。只要某个集合满足这些规则,你就能用这套接口上的所有结论和算法。


群(Group)

群是最基础的代数结构。一个群 ( G , ⋅ ) (G, \cdot) (G,⋅) 由一个集合 G G G 和一个二元运算 ⋅ \cdot ⋅ 组成,满足以下四条公理:

公理 含义
封闭性 对任意 a , b ∈ G a, b \in G a,b∈G,都有 a ⋅ b ∈ G a \cdot b \in G a⋅b∈G
结合律 对任意 a , b , c ∈ G a, b, c \in G a,b,c∈G,都有 ( a ⋅ b ) ⋅ c = a ⋅ ( b ⋅ c ) (a \cdot b) \cdot c = a \cdot (b \cdot c) (a⋅b)⋅c=a⋅(b⋅c)
单位元 存在 e ∈ G e \in G e∈G,使得对任意 a ∈ G a \in G a∈G,都有 e ⋅ a = a ⋅ e = a e \cdot a = a \cdot e = a e⋅a=a⋅e=a
逆元 对任意 a ∈ G a \in G a∈G,存在 a − 1 ∈ G a^{-1} \in G a−1∈G,使得 a ⋅ a − 1 = a − 1 ⋅ a = e a \cdot a^{-1} = a^{-1} \cdot a = e a⋅a−1=a−1⋅a=e

如果还满足交换律 (对任意 a , b ∈ G a, b \in G a,b∈G,都有 a ⋅ b = b ⋅ a a \cdot b = b \cdot a a⋅b=b⋅a),那就叫阿贝尔群(Abelian Group)或交换群。

举几个群的例子

例 1:整数加法群 ( Z , + ) (\mathbb{Z}, +) (Z,+)

  • 封闭性:整数加整数还是整数 ✓
  • 结合律:加法结合律成立 ✓
  • 单位元:0,因为 a + 0 = a a + 0 = a a+0=a ✓
  • 逆元: − a -a −a,因为 a + ( − a ) = 0 a + (-a) = 0 a+(−a)=0 ✓
  • 交换律:加法交换律成立 ✓
  • 这是一个阿贝尔群

例 2:模 7 乘法群 ( Z 7 ∗ , × ) (\mathbb{Z}_7^*, \times) (Z7∗,×)

  • 集合是 {1, 2, 3, 4, 5, 6}(所有和 7 互质的数)
  • 封闭性:两个数相乘 mod 7,结果还是 1~6 之间 ✓
  • 结合律:乘法结合律成立 ✓
  • 单位元:1 ✓
  • 逆元:每个数都有逆元(比如 3 的逆元是 5) ✓
  • 交换律:乘法交换律成立 ✓
  • 这也是一个阿贝尔群

例 3:2×2 可逆矩阵乘法群

  • 封闭性:可逆矩阵相乘还是可逆矩阵 ✓
  • 结合律:矩阵乘法结合律成立 ✓
  • 单位元:单位矩阵 ✓
  • 逆元:逆矩阵 ✓
  • 交换律:不成立(矩阵乘法一般不可交换)
  • 这是一个群,但不是阿贝尔群

环(Ring)

环比群多了一个运算。一个环 ( R , + , × ) (R, +, \times) (R,+,×) 有两个二元运算:加法和乘法,满足:

  1. ( R , + ) (R, +) (R,+) 是一个阿贝尔群(加法交换群)
  2. 乘法满足封闭性和结合律
  3. 分配律 :对任意 a , b , c ∈ R a, b, c \in R a,b,c∈R,都有
    • a × ( b + c ) = a × b + a × c a \times (b + c) = a \times b + a \times c a×(b+c)=a×b+a×c(左分配)
    • ( a + b ) × c = a × c + b × c (a + b) \times c = a \times c + b \times c (a+b)×c=a×c+b×c(右分配)

简单说:环就是"加法是交换群,乘法是半群(只有封闭性和结合律),加法和乘法之间满足分配律"。

环的乘法不要求有单位元,也不要求有逆元,甚至不要求交换。

环的例子

例 1:整数环 ( Z , + , × ) (\mathbb{Z}, +, \times) (Z,+,×)

  • 加法是交换群 ✓
  • 乘法封闭、结合律成立 ✓
  • 分配律成立 ✓
  • 这是一个有单位元(1)的交换环

例 2:n×n 矩阵环

  • 加法是交换群 ✓
  • 乘法封闭、结合律成立 ✓
  • 分配律成立 ✓
  • 有单位元(单位矩阵),但乘法不交换
  • 这是一个有单位元的非交换环

域(Field)

域是要求最严格的代数结构。一个域 ( F , + , × ) (F, +, \times) (F,+,×) 有两个二元运算,满足:

  1. ( F , + ) (F, +) (F,+) 是一个阿贝尔群(加法交换群)
  2. ( F ∖ { 0 } , × ) (F \setminus \{0\}, \times) (F∖{0},×) 是一个阿贝尔群(非零元乘法交换群)
  3. 乘法对加法满足分配律

简单说:域里加减乘除都能做(除以零除外),而且运算都满足交换律、结合律、分配律------和你熟悉的有理数、实数运算规则一模一样。

域的例子

例 1:有理数域 Q \mathbb{Q} Q

例 2:实数域 R \mathbb{R} R

例 3:复数域 C \mathbb{C} C

这些都是无限域,元素有无穷多个。

但密码学里用的大多是有限域(Finite Field),也叫伽罗瓦域(Galois Field,简称 GF)------元素个数有限的域。


三者的关系

从群到环到域,要求越来越严格:

域 ⊂ 环 ⊂ 群 \text{域} \subset \text{环} \subset \text{群} 域⊂环⊂群

  • 群:只有一个运算,要求最少
  • 环:有两个运算,乘法要求不高
  • 域:有两个运算,加减乘除都齐全,要求最高

💡 一句话理解:群是"有一个运算且性质良好"的集合,环是"有两个运算但乘法要求不高"的集合,域是"加减乘除都能做"的集合。密码学的核心运算大多是在有限域上进行的。


5.2 有限域 GF§

什么是有限域?

有限域,顾名思义,就是元素个数有限的域。

有限域有个很重要的性质:有限域的元素个数一定是某个质数的幂 ,也就是 p n p^n pn,其中 p p p 是质数, n n n 是正整数。

当 n = 1 n=1 n=1 时,就是 G F ( p ) GF(p) GF(p),叫做素域(Prime Field)------最简单的有限域。

GF§ 的定义

G F ( p ) GF(p) GF(p)(读作"伽罗瓦域 p")的元素是 0 到 p-1 的整数:

G F ( p ) = { 0 , 1 , 2 , . . . , p − 1 } GF(p) = \{0, 1, 2, ..., p-1\} GF(p)={0,1,2,...,p−1}

上面的运算就是模 p 的加法和乘法:

  • 加法: a + b ( m o d p ) a + b \pmod{p} a+b(modp)
  • 乘法: a × b ( m o d p ) a \times b \pmod{p} a×b(modp)

为什么 p 必须是质数?

这是个关键问题。因为域要求每个非零元都有乘法逆元。

如果 p 是质数,那么 1 到 p-1 的每个数都和 p 互质,根据之前讲的,每个数都有模 p 下的逆元------满足域的要求。

如果 p 不是质数呢?比如 p = 4:

  • 元素是 {0, 1, 2, 3}
  • 2 有没有逆元?找 x 使得 2x ≡ 1 mod 4
  • 2×1=2, 2×2=0, 2×3=2... 永远得不到 1
  • 2 没有逆元,不满足域的要求

所以 p 必须是质数,才能保证每个非零元都有逆元,才能构成域。

具体例子:GF(5)

我们来完整看一下 GF(5) 的结构。

元素:{0, 1, 2, 3, 4}

加法表(模 5 加法):

+ 0 1 2 3 4
0 0 1 2 3 4
1 1 2 3 4 0
2 2 3 4 0 1
3 3 4 0 1 2
4 4 0 1 2 3

乘法表(模 5 乘法):

× 0 1 2 3 4
0 0 0 0 0 0
1 0 1 2 3 4
2 0 2 4 1 3
3 0 3 1 4 2
4 0 4 3 2 1

验证一下逆元:

  • 1 的逆元是 1(1×1=1)
  • 2 的逆元是 3(2×3=6≡1 mod 5)
  • 3 的逆元是 2(3×2=6≡1 mod 5)
  • 4 的逆元是 4(4×4=16≡1 mod 5)

每个非零元都有逆元 ✓


生成元(本原元)

在 GF§ 的乘法群里,存在一些特殊的元素,叫做生成元 (Generator)或本原元(Primitive Element)。

生成元 g 的特点是:g 的各次幂能遍历所有非零元素。

比如在 GF(5) 中,2 是生成元:

  • 2¹ = 2
  • 2² = 4
  • 2³ = 8 ≡ 3
  • 2⁴ = 16 ≡ 1

正好遍历了 {1, 2, 3, 4} 所有非零元素。

而 4 就不是生成元:

  • 4¹ = 4
  • 4² = 16 ≡ 1
  • 4³ = 4
  • ...

只能遍历 {1, 4},周期只有 2。

生成元在密码学中很重要------Diffie-Hellman、ElGamal 这些算法的底数 g 用的就是生成元,这样离散对数的搜索空间才最大。

💡 GF§ 是密码学中最常用的有限域之一。RSA、Diffie-Hellman、ElGamal、DSA......这些经典公钥算法的运算全都是在 GF§ 上进行的。


5.3 有限域 GF(2ⁿ)

为什么需要 GF(2ⁿ)?

GF§ 虽然好用,但计算机是二进制的,处理 2 的幂次方的数特别方便。所以密码学中还有另一类非常重要的有限域:GF(2ⁿ)------特征为 2 的有限域。

GF(2ⁿ) 的元素个数是 2ⁿ 个,正好对应 n 位二进制数,非常适合计算机处理。AES 加密算法的核心运算就是在 GF(2⁸) 上进行的。

GF(2):最基础的二元域

先从最小的 GF(2ⁿ) 说起------GF(2),也就是 n=1 的情况。

GF(2) 只有两个元素:{0, 1}

运算规则:

加法(就是异或 XOR):

+ 0 1
0 0 1
1 1 0

乘法(就是与 AND):

× 0 1
0 0 0
1 0 1

你看,GF(2) 的加法就是异或,乘法就是按位与------计算机原生支持,效率极高。

验证一下 GF(2) 是不是域:

  • 加法交换群:单位元 0,每个元素的逆元是自己(因为 0+0=0,1+1=0)✓
  • 非零元乘法交换群:只有 1 一个元素,1×1=1,单位元 1,逆元 1 ✓
  • 分配律:成立 ✓

确实是域。


GF(2ⁿ) 的多项式表示

GF(2ⁿ) 中的每个元素,可以表示为一个次数小于 n 的多项式,系数在 GF(2) 中(也就是系数只能是 0 或 1)。

比如 GF(2³) = GF(8),每个元素是次数小于 3 的多项式:

二进制 多项式 十进制
000 0 0
001 1 1
010 x 2
011 x + 1 3
100 4
101 x² + 1 5
110 x² + x 6
111 x² + x + 1 7

一个元素有三种表示方式:二进制数、多项式、十进制整数,本质上是一样的。

GF(2ⁿ) 上的加法

加法非常简单:对应系数相加(GF(2) 加法,也就是异或)

因为系数只能是 0 或 1,加起来就是异或。

用二进制表示的话,加法就是按位异或

例子(GF(8) 中):

  • (x² + 1) + (x + 1) = x² + x + (1+1) = x² + x + 0 = x² + x
  • 二进制:101 + 011 = 110
  • 十进制:5 + 3 = 6

验证:5 XOR 3 = 6 ✓

注意:GF(2ⁿ) 中,每个元素的加法逆元就是它自己!因为 a + a = 0(每一位都是 1+1=0 或 0+0=0)。

GF(2ⁿ) 上的乘法

乘法稍微复杂一点。两个多项式相乘,然后模一个 n 次的不可约多项式

什么是不可约多项式?就是在 GF(2) 上不能再分解因式的多项式,类似于整数中的质数。

为什么要模不可约多项式?和 GF§ 中 p 必须是质数的道理一样------为了保证每个非零元都有逆元,从而构成域。


完整手算例子:GF(8) 乘法

我们用 GF(2³) = GF(8) 来演示,选取不可约多项式 p ( x ) = x 3 + x + 1 p(x) = x^3 + x + 1 p(x)=x3+x+1。

例 1:计算 ( x 2 + 1 ) × ( x + 1 ) (x^2 + 1) \times (x + 1) (x2+1)×(x+1),也就是 5 × 3。

第一步:多项式相乘

( x 2 + 1 ) ( x + 1 ) = x 3 + x 2 + x + 1 (x^2 + 1)(x + 1) = x^3 + x^2 + x + 1 (x2+1)(x+1)=x3+x2+x+1

第二步:模不可约多项式 x 3 + x + 1 x^3 + x + 1 x3+x+1

因为 x 3 ≡ x + 1 ( m o d p ( x ) ) x^3 \equiv x + 1 \pmod{p(x)} x3≡x+1(modp(x))(从 x 3 + x + 1 ≡ 0 x^3 + x + 1 \equiv 0 x3+x+1≡0 移项得到),把 x 3 x^3 x3 替换掉:

x 3 + x 2 + x + 1 ≡ ( x + 1 ) + x 2 + x + 1 = x 2 + 2 x + 2 = x 2 x^3 + x^2 + x + 1 \equiv (x + 1) + x^2 + x + 1 = x^2 + 2x + 2 = x^2 x3+x2+x+1≡(x+1)+x2+x+1=x2+2x+2=x2

(注意系数是 GF(2) 的,2 ≡ 0)

所以结果是 x 2 x^2 x2,也就是二进制 100,十进制 4。

验证一下:5 × 3 = 15,在 GF(8) 中结果是 4?我们换个方式验证:

  • 4 × 3 = 12,按同样方法算应该等于 5(因为如果 5×3=4,那么 4×3⁻¹ 应该等于 5)
  • 暂时先相信推导过程,后面会验证乘法群的性质。

例 2:计算 x × x²,也就是 2 × 4。

  • x × x² = x³ ≡ x + 1
  • 结果是 x + 1,也就是 011,十进制 3

生成元验证

还是用 GF(8),不可约多项式 x³ + x + 1。

我们看看 x(也就是 2)是不是生成元:

  • x¹ = x = 2
  • x² = x² = 4
  • x³ = x + 1 = 3
  • x⁴ = x × x³ = x(x + 1) = x² + x = 6
  • x⁵ = x × x⁴ = x(x² + x) = x³ + x² = (x+1) + x² = x² + x + 1 = 7
  • x⁶ = x × x⁵ = x(x² + x + 1) = x³ + x² + x = (x+1) + x² + x = x² + 1 = 5
  • x⁷ = x × x⁶ = x(x² + 1) = x³ + x = (x+1) + x = 1

正好遍历了 1 到 7 所有非零元素!x 确实是生成元。

这也验证了乘法确实构成一个群------封闭性、单位元、逆元都有了。


AES 中的 GF(2⁸)

实际密码学中最常用的是 GF(2⁸),也就是 256 个元素的有限域。AES 加密算法的 S 盒就是基于 GF(2⁸) 上的乘法逆元构造的。

AES 使用的不可约多项式是:

x 8 + x 4 + x 3 + x + 1 x^8 + x^4 + x^3 + x + 1 x8+x4+x3+x+1

每个字节(8 位)就是 GF(2⁸) 中的一个元素,字节的异或就是 GF(2⁸) 加法,字节乘法就是 GF(2⁸) 乘法------非常自然。

💡 GF(2ⁿ) 是计算机最擅长处理的有限域。加法就是异或,硬件一秒钟能做几十亿次;乘法虽然复杂一点,但也有高效的硬件实现。对称密码、哈希函数、纠错码......很多地方都能看到 GF(2ⁿ) 的身影。


5.4 有限域在密码学中的作用

为什么密码学偏爱有限域?

简单说,有限域就是一个"数字游乐场"------里面的数字是有限的,但加减乘除都能玩,而且结果永远不会跑出这个圈子。

密码学选它当数学基础,主要因为三个好处:

1. 数字不会越变越大(不会溢出)

计算机存数据的空间是有限的,不能无限增长。

普通整数乘法:2 × 3 = 6,再乘 5 = 30......越乘越大,最后存不下。

有限域里的运算就不一样了------不管你怎么加、怎么乘,结果永远在这个有限集合里,位数固定,存储空间可控,效率稳定。

2. 四则运算齐全(想怎么算怎么算)

有限域里加减乘除都能用,而且运算规则和你学过的实数差不多(交换律、结合律、分配律都成立)。

这意味着程序员可以在上面构建各种复杂算法,不用担心"这个运算合不合法"的问题。

3. 藏着难解的数学题(保证安全)

有限域上有个叫"离散对数问题"的东西,公认很难算。正因为难,密码算法才安全------黑客想破解,就得解这道难题,而目前还没有快速解法。


具体用在哪些地方?

应用 1:对称加密(AES 等)

你可能听过 AES 加密,它的核心运算全都是在 GF(2⁸) 上进行的。

不用纠结这个符号是什么意思,记住一点就行:一个字节(8位)正好对应 GF(2⁸) 里的一个元素,硬件实现起来特别高效。

不只是 AES,很多对称加密算法(SM4、Camellia 等)都用到了类似的思路。

应用 2:公钥加密(RSA、DH 等)

RSA、Diffie-Hellman 密钥交换这些经典公钥算法,都是在 GF§ 上玩的。

  • RSA 加密解密:本质是"模幂运算"(一个数的多少次方,再取模)
  • DH 密钥交换:两个人各自算一个数,最后能得到相同的密钥

这些算法的安全性,全靠"离散对数很难算"这个假设撑着。

应用 3:椭圆曲线加密(ECC)

椭圆曲线密码学(ECC)现在越来越火,因为它用更短的密钥就能达到同样的安全级别

虽然不是直接在有限域的乘法群上,但椭圆曲线本身就是定义在有限域上的。曲线上的点构成一个"群",这个群上的离散对数问题比 GF§ 上的更难解------所以更安全、更高效。

应用 4:纠错码

CD、DVD、二维码、深空通信......这些地方都用到了纠错码(比如 Reed-Solomon 码),背后的数学也是有限域。

简单说就是:数据传输时可能出错,纠错码能帮你把错的地方找出来、修正过来。

应用 5:哈希函数

一些哈希函数的设计也用到了有限域的思想,比如用线性变换来"打乱"数据,让输出更均匀、更难预测。


一句话总结

有限域是密码学的"运算舞台"。

所有加密、解密、签名、验证......这些操作都是在某个有限域上进行的。它提供了:

  • 封闭的运算空间(不会溢出)
  • 完整的代数结构(加减乘除都能用)
  • 难解的数学问题(保证安全)

理解了有限域,你就理解了密码算法为什么这么设计、为什么能工作、为什么安全。

🔑 一句话记住:群环域是抽象代数的三层结构,有限域是密码学的运算舞台。从对称密码到公钥密码,从 AES 到 ECC,背后都有它的身影。


相关推荐
QN1幻化引擎3 小时前
Dalin L — 我造了一门支持中文编程的语言,完整移植到 Rust 了
人工智能·算法·机器学习
Rainy Blue8833 小时前
C转C++速成
c语言·c++·算法
珠***格3 小时前
XGF10-Z-4典型接入方式解析:10千伏专变用户380伏多点分布式光伏如何并网?
网络·人工智能·分布式·安全·边缘计算
txzrxz3 小时前
二分图详解
数据结构·c++·算法·图论·深度优先搜索·二分图染色
盟接之桥4 小时前
跨越数字鸿沟,重塑全球信任:盟接之桥EDI如何以长期主义赋能中国制造出海
网络·人工智能·安全·汽车·制造
一只小菜鸡..4 小时前
Stanford CS144 学习笔记 (四):网络拥塞控制与 AIMD 算法
网络·笔记·学习
gaosushexiangji4 小时前
数字图像相关DIC系统在结构振动模态测量中的应用
算法
KaMeidebaby4 小时前
卡梅德生物技术快报|如何制备单克隆抗体:小众禽类靶点单抗制备实操流程:双载体抗原交叉筛选完整工艺记录
人工智能·python·深度学习·算法·机器学习
AFinalStone4 小时前
Android 7系统网络(八)应用API层—ConnectivityManager使用与实战调试
android·网络
北域码匠5 小时前
Karatsuba乘法超详细解析(原理+流程+性能分析+纯原生C#无第三方库完整实现)
算法·c#·大数乘法·高精度计算·数据结构与算法·分治算法·karatsuba 乘法