Rufen Sie den öffentlichen Schlüssel einer Adresse ab, um Off-Chain-Signaturen zu überprüfen

Ich habe eine Frage zur Verwendung von Schlüsseln in Ethereum zum Signieren von Anfragen/Daten AUSSERHALB von Ethereum.

Nehmen wir an, ich habe einen regulären Ethereum-EOA mit einer Adresse und einem Schlüsselpaar. Ich möchte nun eine Information an einen Dritten (Off-Chain) übertragen lassen und diesem Dritten die Überprüfung ermöglichen, dass diese Information von mir, also dem Inhaber des betreffenden EOA, stammt. Ansatz: Signieren Sie die Informationen digital mit meinem privaten Schlüssel, der mit meinem EOA verknüpft ist.

Nun stellt sich die Frage, wie der Dritte die Signatur verifiziert. Was er hat, ist die Unterschrift, die Daten und (nehmen wir an, wir haben dies angegeben) die Adresse meines EOA.

Damit er die Signatur verifizieren kann, benötigt er den öffentlichen Schlüssel meines EOA. Um ihm das zukommen zu lassen, sehe ich zwei Möglichkeiten:

  1. GEBEN Sie ihm den öffentlichen Schlüssel direkt, z. B. indem Sie eine ERC1056-Registrierung verwenden, in der ich den öffentlichen Schlüssel für jede Adresse speichere. Jetzt müsste ich On-Chain (dh in Solidity) überprüfen, ob der als Attribut festgelegte öffentliche Schlüssel mit der Adresse übereinstimmt (dh, dass die ersten 20 Bytes des Hashs des öffentlichen Schlüssels gleich sind). Kommt mir gastechnisch ziemlich kostenintensiv vor. Jedenfalls Erste Frage : Wie berechnet man die Adresse aus einem öffentlichen Schlüssel (String) in Solidity für eine solche Überprüfung?
  2. Leiten Sie den öffentlichen Schlüssel außerhalb der Kette (auf der Empfängerseite) von der Signatur ab. Ich habe gelesen, dass dies möglich wäre, ich bin mir nur nicht sicher, wie genau das geht. Der Empfänger würde dann den öffentlichen Schlüssel berechnen, die Signatur verifizieren und verifizieren, dass die Signatur tatsächlich von der EOA (Adresse) erstellt wurde, von der sie angeblich stammt. Daher meine zweite Frage : Wie macht man das in Java? Gibt es dafür eine Lib?

Gibt es eine dritte, noch bessere Option?

Antworten (2)

Erstens ist es nicht möglich, den öffentlichen Schlüssel nur von einer Adresse abzuleiten. Die Adresse ist Teil des Keccak256-Hashes des öffentlichen Schlüssels, und Hash-Funktionen sind Einwegfunktionen. Sie können eine Adresse aus einem öffentlichen Schlüssel berechnen, indem Sie die letzten 40 Bytes des Keccak256-Hashes nehmen.

Sie benötigen den öffentlichen Schlüssel jedoch nicht, um eine Signatur zu überprüfen. Angenommen, Sie verwenden ECDSA-Signaturen (Standard in Ethereum), können Sie den öffentlichen Schlüssel aus einer Signatur und Nachricht wiederherstellen. Wenn Sie beispielsweise eine Nachricht mit MyCrypto signieren, sieht die „signierte Nachricht“ folgendermaßen aus:

{
  "address": "0xa6ad0945cd3c5539d92d49b140842a0673e17041",
  "msg": "Hello, world!",
  "sig": "0x1e15325a942ae03788b63902ccc4703a65993dc9c692cf14d2bf16d62407da6824f045ebbbc0eb8423934a87c95872703fcec742ea03ded4d88aa5e219d9494c1c",
  "version": "2"
}

Dann können Sie den öffentlichen Schlüssel mit der Wiederherstellungsfunktion von ECDSA ( ecrecoverin Solidity) berechnen, indem Sie den öffentlichen Schlüssel der Nachricht und Signatur berechnen, den öffentlichen Schlüssel mit Keccak256 hashen und die letzten 40 Bytes nehmen, um die Adresse zu erhalten. Die Signatur ist gültig, wenn die wiederhergestellte Adresse mit der in der signierten Nachricht angegebenen übereinstimmt.

Sie sollten einige spezifische Informationen in die Nachricht aufnehmen (z. B. „Nachricht von Person am Datum signiert“), um sicherzustellen, dass die Nachricht tatsächlich von der Person signiert wurde, die behauptet, sie signiert zu haben (und um mögliche Replay-Angriffe zu verhindern).

Mit Java können Sie web3j verwenden Sign.recoverFromSignature. Leider scheint dies nicht dokumentiert zu sein, aber der Quellcode sollte Ihnen den Einstieg erleichtern.

Ich habe einen ausführlichen Artikel über ECDSA-Signaturen auf Ethereum geschrieben, den Sie hier finden können , wenn Sie daran interessiert sind.

Diese Bibliothek hilft Ihnen, den öffentlichen Schlüssel aus der ECDSA-signierten Nachricht https://github.com/0xcyphered/secp256k1-solidity wiederherzustellen

Beispiel:

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "@0xcyphered/secp256k1-solidity/contracts/SECP256K1.sol";
contract Example {
    function recoverPersonalSignPublicKey(
        bytes32 message,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public pure returns (bytes memory) {
        string memory header = '\x19Ethereum Signed Message:\n32';
        bytes32 _message = keccak256(abi.encodePacked(header, message));
        (uint256 x, uint256 y) = SECP256K1.recover(uint256(_message), v - 27, uint256(r), uint256(s));
        return abi.encodePacked(x, y);
    }
}