☰
WinRAR rarreg.key 生成原理深度解析:基于复合域 GF((2^15)^17) 的 SM2 变体 ECC 签名算法
2026/10/6 2:26:55 网站建设 项目流程
  • 密码学
  • 逆向工程
  • CLI

【免费下载链接】winrar-keygen

Principle of WinRAR key generation.

项目地址:https://gitcode.com/gh_mirrors/wi/winrar-keygen
点击查看免费下载

导读:WinRAR 的授权文件rarreg.key并非简单的用户名拼接,而是由一套基于椭圆曲线密码学(ECC)的签名算法动态生成。本文以仓库文档 README.HOW_DOES_IT_WORK.md 为骨架,完整拆解其背后的数学构造——定义在复合域 GF((2^15)^17) 上的 SM2 变体数字签名方案,包括域构造、椭圆曲线参数、特殊 SHA1 哈希处理、签名与私钥派生算法,以及最终组装rarreg.key的 13 步完整流程,并逐一对齐仓库源码中的实现细节。读完本文,你将能理解 WinRAR 授权文件的全部生成链路,并能在源码中找到每一步对应的实现位置。

1. 背景:rarreg.key 与 SM2 变体 ECC 签名

WinRAR 使用基于 ECC 的签名算法来生成rarreg.key。该算法是中国 SM2 数字签名算法的一个变体,与常见的标准 ECDSA 不同,WinRAR 选用的椭圆曲线定义在一个复合域GF((2^15)^17) 之上。

整个算法链条可以概括为五个环节,也是本文随后依次展开的主线:

  1. 复合域构造:GF(2^15) 的 17 次扩张,得到 GF((2^15)^17);
  2. 椭圆曲线:定义在复合域上的二元域曲线及其基点 G、阶 n;
  3. 消息哈希:将输入消息的 SHA1 状态值直接构造成大整数 h;
  4. 签名算法:SM2 变体的 ECC 签名,输出 (r, s);
  5. 密钥派生:由用户名等输入数据派生私钥,再组装输出rarreg.key。

2. 复合域 GF((2^15)^17) 的构造

2.1 基域 GF(2^15)

基域 GF(2^15) 中的元素采用**标准基(多项式基)**表示,其不可约多项式为:

即x^15 + x + 1,其中每个系数都在 GF(2) 中。若以

(即 {1, α, α², …, α^14})作为基域的标准基,则 GF(2^15) 中的任意元素 A 可以表示为:

由于 x^15 + x + 1 是本原多项式,GF(2^15) 的乘法群由 α 生成,阶为 2^15 − 1 = 0x7FFF。这一点在源码中体现为GF2p15LogExpTableType = uint16_t[0x8000]的对数/指数查表(参见 WinRarConfig.hpp):InitializeGF2p15Table用temp *= 2,若溢出则temp ^= 0x8003完成模 x^15+x+1 归约,恰好对应生成元乘法。

2.2 二次扩张构造复合域

复合域 GF((2^15)^17) 的不可约多项式为:

即y^17 + y^3 + 1,其中每个系数都在 GF(2^15) 中。以

(即 {1, β, β², …, β^16})作为复合域标准基,则复合域中任意元素 B 可表示为:

源码中GF2p15p17Traits(WinRarConfig.hpp)正是这一结构的直接映射:ElementType为uint16_t Items[17],即 17 个 15 位系数;BitSizeValue = 15 * 17 = 255;Verify校验每个系数 < 0x8000;乘法先做教科书式全乘(FullMultiplySchoolBook,依赖 GF(2^15) 对数/指数表),再按y^17 + y^3 + 1归约(ModularReduction中A[i-17] ^= A[i]、A[i-14] ^= A[i]对应 y^17 与 y^3 两项)。

2.3 元素与 255 位整数的映射

为表述方便,文档用255 位整数 D表示复合域中的元素 B,其映射关系为:

从 README.VERIFY_Point_G.md 给出的转换 Python 代码可以看出,这个映射按**每 15 位一段(小端顺序)**把 255 位整数切分成 17 个 15 位系数:

def to_field_repr(val, bits=15, count=17): mask = (1 << bits) - 1 result = [] for _ in range(count): result.append(val & mask) val >>= bits return result

3. 定义在复合域上的椭圆曲线

WinRAR 使用的椭圆曲线方程为:

即二元域标准曲线形式y² + xy = x³ + ax² + b中取 a = 0、b = 161(0xA1)。这与源码中曲线定义完全一致(WinRarConfig.hpp):

static inline const EllipticCurveGF2m<GaloisField<GF2p15p17Traits>> Curve{ { GaloisFieldInitByZero{} }, // A = 0 { GaloisFieldInitByElement{}, { 161 } } // B = 161 };

基点 G 为:

其大整数表示为(README.VERIFY_Point_G.md):

Gx = 0x56fdcbc6a27acee0cc2996e0096ae74feb1acf220a2341b898b549440297b8cc Gy = 0x20da32e8afc90b7cf0e76bde44496b4d0794054e6ea60f388682463132f931a7

转换到 17×15 位小端分段表示后,与 WinRarConfig.hpp 中基点 G 的 34 个 16 位系数逐一对齐(Gx 系数以0x38CC, 0x052F, …, 0x56FD开头,Gy 系数以0x31A7, 0x65F2, …, 0x20DA开头)。仓库还提供了完整的落点验证脚本 README.VERIFY_Point_G.md:在纯 Python 中实现 GF(2^15) 乘法(模 x^15+x+1)、GF((2^15)^17) 多项式乘法与归约(模 y^17+y^3+1),然后通过y² + xy == x³ + b校验 G 与公钥 PK 是否在曲线上。

基点 G 的阶 n 为:

即n = 0x1026dd85081b82314691ced9bbec30547840e4bf72d8b5e0d258442bbcd31(WinRarConfig.hpp),共 241 位。这个阶的位长是后续私钥合法性判断的关键(见第 6 节)。

4. 消息哈希:SHA1 状态值直接构造成大整数

设输入消息为:

即长度为 l 的消息 M。其标准 SHA1 值为:

其中 s₀…s₄ 是 SHA1 输出时的 5 个状态值。通常,最终 SHA1 值是这 5 个状态值按大端序各自序列化后拼接而成的 160 字节(实为 20 字节)摘要。

然而 WinRAR 并不对这 5 个状态值做标准序列化。相反,它直接用一个大整数 h 作为输入消息的哈希:

源码实现GenerateHashInteger(WinRarKeygen.hpp)印证了这一点:它把 SHA1 的 5 个 32 位状态字逐个大端字节序转换后写入RawHash[0..4],再填入RawHash[5..9]为固定常量(注释注明是"SHA1("") with all-zeroed initial value",即0x0ffd8d43, 0xb4e33c7c, 0x53461bd1, 0x0f27a546, 0x1050d90d),最终取前240 位构成哈希整数 h。

5. ECC 数字签名算法(SM2 变体)

设私钥为 k、公钥为 P,则必然有:

即P = k·G(源码中GeneratePublicKey直接计算__ConfigType::G * PrivateKey,见 WinRarKeygen.hpp)。

以 h 表示输入数据的哈希,WinRAR 使用如下算法完成签名:

  1. 生成随机大整数 Rnd,满足条件:

    即 0 < Rnd < n。实现上GenerateRandomInteger(WinRarKeygen.hpp)以srand(time(nullptr))为种子,生成 15 个 16 位随机值组成 240 位整数——由于 240 位 < 241 位的阶 n,天然满足 Rnd < n。

  2. 计算 r:

    其中 X(Rnd·G) 表示取 Rnd·G 的 X 坐标,并把它从 GF((2^15)^17) 转换回大整数。源码(WinRarKeygen.hpp)为:

    Signature.r.Load(false, (__ConfigType::G * Random).GetX().Dump(), true); Signature.r += Hash; Signature.r %= __ConfigType::Order;

    若 r = 0 或 r + Rnd = n(即 r ≡ −Rnd mod n),则回到步骤 1 重新生成随机数。

  3. 计算 s:

    即s = (Rnd − k·r) mod n。源码(WinRarKeygen.hpp)为:

    Signature.s = Random - __ConfigType::PrivateKey * Signature.r; Signature.s %= __ConfigType::Order;

    若 s = 0,回到步骤 1。

  4. 输出签名对 (r, s)。

与标准方案对比:标准 SM2 中 r 取(e + x1) mod n(e 为经 ZA 处理的哈希),而 s 形如(1 + dA)^−1 · (k − r·dA) mod n;WinRAR 保留了 r 的"哈希加 x 坐标"形态(此处 e 直接取 240 位哈希整数 h),但 s 的计算简化为了Rnd − k·r mod n,省略了(1+d)^−1因子。从公式结构与源码实现(WinRarKeygen.hpp)可以推断,这是 SM2 框架下的一种简化变体,而非标准的 ECDSA(标准 ECDSA 的 s 还包含随机数逆元项)。

6. WinRAR 私钥生成算法(种子 → 240 位私钥)

设输入数据为:

即长度为 l 的数据 T。WinRAR 用它生成私钥 k,过程如下:

  1. 用 g₀…g₅ 表示 6 个 32 位整数,满足:

  2. 令g₀ = 0。

  3. 若 l ≠ 0,计算 T 的 SHA1 值,并把 SHA1 状态值 Sᵢ 赋给 gᵢ₊₁(i = 0…4):

    否则(l = 0)令:

    即 g₁…g₅ 取固定常量。源码中对应GeneratePrivateKey(WinRarKeygen.hpp):有种子时取 SHA1 摘要的 5 个 32 位字(大端转换后)填入Generator[1..5];无种子时直接填入0xeb3eb781, 0x50265329, 0xdc5ef4a3, 0x6847b9d5, 0xcde43b4c。

  4. 把 g₀ 视为计数器,自增 1,计算:

    取 S₀ 的最低 16 位记为 K_g0。对应源码循环(WinRarKeygen.hpp):Generator[0] = i + 1,对 24 字节的 Generator 计算 SHA1,取第一个状态字的低 16 位写入RawPrivateKey[i]。

  5. 将步骤 4 再重复14 次(共 15 轮,i = 1…15),得到 k₁…k₁₅。

  6. 输出私钥:

    即k = k₁ + k₂·2¹⁶ + k₃·2³² + … + k₁₅·2²²⁴,是一个 15×16 = 240 位的整数。源码注释(WinRarKeygen.hpp)特别指出:Order有 241 位,而RawPrivateKey最多只有 240 位,因此派生出的私钥必然小于阶 n,必然是合法私钥,无需再做范围校验。

7. WinRAR 的固定私钥与公钥

WinRAR 自己的私钥 k 为:

即k = 0x59fe6abcca90bdb95f0105271fa85fb9f11f467450c1ae9044b7fd61d65e(240 位,见 WinRarConfig.hpp)。该私钥正是用第 6 节的算法、以长度 l = 0 的空数据为种子生成的(源码注释标注"Generated byWinRarKeygen<WinRarConfig>::GeneratePrivateKey(nullptr, 0);")。

对应的公钥 P 为:

源码中以 17×15 位小端分段给出(WinRarConfig.hpp):PKx 系数以0x3A1A, 0x1109, …, 0x3861开头,PKy 系数以0x6C20, 0x6027, …, 0x12B6开头,与 README.VERIFY_Point_G.md 中的验证脚本输入完全一致。

8. rarreg.key 的完整生成流程

生成授权文件rarreg.key需要两个输入参数:

  1. 用户名 U:ANSI 编码字符串(不含null 终止符)。
  2. 授权类型 L:ANSI 编码字符串(不含null 终止符)。

完整流程如下(源码总入口为GenerateRegisterInfo,见 WinRarKeygen.hpp):

8.1 由用户名派生密钥对并得到 Temp

用第 6 节算法以 U 为种子生成私钥 kᵤ 与公钥 pᵤ,然后将公钥以SM2 压缩公钥格式输出为十六进制字符串 Temp。Temp 长度应为 64,不足 64 位时用字符'0'左侧补齐。对应源码GeneratePublicKeySM2CompressedFormat(WinRarKeygen.hpp):公钥先按 SEC1 压缩(0x02/0x03前缀 + X 坐标),再变换为"2·x + 奇偶位"的 256 位整数,十六进制化后补齐到 64 字符。

8.2 由 Data3 派生 Data0

令 Data3 为:

即固定前缀"60"后接 Temp 的前 48 个十六进制字符(共 50 字符),对应源码HelperStringFormat("60%.48s", temp.c_str())。再用第 6 节算法以 Data3 为种子生成私钥 k_data3 与公钥 p_data3,同样以 SM2 压缩公钥格式输出十六进制串Data0,长度 64,不足补'0'。

8.3 计算 UID

令 UID 为:

即UID = Temp 的后 16 个字符 + Data0 的前 4 个字符(共 20 个十六进制字符),对应源码HelperStringFormat("%.16s%.4s", temp.c_str() + 48, RegInfo.Items[0].c_str())。

8.4 对授权类型签名得到 Data1

用第 5 节签名算法,以 L 为消息、第 7 节的固定私钥 k 签名,得到 (rₗ, sₗ)。rₗ 与 sₗ 的位长不得超过 240,否则重复本步。转换为不带"0x"前缀的十六进制串 SZrₗ、SZsₗ,长度不足 60 时补'0'。然后令 Data1 为:

即Data1 = "60" + SZsₗ + SZrₗ(2 + 60 + 60 = 122 字符)。源码循环直至 r、s 十六进制串均恰好为 60 长度,再以"60%s%s"(先 s 后 r)格式化(WinRarKeygen.hpp)。

8.5 对 (U + Data0) 签名得到 Data2

重新令 Temp 为:

即Temp = U + Data0(对应源码temp = RegInfo.UserName + RegInfo.Items[0];)。用第 5 节算法以 Temp 为消息、固定私钥 k 签名,得到 (r_Temp, s_Temp),位长同样不得超过 240。转换为十六进制串 SZr_Temp、SZs_Temp(无"0x"前缀,不足 60 补'0'),令 Data2 为:

即Data2 = "60" + SZs_Temp + SZr_Temp(122 字符),对应源码HelperStringFormat("60%s%s", …)(WinRarKeygen.hpp)。

8.6 计算 CRC32 校验和

对以下消息计算 CRC32:

即L + U + Data0 + Data1 + Data2 + Data3的拼接。最终校验和为 CRC32 值的按位取反(补码),再转换为十进制字符串 SZchecksum,不足 10 位时补'0'。源码CalculateChecksum(WinRarKeygen.hpp)依次对 LicenseType、UserName、Items[0…3] 调用Crc32.Update,最终Info.Checksum = ~Crc32.Evaluate(),其中 CRC32 使用反射多项式0xEDB88320(HasherCrc32Traits.hpp)。

8.7 组装 Data 并输出

令 Data 为:

即Data = 各段长度(十进制)拼接 + Data0 + Data1 + Data2 + Data3 + 校验和。源码格式为"%zu%zu%zu%zu%s%s%s%s%010lu"(WinRarKeygen.hpp):先输出 Data0、Data1、Data2、Data3 的长度 64、122、122、50,即前缀"6412212250",随后接四个数据段,最后是 10 位十进制校验和。整串共 10 + (64+122+122+50) + 10 = 378 字符,恰好 378 ÷ 54 = 7 行整——这也解释了为何最终文件按每行 54 字符输出(源码中若长度非 54 的倍数会抛出InternalError)。

最终文件按如下格式输出:

  • 固定首行RAR registration data;

  • 用户名,占一行;

  • 授权类型,占一行;

  • UID,占一行,格式为:

    即UID=+ 20 位十六进制串;

  • Data 内容,每行 54 字符。

仓库 README.md 给出了一个真实输出样例,可与上述结构逐行核对:

RAR registration data Github Single PC usage license UID=3a3d02329a32b63da7d8 6412212250a7d8753c5e7037d83011171578c57042fa30c506caae 9954e4853d415ec594e46017cb3db740bc4b32e47aea25db62f350 9f22065a27da4f8216d2938e1050b6e3347501a3767d1fdd7ee130 dd4ab952600ba16a99236d910bfa995d5f60651ec451f462511507 95b3722d059f2d5303a231e396cf21f17098edeec0b6e3347501a3 767d1fdd7ee45388769767642338ee8a63178f3458b71de5609b18 5eede7ed46566b10bf033daa6384062b259194b1acbd0378116064

可以看到第 4 行 UID 恰为 20 位十六进制,第 5 行以6412212250(四个数据段长度)开头,后续共 7 行 × 54 字符。

9. 源码级对照:命令行使用与验证

整个算法在仓库中的实现入口为WinRarKeygen<WinRarConfig>::GenerateRegisterInfo(WinRarKeygen.hpp),它返回RegisterInfo(含 UserName、LicenseType、UID、Items[4]、Checksum、HexData),随后由命令行程序 _tmain.cpp 负责编码转换与文件输出。

命令行用法(详见 README.md):

winrar-keygen.exe <Username> <LicenseName> [options] winrar-keygen.exe -v | --version winrar-keygen.exe -h | --help
参数说明
-e, --encoding <enc>编码:utf8(默认)、ascii、ansi
-o, --output <file>输出文件路径(默认rarreg.key)
-a, --activate直接写入%APPDATA%\WinRAR\rarreg.key
-t, --text仅打印到控制台,不写文件
-u, --update检查更新
-v, --version显示版本
-h, --help显示帮助

一个 ASCII 编码的示例(README.md):

./winrar-keygen.exe "Github" "Single PC usage license" -t -e ascii

需要说明的编码前提:本文档描述的生成算法中,用户名与授权类型均以ANSI 编码(不含 null 终止符)参与哈希与签名;而 CLI 默认utf8编码,非 ASCII 字符会按 README 所述自动添加utf8:前缀;ansi编码依赖当前 Windows 区域代码页(如 Windows-1252),跨区域可能产生乱码;ascii编码只接受纯 ASCII 字符。这与文档"ANSI 编码字符串"的前提是配套的——内部算法只关心参与运算的字节串,编码层由 CLI 负责。

若希望独立验证曲线参数的正确性,仓库提供了不依赖 C++ 代码的纯 Python 验证脚本 README.VERIFY_Point_G.md,可独立运行并打印Verify whether the point G is on the curve: True与Verify whether the PK is on the curve: True,是理解复合域运算与曲线方程最直观的入门材料。

10. 总结

rarreg.key的生成可以浓缩为一条完整的密码学链路:

  1. 域:GF((2^15)^17) = GF(2^15)[y]/(y^17+y^3+1),基域不可约多项式为 x^15+x+1;
  2. 曲线:y² + xy = x³ + b(a = 0,b = 161),基点 G、241 位阶 n 见 WinRarConfig.hpp;
  3. 哈希:SHA1 的 5 个状态值不做标准序列化,直接拼成大整数 h(WinRarKeygen.hpp);
  4. 签名:r = X(Rnd·G) + h mod n,s = (Rnd − k·r) mod n,是 SM2 的一种简化变体(WinRarKeygen.hpp);
  5. 私钥派生:以输入串为种子经 15 轮 SHA1 截位生成 240 位私钥(WinRarKeygen.hpp);
  6. 组装:用户名公钥(SM2 压缩格式)→ Data3 → Data0 → UID → 两次签名(Data1、Data2)→ CRC32 补码校验和 → 按 54 字符每行输出。

整个设计最值得注意的两点在于:其一是复合域 GF((2^15)^17)而非主流标准曲线所采用的素数域或单一扩域,配合对数/指数查表实现高速乘法;其二是签名公式对 SM2 的简化(s 省略了 (1+d)^−1 因子),以及哈希采用状态值直构大整数的非标准处理。这三处"非常规"选择共同构成了 WinRAR 授权机制的技术特征,也正因如此,只要掌握了私钥派生算法,任何人理论上都可以复现其rarreg.key生成过程——这正是本仓库的核心价值所在。

  • 密码学
  • 逆向工程
  • CLI

【免费下载链接】winrar-keygen

Principle of WinRAR key generation.

项目地址:https://gitcode.com/gh_mirrors/wi/winrar-keygen
点击查看免费下载

相关推荐

上一篇:WVP-GB28181-Pro终极指南:5步搞定多品牌监控统一管理
下一篇:3步完成Koikatu HF Patch安装:解锁200+模组的终极游戏增强指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询