Получить публичный ключ любой учетной записи ethereum

Могу ли я каким-то образом получить открытый ключ учетной записи ethereum, зная только соответствующий адрес ethereum (например, 0x54dbb737eac5007103e729e9ab7ce64a6850a310)?

Ответы (5)

Вы можете, если и только если транзакция была отправлена ​​​​со счета. Когда вы отправляете tx, вы подписываете транзакцию, и она включает в себя эти v rи sзначения. Вы анализируете их из подписанного tx, а затем передаете их v r, sзначения и хэш транзакции обратно в функцию, и она выдает открытый ключ. На самом деле именно так вы получаете адрес отправителя транзакции.

Вы можете сделать это самостоятельно, используя такой инструмент, как ethereumjs-utils :

/**
 * ECDSA public key recovery from signature
 * @param {Buffer} msgHash
 * @param {Number} v
 * @param {Buffer} r
 * @param {Buffer} s
 * @return {Buffer} publicKey
 */
exports.ecrecover = function (msgHash, v, r, s) {
  var signature = Buffer.concat([exports.setLength(r, 32), exports.setLength(s, 32)], 64)
  var recovery = v - 27
  if (recovery !== 0 && recovery !== 1) {
    throw new Error('Invalid signature v value')
  }
  var senderPubKey = secp256k1.recover(msgHash, signature, recovery)
  return secp256k1.publicKeyConvert(senderPubKey, false).slice(1)
}

В качестве другого реального сценария ethereumjs-tx использует эту функцию для проверки подписи:

/**
* Determines if the signature is valid
* @return {Boolean}
*/
verifySignature () {
  const msgHash = this.hash(false)
  // All transaction signatures whose s-value is greater than secp256k1n/2 are considered invalid.
  if (this._homestead && new BN(this.s).cmp(N_DIV_2) === 1) {
    return false
  }

  try {
    let v = ethUtil.bufferToInt(this.v)
    if (this._chainId > 0) {
      v -= this._chainId * 2 + 8
    }
    this._senderPubKey = ethUtil.ecrecover(msgHash, v, this.r, this.s)
  } catch (e) {
    return false
  }

  return !!this._senderPubKey
}

Для получения дополнительной информации о v rи s:

v, r и s — это параметры, которые можно проанализировать из подписи. Вот хороший пример из библиотеки утилит ethereumjs:

 var sig = secp256k1.sign(msgHash, privateKey)
  var ret = {}
  ret.r = sig.signature.slice(0, 32)
  ret.s = sig.signature.slice(32, 64)
  ret.v = sig.recovery + 27

Обратите внимание, как вы можете анализировать каждое значение из данной подписи.

источник

Очень полезно. Мне пришлось искать, зачем secp256k1.publicKeyConvertнужен «дополнительный» вызов, и причина, по-видимому, в том, чтобы получить распакованный открытый ключ, поскольку secp256k1.recoverон возвращает сжатый открытый ключ.
Что означают значения s, r, v?
Что такое msgHash? Это хэш сообщения, отправленного как часть транзакции, или фактический хэш самой транзакции?
Как вы собираетесь анализировать v,r, если у вас нет закрытого ключа конечного пользователя?

Теперь можно восстановить открытый ключ из транзакции Ethereum без какого-либо кодирования:

  1. Откройте транзакцию в Etherscan.io
  2. Нажмите на вертикальное многоточие в правом верхнем углу
  3. Нажмите «Получить Raw Tx Hex» во всплывающем меню.
  4. Вы увидите необработанную транзакцию в шестнадцатеричном формате, скопируйте ее
  5. Откройте инструмент «Восстановить адрес» из ABDK Toolkit.
  6. Выберите переключатель «Транзакция»
  7. Вставьте необработанную транзакцию в поле «Сообщение».
  8. См. открытый ключ в текстовой стрелке ниже
Это работает очень хорошо. Однако я нигде не смог найти исходный код для этого, хотя они говорят, что это с открытым исходным кодом.
Лучшее, что я мог сделать, это создать файл WARC для автономного использования здесь .
Как это поможет, просто зная адрес?

Я не думаю, что это возможно, так как вы теряете информацию при переходе от открытого ключа к адресу:

  1. Начните с открытого ключа (64 байта)
  2. Возьмите хэш открытого ключа Keccak-256. Теперь у вас должна быть строка размером 32 байта. (примечание: SHA3-256 со временем стал стандартом, но Ethereum использует Keccak)
  3. Возьмите последние 20 байт этого открытого ключа (Keccak-256). Или, другими словами, отбросить первые 12 байт . Эти 20 байт являются адресом или 40 символами. С префиксом 0x он становится длиной 42 символа.

Как генерируются эфириум-адреса?

Я думал, что все транзакции с адреса ethereum подписываются с использованием закрытого ключа учетной записи и проверяются в цепочке с использованием открытого ключа учетной записи. Если это так, то открытые ключи всех учетных записей должны быть доступны для всех узлов в сети. Я ошибаюсь?
@EdwardRuchevits Адрес в вопросе не подписал транзакцию, поэтому получить открытый ключ невозможно. Когда есть подпись, это может помочь crypto.stackexchange.com/questions/18105/… и stackoverflow.com/questions/19665491/… Возможно, некоторые люди также могут добавить сюда отличные ответы.
Замечательным свойством адресов является то, что, поскольку открытый ключ неизвестен до тех пор, пока учетная запись не будет использована, учетная запись защищена от квантовых атак до тех пор, пока она не будет израсходована.
@TjadenHess Существуют ли квантовые атаки в наши дни?

Используя подсказку @tayvano, вы можете сделать это следующим образом:

  1. Перейдите на etherscan и проверьте, есть ли исходящие транзакции: https://etherscan.io/address/0x54dbb737eac5007103e729e9ab7ce64a6850a310 .
  2. Получите хэш одного из них, например0xa8206c5fcfb6a2527fb8540ab543b4701f4c86d1c21862ad89fa220c84bad260
  3. использовать ipython с web3
In [1]: import web3
w3 = web3.Web3(web3.HTTPProvider('https://geth.golem.network:55555'))
tx = w3.eth.getTransaction(0xa8206c5fcfb6a2527fb8540ab543b4701f4c86d1c21862ad89fa220c84bad260)
tx.hash
Out[1]: HexBytes('0xa8206c5fcfb6a2527fb8540ab543b4701f4c86d1c21862ad89fa220c84bad260')

In [2]: from eth_account.internal.signing import extract_chain_id, to_standard_v
s = w3.eth.account._keys.Signature(vrs=(
    to_standard_v(extract_chain_id(tx.v)[1]),
    w3.toInt(tx.r),
    w3.toInt(tx.s)
))

from eth_account.internal.transactions import ALLOWED_TRANSACTION_KEYS
tt = {k:tx[k] for k in ALLOWED_TRANSACTION_KEYS - {'chainId', 'data'}}
tt['data']=tx.input
tt['chainId']=extract_chain_id(tx.v)[0]

from eth_account.internal.transactions import serializable_unsigned_transaction_from_dict
ut = serializable_unsigned_transaction_from_dict(tt)
s.recover_public_key_from_msg_hash(ut.hash())
Out[2]: '0x9678ad0aa2fbd7f212239e21ed1472e84ca558fecf70a54bbf7901d89c306191c52e7f10012960085ecdbbeeb22e63a8e86b58f788990b4db53cdf4e0a55ac1e'

In [3]: s.recover_public_key_from_msg_hash(ut.hash()).to_checksum_address()
Out[3]: '0x54Dbb737EaC5007103E729E9aB7ce64a6850a310'

In [4]: t['from']
Out[4]: '0x54Dbb737EaC5007103E729E9aB7ce64a6850a310'
Это не работает для транзакций развертывания контракта. И для последней версии вам нужно настроить код наfrom eth_account._utils.signing import extract_chain_id, to_standard_v, serializable_unsigned_transaction_from_dict from eth_account._utils.transactions import ALLOWED_TRANSACTION_KEYS

Решение на Java. Возможно, это можно сделать проще, но это работает.

Вспомогательные классы взяты из web3j.crypto.

    BigInteger v = new BigInteger("26", 16);
    BigInteger r = new BigInteger("5fd883bb01a10915ebc06621b925bd6d624cb6768976b73c0d468b31f657d15b", 16);
    BigInteger s = new BigInteger("121d855c539a23aadf6f06ac21165db1ad5efd261842e82a719c9863ca4ac04c", 16);
    BigInteger chainId = new BigInteger("1", 16);
    v = v.subtract(chainId.multiply(BigInteger.valueOf(2)).add(BigInteger.valueOf(8)));
    Sign.SignatureData signatureData = new Sign.SignatureData(v.toByteArray(), r.toByteArray(), s.toByteArray());
    byte[] raw = DatatypeConverter.parseHexBinary("f86b0b85250523760082520894eafaf9bb8f35235d0df61275e86fd65d9ef2c3f9870aaa0065c66b8b8026a05fd883bb01a10915ebc06621b925bd6d624cb6768976b73c0d468b31f657d15ba0121d855c539a23aadf6f06ac21165db1ad5efd261842e82a719c9863ca4ac04c");

    RawTransaction decoded = TransactionDecoder.decode(DatatypeConverter.printHexBinary(raw));
    byte[] encoded = TransactionEncoder.encode(decoded, chainId.longValue());
    byte[] rawTxHash = Hash.sha3(encoded);

    System.out.println("Raw tx hash:                    " + DatatypeConverter.printHexBinary(rawTxHash));
    System.out.println("Pub key from raw tx hash :      " + signedMessageHashToKey(rawTxHash, signatureData).toString(16));