Kann ich den öffentlichen Schlüssel eines Ethereum-Kontos irgendwie bekommen, wenn ich nur die entsprechende Ethereum-Adresse (z. B. 0x54dbb737eac5007103e729e9ab7ce64a6850a310
) kenne?
Dies ist nur möglich, wenn eine Transaktion vom Konto gesendet wurde. Wenn Sie eine tx senden, signieren Sie die Transaktion und sie enthält diese v
r
und s
Werte. Sie analysieren diese aus dem signierten TX und übergeben diese v
r
und s
Werte und den Hash der Transaktion zurück an eine Funktion, die den öffentlichen Schlüssel ausspuckt. Auf diese Weise erhalten Sie eigentlich die Absenderadresse einer Transaktion.
Sie können dies selbst mit einem Tool wie ethereumjs-utils tun :
/**
* 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)
}
Als weiteres reales Szenario verwendet ethereumjs-tx diese Funktion, um die Signatur zu überprüfen:
/**
* 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
}
Weitere Informationen zu v
r
und s
:
v, r und s sind Parameter, die aus der Signatur geparst werden können. Hier ist ein gutes Beispiel aus der utils-Bibliothek von 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
Beachten Sie, wie Sie jeden Wert einer bestimmten Signatur parsen können.
Es ist jetzt möglich, den öffentlichen Schlüssel aus der Ethereum-Transaktion ohne Codierung wiederherzustellen:
Ich denke nicht, dass dies möglich ist, da Sie Informationen verlieren, wenn Sie vom öffentlichen Schlüssel zur Adresse wechseln:
- Beginnen Sie mit dem öffentlichen Schlüssel (64 Bytes)
- Nehmen Sie den Keccak-256-Hash des öffentlichen Schlüssels. Sie sollten jetzt eine Zeichenfolge mit 32 Bytes haben. (Hinweis: SHA3-256 wurde schließlich zum Standard, aber Ethereum verwendet Keccak)
- Nehmen Sie die letzten 20 Bytes dieses öffentlichen Schlüssels (Keccak-256). Oder mit anderen Worten, die ersten 12 Bytes löschen . Diese 20 Bytes sind die Adresse oder 40 Zeichen. Wenn 0x vorangestellt wird, wird es 42 Zeichen lang.
Mit dem @tayvano-Hinweis können Sie dies wie folgt tun:
0xa8206c5fcfb6a2527fb8540ab543b4701f4c86d1c21862ad89fa220c84bad260
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
Lösung in Java. Vielleicht geht es einfacher, aber es funktioniert.
Utility-Klassen kommen von 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));
eth
secp256k1.publicKeyConvert
erforderlich ist, und der Grund scheint darin zu bestehen, den dekomprimierten öffentlichen Schlüssel zu erhalten, dasecp256k1.recover
ein komprimierter öffentlicher Schlüssel zurückgegeben wird.John
Tech-Ingenieur aus New York
Julian