为啥相同的 HSM 密钥每次都验证到不同的以太坊地址?

Posted

技术标签:

【中文标题】为啥相同的 HSM 密钥每次都验证到不同的以太坊地址?【英文标题】:Why does the same HSM key verify to a different ethereum address every time?为什么相同的 HSM 密钥每次都验证到不同的以太坊地址? 【发布时间】:2019-04-24 14:44:21 【问题描述】:

我正在与一个使用以太坊标准 (secp256k1) 生成和签名的 HSM 进行交互。我正在使用一个名为 Graphene 的包与 HSM 进行交互。我使用其“pointEC”属性提取公钥: 0xc87c1d67c1909ebf8b54c9ce3d8e0f0cde41561c8115481321e45b364a8f3334b6e826363d8e895110fc9ca2d75e84cc7c56b8e9fbcd70c726cb44f5506848fa

我可以用来生成地址:0x21d20b04719f25d2ba0c68e851bb64fa570a9465

但是,当我尝试使用密钥对来自 dApp 的个人消息进行签名时,签名总是评估为不同的地址。例如,随机数/消息: wAMqcOCD2KKz2n0Dfbu1nRYbeLw_qbLxrW1gpTBwkq有签名:

0x2413f8d2ab4df2f3d87560493f21f0dfd570dc61136c53c236731bf56a9ce02cb23692e6a5cec96c62359f6eb4080d80328a567d14387f487f3c50d9ce61503b1c

但它恢复了0xFC0561D848b0cDE5877068D94a4d803A0a933785有效地址

这可能都是使用相同的私钥/公钥。当然,我只是附加了“1c”恢复值,但即使我尝试使用其他值,我也没有运气。这里还有几个例子:

Nonce: WRH_ApTkfN7yFAEpbGwU9BiE2M6eKTZMklPYK50djnx
Sig: 0x70242adabfe27c12e54abced8de87b45f511a194609eb27b215b153594b5697b7fb5e7279285663f80c82c2a2f2920916f76fd845cdecb45ace19f76b0622ac41c
Address: 0x1A086eD40FF90E75764260E2Eb42fab4Db519E53

Nonce: TZV6qhplddJgcKaN7qtpcIhudFhiQ
Sig: 0x3607beb3d58ff35ca1059f3ea44f41e79e76d8ffe35a4f716e78030f0fe2ca1da51f138c31d4ec4b9fc3546c4de1185736a4c4c7030a8b1965e30cb0af6ba2ee1c
Address: 0xa61A518cf73163Fd92461942c26C67c203bda379

我的签名信息代码:

        let alg: graphene.MechanismType;
        alg = graphene.MechanismEnum.ECDSA;
        const session = get_session();

        let key: graphene.Key | null = null;
        //#region Find signing key
        const objects = session.find(label: GEN_KEY_LABEL);
        for (let i = 0; i < objects.length; i++) 
            const obj = objects.items(i);
            if ((obj.class === graphene.ObjectClass.PRIVATE_KEY ||
                obj.class === graphene.ObjectClass.SECRET_KEY) &&
                obj.handle.toString('hex') == params.handle
            ) 
                key = obj.toType<graphene.Key>();
                break;
            
        
        if (!key) 
            throw new Error("Cannot find signing key");
        
        var sign = session.createSign(alg,key);
        if (!params.data) 
            console.log("No data found. Signing 'test' string");
            params.data = 'test';
        
        sign.update(Buffer.from(params.data.toString().trim()));
        var signature = sign.final();



        console.log(signature.toString('hex'));

请记住,即使只有 1 个密钥,它也会失败。

【问题讨论】:

您可能需要共享您用于签署消息的代码以及用于恢复签名者的代码。 恢复签名者的代码就是web3的JS库。 很难说没有看到代码,但你对web3 所做的可能是期望签名的数据首先用 keccak256 散列,然后以 "\x19Ethereum 签名消息:32 \n”。您的签名代码中肯定没有这样做。 被签名的数据已经是那种格式。所有这一切都是将其发送到 HSM 进行签名并返回签名。 那么params.data.toString() 的实际值是多少?究竟是什么传递给sign.update 【参考方案1】:

地址是通过公钥计算出来的,而签名是使用 ECDSA 生成的。 ECDSA 包含一个随机值 r 和一个特定于该随机值的签名 s(当然还有私钥)。更多信息here (Wikipedia on ECDSA).

您看不到这一点,因为它们只是被编码为静态大小(无符号,大整数)的值,然后连接在一起称为“签名”(因此签名的大小是密钥大小的两倍, 64 字节而不是 32 字节)。验证将解析签名并再次使用单独的值。对于以太坊和比特币,可以在签名前加上一个额外的字节,这样就可以取回公钥,然后重新计算地址。这也改变了 签名生成,因此您不再是在谈论普通的 ECDSA。

还有 X9.62 签名格式,它仍然由两个单独的整数组成,使用 ASN.1 / DER 编码进行编码。由于分离/编码两个整数所需的开销,这些签名看起来只是部分随机的。

【讨论】:

如何使用从公钥生成的地址的正确值进行签名? 嗯,我得查一下:|不是区块链专家,只是一位加密专家,前几天刚刚查看了地址是如何计算的。这可能需要一些时间。请注意,虽然不会直接比较签名,但您只需验证它们的正确性。 我只是不明白如何以签名将验证生成地址的方式进行签名。可以从签名中获取公钥吗? 在此处查看答案:crypto.stackexchange.com/q/18105/1172。请注意第二个答案下的评论:“事实证明,在我的情况下(比特币消息签名),他们在签名中附加了一个额外的字节,以识别两个点中的哪一个是公钥。一旦完成,他们就会计算一个公钥的 160 位散列。这个散列被称为比特币地址。然后他们检查生成的地址是否与给定的地址相等,以及消息是否经过身份验证。通过使用这种方式,他们赢得了一些字节。- Jan Moritz 2014 年 7 月 15 日 6:04" 请注意,签名格式有一个字节前缀:查看更多信息here。 This 似乎是该链接的实现。我假设每个签名都会生成一种可能的公钥格式,然后您必须对其进行更改才能获得 org.返回键。【参考方案2】:

原来我使用的是已弃用的 Buffer.from 函数,因为更新版本要求您指定传入数据的格式。

E.g. Buffer.from("04021a","hex")

由于它是最终的“输入”和计算,我花了很长时间才意识到当时数据被错误地转换了。我以为我在每个步骤中都多次检查和重新检查数据,但错过了最直接的部分。

另外,我了解到要创建正确的签名并防止交易延展性,您必须不断辞职,以使 's' 的值最终小于: (0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141)/2

然后当把'r'和's'通过地址恢复函数时,它应该尝试恢复v=27或v=28(0x1a或0x1b)的地址,基本上在这一点上是反复试验。大多数情况下,它会恢复 正确 v=27 的地址。

【讨论】:

你能分享更多信息吗?我正在使用正确的 buffer.from,但我正面临这个问题。我正在使用石墨烯并面临完全相同的问题。就是这样,我正在寻找 rs 和 v 值。 ethTx.r = sig.slice(0, 32) ethTx.s = sig.slice(32, 64) ethTx.v = chainId ? recovery + (chainId * 2 + 35) : recovery + 27;

以上是关于为啥相同的 HSM 密钥每次都验证到不同的以太坊地址?的主要内容,如果未能解决你的问题,请参考以下文章

如何将公共RSA密钥上传到HSM(使用PKCS#11库)?

PKCS11 derivedKey 每次返回不同的值

每次输入验证码都不正确是为啥

使用Thales nShield HSM的PKCS11interop c#wrapper库导出/导入RSA密钥对?

使用 HSM 的对称加密密钥

为啥我新签名的 apk 中的指纹不同?