Rabin 加密算法是一种基于大数分解问题的非对称加密算法。它由 Michael O. Rabin 在 1979 年提出,具有与 RSA 类似的安全性,但在某些方面具有更高的效率。

在本章中,我们将深入分析 Rabin 加密算法的核心原理和实现细节。我们将从 Rabin 的数学基础开始,逐步揭示其加密和解密过程中的关键步骤。通过对 Rabin 算法的详细解析,你将能够理解它是如何利用大数分解的困难性来确保数据安全的。

Rabin 加密过程 😁

Rabin 加密算法的加密过程与 RSA 类似,甚至我们可以将其视为 RSA 的一个特例。

既然前面已经介绍了 RSA 的加密过程,我们直接点明 Rabin 加密算法的加密不同之处:

在选择公钥时,Rabin 加密算法选择 $e = 2$,而不是 RSA 中常用的 $e = 65537$。

因此,Rabin 的加密公式如下:

其中 $n = p \times q$,$p$ 和 $q$ 是两个大素数。

Rabin 加密数学原理 😇

Rabin 加密算法的安全性同样基于大数分解的困难性。对于一个足够大的 $n$,分解 $n$ 成其素因数 $p$ 和 $q$ 是非常困难的。这意味着攻击者无法轻易地从公钥 $(2, n)$ 中推断出私钥,从而无法解密密文。

Rabin 加密算法的解密过程相较于 RSA 更为复杂,因为平方根的计算比指数运算更为困难。具体来说,解密过程需要计算 $C$ 的平方根,这涉及到求解二次剩余问题。

而且,Rabin 加密算法的解密结果经过中国剩余定理(CRT)合并后有四个不同的平方根,因此需要额外的信息来确定正确的解密结果,当然一般情况下直接将所有结果都输出就行。

我们可以直接使用 Tonelli-Shanks 算法 来计算平方根,或者在选择 $p$ 和 $q$ 时满足 $p \equiv 3 \mod 4$ 和 $q \equiv 3 \mod 4$,这样可以简化平方根的计算过程。

Rabin 加密算法实现 🥰

下面是一个简单的 Rabin 加密算法的 Python 实现示例:

from sage.all import random_prime, crt

#  Bob 密钥生成阶段
bits = 256
upper = pow(2, bits)
lower = pow(2, bits - 1)
# 生成满足 p ≡ 3 mod 4, q ≡ 3 mod 4 的素数,简化平方根计算
while True:
    p = random_prime(upper, lbound=lower)
    if p % 4 == 3:
        break
while True:
    q = random_prime(upper, lbound=lower)
    if q % 4 == 3 and q != p:
        break
n = p * q
e = 2
public_key = (e, n)
private_key = (p, q)

# Alice 加密阶段
M = "hello_Bob"
long_M = int.from_bytes(M.encode(), 'big')
C = pow(long_M, e, n)

#  Bob 解密阶段 
# 1. 计算模p和模q下的所有平方根(共2×2=4种组合)
# 因为 p, q ≡ 3 mod 4,直接用公式计算平方根
mp1 = pow(C, (p + 1) // 4, p)
mp2 = p - mp1
mq1 = pow(C, (q + 1) // 4, q)
mq2 = q - mq1
print(f"模p的平方根: {mp1}, {mp2}")
print(f"模q的平方根: {mq1}, {mq2}")

# 2. 用CRT合并4种组合,得到4个候选明文
candidates = [
    crt([mp1, mq1], [p, q]),
    crt([mp1, mq2], [p, q]),
    crt([mp2, mq1], [p, q]),
    crt([mp2, mq2], [p, q])
]

# 3. 遍历候选值,还原出正确的明文
for candidate in candidates:
    # 转换为 Python 原生 int 后再转 bytes
    cand_int = int(candidate)
    candidate_bytes = cand_int.to_bytes((cand_int.bit_length() + 7) // 8, byteorder='big')
    try:
        # 尝试用 utf-8 解码
        flag = candidate_bytes.decode()
        print(flag)
    except UnicodeDecodeError:
        # 解码失败时跳过这个候选值
        continue

实际上由于 sage 中的整数类型和 python 中的整数类型不同,所以在非必要情况下不要纯使用sage函数进行算法实现,可以多使用python的密码学库使用。
以下是一个实现:

from Crypto.Util.number import getPrime, inverse, long_to_bytes, bytes_to_long

# Bob 密钥生成阶段
p = getPrime(256)
while p % 4 != 3:
    p = getPrime(256)
q = getPrime(256)
while q % 4 != 3 or q == p:
    q = getPrime(256)
n = p * q
e = 2
public_key = (e, n)
private_key = (p, q)

# Alice 加密阶段
M = "hello_Bob"
long_M = bytes_to_long(M.encode())
# 重要:确保明文的平方小于n,否则加密会出错
C = pow(long_M, e, n)

# Bob 解密阶段
mp1 = pow(C, (p + 1) // 4, p)
mp2 = (p - mp1) % p
mq1 = pow(C, (q + 1) // 4, q)
mq2 = (q - mq1) % q

# 修复CRT计算逻辑
inv_q_mod_p = inverse(q, p)  # q在模p下的逆元
inv_p_mod_q = inverse(p, q)  # p在模q下的逆元

candidates = [
    (mp1 * q * inv_q_mod_p + mq1 * p * inv_p_mod_q) % n,
    (mp1 * q * inv_q_mod_p + mq2 * p * inv_p_mod_q) % n,
    (mp2 * q * inv_q_mod_p + mq1 * p * inv_p_mod_q) % n,
    (mp2 * q * inv_q_mod_p + mq2 * p * inv_p_mod_q) % n,
]

# 遍历候选值,还原出正确的明文
flag = None  # 初始化flag
for idx, candidate in enumerate(candidates):
    try:
        # 尝试解码
        decoded_str = long_to_bytes(candidate).decode()
        # 只有成功解码的才赋值给flag
        flag = decoded_str
        print(flag)
    except (UnicodeDecodeError, ValueError):
        continue

上一章:加解密模式分析 - RSA 加密算法 👈
下一章:加解密模式分析 - ElGamal 加密算法 👈
回到开始:关于我 👈

相关链接:
密码学数学基础 - 同余关系与模运算 👈
密码学数学基础 - 二次剩余 👈
密码学算法 - 中国剩余定理CRT 👈
密码学算法 - Tonelli-Shanks算法 👈