恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
RSA公钥逆向计算私钥:原理、工具与实践指南
首页
资讯中心
/
RSA公钥逆向计算私钥:原理、工具与实践指南
RSA公钥逆向计算私钥:原理、工具与实践指南
发布时间:2026/8/8 1:24:43
1. 项目概述从公钥到私钥的“逆向工程”在密码学的世界里RSA算法就像一把经典的双子锁公钥负责上锁私钥负责开锁。我们日常接触到的HTTPS、SSH登录、软件签名背后都有它的身影。通常情况下我们生成一对密钥后公钥可以放心地交给任何人而私钥则必须像保险柜钥匙一样严密保管。但今天我们要探讨的是一个有点“叛逆”的课题已知RSA的公钥(e, n)如何计算出对应的私钥d这听起来像是要破解RSA的安全性但实际上它更多是出现在一些特定的学习和研究场景中。比如你在分析一个旧的加密系统手头只有公开的公钥文件或者你在做CTF夺旗赛的密码学题目题目只给了你公钥参数又或者你只是单纯想深入理解RSA算法中公钥与私钥那精妙的数学纽带。这个计算过程本质上是对RSA密钥生成过程的一次“逆向推导”其核心在于利用公钥中的模数n分解出构成它的两个大质数p和q。一旦分解成功计算私钥d就只是一个简单的模逆运算。这个过程充满了挑战因为RSA的安全性正是建立在“大整数分解是计算上不可行的”这一假设之上。对于现代密码学标准中使用的2048位乃至4096位的n用常规方法分解可能需要宇宙年龄那么长的时间。因此我们这里讨论的通常是针对教学、研究或特定弱密钥场景下的计算。接下来我将带你一步步拆解这个过程的原理、工具和实践中的那些“坑”。2. 核心原理与数学基础拆解要理解如何从(e, n)得到d我们必须回到RSA算法的数学根基。它不是魔法而是一系列精心设计的数论运算。2.1 RSA密钥生成过程回顾标准的RSA密钥生成可以概括为以下五步选择两个大质数随机选择两个足够大的、不同的质数p和q。这是所有安全性的起点。计算模数 nn p * q。这个n就是公钥和私钥中都包含的模数它的长度比特数就是我们常说的密钥长度比如2048位。计算欧拉函数 φ(n)φ(n) (p-1) * (q-1)。欧拉函数计算的是小于n且与n互质的正整数的个数。由于n是两个质数的乘积这个公式成立。选择公钥指数 e选择一个整数e满足1 e φ(n)且e与φ(n)互质即gcd(e, φ(n)) 1。通常为了效率会选择65537 (0x10001)因为它二进制表示中1很少计算速度快且足够大。计算私钥指数 d计算e对于模φ(n)的模逆元d。即d满足e * d ≡ 1 (mod φ(n))。这个d就是私钥的核心部分。最终公钥由(e, n)组成私钥通常包含(d, n)有时也会包含(p, q, d, φ(n))等中间参数以加速运算。2.2 逆向计算的关键分解n从上面的过程可以清晰地看到正向生成是从p, q推导出e, n, d。而逆向计算我们已知e和n目标是求d。观察公式d ≡ e^(-1) (mod φ(n))。要计算d我们需要φ(n)。而φ(n) (p-1)*(q-1)。因此问题的核心从“求d”转化为了“通过n求p和q”也就是对大整数n进行质因数分解。一旦我们成功分解n得到p和q后续计算就变得直接计算φ(n) (p-1) * (q-1)使用扩展欧几里得算法求解方程e * d k * φ(n) 1中的d实际上我们只关心d关于模φ(n)的逆元。所以整个挑战和乐趣或者说难点几乎全部集中在了分解模数n这一步。2.3 为什么分解n如此困难这就是RSA安全性的基石。两个大质数相乘 (p * q n) 极其容易但想从一个巨大的乘积n倒推回原来的两个质数对于经典计算机而言目前没有已知的多项式时间算法。最有效的通用算法如通用数域筛法GNFS的时间复杂度是n长度的亚指数函数。这意味着当p和q都足够大例如1024位以上时即使动用全球的计算资源分解所需的时间也远超宇宙年龄在实践上被视为“不可行”。因此当我们谈论“已知公钥计算私钥”时隐含的前提往往是n较小例如小于256位可以直接用工具或简单算法分解。n本身存在弱点例如p和q非常接近或者p-1、q-1的质因数很小使得某些特殊算法如费马分解法、Pollard‘s p-1算法生效。这是一个教学或竞赛环境n被故意设置为可分解的。注意在真实的安全系统中试图分解一个正在使用的、足够长的RSA模数来获取私钥在法律和伦理上都是被严格禁止的这等同于破解加密系统。本文内容仅用于密码学学习、安全审计对自有系统和问题排查。3. 实操流程与工具链解析理论清晰后我们进入实战环节。整个过程可以系统化为几个步骤我会结合常用的工具和命令来讲解。3.1 第一步获取并解析公钥公钥可能以多种格式存在我们需要从中提取出(e, n)这对数值。常见格式PEM格式最常见以-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----包裹的Base64编码文本。SSH公钥ssh-rsa AAAAB3NzaC1yc2E... userhost这种形式AAAAB3NzaC1yc2E后面就是Base64编码的公钥数据。裸参数直接给出n和e的十六进制或十进制数值。提取方法对于PEM或SSH格式我们需要解码。使用OpenSSL是最直接的方式。# 查看PEM格式公钥的详细参数 openssl rsa -pubin -in public_key.pem -text -noout执行这个命令后你会看到类似以下的输出RSA Public-Key: (2048 bit) Modulus: 00:aa:bb:cc:dd... (很长的一串十六进制) Exponent: 65537 (0x10001)这里Modulus就是模数n十六进制Exponent就是公钥指数e通常是65537。如果公钥是SSH格式可以先将其转换为PEM格式再解析ssh-keygen -f id_rsa.pub -e -m pem public_key.pem实操心得有时你拿到的可能是一个证书.crt或.cer文件里面包含了公钥。可以用openssl x509 -in certificate.crt -pubkey -noout先提取出公钥PEM再进行上述操作。如果输出中的Exponent是3或17等小数字这是一个潜在的安全弱点提示因为小指数e在某些特定情况下可能带来风险如广播攻击但这并不影响我们分解n的难度。3.2 第二步分解模数n这是最具技术挑战性的一步。根据n的大小和性质我们选择不同的工具和策略。1. 对于小n 256位在线工具或本地脚本对于很小的n比如小于100位十进制数你可以直接使用一些在线分解工具搜索“integer factorization calculator”或者用本地数学工具如Python的sympy库。import sympy from Crypto.Util.number import long_to_bytes, bytes_to_long # 假设你从公钥中提取出了 n 和 e n 0x726639... # 你的n的十六进制值 e 65537 # 使用sympy的factorint函数分解n factors sympy.factorint(n) print(factors) # 输出类似 {p: 1, q: 1}表示 n p^1 * q^1 p, q list(factors.keys())sympy对于教学和CTF中常见的小n通常不超过512位非常有效。2. 对于有弱点的n使用专用算法如果n较大但存在弱点可以使用一些高效的算法。费马分解法适用于p和q非常接近的情况即|p-q|很小。原理是n a^2 - b^2寻找平方差。Pollard‘s p-1算法适用于p-1或q-1的质因数都很小的情况。Williams‘ p1算法与p-1算法类似适用于p1质因数小的情况。这些算法在sage一个基于Python的数学软件系统或一些专门的分解工具集如yafu中都有实现。3. 对于“标准”的n使用强大的分解工具对于CTF中常见的1024位或以下的可分解n最强大的工具之一是yafuYet Another Factorization Utility。它是一个命令行工具能自动尝试多种算法包括上述的弱算法以及椭圆曲线法ECM、二次筛法QS等。基本用法很简单yafu “factor(0x你的n的十六进制值)”或者将n的十进制值保存到文件num.txt中然后运行yafu “factor()” -batchfile num.txtyafu会输出分解结果类似P1 123...P2 456...。4. 利用已知的质数数据库对于特别常见的、公开的质数或者CTF中故意使用的“著名”质数可以尝试在 factordb.com 这类网站查询。有时n可能直接就在它的数据库里。重要注意事项 分解是计算密集型任务耗时与n的大小呈指数级增长。分解一个256位的n可能只需一秒512位可能需要几分钟到几小时而768位以上的分解在个人电脑上就可能需要数天甚至更久且不保证成功。1024位在现代标准下已被认为不安全但分解它仍然需要巨大的计算集群。永远不要尝试分解一个来自真实生产环境的、长度超过1024位的RSA密钥。3.3 第三步计算私钥d一旦成功获取p和q剩下的就是纯计算了。我们需要计算φ(n)和d。计算 φ(n)φ_n (p - 1) * (q - 1)确保你的p和q确实是质数可以用工具验证然后进行这个大整数乘法。计算 d计算d是求e模φ(n)的乘法逆元。即求解满足e * d ≡ 1 (mod φ_n)的d。这可以通过扩展欧几里得算法高效完成。同样用Python的Crypto库或gmpy2库非常方便from Crypto.Util.number import inverse # 或者 from gmpy2 import invert # 假设已有 p, q, e phi_n (p - 1) * (q - 1) d inverse(e, phi_n) # 使用Crypto库 # 或者 d gmpy2.invert(e, phi_n) print(f“私钥指数 d {d}”) print(f“d 的十六进制 {hex(d)}”)3.4 第四步组装私钥计算出d后我们就得到了私钥的核心参数(d, n)。但通常我们需要将其封装成标准的格式以便使用如PKCS#1格式的PEM私钥。在Python中使用Crypto库可以方便地构造私钥对象并导出from Crypto.PublicKey import RSA # 参数n, e, d, p, q key RSA.construct((n, e, d, p, q)) private_key_pem key.export_key() print(private_key_pem.decode()) # 输出PEM格式的私钥 # 也可以导出为OpenSSH格式等 # private_key_openssh key.export_key(‘OpenSSH’)如果你只有(n, e, d)而没有p和q理论上RSA.construct((n, e, d))也可以但库函数内部可能需要重新计算p和q这又是一个分解问题或者某些操作会受限。因此如果可能尽量提供p和q。4. 常见场景、问题与排查实录在实际操作中你可能会遇到各种预料之外的情况。下面我整理了一些典型场景和踩过的“坑”。4.1 场景一CTF竞赛中的RSA题目这是最常见的应用场景。题目通常会给你一个pub.key文件或者一段代码输出(e, n)有时还会给一段密文c。解题步骤非常标准化提取(e, n)用openssl或Python解析公钥文件。分解n这是题目的核心考点。n可能很小直接sympy.factorint。有弱点如p和q接近费马分解或p-1光滑Pollard‘s p-1。可能需要写脚本或使用sage。共用模数如果多个密文共用同一个n但不同e且e互质可能不需要分解n而是通过共模攻击解密。由多个小质数组成n可能不是两个大质数而是多个小质数的乘积这时分解更容易但需要恢复正确的φ(n)欧拉函数是乘性的。计算d并解密得到d后解密公式为m c^d mod nc是密文整数m是明文整数。排查技巧拿到n先看长度比特数或十进制位数对难度有个预估。尝试在 factordb.com 查询可能有意外之喜。检查e是否异常小如3可能导致小明文攻击或广播攻击从而绕过分解。如果n是十进制或十六进制字符串注意转换时的格式问题确保在Python中是正确的长整数。4.2 场景二分析或恢复旧系统密钥你可能需要分析一个遗留系统只有公钥但私钥丢失或损坏。这时如果密钥长度很短比如512位理论上可以尝试分解。但务必注意法律合规确保这是你拥有完全权限的系统未经授权尝试恢复他人私钥是违法行为。可行性评估512位RSA在当今已被彻底破解1999年即被分解利用云资源或公开的分解记录可能成功。768位2009年被分解在个人电脑上已极难1024位目前仍具有相当挑战性。对于2048位及以上放弃分解的想法考虑其他恢复途径如备份。4.3 常见错误与问题排查openssl命令报错“Expecting: PUBLIC KEY”原因你的公钥文件格式可能不是标准的PKCS#8公钥PEM格式。可能是SSH格式、PKCS#1格式-----BEGIN RSA PUBLIC KEY-----或证书。解决SSH转PEMssh-keygen -f key.pub -e -m pemPKCS#1转PKCS#8openssl rsa -RSAPublicKey_in -in pubkey_pkcs1.pem -pubout从证书提取openssl x509 -in cert.crt -pubkey -noout pubkey.pem分解工具长时间无响应原因n太大或太“强”选择的算法不合适或计算资源不足。解决先用sympy.isprime或Miller-Rabin测试快速判断n是否为质数如果是那根本不是RSA公钥。尝试用yafu它会自动尝试多种轻量级算法如试除法、Pollard Rho。如果卡住可以尝试指定算法如yafu “factor(…) -one -p”其中-p代表Pollard‘s p-1。对于较大的n如300位考虑使用yafu的siqs二次筛法或gnfs数域筛法命令但这需要大量时间和内存。在个人电脑上256-512位是相对现实的尝试范围。计算出d后构造私钥或解密失败原因1分解得到的p和q不正确。这是最可能的原因。务必验证p * q n且p和q都是质数。原因2计算φ(n)时出错。必须是(p-1)*(q-1)。原因3在计算模逆d时e和φ(n)不互质。这违反了RSA的基本条件意味着公钥e选择错误或者p,q分解有误。排查编写验证脚本。# 验证脚本 assert p * q n, “p*q ! n” assert sympy.isprime(p) and sympy.isprime(q), “p或q不是质数” assert math.gcd(e, phi_n) 1, “e和φ(n)不互质” assert (e * d) % phi_n 1, “d不是e的模逆元” # 验证加解密用公钥加密一个随机数再用私钥解密应得到原数 test_m 123456789 test_c pow(test_m, e, n) decrypted_m pow(test_c, d, n) assert test_m decrypted_m, “加解密验证失败”得到的明文是乱码原因RSA解密后得到的是整数形式的明文m。你需要将其转换为字节串。如果明文最初是文本它可能经过PKCS#1 v1.5或OAEP填充。直接long_to_bytes(m)得到的可能是带填充的数据。解决查看题目或上下文说明。如果是简单的将字符串转为整数如bytes_to_long(b‘flag{…}’)直接转换即可。如果使用了标准填充则需要用Crypto库的PKCS1_v1_5或PKCS1_OAEP模块进行解密操作而不是简单的模幂运算。5. 工具链推荐与脚本编写工欲善其事必先利其器。一套顺手的工具能极大提升效率。5.1 核心工具集OpenSSL瑞士军刀。用于密钥格式转换、解析、基本操作。几乎所有系统都预装或可轻松安装。Python 密码学库主力编程环境。pycryptodome/Crypto功能全面的密码学库包含RSA构造、加解密、数字签名等。gmpy2提供高性能的大整数运算和模逆计算比Python原生整数运算快得多。sympy符号计算库其factorint函数对于分解小整数非常方便。sage基于Python的数学软件集成了大量数论和密码学高级算法是CTF密码学研究的利器。可以在线使用如cocalc.com或本地安装。yafu强大的自动分解工具是处理“可分解”大整数的首选。RsaCtfTool一个用Python编写的专门用于攻击RSA的瑞士军刀集成了多种攻击方式弱密钥、共模、广播、维纳攻击等并且能自动尝试分解通过调用yafu等。在CTF中非常流行。5.2 编写一个完整的辅助脚本将上述步骤自动化是一个好习惯。下面是一个功能相对完整的Python脚本框架你可以根据需要填充和修改#!/usr/bin/env python3 “”” RSA公钥解析与私钥计算辅助脚本 “”” import sys import math from Crypto.Util.number import long_to_bytes, bytes_to_long, inverse, GCD from Crypto.PublicKey import RSA import sympy import subprocess import tempfile import os def parse_public_key(pem_file_path): “”“从PEM文件解析n和e”“” with open(pem_file_path, ‘rb’) as f: key RSA.import_key(f.read()) return key.n, key.e def factorize_n_small(n): “”“尝试用sympy分解较小的n”“” try: factors sympy.factorint(n) if len(factors) 2 and all(exp 1 for exp in factors.values()): p, q list(factors.keys()) return p, q else: print(f“[!] 分解结果不是两个质数: {factors}”) return None, None except Exception as e: print(f“[!] sympy分解失败: {e}”) return None, None def factorize_n_with_yafu(n_hex): “”“调用yafu进行分解需要本地安装yafu”“” yafu_path “./yafu” # 修改为你的yafu路径 with tempfile.NamedTemporaryFile(mode‘w’, deleteFalse, suffix‘.txt’) as f: f.write(f“factor(0x{n_hex})\n”) temp_file f.name try: # 运行yafu这里可能需要根据yafu的版本调整参数 cmd [yafu_path, “factor()”, “-batchfile”, temp_file] result subprocess.run(cmd, capture_outputTrue, textTrue, timeout300) output result.stdout result.stderr print(“[yafu输出]”, output) # 简单的输出解析寻找P1, P2行这需要根据yafu实际输出调整 lines output.split(‘\n’) factors [] for line in lines: if line.strip().startswith(‘P1 ’) or line.strip().startswith(‘P2 ’): # 解析行例如 “P1 1234567890123456789012345678901234567890” parts line.split(‘’) if len(parts) 2: factor_str parts[1].strip() # 处理可能的后缀如 ‘ (prime)’ if ‘ ‘ in factor_str: factor_str factor_str.split(‘ ‘)[0] factors.append(int(factor_str)) if len(factors) 2: return factors[0], factors[1] else: print(“[!] 未能从yafu输出中解析出两个质因数”) return None, None except subprocess.TimeoutExpired: print(“[!] yafu运行超时”) return None, None except Exception as e: print(f“[!] 调用yafu出错: {e}”) return None, None finally: os.unlink(temp_file) def calculate_private_key(n, e, p, q): “”“根据p, q计算私钥d并验证”“” if p is None or q is None: return None # 验证 if p * q ! n: print(f“[!] 错误: p*q ({p*q}) 不等于 n ({n})”) return None if not (sympy.isprime(p) and sympy.isprime(q)): print(“[!] 警告: p或q可能不是质数建议进一步验证”) phi_n (p - 1) * (q - 1) if math.gcd(e, phi_n) ! 1: print(f“[!] 错误: e ({e}) 与 φ(n) 不互质无法计算逆元”) return None d inverse(e, phi_n) print(f“[] 计算成功!”) print(f“ p {p}”) print(f“ q {q}”) print(f“ φ(n) {phi_n}”) print(f“ d {d}”) print(f“ d (hex) {hex(d)}”) # 简单验证 test_m 42 test_c pow(test_m, e, n) dec_m pow(test_c, d, n) if test_m dec_m: print(“[] 加解密验证通过”) else: print(“[!] 加解密验证失败!”) # 尝试构造PEM私钥 try: key RSA.construct((n, e, d, p, q)) private_pem key.export_key() print(“\n[] PEM格式私钥:”) print(private_pem.decode()) # 可选保存到文件 # with open(‘private_key.pem’, ‘wb’) as f: # f.write(private_pem) except Exception as constr_err: print(f“[!] 构造私钥对象失败: {constr_err}”) return d def main(): if len(sys.argv) 2: print(f“用法: {sys.argv[0]} 公钥PEM文件”) sys.exit(1) pem_file sys.argv[1] print(f“[*] 正在解析公钥文件: {pem_file}”) n, e parse_public_key(pem_file) print(f“[] 解析成功: n{n}\n e{e}”) print(f“[] n的位数: {n.bit_length()} bits”) # 策略选择 p, q None, None if n.bit_length() 256: print(“[*] n较小尝试使用sympy分解...”) p, q factorize_n_small(n) else: print(“[*] n较大建议使用yafu等专业工具分解。”) print(“[*] 本脚本提供调用yafu的示例需预先安装配置yafu。”) # 取消下面一行的注释以尝试调用yafu # p, q factorize_n_with_yafu(hex(n)[2:]) if p and q: calculate_private_key(n, e, p, q) else: print(“[-] 未能分解n无法计算私钥。”) print(“[*] 后续建议:”) print(“ 1. 检查n是否真的是RSA模数可能是质数。“) print(“ 2. 对于较大的n手动使用yafu或在线分解数据库尝试。”) print(“ 3. 考虑是否存在其他攻击方式如共模、低指数等无需分解n。”) if __name__ “__main__”: main()这个脚本提供了一个自动化起点涵盖了解析、小整数分解、调用外部工具、计算验证和格式导出的基本流程。你可以根据实际需求比如集成更多分解算法费马、Pollard Rho或更复杂的输出解析逻辑来增强它。6. 安全警示与最佳实践在结束这篇长文之前我必须再次强调安全与合规的重要性。从公钥计算私钥这项技术是一把双刃剑。核心安全警示法律红线绝对禁止对不属于你且未经明确授权的系统进行密钥恢复尝试。这包括但不限于他人的网站、服务器、通信流量、软件等。此类行为可能构成计算机犯罪。伦理边界即使在研究或测试环境中也应使用自己生成的、或明确声明可用于安全研究的密钥材料。现实可行性对于符合现代安全标准如2048位及以上e65537p和q随机且强度足够的RSA密钥通过分解n来恢复私钥在计算上是不可行的。任何声称能快速破解此类密钥的服务或工具极大概率是骗局。作为防御方的建议如果你在管理使用RSA的系统使用足够长的密钥目前推荐至少使用2048位RSA密钥对于长期安全要求高的系统应使用3072或4096位。1024位密钥已被认为不安全应尽快淘汰。确保随机性密钥生成时p和q必须来自密码学安全的随机数生成器CSPRNG。避免使用有缺陷的随机源如某些旧版嵌入式设备。选择适当的e公钥指数e通常选用65537。它平衡了安全性和加密/验证效率。避免使用过小的e如3尽管它能使加密更快但可能引入风险。保护私钥私钥的保密性至关重要。使用强密码对私钥进行加密存储并严格控制访问权限。考虑使用硬件安全模块HSM来存储顶级私钥。定期更换密钥制定合理的密钥轮换策略即使没有泄露迹象定期更换密钥也能限制潜在损失的范围。关注密码学进展关注NIST等标准机构的最新建议。RSA算法未来可能会被量子计算机威胁Shor算法后量子密码学PQC是发展方向。对于长期数据应考虑加密算法的可升级性。理解“如何从公钥计算私钥”的过程最终目的不是为了去破解而是为了更深刻地理解RSA的工作原理、其安全性的边界所在从而能够更好地评估风险、设计系统和解决那些在合法合规范围内出现的技术问题。当你下次再看到一对(e, n)时希望你能清晰地看到背后那对隐藏的(p, q)以及连接它们的那道坚固却又在特定条件下可以被审视的数学桥梁。