环签名与门罗币隐私保护机制

密码学 2026-07-15 · 阅读约 22 分钟

比特币的匿名性是一种错觉:地址不记名,但所有交易公开可查,地址聚类分析能把资金流向还原得相当彻底。门罗币(Monero)的设计目标是真正的交易隐私——发送者不可追踪、接收者不可关联、金额不可见。支撑前两个目标的核心原语就是环签名(Ring Signature):签名者把自己的公钥混进一个诱饵公钥环里签名,验证者只能确认『签名来自环上某位成员』,却无法确定是哪一位。本文讲清环签名的数学结构、它如何与密钥镜像组合成可链接环签名来防双花,再用一段纯 Python 实验把『可验证但不可定位』这个看似矛盾的性质真正跑出来。

一、环签名:环形闭合的签名结构

环签名的经典构造(以 Schnorr 风格为例)把一个签名摊平成一个闭环:

  1. 签名者从自己的位置出发,选一个只有自己知道的随机数,算出初始承诺;
  2. 对环上其他每个成员,用其公钥和随机生成的响应值反推出承诺,使链条向前传递:每个 cᵢ₊₁ = H(msg, 承诺ᵢ);
  3. 回到自己位置时,用私钥和最初藏下的随机数闭合环路——只有持有私钥的人能让这个环严丝合缝地闭上。

最终签名是(c₀, s₀, …, sₙ₋₁)。验证者从 c₀ 出发绕行一环,若最后一站算出来的 c 恰好等于 c₀,签名有效。关键性质:环上所有成员的 s 值在数学上地位完全对称——真实签名者的那个 s 与其余诱饵成员的 s 没有任何统计特征上的差异。这不是计算安全(算力够大就能破解),而是无条件匿名性:即使无限算力也无法从签名本身区分签名者。

二、从匿名到可用:密钥镜像与防双花

纯环签名有个致命问题:签名者匿名到连系统都不知道是谁签的,那同一个人把同一笔钱花两次怎么办?门罗币的答案是密钥镜像(key image):

  • 每笔一次性输出对应一个密钥镜像 I = H(P)^x,其中 x 是私钥、P 是输出公钥;
  • 镜像的性质:同一私钥对同一输出算出的镜像恒定不变,不同私钥的镜像互不相同,且从镜像反推不出私钥或环上位置;
  • 签名时附带镜像,全网节点维护已用镜像集合——同一镜像出现第二次即双花,直接拒绝。

这就是『可链接环签名』的精髓:链接的不是『谁签的』,而是『这把密钥用没用过』。匿名性与防双花这两个看似冲突的目标,被镜像机制恰好分开解决。

三、门罗币的完整隐私栈

环签名只是三层防御的第一层,完整机制是三件套:

  • 环签名(发送者不可追踪):每笔交易的输入混在 16 个诱饵输出中(2018 年起固定环大小 16),外界无法确定真正被花掉的是哪一个;
  • 隐身地址(接收者不可关联):每笔交易的收款地址是从接收者公开地址派生的一次性地址,链上观察者无法把两笔付款关联到同一个人;
  • RingCT(金额不可见):用 Pedersen 承诺隐藏交易金额,配合 Bulletproofs 范围证明确保『输入≥输出且金额非负』而不暴露具体数字——防的是凭空印钞。

四、动手实验:环签名的可验证匿名性

下面代码实现 Schnorr 风格的教学环签名:5 人公钥环上真实签名者签名、验证者验证、篡改检测,再演示密钥镜像的可链接性。零依赖,Python 3 直接运行:

def ring_sign(msg, ring_pks, my_sk, my_idx):
    n = len(ring_pks)
    r = random.randrange(1, P - 1)
    c, s = [0]*n, [0]*n
    start = (my_idx + 1) % n
    c[start] = H(msg, pow(G, r, P))        # 自己的随机承诺
    i = start
    while i != my_idx:                      # 诱饵成员: 随机 s 反推承诺
        s[i] = random.randrange(1, P - 1)
        commit = pow(G, s[i], P) * pow(ring_pks[i], c[i], P) % P
        c[(i+1) % n] = H(msg, commit)
        i = (i + 1) % n
    s[my_idx] = (r - c[my_idx]*my_sk) % (P-1)  # 私钥闭合环路
    return c[0], s

def ring_verify(msg, ring_pks, c0, s):
    c = c0
    for i in range(len(ring_pks)):
        commit = pow(G, s[i], P) * pow(ring_pks[i], c, P) % P
        c = H(msg, commit)
    return c == c0                          # 绕行一周回到起点

# 密钥镜像: 同一私钥同一镜像, 双花可检测
def key_image(sk, pk):
    hp = int(sha256(str(pk).encode()).hexdigest(), 16) % P
    return pow(hp, sk, P)

在您的服务器(Python 3,无第三方库)上的真实运行输出:

== 环签名: 5 人公钥环, 真实签名者隐藏其中 ==
  环大小: 5 个公钥
  真实签名者位置: #2 (验证者不可见)
  签名验证: 通过
  篡改消息后验证: 失败 -> 伪造被检测

== 匿名性: 验证者视角 ==
  验证者只看到: 5 个公钥 + 签名 (c0, s0..s4)
  5 个 s 值在数学上地位完全对称, 没有任何统计特征暴露真实签名者
  -> 无条件匿名性(unconditional anonymity): 即使无限算力也无法区分

== 可链接性: 同一密钥重复签名可被检测 ==
  同一密钥两次签名的镜像相同: True
  不同密钥的镜像不同: True
  -> 链上维护镜像集合, 重复镜像 = 双花, 可拒绝

PASS: 环签名匿名且可验证, 可链接镜像实现防双花

五、结果解读

  • 验证通过与不可定位同时成立:验证者确认签名有效(数学上环闭合),但 5 个 s 值的对称结构让签名者位置在信息论层面不可区分——这是与混币服务(依赖可信中介)的本质区别。
  • 镜像机制的分工清晰:同一密钥镜像恒等(True)、不同密钥镜像互异(True)——双花检测不依赖任何身份信息,只依赖密钥使用记录。
  • 匿名集大小的意义:实验用 5 人环,门罗币主网是 16 人环。环越大匿名性越强,但签名体积与验证开销线性增长——环大小是隐私与效率的直接权衡点。

六、工程应用

  • 门罗币主网:环签名 + 隐身地址 + RingCT 三件套自 2017 年强制启用,是隐私币中唯一默认全隐私的主流实现;
  • 告密者保护:环签名的原始动机(2001 年 Rivest-Shamir-Tauman 论文)就是让内部人士能签名爆料而无法被定位;
  • 治理与投票:DAO 匿名投票用环签名证明『我是合法成员』但不暴露身份,配合镜像防重复投票;
  • 与其他隐私技术的关系:环签名保护发送者、隐身地址保护接收者、RingCT 保护金额;Zcash 的 zk-SNARK(见本站 Groth16 一文)则用零知识证明一次性覆盖全部三个维度,代价是需要可信设置与更高的构造复杂度。

七、常见误区

  1. 以为环越大越安全可以无限加:环大小直接决定交易体积与验证时间,16 是在隐私收益递减点上的工程选择,不是理论极限。
  2. 把诱饵选择当随机就行:早期门罗币诱饵按链龄分布抽样,若分布失真(比如全选很老的输出),统计分析可以缩小真实输入的范围——诱饵抽样分布是匿名性的实际防线。
  3. 忽视网络层泄露:环签名保护链上数据,但交易广播的源 IP 仍可泄露发送者——门罗币配套 Dandelion++ 做广播混淆,隐私是栈式工程。
  4. 认为镜像可推回身份:镜像与私钥、环位置之间没有可计算的映射,这正是它能公开上链的原因;它泄露的唯一信息就是『这把钥匙用过没』。
  5. 把隐私币等同违法工具:财务隐私本身是正当需求(工资、商业往来),现金的隐私性从未被视为问题——技术中立,监管关注点应在出入金环节。

八、参考文献

  • Rivest, R., Shamir, A., Tauman, Y. How to Leak a Secret (Ring Signatures). ASIACRYPT 2001.
  • Van Saberhagen, N. CryptoNote v2.0. 2013(门罗币协议基础).
  • Noether, S. et al. Ring Confidential Transactions (RingCT). Ledger 1, 2016.
  • Bünz, B. et al. Bulletproofs: Short Proofs for Confidential Transactions. IEEE S&P 2018.
  • Liu, J., Wei, V., Wong, D. Linkable Spontaneous Anonymous Group Signature (LSAG). ACISP 2004.