Update: Partial solution available on Git
编辑:可在https://github.com/makerofthings7/Bitcoin-MessageSignerVerifier找到此文件的编译版本
请注意,要验证的消息必须以Bitcoin Signed Message:\n
作为前缀。Source1Source2
C实现中有一些错误,我可以从this Python implementation中更正
它似乎有一个问题,实际上提出了正确的基58地址。
下面是我的邮件、签名和Base58地址。我打算从签名中提取密钥,散列该密钥,并比较Base58散列。
我的问题是:如何从签名中提取密钥?(在这篇文章的底部编辑I found the c++ code,需要在Bouncy Castle/或C#中编辑)
消息
StackOverflow test 123
签名
IB7XjSi9TdBbB3dVUK4+Uzqf2Pqk71XkZ5PUsVUN+2gnb3TaZWJwWW2jt0OjhHc4B++yYYRy1Lg2kl+WaiF+Xsc=
Base58比特币地址“hash”
1Kb76YK9a4mhrif766m321AMocNvzeQxqV
因为Base58比特币地址只是一个散列,所以我不能用它来验证比特币消息。但是,可以从签名中提取公钥。
编辑:我强调我是从签名本身而不是从Base58公钥散列派生公钥的。如果我想(实际上我确实想)我应该能够将这些公钥位转换为Base58散列。我不需要帮助,我只需要帮助提取公钥位和验证签名。
问题
在上面的签名中,这个签名是什么格式的?PKCS10?(回答:不,它是专有的as described here)
如何提取Bouncy Castle中的公钥?
验证签名的正确方法是什么?(假设我已经知道如何将公钥位转换为与上面的比特币哈希值相等的哈希值)
先前的研究
This link描述了如何使用ECDSA曲线,下面的代码将允许我将公钥转换为BC对象,但我不确定如何从签名中获取点Q
。
在下面的示例中,Q是硬编码值
Org.BouncyCastle.Asn1.X9.X9ECParameters ecp = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1");
ECDomainParameters params = new ECDomainParameters(ecp.Curve, ecp.G, ecp.N, ecp.H);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(
ecp .curve.decodePoint(Hex.decode("045894609CCECF9A92533F630DE713A958E96C97CCB8F5ABB5A688A238DEED6DC2D9D0C94EBFB7D526BA6A61764175B99CB6011E2047F9F067293F57F5")), // Q
params);
PublicKey pubKey = f.generatePublic(pubKeySpec);
var signer = SignerUtilities.GetSigner("ECDSA"); // possibly similar to SHA-1withECDSA
signer.Init(false, pubKey);
signer.BlockUpdate(plainTextAsBytes, 0, plainTextAsBytes.Length);
return signer.VerifySignature(signature);
附加研究:
THIS是验证消息的比特币源。
在对签名的Base64进行解码之后,调用RecoverCompact(hash of message, signature)。我不是C++程序员,所以我假设我需要知道^ {< CD3}}是如何工作的。或者key.GetPubKey
// reconstruct public key from a compact signature
// This is only slightly more CPU intensive than just verifying it.
// If this function succeeds, the recovered public key is guaranteed to be valid
// (the signature is a valid signature of the given data for that key)
bool Recover(const uint256 &hash, const unsigned char *p64, int rec)
{
if (rec<0 || rec>=3)
return false;
ECDSA_SIG *sig = ECDSA_SIG_new();
BN_bin2bn(&p64[0], 32, sig->r);
BN_bin2bn(&p64[32], 32, sig->s);
bool ret = ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), rec, 0) == 1;
ECDSA_SIG_free(sig);
return ret;
}
。。。ECDSA_SIG_recover_key_GFp is here的代码
比特币中的自定义签名格式
This answer says there are 4 possible可以生成签名的公钥,它在较新的签名中编码。
恐怕您的样品数据有问题。首先,示例Q的长度是61字节,但是比特币公钥(使用secp256k1曲线)的未压缩形式应该是65字节。您提供的Q没有正确验证消息,但我计算的Q似乎确实验证了它。
我编写的代码计算字符串“StackOverflow test 123”的正确公钥,并使用ECDsaSigner进行验证。但是,此公钥的哈希值是
1HRDe7G7tn925iNxQaeD7R2ZkZiKowN8NW
,而不是1Kb76YK9a4mhrif766m321AMocNvzeQxqV
。请验证您的数据是否正确,并给出消息字符串的确切哈希值,以便我们可以尝试调试,不正确的哈希值可能会把事情搞得一团糟。我使用的代码如下:
编辑 我看到这个仍然没有被评论或接受,所以我编写了一个完整的测试,生成一个私钥和一个公钥,然后使用私钥生成一个有效的签名。之后,它从签名和哈希中恢复公钥,并使用该公钥验证消息的签名。请看下面,如果还有问题请告诉我。
在引用BitcoinJ之后,这些代码示例中的一些似乎缺少对消息的正确准备、double-SHA256散列以及对输入到地址计算的已恢复公共点的可能压缩编码。
下面的代码应该只需要BouncyCastle(可能需要github的最新版本,不确定)。它从bitconij中借用了一些东西,并且只做了足够多的工作来获得小的示例,请参阅内联注释以了解消息大小限制。
它只计算到RIPEMD-160散列,我使用http://gobittest.appspot.com/Address检查结果的最终地址(不幸的是,该网站似乎不支持为公钥输入压缩编码)。
问题中初始数据的示例输出:
如果我们将RIPEMD-160值插入地址检查器,它将返回
如问题所述。
相关问题 更多 >
编程相关推荐