Обновление: Частичное решение доступно на Git
EDIT: скомпилированная версия этого доступна в https://github.com/makerofthings7/Bitcoin-MessageSignerVerifier
Обратите внимание, что сообщение, которое должно быть проверено, должно иметь Bitcoin Signed Message:\n
в качестве префикса. Source1 Источник2
В реализации С# что-то не так, что я, вероятно, могу исправить из эту реализацию Python
Кажется, что проблема связана с правильным адресом базы 58.
У меня есть следующее сообщение, подпись и адрес Base58 ниже. Я намерен извлечь ключ из подписи, использовать этот ключ и сравнить хэши Base58.
Моя проблема: как извлечь ключ из подписи? (Edit Я нашел код С++ в нижней части этой записи, нужно это в Bouncy Castle/или С#)
Сообщение
StackOverflow test 123
Подпись
IB7XjSi9TdBbB3dVUK4+Uzqf2Pqk71XkZ5PUsVUN+2gnb3TaZWJwWW2jt0OjhHc4B++yYYRy1Lg2kl+WaiF+Xsc=
Base58 Биткойн адрес "хэш"
1Kb76YK9a4mhrif766m321AMocNvzeQxqV
Поскольку адрес Bitcoin Base58 - это всего лишь хеш, я не могу использовать его для проверки сообщения Bitcoin. Тем не менее, можно извлечь открытый ключ из подписи.
Изменить: я подчеркиваю, что я получаю открытый ключ из самой подписи, а не из хэша открытого ключа Base58. Если я хочу (и я действительно хочу), я должен иметь возможность преобразовывать эти биты открытого ключа в хэш-таблицу Base58. Мне не нужна помощь в этом, мне просто нужна помощь в извлечении бит открытого ключа и проверке подписи.
Вопрос
-
В подписи выше, в каком формате находится эта подпись? PKCS10? (Ответ: нет, он проприетарно как описано здесь)
-
Как извлечь открытый ключ в Bouncy Castle?
-
Каков правильный способ проверки подписи? (предположим, что я уже знаю, как преобразовать бит открытого ключа в хеш, который равен хэшу биткойна выше)
Предыдущие исследования
Эта ссылка описывает, как использовать кривые 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);
Дополнительные исследования:
ЭТО является источником биткойнов, который проверяет сообщение.
После декодирования Base64 подписи вызывается RecoverCompact (хэш сообщения, подпись). Я не программист на С++, поэтому я предполагаю, что мне нужно выяснить, как работает key.Recover
. Это или 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 здесь
Формат пользовательской подписи в биткойне
В этом ответе говорится, что есть 4 возможных открытых ключа, которые могут создавать подпись, и это закодировано в новых подписях.