[{"data":1,"prerenderedAt":2082},["ShallowReactive",2],{"category-data-crypto":3},[4,1078],{"id":5,"title":6,"body":7,"description":1064,"extension":1065,"meta":1066,"navigation":125,"ogImage":1068,"path":1074,"seo":1075,"stem":1076,"__hash__":1077},"content/blogs/12. ctf-crypto-overview.md","CTF 密码学入门：编码、XOR 与 DH 密钥协商",{"type":8,"value":9,"toc":1054},"minimark",[10,15,19,94,97,208,211,219,222,246,248,254,258,284,294,298,309,314,321,365,367,373,377,382,396,470,472,478,484,488,495,527,531,534,598,600,606,610,614,617,621,626,631,677,681,810,812,818,828,832,836,839,877,881,910,914,917,946,948,954,958,961,966,969,977,1036,1038,1043,1047,1050],[11,12,14],"h3",{"id":13},"一现实背景编码每天都在用","一、现实背景：编码每天都在用",[16,17,18],"p",{},"编码（Encoding）不依赖密钥，任何掌握规则的人都能还原；加密（Encryption）依赖密钥。日常网络中的编码：",[20,21,22,38],"table",{},[23,24,25],"thead",{},[26,27,28,32,35],"tr",{},[29,30,31],"th",{},"编码",[29,33,34],{},"现实应用",[29,36,37],{},"例子",[39,40,41,60,79],"tbody",{},[26,42,43,47,50],{},[44,45,46],"td",{},"Base64",[44,48,49],{},"邮件附件（MIME）、JWT 令牌、Data URI",[44,51,52,56,57],{},[53,54,55],"code",{},"ZmxhZw=="," → ",[53,58,59],{},"flag",[26,61,62,65,72],{},[44,63,64],{},"Hex",[44,66,67,68,71],{},"颜色值 ",[53,69,70],{},"#FF0000","、MAC 地址、哈希摘要",[44,73,74,56,77],{},[53,75,76],{},"666c6167",[53,78,59],{},[26,80,81,84,87],{},[44,82,83],{},"URL 编码",[44,85,86],{},"浏览器地址栏中的中文与特殊字符",[44,88,89,56,92],{},[53,90,91],{},"%66%6c%61%67",[53,93,59],{},[16,95,96],{},"先运行最小的解码例子：",[98,99,104],"pre",{"className":100,"code":101,"language":102,"meta":103,"style":103},"language-python shiki shiki-themes dracula","import base64\n# base64 模块：标准库，提供 base64 编码解码函数。\n\nprint(base64.b64encode(b\"flag\"))\n# b64encode(data)：把字节串编码成 base64，参数是待编码的 bytes，\n# 返回值是编码结果 bytes：b'ZmxhZw=='。\n\nprint(base64.b64decode(\"ZmxhZw==\"))\n# b64decode(s)：把 base64 字符串解码回原始字节，参数可以是 str 或 bytes，\n# 返回值 b'flag'。这一步用于解开题目给的 base64 密文。\n\nprint(bytes.fromhex(\"666c6167\"))\n# bytes.fromhex(hex_str)：把十六进制字符串逐对转成字节，\n# 参数 \"666c6167\" 对应 b'flag'。用于解开题目给的 hex 密文。\n\nprint(b\"flag\".hex())\n# bytes.hex()：反过来，把字节转成十六进制字符串 \"666c6167\"。\n","python","",[53,105,106,114,120,127,133,139,145,150,156,162,168,173,179,185,191,196,202],{"__ignoreMap":103},[107,108,111],"span",{"class":109,"line":110},"line",1,[107,112,113],{},"import base64\n",[107,115,117],{"class":109,"line":116},2,[107,118,119],{},"# base64 模块：标准库，提供 base64 编码解码函数。\n",[107,121,123],{"class":109,"line":122},3,[107,124,126],{"emptyLinePlaceholder":125},true,"\n",[107,128,130],{"class":109,"line":129},4,[107,131,132],{},"print(base64.b64encode(b\"flag\"))\n",[107,134,136],{"class":109,"line":135},5,[107,137,138],{},"# b64encode(data)：把字节串编码成 base64，参数是待编码的 bytes，\n",[107,140,142],{"class":109,"line":141},6,[107,143,144],{},"# 返回值是编码结果 bytes：b'ZmxhZw=='。\n",[107,146,148],{"class":109,"line":147},7,[107,149,126],{"emptyLinePlaceholder":125},[107,151,153],{"class":109,"line":152},8,[107,154,155],{},"print(base64.b64decode(\"ZmxhZw==\"))\n",[107,157,159],{"class":109,"line":158},9,[107,160,161],{},"# b64decode(s)：把 base64 字符串解码回原始字节，参数可以是 str 或 bytes，\n",[107,163,165],{"class":109,"line":164},10,[107,166,167],{},"# 返回值 b'flag'。这一步用于解开题目给的 base64 密文。\n",[107,169,171],{"class":109,"line":170},11,[107,172,126],{"emptyLinePlaceholder":125},[107,174,176],{"class":109,"line":175},12,[107,177,178],{},"print(bytes.fromhex(\"666c6167\"))\n",[107,180,182],{"class":109,"line":181},13,[107,183,184],{},"# bytes.fromhex(hex_str)：把十六进制字符串逐对转成字节，\n",[107,186,188],{"class":109,"line":187},14,[107,189,190],{},"# 参数 \"666c6167\" 对应 b'flag'。用于解开题目给的 hex 密文。\n",[107,192,194],{"class":109,"line":193},15,[107,195,126],{"emptyLinePlaceholder":125},[107,197,199],{"class":109,"line":198},16,[107,200,201],{},"print(b\"flag\".hex())\n",[107,203,205],{"class":109,"line":204},17,[107,206,207],{},"# bytes.hex()：反过来，把字节转成十六进制字符串 \"666c6167\"。\n",[16,209,210],{},"实际输出：",[98,212,217],{"className":213,"code":215,"language":216,"meta":103},[214],"language-text","b'ZmxhZw=='\nb'flag'\nb'flag'\n666c6167\n","text",[53,218,215],{"__ignoreMap":103},[16,220,221],{},"URL 编码：",[98,223,225],{"className":100,"code":224,"language":102,"meta":103,"style":103},"from urllib.parse import quote, unquote\n\nprint(quote(\"你好\"))                    # %E4%BD%A0%E5%A5%BD\nprint(unquote(\"%66%6c%61%67\"))          # flag\n",[53,226,227,232,236,241],{"__ignoreMap":103},[107,228,229],{"class":109,"line":110},[107,230,231],{},"from urllib.parse import quote, unquote\n",[107,233,234],{"class":109,"line":116},[107,235,126],{"emptyLinePlaceholder":125},[107,237,238],{"class":109,"line":122},[107,239,240],{},"print(quote(\"你好\"))                    # %E4%BD%A0%E5%A5%BD\n",[107,242,243],{"class":109,"line":129},[107,244,245],{},"print(unquote(\"%66%6c%61%67\"))          # flag\n",[16,247,210],{},[98,249,252],{"className":250,"code":251,"language":216,"meta":103},[214],"%E4%BD%A0%E5%A5%BD\nflag\n",[53,253,251],{"__ignoreMap":103},[11,255,257],{"id":256},"二环境准备","二、环境准备",[98,259,263],{"className":260,"code":261,"language":262,"meta":103,"style":103},"language-bash shiki shiki-themes dracula","pip3 install pycryptodome sympy gmpy2\n","bash",[53,264,265],{"__ignoreMap":103},[107,266,267,271,275,278,281],{"class":109,"line":110},[107,268,270],{"class":269},"sAOxA","pip3",[107,272,274],{"class":273},"s-mGx"," install",[107,276,277],{"class":273}," pycryptodome",[107,279,280],{"class":273}," sympy",[107,282,283],{"class":273}," gmpy2\n",[16,285,286,287],{},"浏览器打开 CyberChef（",[288,289,293],"a",{"href":290,"rel":291},"https://gchq.github.io/CyberChef%EF%BC%89%E3%80%82",[292],"nofollow","https://gchq.github.io/CyberChef）。",[11,295,297],{"id":296},"三核心知识-1xor-与单字节爆破","三、核心知识 1：XOR 与单字节爆破",[16,299,300,301,304,305,308],{},"异或的性质：",[53,302,303],{},"A ^ B = C","，且 ",[53,306,307],{},"C ^ B = A","。同一个密钥异或两次就还原，所以加密和解密是同一个运算。",[310,311,313],"h4",{"id":312},"_31-加密","3.1 加密",[16,315,316,320],{},[317,318,319],"strong",{},"目的","：先用已知明文和密钥生成密文，验证\"异或加密\"的写法，同时为后面爆破提供测试对象。",[98,322,324],{"className":100,"code":323,"language":102,"meta":103,"style":103},"plain = b\"flag{xor_is_easy}\"\nkey = 0x42   # 密钥是单个字节（0-255 的整数），这里选 0x42\n\ncipher = bytes([b ^ key for b in plain])\n# 列表推导式：对 plain 的每个字节 b，做 b ^ key（按位异或），\n# bytes(...) 把结果整数列表转回字节串。这就是单字节 XOR 加密。\nprint(cipher.hex())\n# .hex()：把密文字节串转成十六进制字符串，方便在题目里粘贴/对比。\n",[53,325,326,331,336,340,345,350,355,360],{"__ignoreMap":103},[107,327,328],{"class":109,"line":110},[107,329,330],{},"plain = b\"flag{xor_is_easy}\"\n",[107,332,333],{"class":109,"line":116},[107,334,335],{},"key = 0x42   # 密钥是单个字节（0-255 的整数），这里选 0x42\n",[107,337,338],{"class":109,"line":122},[107,339,126],{"emptyLinePlaceholder":125},[107,341,342],{"class":109,"line":129},[107,343,344],{},"cipher = bytes([b ^ key for b in plain])\n",[107,346,347],{"class":109,"line":135},[107,348,349],{},"# 列表推导式：对 plain 的每个字节 b，做 b ^ key（按位异或），\n",[107,351,352],{"class":109,"line":141},[107,353,354],{},"# bytes(...) 把结果整数列表转回字节串。这就是单字节 XOR 加密。\n",[107,356,357],{"class":109,"line":147},[107,358,359],{},"print(cipher.hex())\n",[107,361,362],{"class":109,"line":152},[107,363,364],{},"# .hex()：把密文字节串转成十六进制字符串，方便在题目里粘贴/对比。\n",[16,366,210],{},[98,368,371],{"className":369,"code":370,"language":216,"meta":103},[214],"242e2325393a2d301d2b311d2723313b3f\n",[53,372,370],{"__ignoreMap":103},[310,374,376],{"id":375},"_32-爆破密钥只有-256-种可能","3.2 爆破（密钥只有 256 种可能）",[16,378,379,381],{},[317,380,319],{},"：不知道密钥时，把 256 种可能全部试一遍。",[16,383,384,387,388,391,392,395],{},[317,385,386],{},"思路","：如果密钥猜对了，解密结果会是一段正常文本；CTF 里 flag 固定以 ",[53,389,390],{},"flag{"," 开头，这就是天然的判据。用 ",[53,393,394],{},"b\"flag{\" in plain"," 过滤即可。",[98,397,399],{"className":100,"code":398,"language":102,"meta":103,"style":103},"cipher = bytes.fromhex(\"242e2325393a2d301d2b311d2723313b3f\")\n# bytes.fromhex(...)：把题目给的十六进制密文还原成字节串。\n\nfor k in range(256):\n    # 枚举 0-255 共 256 个可能的单字节密钥。\n    plain = bytes([b ^ k for b in cipher])\n    # 对每个密钥尝试解密：每个密文字节与 k 异或。\n    # 异或的自反性保证：加密用 key，解密用同一个 key。\n    if b\"flag{\" in plain:\n        # 如果解密结果包含 flag 前缀，说明密钥猜对了。\n        print(\"key =\", k)           # 输出密钥数值\n        print(plain.decode())       # 输出解密出的明文\n        break\n        # 找到即停止，避免继续打印无意义结果。\n",[53,400,401,406,411,415,420,425,430,435,440,445,450,455,460,465],{"__ignoreMap":103},[107,402,403],{"class":109,"line":110},[107,404,405],{},"cipher = bytes.fromhex(\"242e2325393a2d301d2b311d2723313b3f\")\n",[107,407,408],{"class":109,"line":116},[107,409,410],{},"# bytes.fromhex(...)：把题目给的十六进制密文还原成字节串。\n",[107,412,413],{"class":109,"line":122},[107,414,126],{"emptyLinePlaceholder":125},[107,416,417],{"class":109,"line":129},[107,418,419],{},"for k in range(256):\n",[107,421,422],{"class":109,"line":135},[107,423,424],{},"    # 枚举 0-255 共 256 个可能的单字节密钥。\n",[107,426,427],{"class":109,"line":141},[107,428,429],{},"    plain = bytes([b ^ k for b in cipher])\n",[107,431,432],{"class":109,"line":147},[107,433,434],{},"    # 对每个密钥尝试解密：每个密文字节与 k 异或。\n",[107,436,437],{"class":109,"line":152},[107,438,439],{},"    # 异或的自反性保证：加密用 key，解密用同一个 key。\n",[107,441,442],{"class":109,"line":158},[107,443,444],{},"    if b\"flag{\" in plain:\n",[107,446,447],{"class":109,"line":164},[107,448,449],{},"        # 如果解密结果包含 flag 前缀，说明密钥猜对了。\n",[107,451,452],{"class":109,"line":170},[107,453,454],{},"        print(\"key =\", k)           # 输出密钥数值\n",[107,456,457],{"class":109,"line":175},[107,458,459],{},"        print(plain.decode())       # 输出解密出的明文\n",[107,461,462],{"class":109,"line":181},[107,463,464],{},"        break\n",[107,466,467],{"class":109,"line":187},[107,468,469],{},"        # 找到即停止，避免继续打印无意义结果。\n",[16,471,210],{},[98,473,476],{"className":474,"code":475,"language":216,"meta":103},[214],"key = 66\nflag{xor_is_easy}\n",[53,477,475],{"__ignoreMap":103},[16,479,480,481,483],{},"判断依据：明文包含 ",[53,482,390],{}," 前缀。CTF 里 flag 都有固定格式，这就是天然的校验。",[310,485,487],{"id":486},"_33-多字节-xor-简介","3.3 多字节 XOR 简介",[16,489,490,491,494],{},"密钥是多个字节循环使用时，先猜密钥长度，再对每个位置分别做单字节分析。工具 ",[53,492,493],{},"xortool","：",[98,496,498],{"className":260,"code":497,"language":262,"meta":103,"style":103},"pip install xortool\nxortool -x -c 20 cipher.bin\n",[53,499,500,510],{"__ignoreMap":103},[107,501,502,505,507],{"class":109,"line":110},[107,503,504],{"class":269},"pip",[107,506,274],{"class":273},[107,508,509],{"class":273}," xortool\n",[107,511,512,514,518,521,524],{"class":109,"line":116},[107,513,493],{"class":269},[107,515,517],{"class":516},"sIQBb"," -x",[107,519,520],{"class":516}," -c",[107,522,523],{"class":516}," 20",[107,525,526],{"class":273}," cipher.bin\n",[11,528,530],{"id":529},"四核心知识-2凯撒密码","四、核心知识 2：凯撒密码",[16,532,533],{},"每个字母平移固定位数：",[98,535,537],{"className":100,"code":536,"language":102,"meta":103,"style":103},"def caesar(s, shift):\n    out = \"\"\n    for c in s:\n        if c.isalpha():\n            base = ord('a') if c.islower() else ord('A')\n            out += chr((ord(c) - base + shift) % 26 + base)\n        else:\n            out += c\n    return out\n\nprint(caesar(\"flag{caesar_is_fun}\", 3))    # 加密\nprint(caesar(\"iodj{fdhvdu_lv_ixq}\", -3))   # 解密\n",[53,538,539,544,549,554,559,564,569,574,579,584,588,593],{"__ignoreMap":103},[107,540,541],{"class":109,"line":110},[107,542,543],{},"def caesar(s, shift):\n",[107,545,546],{"class":109,"line":116},[107,547,548],{},"    out = \"\"\n",[107,550,551],{"class":109,"line":122},[107,552,553],{},"    for c in s:\n",[107,555,556],{"class":109,"line":129},[107,557,558],{},"        if c.isalpha():\n",[107,560,561],{"class":109,"line":135},[107,562,563],{},"            base = ord('a') if c.islower() else ord('A')\n",[107,565,566],{"class":109,"line":141},[107,567,568],{},"            out += chr((ord(c) - base + shift) % 26 + base)\n",[107,570,571],{"class":109,"line":147},[107,572,573],{},"        else:\n",[107,575,576],{"class":109,"line":152},[107,577,578],{},"            out += c\n",[107,580,581],{"class":109,"line":158},[107,582,583],{},"    return out\n",[107,585,586],{"class":109,"line":164},[107,587,126],{"emptyLinePlaceholder":125},[107,589,590],{"class":109,"line":170},[107,591,592],{},"print(caesar(\"flag{caesar_is_fun}\", 3))    # 加密\n",[107,594,595],{"class":109,"line":175},[107,596,597],{},"print(caesar(\"iodj{fdhvdu_lv_ixq}\", -3))   # 解密\n",[16,599,210],{},[98,601,604],{"className":602,"code":603,"language":216,"meta":103},[214],"iodj{fdhvdu_lv_ixq}\nflag{caesar_is_fun}\n",[53,605,603],{"__ignoreMap":103},[11,607,609],{"id":608},"五核心知识-3dh-密钥协商","五、核心知识 3：DH 密钥协商",[310,611,613],{"id":612},"_51-要解决的问题","5.1 要解决的问题",[16,615,616],{},"对称加密要求双方共享同一密钥，但互联网上的双方从未见过面，信道还可能被窃听。DH（Diffie-Hellman）解决\"在公开信道协商共享密钥\"。",[310,618,620],{"id":619},"_52-流程","5.2 流程",[16,622,623,625],{},[317,624,319],{},"：理解双方如何在\"只看得到公开数值\"的情况下得到同一个秘密。",[16,627,628,630],{},[317,629,386],{},"：秘密不在信道上传输，而是双方各自用\"自己的私钥 + 对方的公钥\"算出同一个数。窃听者缺少任一个私钥，就算不出来。",[632,633,634,645,654,663],"ol",{},[635,636,637,638,640,641,644],"li",{},"双方公开约定大素数 ",[53,639,16],{}," 与生成元 ",[53,642,643],{},"g","。",[635,646,647,648,650,651,644],{},"Alice 选私钥 ",[53,649,288],{},"，发送 ",[53,652,653],{},"A = g^a mod p",[635,655,656,657,650,660,644],{},"Bob 选私钥 ",[53,658,659],{},"b",[53,661,662],{},"B = g^b mod p",[635,664,665,666,669,670,673,674,644],{},"双方分别计算 ",[53,667,668],{},"B^a mod p"," 与 ",[53,671,672],{},"A^b mod p","，得到相同值 ",[53,675,676],{},"g^(ab) mod p",[310,678,680],{"id":679},"_53-最小实现","5.3 最小实现",[98,682,684],{"className":100,"code":683,"language":102,"meta":103,"style":103},"p, g = 0xffffffffffffffc5, 2\n# p：双方公开约定的素数（演示用 60 位，实际协议要求 2048 位以上）。\n# g：生成元，双方公开约定。\na, b = 1234567, 7654321\n# a 是 Alice 的私钥，b 是 Bob 的私钥——这两个值绝不能公开。\n\nA = pow(g, a, p)\n# pow(g, a, p) = g^a mod p，即快速幂取模。\n# A 是 Alice 的\"公钥\"，可以公开，Alice 把它发给 Bob。\nB = pow(g, b, p)\n# 同理，B 是 Bob 的公钥，Bob 发给 Alice。\n\ns_alice = pow(B, a, p)\n# Alice 用 Bob 的公钥 B 和自己的私钥 a 计算 (g^b)^a = g^(ab)。\ns_bob = pow(A, b, p)\n# Bob 用 Alice 的公钥 A 和自己的私钥 b 计算 (g^a)^b = g^(ab)。\n# 两个结果相等，这就是共享密钥。\n\nprint(\"A =\", A)\nprint(\"B =\", B)\nprint(\"Alice 计算:\", s_alice)\nprint(\"Bob 计算:\", s_bob)\nprint(\"一致:\", s_alice == s_bob)\n# 输出一致: True，说明双方在公开信道中成功协商出同一密钥。\n",[53,685,686,691,696,701,706,711,715,720,725,730,735,740,744,749,754,759,764,769,774,780,786,792,798,804],{"__ignoreMap":103},[107,687,688],{"class":109,"line":110},[107,689,690],{},"p, g = 0xffffffffffffffc5, 2\n",[107,692,693],{"class":109,"line":116},[107,694,695],{},"# p：双方公开约定的素数（演示用 60 位，实际协议要求 2048 位以上）。\n",[107,697,698],{"class":109,"line":122},[107,699,700],{},"# g：生成元，双方公开约定。\n",[107,702,703],{"class":109,"line":129},[107,704,705],{},"a, b = 1234567, 7654321\n",[107,707,708],{"class":109,"line":135},[107,709,710],{},"# a 是 Alice 的私钥，b 是 Bob 的私钥——这两个值绝不能公开。\n",[107,712,713],{"class":109,"line":141},[107,714,126],{"emptyLinePlaceholder":125},[107,716,717],{"class":109,"line":147},[107,718,719],{},"A = pow(g, a, p)\n",[107,721,722],{"class":109,"line":152},[107,723,724],{},"# pow(g, a, p) = g^a mod p，即快速幂取模。\n",[107,726,727],{"class":109,"line":158},[107,728,729],{},"# A 是 Alice 的\"公钥\"，可以公开，Alice 把它发给 Bob。\n",[107,731,732],{"class":109,"line":164},[107,733,734],{},"B = pow(g, b, p)\n",[107,736,737],{"class":109,"line":170},[107,738,739],{},"# 同理，B 是 Bob 的公钥，Bob 发给 Alice。\n",[107,741,742],{"class":109,"line":175},[107,743,126],{"emptyLinePlaceholder":125},[107,745,746],{"class":109,"line":181},[107,747,748],{},"s_alice = pow(B, a, p)\n",[107,750,751],{"class":109,"line":187},[107,752,753],{},"# Alice 用 Bob 的公钥 B 和自己的私钥 a 计算 (g^b)^a = g^(ab)。\n",[107,755,756],{"class":109,"line":193},[107,757,758],{},"s_bob = pow(A, b, p)\n",[107,760,761],{"class":109,"line":198},[107,762,763],{},"# Bob 用 Alice 的公钥 A 和自己的私钥 b 计算 (g^a)^b = g^(ab)。\n",[107,765,766],{"class":109,"line":204},[107,767,768],{},"# 两个结果相等，这就是共享密钥。\n",[107,770,772],{"class":109,"line":771},18,[107,773,126],{"emptyLinePlaceholder":125},[107,775,777],{"class":109,"line":776},19,[107,778,779],{},"print(\"A =\", A)\n",[107,781,783],{"class":109,"line":782},20,[107,784,785],{},"print(\"B =\", B)\n",[107,787,789],{"class":109,"line":788},21,[107,790,791],{},"print(\"Alice 计算:\", s_alice)\n",[107,793,795],{"class":109,"line":794},22,[107,796,797],{},"print(\"Bob 计算:\", s_bob)\n",[107,799,801],{"class":109,"line":800},23,[107,802,803],{},"print(\"一致:\", s_alice == s_bob)\n",[107,805,807],{"class":109,"line":806},24,[107,808,809],{},"# 输出一致: True，说明双方在公开信道中成功协商出同一密钥。\n",[16,811,210],{},[98,813,816],{"className":814,"code":815,"language":216,"meta":103},[214],"A = 6842530808116395773\nB = 3514199851527377648\nAlice 计算: 17750252846772688935\nBob 计算: 17750252846772688935\n一致: True\n",[53,817,815],{"__ignoreMap":103},[16,819,820,821,824,825,827],{},"窃听者只能看到 ",[53,822,823],{},"p, g, A, B","，要算出 ",[53,826,288],{}," 必须求解离散对数，参数足够大时不可行。HTTPS 的 TLS 协议正是用 DH 协商密钥、用 RSA/ECDSA 证书认证身份。",[11,829,831],{"id":830},"六必学工具","六、必学工具",[310,833,835],{"id":834},"工具-1cyberchef","工具 1：CyberChef",[16,837,838],{},"浏览器工具，三个常用操作：",[20,840,841,851],{},[23,842,843],{},[26,844,845,848],{},[29,846,847],{},"操作",[29,849,850],{},"用法",[39,852,853,861,869],{},[26,854,855,858],{},[44,856,857],{},"From Base64",[44,859,860],{},"左侧搜 Base64 拖入，粘贴密文，自动解码",[26,862,863,866],{},[44,864,865],{},"From Hex",[44,867,868],{},"拖入 From Hex 解码十六进制",[26,870,871,874],{},[44,872,873],{},"Magic",[44,875,876],{},"直接粘贴未知文本，自动猜测编码方式",[310,878,880],{"id":879},"工具-2python-标准库","工具 2：Python 标准库",[98,882,884],{"className":100,"code":883,"language":102,"meta":103,"style":103},"import base64, binascii\n\nprint(base64.b64decode(\"ZmxhZw==\"))   # b'flag'\nprint(bytes.fromhex(\"666c6167\"))      # b'flag'\nprint(ord('A'))                       # 65，ASCII 编码\n",[53,885,886,891,895,900,905],{"__ignoreMap":103},[107,887,888],{"class":109,"line":110},[107,889,890],{},"import base64, binascii\n",[107,892,893],{"class":109,"line":116},[107,894,126],{"emptyLinePlaceholder":125},[107,896,897],{"class":109,"line":122},[107,898,899],{},"print(base64.b64decode(\"ZmxhZw==\"))   # b'flag'\n",[107,901,902],{"class":109,"line":129},[107,903,904],{},"print(bytes.fromhex(\"666c6167\"))      # b'flag'\n",[107,906,907],{"class":109,"line":135},[107,908,909],{},"print(ord('A'))                       # 65，ASCII 编码\n",[310,911,913],{"id":912},"工具-3pycryptodome","工具 3：pycryptodome",[16,915,916],{},"CTF 密码学最常用的库，三个函数：",[98,918,920],{"className":100,"code":919,"language":102,"meta":103,"style":103},"from Crypto.Util.number import long_to_bytes, bytes_to_long, inverse\n\nprint(inverse(3, 7))                    # 5：3 在模 7 下的逆元\nprint(long_to_bytes(0x666c6167))        # b'flag'\nprint(bytes_to_long(b\"flag\"))           # 1718378855\n",[53,921,922,927,931,936,941],{"__ignoreMap":103},[107,923,924],{"class":109,"line":110},[107,925,926],{},"from Crypto.Util.number import long_to_bytes, bytes_to_long, inverse\n",[107,928,929],{"class":109,"line":116},[107,930,126],{"emptyLinePlaceholder":125},[107,932,933],{"class":109,"line":122},[107,934,935],{},"print(inverse(3, 7))                    # 5：3 在模 7 下的逆元\n",[107,937,938],{"class":109,"line":129},[107,939,940],{},"print(long_to_bytes(0x666c6167))        # b'flag'\n",[107,942,943],{"class":109,"line":135},[107,944,945],{},"print(bytes_to_long(b\"flag\"))           # 1718378855\n",[16,947,210],{},[98,949,952],{"className":950,"code":951,"language":216,"meta":103},[214],"5\nb'flag'\n1718378855\n",[53,953,951],{"__ignoreMap":103},[11,955,957],{"id":956},"七小-ctf-实战单字节-xor","七、小 CTF 实战：单字节 XOR",[16,959,960],{},"题目：给出密文 hex，提示\"密钥是一个字节，明文是 flag\"。",[98,962,964],{"className":963,"code":370,"language":216,"meta":103},[214],[53,965,370],{"__ignoreMap":103},[16,967,968],{},"解题脚本：",[16,970,971,973,974,976],{},[317,972,386],{},"：密文是字节串，密钥只可能 0-255；逐个试，用 ",[53,975,390],{}," 前缀过滤正确结果。",[98,978,980],{"className":100,"code":979,"language":102,"meta":103,"style":103},"from binascii import unhexlify\n# unhexlify 与 bytes.fromhex 等价：把十六进制字符串转成字节串。\n\ncipher = unhexlify(\"242e2325393a2d301d2b311d2723313b3f\")\n\nfor k in range(256):\n    plain = bytes([b ^ k for b in cipher])\n    # 每个密文字节与候选密钥 k 异或，得到候选明文。\n    if b\"flag{\" in plain:\n        print(\"key =\", k)          # 预期 66\n        print(plain.decode())      # 预期 flag{xor_is_easy}\n        break\n",[53,981,982,987,992,996,1001,1005,1009,1013,1018,1022,1027,1032],{"__ignoreMap":103},[107,983,984],{"class":109,"line":110},[107,985,986],{},"from binascii import unhexlify\n",[107,988,989],{"class":109,"line":116},[107,990,991],{},"# unhexlify 与 bytes.fromhex 等价：把十六进制字符串转成字节串。\n",[107,993,994],{"class":109,"line":122},[107,995,126],{"emptyLinePlaceholder":125},[107,997,998],{"class":109,"line":129},[107,999,1000],{},"cipher = unhexlify(\"242e2325393a2d301d2b311d2723313b3f\")\n",[107,1002,1003],{"class":109,"line":135},[107,1004,126],{"emptyLinePlaceholder":125},[107,1006,1007],{"class":109,"line":141},[107,1008,419],{},[107,1010,1011],{"class":109,"line":147},[107,1012,429],{},[107,1014,1015],{"class":109,"line":152},[107,1016,1017],{},"    # 每个密文字节与候选密钥 k 异或，得到候选明文。\n",[107,1019,1020],{"class":109,"line":158},[107,1021,444],{},[107,1023,1024],{"class":109,"line":164},[107,1025,1026],{},"        print(\"key =\", k)          # 预期 66\n",[107,1028,1029],{"class":109,"line":170},[107,1030,1031],{},"        print(plain.decode())      # 预期 flag{xor_is_easy}\n",[107,1033,1034],{"class":109,"line":175},[107,1035,464],{},[16,1037,210],{},[98,1039,1041],{"className":1040,"code":475,"language":216,"meta":103},[214],[53,1042,475],{"__ignoreMap":103},[11,1044,1046],{"id":1045},"八小结","八、小结",[16,1048,1049],{},"密码学入门三点：编码识别（Base64/Hex/URL）、XOR 与单字节爆破、DH 的数学原理。这三样足以解决入门级密码题；RSA 与分组密码的误用攻击见《RSA 深入实战》。",[1051,1052,1053],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}",{"title":103,"searchDepth":116,"depth":116,"links":1055},[1056,1057,1058,1059,1060,1061,1062,1063],{"id":13,"depth":122,"text":14},{"id":256,"depth":122,"text":257},{"id":296,"depth":122,"text":297},{"id":529,"depth":122,"text":530},{"id":608,"depth":122,"text":609},{"id":830,"depth":122,"text":831},{"id":956,"depth":122,"text":957},{"id":1045,"depth":122,"text":1046},"编码在现实网络中的应用、单字节 XOR 的加密与爆破、DH 密钥协商的最小实现。","md",{"date":1067,"image":1068,"alt":1069,"tags":1070,"published":125},"14th Aug 2026","/blogs-img/blog3.png","CTF 密码学入门",[1071,1072,1073],"ctf","crypto","example","/blogs/ctf-crypto-overview",{"title":6,"description":1064},"blogs/12. ctf-crypto-overview","DxG_dqKWLnmfDkyKuCyhXivf1lWCPPVHNvci7wmQWK8",{"id":1079,"title":1080,"body":1081,"description":2072,"extension":1065,"meta":2073,"navigation":125,"ogImage":2074,"path":2078,"seo":2079,"stem":2080,"__hash__":2081},"content/blogs/19. ctf-crypto-rsa-deep-dive.md","RSA 深入实战：原理、小 n 分解与共模攻击",{"type":8,"value":1082,"toc":2060},[1083,1087,1090,1093,1116,1130,1132,1148,1152,1158,1161,1176,1180,1185,1190,1279,1281,1287,1294,1298,1303,1308,1312,1331,1333,1339,1345,1349,1354,1439,1441,1447,1457,1461,1469,1498,1502,1506,1516,1522,1526,1531,1536,1543,1549,1552,1558,1562,1734,1736,1742,1746,1813,1817,1882,1891,1895,1898,1904,1907,1911,1916,1930,1932,1938,1942,1951,2001,2003,2009,2013,2047,2051,2058],[11,1084,1086],{"id":1085},"一现实背景dh-与公钥密码学","一、现实背景：DH 与公钥密码学",[16,1088,1089],{},"HTTPS（TLS）流程中：先用 DH 协商对称密钥，再用 RSA/ECDSA 证书认证身份。DH 解决\"公开信道建立共享密钥\"，RSA 解决\"身份认证与加密\"。",[16,1091,1092],{},"DH 最小实现（见密码学全景篇）：",[98,1094,1096],{"className":100,"code":1095,"language":102,"meta":103,"style":103},"p, g = 0xffffffffffffffc5, 2\na, b = 1234567, 7654321\nA, B = pow(g, a, p), pow(g, b, p)\nassert pow(B, a, p) == pow(A, b, p)   # 双方共享密钥一致\n",[53,1097,1098,1102,1106,1111],{"__ignoreMap":103},[107,1099,1100],{"class":109,"line":110},[107,1101,690],{},[107,1103,1104],{"class":109,"line":116},[107,1105,705],{},[107,1107,1108],{"class":109,"line":122},[107,1109,1110],{},"A, B = pow(g, a, p), pow(g, b, p)\n",[107,1112,1113],{"class":109,"line":129},[107,1114,1115],{},"assert pow(B, a, p) == pow(A, b, p)   # 双方共享密钥一致\n",[16,1117,1118,1119,1122,1123,1125,1126,1129],{},"RSA 的安全基础是：已知 ",[53,1120,1121],{},"n"," 求 ",[53,1124,16],{},"、",[53,1127,1128],{},"q","（分解）在计算上不可行。CTF 题把参数做小或做错，让分解或绕过分解成为可能。",[11,1131,257],{"id":256},[98,1133,1134],{"className":260,"code":261,"language":262,"meta":103,"style":103},[53,1135,1136],{"__ignoreMap":103},[107,1137,1138,1140,1142,1144,1146],{"class":109,"line":110},[107,1139,270],{"class":269},[107,1141,274],{"class":273},[107,1143,277],{"class":273},[107,1145,280],{"class":273},[107,1147,283],{"class":273},[11,1149,1151],{"id":1150},"三核心知识rsa-公式","三、核心知识：RSA 公式",[98,1153,1156],{"className":1154,"code":1155,"language":216,"meta":103},[214],"n = p × q\nφ(n) = (p-1)(q-1)\ne × d ≡ 1 (mod φ(n))        # d 是 e 的模逆元\n加密：c = m^e mod n\n解密：m = c^d mod n\n",[53,1157,1155],{"__ignoreMap":103},[16,1159,1160],{},"三个必须会用的 Python 运算：",[98,1162,1164],{"className":100,"code":1163,"language":102,"meta":103,"style":103},"pow(3, 4, 7)        # 快速幂取模 = 4\npow(e, -1, phi)     # 求 e 在模 phi 下的逆元\n",[53,1165,1166,1171],{"__ignoreMap":103},[107,1167,1168],{"class":109,"line":110},[107,1169,1170],{},"pow(3, 4, 7)        # 快速幂取模 = 4\n",[107,1172,1173],{"class":109,"line":116},[107,1174,1175],{},"pow(e, -1, phi)     # 求 e 在模 phi 下的逆元\n",[11,1177,1179],{"id":1178},"四核心步骤-1手算验证","四、核心步骤 1：手算验证",[16,1181,1182,1184],{},[317,1183,319],{},"：用最小参数亲手跑通 RSA 的生成与加解密，确认公式理解正确。",[16,1186,1187,1189],{},[317,1188,386],{},"：先算 n 与 φ(n)，再求 d（e 的模逆元），最后验证 pow(c,d,n) 能还原 m。这四步是后面所有攻击的基础。",[98,1191,1193],{"className":100,"code":1192,"language":102,"meta":103,"style":103},"p, q, e = 61, 53, 17\nn = p * q\n# n = 3233，公钥模数。\nphi = (p - 1) * (q - 1)\n# φ(n) = 60 × 52 = 3120，欧拉函数值。\nd = pow(e, -1, phi)\n# pow(e, -1, phi)：求 e 在模 phi 下的乘法逆元，即私钥 d = 2753。\n\nm = 65\nc = pow(m, e, n)\n# pow(m, e, n)：快速幂取模，即 m^e mod n，加密得 c = 2790。\nprint(\"n =\", n)\nprint(\"phi =\", phi)\nprint(\"d =\", d)\nprint(\"c =\", c)\nprint(\"解密 =\", pow(c, d, n))\n# pow(c, d, n)：用私钥解密，应还原 65，验证公式正确。\n",[53,1194,1195,1200,1205,1210,1215,1220,1225,1230,1234,1239,1244,1249,1254,1259,1264,1269,1274],{"__ignoreMap":103},[107,1196,1197],{"class":109,"line":110},[107,1198,1199],{},"p, q, e = 61, 53, 17\n",[107,1201,1202],{"class":109,"line":116},[107,1203,1204],{},"n = p * q\n",[107,1206,1207],{"class":109,"line":122},[107,1208,1209],{},"# n = 3233，公钥模数。\n",[107,1211,1212],{"class":109,"line":129},[107,1213,1214],{},"phi = (p - 1) * (q - 1)\n",[107,1216,1217],{"class":109,"line":135},[107,1218,1219],{},"# φ(n) = 60 × 52 = 3120，欧拉函数值。\n",[107,1221,1222],{"class":109,"line":141},[107,1223,1224],{},"d = pow(e, -1, phi)\n",[107,1226,1227],{"class":109,"line":147},[107,1228,1229],{},"# pow(e, -1, phi)：求 e 在模 phi 下的乘法逆元，即私钥 d = 2753。\n",[107,1231,1232],{"class":109,"line":152},[107,1233,126],{"emptyLinePlaceholder":125},[107,1235,1236],{"class":109,"line":158},[107,1237,1238],{},"m = 65\n",[107,1240,1241],{"class":109,"line":164},[107,1242,1243],{},"c = pow(m, e, n)\n",[107,1245,1246],{"class":109,"line":170},[107,1247,1248],{},"# pow(m, e, n)：快速幂取模，即 m^e mod n，加密得 c = 2790。\n",[107,1250,1251],{"class":109,"line":175},[107,1252,1253],{},"print(\"n =\", n)\n",[107,1255,1256],{"class":109,"line":181},[107,1257,1258],{},"print(\"phi =\", phi)\n",[107,1260,1261],{"class":109,"line":187},[107,1262,1263],{},"print(\"d =\", d)\n",[107,1265,1266],{"class":109,"line":193},[107,1267,1268],{},"print(\"c =\", c)\n",[107,1270,1271],{"class":109,"line":198},[107,1272,1273],{},"print(\"解密 =\", pow(c, d, n))\n",[107,1275,1276],{"class":109,"line":204},[107,1277,1278],{},"# pow(c, d, n)：用私钥解密，应还原 65，验证公式正确。\n",[16,1280,210],{},[98,1282,1285],{"className":1283,"code":1284,"language":216,"meta":103},[214],"n = 3233\nphi = 3120\nd = 2753\nc = 2790\n解密 = 65\n",[53,1286,1284],{"__ignoreMap":103},[16,1288,1289,1290,1293],{},"攻击者只有 ",[53,1291,1292],{},"(n=3233, e=17, c=2790)","，没有 d。要解密，第一步就是分解 n。",[11,1295,1297],{"id":1296},"五核心步骤-2小-n-分解","五、核心步骤 2：小 n 分解",[16,1299,1300,1302],{},[317,1301,319],{},"：拿到公钥后第一步永远是\"尝试分解 n\"——一旦分解成功，私钥直接可算。",[16,1304,1305,1307],{},[317,1306,386],{},"：n 小 → 本地工具直接分解；n 大 → 先查 FactorDB 是否有人分解过。分解出 p、q 后按公式算出 d 解密。",[310,1309,1311],{"id":1310},"_51-用-sympy-分解","5.1 用 sympy 分解",[98,1313,1315],{"className":100,"code":1314,"language":102,"meta":103,"style":103},"from sympy import factorint\n\nprint(factorint(3233))\n",[53,1316,1317,1322,1326],{"__ignoreMap":103},[107,1318,1319],{"class":109,"line":110},[107,1320,1321],{},"from sympy import factorint\n",[107,1323,1324],{"class":109,"line":116},[107,1325,126],{"emptyLinePlaceholder":125},[107,1327,1328],{"class":109,"line":122},[107,1329,1330],{},"print(factorint(3233))\n",[16,1332,210],{},[98,1334,1337],{"className":1335,"code":1336,"language":216,"meta":103},[214],"{53: 1, 61: 1}\n",[53,1338,1336],{"__ignoreMap":103},[16,1340,1341,1342,644],{},"得到 ",[53,1343,1344],{},"p=53, q=61",[310,1346,1348],{"id":1347},"_52-完整解密验证公式","5.2 完整解密（验证公式）",[16,1350,1351,1353],{},[317,1352,386],{},"：分解得到 p、q → 算 φ(n) → 算 d → pow(c,d,n) → long_to_bytes 转回文本。",[98,1355,1357],{"className":100,"code":1356,"language":102,"meta":103,"style":103},"from Crypto.Util.number import long_to_bytes\n# long_to_bytes(x)：把大整数转成字节串（高位在前），用于查看解密出的文本。\n\nn = 3233\ne = 17\nc = 2790\np, q = 53, 61\n\nphi = (p - 1) * (q - 1)\nd = pow(e, -1, phi)\nm = pow(c, d, n)\n# 解密：m = c^d mod n。\n\nprint(\"m =\", m)\nprint(long_to_bytes(m))\n# 65 对应 ASCII 字符 'A'，说明这个最小例子只能承载一个字节的明文；\n# 真实题目的 n 足够大，能装下整个 flag，方法完全一样。\n",[53,1358,1359,1364,1369,1373,1378,1383,1388,1393,1397,1401,1405,1410,1415,1419,1424,1429,1434],{"__ignoreMap":103},[107,1360,1361],{"class":109,"line":110},[107,1362,1363],{},"from Crypto.Util.number import long_to_bytes\n",[107,1365,1366],{"class":109,"line":116},[107,1367,1368],{},"# long_to_bytes(x)：把大整数转成字节串（高位在前），用于查看解密出的文本。\n",[107,1370,1371],{"class":109,"line":122},[107,1372,126],{"emptyLinePlaceholder":125},[107,1374,1375],{"class":109,"line":129},[107,1376,1377],{},"n = 3233\n",[107,1379,1380],{"class":109,"line":135},[107,1381,1382],{},"e = 17\n",[107,1384,1385],{"class":109,"line":141},[107,1386,1387],{},"c = 2790\n",[107,1389,1390],{"class":109,"line":147},[107,1391,1392],{},"p, q = 53, 61\n",[107,1394,1395],{"class":109,"line":152},[107,1396,126],{"emptyLinePlaceholder":125},[107,1398,1399],{"class":109,"line":158},[107,1400,1214],{},[107,1402,1403],{"class":109,"line":164},[107,1404,1224],{},[107,1406,1407],{"class":109,"line":170},[107,1408,1409],{},"m = pow(c, d, n)\n",[107,1411,1412],{"class":109,"line":175},[107,1413,1414],{},"# 解密：m = c^d mod n。\n",[107,1416,1417],{"class":109,"line":181},[107,1418,126],{"emptyLinePlaceholder":125},[107,1420,1421],{"class":109,"line":187},[107,1422,1423],{},"print(\"m =\", m)\n",[107,1425,1426],{"class":109,"line":193},[107,1427,1428],{},"print(long_to_bytes(m))\n",[107,1430,1431],{"class":109,"line":198},[107,1432,1433],{},"# 65 对应 ASCII 字符 'A'，说明这个最小例子只能承载一个字节的明文；\n",[107,1435,1436],{"class":109,"line":204},[107,1437,1438],{},"# 真实题目的 n 足够大，能装下整个 flag，方法完全一样。\n",[16,1440,210],{},[98,1442,1445],{"className":1443,"code":1444,"language":216,"meta":103},[214],"m = 65\nb'A'\n",[53,1446,1444],{"__ignoreMap":103},[16,1448,1449,1452,1453,1456],{},[53,1450,1451],{},"65"," 对应的 ASCII 字符是 ",[53,1454,1455],{},"A","。真实的明文是 flag 对应的长整数，但解密公式完全相同。",[310,1458,1460],{"id":1459},"_53-用-factordb-查","5.3 用 FactorDB 查",[16,1462,1463,1464,1468],{},"如果 n 太大本地分解不动，去 ",[288,1465,1466],{"href":1466,"rel":1467},"https://factordb.com",[292]," 粘贴 n 查询，或：",[98,1470,1472],{"className":100,"code":1471,"language":102,"meta":103,"style":103},"from factordb.factordb import FactorDB\n\nf = FactorDB(n)\nf.connect()\np, q = f.get_factor_list()\n",[53,1473,1474,1479,1483,1488,1493],{"__ignoreMap":103},[107,1475,1476],{"class":109,"line":110},[107,1477,1478],{},"from factordb.factordb import FactorDB\n",[107,1480,1481],{"class":109,"line":116},[107,1482,126],{"emptyLinePlaceholder":125},[107,1484,1485],{"class":109,"line":122},[107,1486,1487],{},"f = FactorDB(n)\n",[107,1489,1490],{"class":109,"line":129},[107,1491,1492],{},"f.connect()\n",[107,1494,1495],{"class":109,"line":135},[107,1496,1497],{},"p, q = f.get_factor_list()\n",[11,1499,1501],{"id":1500},"六核心步骤-3共模攻击","六、核心步骤 3：共模攻击",[310,1503,1505],{"id":1504},"_61-场景","6.1 场景",[16,1507,1508,1509,1511,1512,1515],{},"同一个 ",[53,1510,1121],{},"、两个互质的 ",[53,1513,1514],{},"e1, e2","、同一明文：",[98,1517,1520],{"className":1518,"code":1519,"language":216,"meta":103},[214],"c1 = m^e1 mod n\nc2 = m^e2 mod n\n",[53,1521,1519],{"__ignoreMap":103},[310,1523,1525],{"id":1524},"_62-数学原理","6.2 数学原理",[16,1527,1528,1530],{},[317,1529,319],{},"：理解为什么\"两个密文能拼出明文\"。",[16,1532,1533,1535],{},[317,1534,386],{},"：扩展欧几里得求出 a、b 使 a×e1+b×e2=1；把两个密文分别取 a、b 次方再相乘，指数恰好合并成 1，明文就还原了。验证方式：恢复结果与原始 m 相等。",[16,1537,1538,1539,1542],{},"扩展欧几里得算法求出 ",[53,1540,1541],{},"a, b"," 使：",[98,1544,1547],{"className":1545,"code":1546,"language":216,"meta":103},[214],"a × e1 + b × e2 = 1\n",[53,1548,1546],{"__ignoreMap":103},[16,1550,1551],{},"则：",[98,1553,1556],{"className":1554,"code":1555,"language":216,"meta":103},[214],"c1^a × c2^b\n= m^(a×e1) × m^(b×e2)\n= m^(a×e1 + b×e2)\n= m^1 = m (mod n)\n",[53,1557,1555],{"__ignoreMap":103},[310,1559,1561],{"id":1560},"_63-完整代码","6.3 完整代码",[98,1563,1565],{"className":100,"code":1564,"language":102,"meta":103,"style":103},"def egcd(a, b):\n    # 扩展欧几里得算法：返回 (gcd, x, y)，满足 a*x + b*y = gcd(a,b)。\n    if b == 0:\n        return a, 1, 0\n    g, x, y = egcd(b, a % b)\n    return g, y, x - (a // b) * y\n\nn = 3233\ne1, e2 = 17, 19\nm = 65\nc1 = pow(m, e1, n)      # 2790：e1 加密结果\nc2 = pow(m, e2, n)      # 232：e2 加密结果\n\n_, a, b = egcd(e1, e2)\n# 忽略第一个返回值（gcd=1），取 a、b。\nprint(\"a =\", a, \"b =\", b, \"a*e1+b*e2 =\", a*e1 + b*e2)\n# 应输出 a=9, b=-8，验证 9×17 + (-8)×19 = 1。\n\n# 负指数要先取模逆元\nif a \u003C 0:\n    c1 = pow(pow(c1, -1, n), -a, n)\n    # pow(c1, -1, n)：c1 在模 n 下的逆元；再取 -a 次方（-a 为正）。\nelse:\n    c1 = pow(c1, a, n)\nif b \u003C 0:\n    c2 = pow(pow(c2, -1, n), -b, n)\nelse:\n    c2 = pow(c2, b, n)\n\nrecovered = (c1 * c2) % n\n# c1^a × c2^b = m^(a·e1 + b·e2) = m^1 = m (mod n)。\nprint(\"恢复 m =\", recovered)\n# 预期 65，与原始 m 相等，攻击成功。\n",[53,1566,1567,1572,1577,1582,1587,1592,1597,1601,1605,1610,1614,1619,1624,1628,1633,1638,1643,1648,1652,1657,1662,1667,1672,1677,1682,1688,1694,1699,1705,1710,1716,1722,1728],{"__ignoreMap":103},[107,1568,1569],{"class":109,"line":110},[107,1570,1571],{},"def egcd(a, b):\n",[107,1573,1574],{"class":109,"line":116},[107,1575,1576],{},"    # 扩展欧几里得算法：返回 (gcd, x, y)，满足 a*x + b*y = gcd(a,b)。\n",[107,1578,1579],{"class":109,"line":122},[107,1580,1581],{},"    if b == 0:\n",[107,1583,1584],{"class":109,"line":129},[107,1585,1586],{},"        return a, 1, 0\n",[107,1588,1589],{"class":109,"line":135},[107,1590,1591],{},"    g, x, y = egcd(b, a % b)\n",[107,1593,1594],{"class":109,"line":141},[107,1595,1596],{},"    return g, y, x - (a // b) * y\n",[107,1598,1599],{"class":109,"line":147},[107,1600,126],{"emptyLinePlaceholder":125},[107,1602,1603],{"class":109,"line":152},[107,1604,1377],{},[107,1606,1607],{"class":109,"line":158},[107,1608,1609],{},"e1, e2 = 17, 19\n",[107,1611,1612],{"class":109,"line":164},[107,1613,1238],{},[107,1615,1616],{"class":109,"line":170},[107,1617,1618],{},"c1 = pow(m, e1, n)      # 2790：e1 加密结果\n",[107,1620,1621],{"class":109,"line":175},[107,1622,1623],{},"c2 = pow(m, e2, n)      # 232：e2 加密结果\n",[107,1625,1626],{"class":109,"line":181},[107,1627,126],{"emptyLinePlaceholder":125},[107,1629,1630],{"class":109,"line":187},[107,1631,1632],{},"_, a, b = egcd(e1, e2)\n",[107,1634,1635],{"class":109,"line":193},[107,1636,1637],{},"# 忽略第一个返回值（gcd=1），取 a、b。\n",[107,1639,1640],{"class":109,"line":198},[107,1641,1642],{},"print(\"a =\", a, \"b =\", b, \"a*e1+b*e2 =\", a*e1 + b*e2)\n",[107,1644,1645],{"class":109,"line":204},[107,1646,1647],{},"# 应输出 a=9, b=-8，验证 9×17 + (-8)×19 = 1。\n",[107,1649,1650],{"class":109,"line":771},[107,1651,126],{"emptyLinePlaceholder":125},[107,1653,1654],{"class":109,"line":776},[107,1655,1656],{},"# 负指数要先取模逆元\n",[107,1658,1659],{"class":109,"line":782},[107,1660,1661],{},"if a \u003C 0:\n",[107,1663,1664],{"class":109,"line":788},[107,1665,1666],{},"    c1 = pow(pow(c1, -1, n), -a, n)\n",[107,1668,1669],{"class":109,"line":794},[107,1670,1671],{},"    # pow(c1, -1, n)：c1 在模 n 下的逆元；再取 -a 次方（-a 为正）。\n",[107,1673,1674],{"class":109,"line":800},[107,1675,1676],{},"else:\n",[107,1678,1679],{"class":109,"line":806},[107,1680,1681],{},"    c1 = pow(c1, a, n)\n",[107,1683,1685],{"class":109,"line":1684},25,[107,1686,1687],{},"if b \u003C 0:\n",[107,1689,1691],{"class":109,"line":1690},26,[107,1692,1693],{},"    c2 = pow(pow(c2, -1, n), -b, n)\n",[107,1695,1697],{"class":109,"line":1696},27,[107,1698,1676],{},[107,1700,1702],{"class":109,"line":1701},28,[107,1703,1704],{},"    c2 = pow(c2, b, n)\n",[107,1706,1708],{"class":109,"line":1707},29,[107,1709,126],{"emptyLinePlaceholder":125},[107,1711,1713],{"class":109,"line":1712},30,[107,1714,1715],{},"recovered = (c1 * c2) % n\n",[107,1717,1719],{"class":109,"line":1718},31,[107,1720,1721],{},"# c1^a × c2^b = m^(a·e1 + b·e2) = m^1 = m (mod n)。\n",[107,1723,1725],{"class":109,"line":1724},32,[107,1726,1727],{},"print(\"恢复 m =\", recovered)\n",[107,1729,1731],{"class":109,"line":1730},33,[107,1732,1733],{},"# 预期 65，与原始 m 相等，攻击成功。\n",[16,1735,210],{},[98,1737,1740],{"className":1738,"code":1739,"language":216,"meta":103},[214],"a = 9 b = -8 a*e1+b*e2 = 1\n恢复 m = 65\n",[53,1741,1739],{"__ignoreMap":103},[11,1743,1745],{"id":1744},"七其他常见攻击速查","七、其他常见攻击速查",[20,1747,1748,1758],{},[23,1749,1750],{},[26,1751,1752,1755],{},[29,1753,1754],{},"场景",[29,1756,1757],{},"攻击",[39,1759,1760,1778,1786,1794,1802],{},[26,1761,1762,1771],{},[44,1763,1764,1767,1768],{},[53,1765,1766],{},"e=3"," 且 ",[53,1769,1770],{},"m^e \u003C n",[44,1772,1773,1774,1777],{},"直接开立方根（",[53,1775,1776],{},"gmpy2.iroot(c, 3)","）",[26,1779,1780,1783],{},[44,1781,1782],{},"同一明文发给 3 个不同 n",[44,1784,1785],{},"Håstad 广播攻击（中国剩余定理 + 开方）",[26,1787,1788,1791],{},[44,1789,1790],{},"e 特别大（d 很小）",[44,1792,1793],{},"Wiener 攻击（连分数）",[26,1795,1796,1799],{},[44,1797,1798],{},"p、q 很接近",[44,1800,1801],{},"Fermat 分解（平方差）",[26,1803,1804,1807],{},[44,1805,1806],{},"泄露 dp",[44,1808,1809,1810],{},"枚举 k，",[53,1811,1812],{},"p = (e*dp-1)//k + 1",[11,1814,1816],{"id":1815},"八必学工具","八、必学工具",[20,1818,1819,1832],{},[23,1820,1821],{},[26,1822,1823,1826,1829],{},[29,1824,1825],{},"工具",[29,1827,1828],{},"3 个核心功能",[29,1830,1831],{},"示例",[39,1833,1834,1853,1869],{},[26,1835,1836,1839,1850],{},[44,1837,1838],{},"pycryptodome",[44,1840,1841,1125,1844,1125,1847],{},[53,1842,1843],{},"long_to_bytes",[53,1845,1846],{},"inverse",[53,1848,1849],{},"pow(m,e,n)",[44,1851,1852],{},"见上文",[26,1854,1855,1858,1864],{},[44,1856,1857],{},"sympy",[44,1859,1860,1863],{},[53,1861,1862],{},"factorint"," 分解",[44,1865,1866],{},[53,1867,1868],{},"factorint(3233)",[26,1870,1871,1874,1877],{},[44,1872,1873],{},"RsaCtfTool",[44,1875,1876],{},"自动尝试多种攻击、从公钥文件读取",[44,1878,1879],{},[53,1880,1881],{},"./RsaCtfTool.py -n N -e E --uncipher C --attack all",[16,1883,1884,1885,1890],{},"FactorDB（",[288,1886,1889],{"href":1887,"rel":1888},"https://factordb.com%EF%BC%89%E7%94%A8%E4%BA%8E%E6%9F%A5",[292],"https://factordb.com）用于查"," n 的已知分解。",[11,1892,1894],{"id":1893},"九小-ctf-实战","九、小 CTF 实战",[16,1896,1897],{},"题目：",[98,1899,1902],{"className":1900,"code":1901,"language":216,"meta":103},[214],"n = 1000000016000000063\ne = 65537\nc = 856868007642616233\n",[53,1903,1901],{"__ignoreMap":103},[16,1905,1906],{},"说明：真实题目的 n 有数百位，这里为了能在本地秒级分解，使用 19 位 n；分解方法完全一致。",[310,1908,1910],{"id":1909},"第一步分解","第一步：分解",[16,1912,1913,1915],{},[317,1914,386],{},"：n 只有 19 位，sympy 的 factorint 用试除/快速算法秒级分解；若 n 更大，改用 yafu 或查 FactorDB。",[98,1917,1919],{"className":100,"code":1918,"language":102,"meta":103,"style":103},"from sympy import factorint\nprint(factorint(1000000016000000063))\n",[53,1920,1921,1925],{"__ignoreMap":103},[107,1922,1923],{"class":109,"line":110},[107,1924,1321],{},[107,1926,1927],{"class":109,"line":116},[107,1928,1929],{},"print(factorint(1000000016000000063))\n",[16,1931,210],{},[98,1933,1936],{"className":1934,"code":1935,"language":216,"meta":103},[214],"{1000000007: 1, 1000000009: 1}\n",[53,1937,1935],{"__ignoreMap":103},[310,1939,1941],{"id":1940},"第二步解密","第二步：解密",[16,1943,1944,1946,1947,1950],{},[317,1945,386],{},"：与 5.2 完全相同的公式，只是参数换成实际值；输出 ",[53,1948,1949],{},"b'flag'"," 说明解密成功。",[98,1952,1954],{"className":100,"code":1953,"language":102,"meta":103,"style":103},"from Crypto.Util.number import long_to_bytes\n\nn = 1000000016000000063\ne = 65537\nc = 856868007642616233\np, q = 1000000007, 1000000009\n\nphi = (p - 1) * (q - 1)\nd = pow(e, -1, phi)\nprint(long_to_bytes(pow(c, d, n)))\n",[53,1955,1956,1960,1964,1969,1974,1979,1984,1988,1992,1996],{"__ignoreMap":103},[107,1957,1958],{"class":109,"line":110},[107,1959,1363],{},[107,1961,1962],{"class":109,"line":116},[107,1963,126],{"emptyLinePlaceholder":125},[107,1965,1966],{"class":109,"line":122},[107,1967,1968],{},"n = 1000000016000000063\n",[107,1970,1971],{"class":109,"line":129},[107,1972,1973],{},"e = 65537\n",[107,1975,1976],{"class":109,"line":135},[107,1977,1978],{},"c = 856868007642616233\n",[107,1980,1981],{"class":109,"line":141},[107,1982,1983],{},"p, q = 1000000007, 1000000009\n",[107,1985,1986],{"class":109,"line":147},[107,1987,126],{"emptyLinePlaceholder":125},[107,1989,1990],{"class":109,"line":152},[107,1991,1214],{},[107,1993,1994],{"class":109,"line":158},[107,1995,1224],{},[107,1997,1998],{"class":109,"line":164},[107,1999,2000],{},"print(long_to_bytes(pow(c, d, n)))\n",[16,2002,210],{},[98,2004,2007],{"className":2005,"code":2006,"language":216,"meta":103},[214],"b'flag'\n",[53,2008,2006],{"__ignoreMap":103},[310,2010,2012],{"id":2011},"第三步工具验证","第三步：工具验证",[98,2014,2016],{"className":260,"code":2015,"language":262,"meta":103,"style":103},"./RsaCtfTool.py -n 1000000016000000063 -e 65537 --uncipher 856868007642616233 --attack all\n",[53,2017,2018],{"__ignoreMap":103},[107,2019,2020,2023,2026,2029,2032,2035,2038,2041,2044],{"class":109,"line":110},[107,2021,2022],{"class":269},"./RsaCtfTool.py",[107,2024,2025],{"class":516}," -n",[107,2027,2028],{"class":516}," 1000000016000000063",[107,2030,2031],{"class":516}," -e",[107,2033,2034],{"class":516}," 65537",[107,2036,2037],{"class":516}," --uncipher",[107,2039,2040],{"class":516}," 856868007642616233",[107,2042,2043],{"class":516}," --attack",[107,2045,2046],{"class":273}," all\n",[11,2048,2050],{"id":2049},"十小结","十、小结",[16,2052,2053,2054,2057],{},"RSA 解题顺序：",[317,2055,2056],{},"先查 n 能否分解（FactorDB/sympy）→ 能分解就按公式解密 → 不能分解就检查共模、低指数、Wiener、Fermat 等结构性错误","。每个攻击对应一种\"实现错误\"，识别特征后套模板即可。",[1051,2059,1053],{},{"title":103,"searchDepth":116,"depth":116,"links":2061},[2062,2063,2064,2065,2066,2067,2068,2069,2070,2071],{"id":1085,"depth":122,"text":1086},{"id":256,"depth":122,"text":257},{"id":1150,"depth":122,"text":1151},{"id":1178,"depth":122,"text":1179},{"id":1296,"depth":122,"text":1297},{"id":1500,"depth":122,"text":1501},{"id":1744,"depth":122,"text":1745},{"id":1815,"depth":122,"text":1816},{"id":1893,"depth":122,"text":1894},{"id":2049,"depth":122,"text":2050},"RSA的DH 与公钥密码学背景、数论公式与小数字手算、小 n 分解解密、共模攻击的数学推导与代码。",{"date":1067,"image":2074,"alt":2075,"tags":2076,"published":125},"/blogs-img/blog7.png","RSA 深入实战",[1071,1072,2077,1073],"rsa","/blogs/ctf-crypto-rsa-deep-dive",{"title":1080,"description":2072},"blogs/19. ctf-crypto-rsa-deep-dive","cUGS-VePAPrg-FWNvg-OJ725RRZNhFybQstAdcvf9qo",1786796568539]