字符、字节与数字的相互转换

在诸如 RSA 的现代密码系统中,加密和解密的底层都是纯粹的数学运算。这就要求我们在加密前,必须将由字符组成的“文本消息”转换为“数字”;解密后,再将“数字”还原回“文本消息”。

以下是实现这些转换的核心流程与 Python 原生/第三方方法总结:

1. 基础转换:字符 ASCII 序数

用于单个字符与其对应的 ASCII 数值之间的转换。

  • chr(整数):ASCII 序数 字符。

    • 示例chr(65) 结果为 'A'
  • ord(字符):字符 ASCII 序数(作用与 chr() 相反)。

    • 示例ord('A') 结果为 65

2. 进阶转换:字节流 十六进制 (Hex)

在密码学中,数据经常以十六进制字符串或原始字节流(Bytes)的形式呈现。

  • bytes.fromhex('十六进制字符串'):十六进制 字节流。

    • 示例bytes.fromhex('48656c6c6f') 结果为 b'Hello'
  • 字节变量.hex():字节流 十六进制(这是字节对象自带的实例方法)。

    • 示例b'Hello'.hex() 结果为 '48656c6c6f'

3. 密码学核心链路:消息如何变成大整数?

为了满足数学运算的需求,标准的转换流程通常如下:

  1. 提取序数字节:将文本消息拆解为对应的字节数值。

  2. 转换为十六进制:将这些数值转化为十六进制表示。

  3. 拼接:将所有十六进制数值无缝连接在一起。

  4. 视为大数字:这个长长的十六进制串,既可以直接作为 Base-16(十六进制)数字参与运算,也可以转换为 Base-10(十进制)大整数。

4. 终极武器:PyCryptodome 库

为了省去手动拼接十六进制的繁琐步骤,Python 提供了强大的密码学专用库,可以一步到位实现字节流与大整数的互转。

  • 环境准备

    • 安装:pip install pycryptodome

    • 导入:from Crypto.Util.number import *

  • 核心方法

    • bytes_to_long(字节流):字节流 大整数(正向过程,加密前准备)。

    • long_to_bytes(大整数):大整数 字节流(逆向过程,解密后还原)。

XOR (异或运算) 及其核心性质

1. 核心概念与真值表

XOR(Exclusive OR)是一种按位运算符(Bitwise Operator)。它的核心逻辑非常简单:相同为 0,不同为 1

  • 符号表示:在密码学教材和数学公式中通常记作 ;在编程语言和 CTF 题目中通常使用脱字符 ^

XOR 真值表

AB输出 (A B)
000
011
101
110

XOR logic gate truth table, AI generated

2. 多字节与数据类型的 XOR

在实际运算中,XOR 并不局限于单个比特,它可以扩展到更长的数据:

  • 二进制数:逐位进行运算。例如:

  • 整数 (Integers):在底层会先将十进制整数转换为二进制,再逐位异或。

  • 字符串 (Strings):先提取每个字符的 Unicode/ASCII 整数值,然后再进行异或(如上一题中将 label 的每个字符与 13 异或)。

  • 🛠️ 必备工具:Python 的 pwntools 库提供了一个极其方便的 xor() 函数,可以直接对不同类型和长度的数据(如字节流、字符串)进行批量异或。


3. XOR 的四大核心数学性质 (解密关键)

在破解密码系统(尤其是块密码 Block Ciphers)时,理解并灵活运用以下四个性质是解开加密链条的关键:

  1. 交换律 (Commutative): 运算顺序不影响结果。
  2. 结合律 (Associative): 链式运算可以无视括号顺序。
  3. 单位元 (Identity): 任何数与 0 异或,结果不变(相当于“无操作”)。
  4. 自逆性 / 归零律 (Self-Inverse): 这是最重要的性质! 任何数与自身异或,结果必定为 0。这也意味着 XOR 两次相同的密钥就能还原数据

4. 使用PWNTOOLS进行加密

Pwntools 的 xor() 函数是一个非常强大且灵活的工具,专门用于在 CTF 比赛中进行字节级 XOR(异或)操作。它支持字符串、字节串、整数或列表的异或,可以自动处理不同长度的密钥,是处理加密或混淆数据的理想工具。

常用方法与示例:

  1. 单字节/固定密钥异或:

    from pwn import xor
    # 字符与单字节异或
    print(xor(b'hello', 0x55)) 
    # 字符串与字符串异或
    print(xor(b'hello', b'world')) 
    
  2. 自动循环密钥:
    如果密钥比数据短,xor() 会自动重复该密钥。

    # key 'AB' 会被当作 'ABABAB'
    print(xor(b'123456', b'AB')) 
    
  3. 处理整数:

    # 整数与字节串异或
    print(xor(0x1234, b'\x01\x02')) 
    

总结:

  • 功能: 异或操作。
  • 输入: 支持 bytesstrintlist 等。
  • 输出: 通常为 bytes 类型。
  • 优点: 简洁,自动处理数据与密钥长度不一致的情况。

5.进阶:单字节爆破

# 1. 题目给定的十六进制密文  
hex_string = "73626960647f6b206821204f21254f7d694f7624662065622127234f726927756d"  
  
# 2. 将十六进制转换为可操作的字节流 (Bytes)cipher_bytes = bytes.fromhex(hex_string)  
  
# 3. 开始爆破:遍历单字节的所有可能性 (0 到 255)for key in range(256):  
    decrypted_text = ""  
  
    # 将密文的每一个字节都与当前的 key 进行异或,并转回字符  
    for byte in cipher_bytes:  
        decrypted_text += chr(byte ^ key)  
  
    # 4. 自动化筛选:我们已知正确的明文必然包含 "crypto{"if "crypto{" in decrypted_text:  
        print(f"[+] 爆破成功!")  
        print(f" -> 找到正确密钥: {key} (十六进制: {hex(key)})")  
        print(f" -> 最终 Flag: {decrypted_text}")  
        break  # 找到正确答案后,直接终止循环

转轮密钥

key[i % len(key)] 的核心逻辑被称为 轮转密钥循环密钥 机制。它的作用很简单:当需要加密的“明文”长度超过“密钥”长度时,让密钥首尾相连、循环往复地去匹配明文。

最大公约数

核心概念

最大公约数 (GCD / Highest Common Factor) 是指能同时整除两个正整数 的最大整数。

  • 示例计算
    • 对于
      • 的约数:
      • 的约数:
      • 对比可知最大公共约数为 ,因此

互质 (Coprime)

  • 定义:如果两个整数 满足 ,则称 是互质的(Coprime)。
    • 例如: 均为素数(质数),除数只有 1 和自身,故 ,它们互质。
  • 素数与互质的性质
    1. 如果 都是素数,它们必定互质。
    2. 如果 是素数,且 ,那么 必定互质。

思考题解答

题目提问: 考虑 是素数且 的情况,为什么它们不一定互质?

解答:因为 完全有可能是 的倍数。

例如:假设素数 ,而 (满足 )。此时 。因为最大公约数不是 1,所以这种情况下它们不互质


算法实现:欧几里得算法

在密码学和 CTF 中,处理的数字通常非常大,不能用穷举约数的方法。题目推荐使用欧几里得算法(辗转相除法)

原理:两个正整数 () 的最大公约数等于 除以 的余数 之间的最大公约数。即:

Python 脚本实现

def gcd(a, b):
    # 当余数 b 为 0 时,a 就是最大公约数
    while b != 0:
        a, b = b, a % b
    return a

# 测试题目给的例子
print(f"gcd(12, 8) = {gcd(12, 8)}")

扩展欧几里得算法

核心概念:贝祖等式

普通的 GCD 算法只告诉我们两个数的最大公约数是多少。但扩展欧几里得算法不仅能求出最大公约数,还能同时找到两个整数 ,使得它们满足以下线性组合方程(即贝祖等式):

在实际的密码学计算中,我们往往并不关心最大公约数本身(因为它通常是 1),我们真正渴求的是那两个系数

思考题解答

题目提问:已知 都是素数(prime),你认为 应该是多少?

解答:结果一定是 1。因为不同的素数之间没有任何大于 1 的公共约数,它们必然是互质的。

因此,对于这道题,我们要解的终极方程其实是:

为何它是解密 RSA 的关键?

题目中有一句非常关键的提示:“稍后,当我们学习解密 RSA 密文时,我们将需要这个算法来计算公钥指数的模逆元。”

在 RSA 加密中,为了求出私钥 ,我们需要解一个同余方程:

这个方程在数学上等价于:

仔细对比一下贝祖等式

  • 就是公钥

  • 就是

  • 求出来的系数 就是我们梦寐以求的 RSA 私钥

算法实现与挑战解答

为了在 Python 中实现 eGCD,我们在原本 a, b = b, a % b 的基础上,需要额外跟踪商(quotient),从而逆向推导出

Python 脚本实现

def extended_gcd(a, b):
    # 初始化系数
    old_r, r = a, b
    old_s, s = 1, 0
    old_t, t = 0, 1
 
    while r != 0:
        # 计算商
        quotient = old_r // r
        
        # 核心逻辑:和普通的 gcd 一样更新余数,但同时更新系数
        old_r, r = r, old_r - quotient * r
        old_s, s = s, old_s - quotient * s
        old_t, t = t, old_t - quotient * t
 
    # old_r 是最大公约数
    # old_s 和 old_t 就是我们要求的 u 和 v
    return old_r, old_s, old_t
 
if __name__ == '__main__':
    p = 26513
    q = 32321
    
    gcd_val, u, v = extended_gcd(p, q)
    
    print(f"最大公约数 GCD: {gcd_val}")
    print(f"系数 u: {u}")
    print(f"系数 v: {v}")
    
    # 题目要求提交 u 和 v 中较小的那个数字
    flag = min(u, v)
    print(f"Flag: {flag}")

这是一份为你整理的关于模运算(Modular Arithmetic)的 Obsidian 笔记,你可以直接复制到你的笔记库中。


模运算

基础

核心概念:时钟算术

模运算 是现代密码学(如 RSA、ECC 椭圆曲线加密)的绝对基石。为了防止数字在加密计算过程中无限膨胀,同时利用“单向函数”的数学难度,密码学计算几乎全部都在一个特定的**有限域(模数域)**内进行。

  • 通俗理解:想象一个 12 小时制的表盘。如果现在是下午 4 点,再过 9 个小时,时间不是 13 点,而是凌晨 1 点。

    • 数学表达:
  • 专业定义:同余理论。如果两个整数 满足 除以 的余数等于 除以 的余数,我们就说 在模 下同余,记作

  • 在编程中的映射:同余在代码层面等价于求余数操作。即 a % m == b

思考与应用

在渗透测试或 CTF 实战中,无论是处理古老的凯撒密码、维吉尼亚密码,还是现代的高级公钥密码体制,底层的核心轮转机制都是基于 %(取模运算)来实现数据在指定范围内的循环滚动。

挑战解答

题目要求:计算以下两个同余式中的整数 ,并提交两者中较小的那个数字。

  1. 计算
    • 方程:
    • 逻辑:,余数为
    • 结果:
  2. 计算
    • 方程:
    • 逻辑:通过大数取模运算计算余数。
    • 结果:

最终答案: 题目要求提交 中较小的整数。因为 ,所以最终的 Flag 为: 4

算法实现:Python 求模运算

def solve_mod_arithmetic():
    # 1. 计算 11 mod 6
    x = 11 % 6
    print(f"x 的值为: {x}")
    
    # 2. 计算大数 mod 17
    y = 8146798528947 % 17
    print(f"y 的值为: {y}")
    
    # 3. 获取两者中较小的值作为 Flag
    flag = min(x, y)
    print(f"最终提交的 Flag: {flag}")

if __name__ == '__main__':
    solve_mod_arithmetic()

python模块求模

1. 模幂运算:原生 Python 的 pow()

在 RSA 等加密中,经常需要计算 这样庞大的数字。千万不要写成 (a ** b) % m,这会导致内存爆炸和极长的计算时间。

最简便写法: 使用原生带有三个参数的 pow()

a = 11
b = 3
m = 6

# 极其高效地计算 (11^3) mod 6
result = pow(a, b, m) 
print(result)
2. 求模逆元:pycryptodome 的 inverse()

你刚刚学习了用扩展欧几里得算法(eGCD)去手搓求 。在实际解题中,每次都自己写那个 while 循环太麻烦了。pycryptodome 直接把它封装成了一行代码。

在求 (即求 在模 下的逆元 )时:

最简便写法:

from Crypto.Util.number import inverse

e = 65537
phi = 32321 # 假设的 phi 值

# 直接秒算模逆元(也就是 RSA 的私钥 d)
d = inverse(e, phi) 
print(f"算出的私钥 d 为: {d}")

注:Python 3.8 以上版本,原生的 pow(e, -1, phi) 也能直接求模逆元,效果和 inverse 一样。

模逆元与费马小定理

核心概念:有限域中的“分数”

在普通的数学运算中,如果 ,那么 ,此时 就是 乘法逆元

但在**模运算(有限域)**的世界里,不存在小数和分数,只有整数。

定义:在模 的环境下,如果找一个整数 ,使得它与 相乘后除以 的余数为 1,那么 就是 的模逆元。

  • 数学表达

  • 通俗理解:在整数里找一个充当“分数/倒数”的替身。

示例:求 在模 下的逆元。

因为 ,而 (即 )。

所以,在模 的世界里, 的逆元就是


关键定理:费马小定理

在处理非常大的数字时,不能靠口算或暴力穷举,需要借助数论定理。

  • 定理内容:如果 是一个素数,且 不是 的倍数,那么:

  • 推导逆元公式

    将上式拆解为: 对比逆元的定义式 ,可以直接得出求逆元的绝招: 逆元


挑战解答 (Challenge Solution)

题目要求:计算 ,使得

解题逻辑

这里模数 是素数,直接套用费马小定理推导的公式: ,即求 的值。

计算得出

最终提交答案

9


算法实现

写法 1:原生模幂运算(适用于模数 为素数)

利用费马小定理,结合 Python 极速的原生 pow() 函数:

a = 3
p = 13
# pow(底数, 指数, 模数) -> 计算 a^(p-2) mod p
d = pow(a, p - 2, p) 
print(f"逆元为: {d}")

写法 2:通用求法(适用于任何互质情况,如 RSA)

遇到模数不是素数的情况,直接调用 pycryptodome 库:

from Crypto.Util.number import inverse

a = 3
m = 13
# 底层自动调用扩展欧几里得算法(eGCD)
d = inverse(a, m)
print(f"逆元为: {d}")

二次剩余

核心概念拆解

1. 什么是二次剩余?

在普通的实数世界里,正数可以开平方,负数不能开平方。 在 的有限域世界里,数字同样被分为了两派:

  • 二次剩余:如果存在一个整数 ,使得 ,那么 就是二次剩余。说白了,就是在这个模环境下, 能开得出完美的平方根
  • 二次非剩余:不管你怎么试,都找不到一个 的平方模 等于它。这就相当于模世界里的“负数”,开不了根号。

2. 核心特性:成双成对的根 就像现实中 ,并且 一样。题目里也明确提示了:如果 ,那么

在模 的世界里,负数 其实就等价于

所以,一旦某个数字有平方根,它必定有两个解,分别是

sympy库实现二次剩余

from sympy.ntheory.residue_ntheory import sqrt_mod  
  
p = 29  
# 我们想看看 6 在模 29 下的平方根  
root = sqrt_mod(6, p)  
  
if root:  
    print(f"6 的较小平方根是: {root}")  
    print(f"6 的另一个平方根是: {p - root}")  
else:  
    print("找不到平方根,它是二次非剩余")

勒让德符号与大素数二次剩余求解

核心痛点与解决思路

在模算术中求解二次剩余(即求模平方根 )时,如果模数 是一个极小的素数(如 29),我们可以通过穷举法遍历 来寻找答案。

然而,在真实的密码学环境或 CTF 竞赛中, 往往是一个高达 1024 位或 2048 位的超大素数。此时暴力破解的时间复杂度为 ,计算到宇宙毁灭也无法完成。

为了应对超大素数,我们必须将解题过程拆分为两步,并使用数学定理来实现 级别的极速计算:

  1. 第一步(探测):在不计算平方根的前提下,瞬间判断一个数到底“能不能”开平方。

  2. 第二步(提取):在确认能开平方后,利用特定条件瞬间提取出平方根。


勒让德符号—— 二次剩余探测器

勒让德符号提供了一种极其高效的方法,用于判断一个整数 是否是奇素数 的二次剩余。它的核心思想基于费马小定理的推论。

符号表示

计算公式

探测器输出的三种状态:

根据欧拉准则(Euler’s criterion),将数字 代入上述公式,结果必然且只会是以下三种情况之一:

  • 结果为 :说明 的二次剩余(Quadratic Residue)。即 在模 下必然存在平方根。

  • 结果为 (在模 下表现为 :说明 不是 的二次剩余(Quadratic Non-Residue)。即 在模 下绝对无法开平方,属于无解状态。

  • 结果为 :说明 ,即 的倍数(在实战中极少考察此种边缘情况)。

数学直觉

由费马小定理可知 。我们可以将其视为 。在模 的域中,平方等于 的数只有 。勒让德符号巧妙地利用了这一点,将所有数字强行划分为“有解(1)”和“无解(-1)”两大阵营。

勒让德符号的重要乘法性质(进阶拓展):

  • (即

  • (即

  • (即


第二步:神级捷径公式 —— 当

当我们使用勒让德符号确认了某个数 是二次剩余后,接下来就是求出具体的平方根

在一般情况下,求大素数模平方根需要使用极其复杂的 Tonelli-Shanks 算法。但如果出题人给定的素数 满足特殊条件 ,我们就可以直接使用以下公式秒杀:

(注:求出第一个根 后,由于模运算中平方根总是成对出现,第二个根必定为 )

为什么探测器与捷径可以完美闭环?(底层代数推导)

这个捷径公式之所以成立,完全是因为它建立在勒让德符号的结果之上。

我们将算出的 进行平方验证:

将指数拆分为

关键点:由于我们在第一步已经用勒让德符号确认了 是二次剩余,所以 的值必然等于 1

将其代入式中:

完美证明了公式得出的 就是我们要找的平方根。