본문 바로가기
블록체인

[Java] 이더리움 / 클레이튼 퍼스널 사인 검증하기 Verifying Ethereum / Klaytn signed messages

by 찐코딩 2022. 8. 17.

먼저 web3j 라이브러리를 추가해주어야 한다

 

gradle 기준

dependencies {
    implementation group: 'org.web3j', name: 'core', version: '4.8.4'
}

버전은 여러가지가 있지만 나는 그냥 사용자가 제일 많은 기준으로~

 

 

이더리움

import java.math.BigInteger;
import java.util.Arrays;

import org.web3j.crypto.ECDSASignature;
import org.web3j.crypto.Hash;
import org.web3j.crypto.Keys;
import org.web3j.crypto.Sign;
import org.web3j.crypto.Sign.SignatureData;
import org.web3j.utils.Numeric;

public class EthECRecoverTest {
    /**
     * Ethereum에서 사용자 정의한 모든 서명 메시지는 다음 문자로 시작합니다.
     * Reference resources eth_sign in https://github.com/ethereum/wiki/wiki/JSON-RPC
     */
    private static final String PERSONAL_MESSAGE_PREFIX = "\u0019Ethereum Signed Message:\n";

    /**
     * For signed messages , Original news , The account number and address are authenticated , Judge whether the signature is valid
     * @param signature
     * @param message
     * @param address
     * @return
     */
    private boolean testRecoverAddressFromSignature(String signature, String message, String address) {
        // Reference resources eth_sign in https://github.com/ethereum/wiki/wiki/JSON-RPC
        // The sign method calculates an Ethereum specific signature with:
        //    sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))).
        //
        // 메시지에 접두사를 추가하면 계산된 서명이 이더리움 특정 서명으로 인식될 수 있습니다.
        // 이것은 악의적인 dapp이 임의의 데이터(예: 트랜잭션)에 서명하고 서명을 사용하여 피해자를 가장할 수 있는 오용을 방지합니다.
        String prefix = PERSONAL_MESSAGE_PREFIX + message.length();
        byte[] msgHash = Hash.sha3((prefix + message).getBytes());

        byte[] signatureBytes = Numeric.hexStringToByteArray(signature);
        byte v = signatureBytes[64];
        if (v < 27) {
            v += 27;
        }

        SignatureData sd =
                new SignatureData(
                        v,
                        (byte[]) Arrays.copyOfRange(signatureBytes, 0, 32),
                        (byte[]) Arrays.copyOfRange(signatureBytes, 32, 64));

        String addressRecovered = null;
        boolean match = false;
        BigInteger publicKey = null;  
        String pubKey = null;

        // Iterate for each possible key to recover
        for (int i = 0; i < 4; i++) {
            publicKey = Sign.recoverFromSignature(
                    (byte) i,
                    new ECDSASignature(new BigInteger(1, sd.getR()), new BigInteger(1, sd.getS())),
                    msgHash);
    
            if (publicKey != null) {
                addressRecovered = "0x" + Keys.getAddress(publicKey);
                if (addressRecovered.equals(address)) {
                    match = true;
                    break;
                }
            }
        }
        pubKey = "0x"+publicKey.toString(16);
        System.out.println("지갑 주소 : " + addressRecovered);
        System.out.println("publicKey : " + pubKey);
        return match;
    }        

    public static void main(String[] args) {
        EthECRecoverTest EthECRecoverTest = new EthECRecoverTest();
        String hashResult = "{사인 결과값. 0x 부터 입력}";	// sign 결과 해쉬값 입력
        String msg = "Example `personal_sign` message";	// 사인 메세지
        String address = "{지갑 주소 0x부터 입력}";	// 지갑 주소입력
        EthECRecoverTest.testRecoverAddressFromSignature(hashResult, msg, address);
    }    
}

결과

지갑 주소 : {지갑주소}
publicKey : {퍼블릭 키 해쉬값}

 

+추가) 클레이튼

import java.math.BigInteger;
import java.util.Arrays;

import org.web3j.crypto.ECDSASignature;
import org.web3j.crypto.Hash;
import org.web3j.crypto.Keys;
import org.web3j.crypto.Sign;
import org.web3j.crypto.Sign.SignatureData;
import org.web3j.utils.Numeric;

public class KlaytnECRecoverTest {
    /**
     * Klaytn에서 사용자 정의한 모든 서명 메시지는 다음 문자로 시작합니다.
     * https://ko.docs.klaytn.foundation/dapp/sdk/caver-js/v1.4.1/api-references/caver.klay.accounts
     */
    private static final String PERSONAL_MESSAGE_PREFIX = "\u0019Klaytn Signed Message:\n";
    /**
     * For signed messages , Original news , The account number and address are authenticated , Judge whether the signature is valid
     * @param signature
     * @param message
     * @param address
     * @return
     */
    private boolean testRecoverAddressFromSignature(String signature, String message, String address) {
        String prefix = PERSONAL_MESSAGE_PREFIX + message.length();
        byte[] msgHash = Hash.sha3((prefix + message).getBytes());

        byte[] signatureBytes = Numeric.hexStringToByteArray(signature);
        byte v = signatureBytes[64];
        if (v < 27) {
            v += 27;
        }
        SignatureData sd =
                new SignatureData(
                        Arrays.copyOfRange(signatureBytes, 64, 65),
                        Arrays.copyOfRange(signatureBytes, 0, 32),
                        Arrays.copyOfRange(signatureBytes, 32, 64));

        String addressRecovered = null;
        boolean match = false;
        BigInteger publicKey = null;  
        String pubKey = null;

        // Iterate for each possible key to recover
        for (int i = 0; i < 4; i++) {
            publicKey = Sign.recoverFromSignature(
                    (byte) i,
                    new ECDSASignature(new BigInteger(1, sd.getR()), new BigInteger(1, sd.getS())),
                    msgHash);
    
            if (publicKey != null) {
                addressRecovered = "0x" + Keys.getAddress(publicKey);
                if (addressRecovered.equals(address)) {
                    match = true;
                    break;
                }
            }
        }
        pubKey = "0x"+publicKey.toString(16);
        System.out.println("지갑 주소 : " + addressRecovered);
        System.out.println("publicKey : " + pubKey);
        return match;
    }        

    public static void main(String[] args) {
        KlaytnECRecoverTest KlaytnECRecoverTest = new KlaytnECRecoverTest();
        String hashResult = "{사인 결과값. 0x 부터 입력}";	// sign 결과 해쉬값 입력
        String msg = "Example `personal_sign` message";	// 사인 메세지
        String address = "{지갑 주소 0x부터 입력}";	// 지갑 주소입력
        KlaytnECRecoverTest.testRecoverAddressFromSignature(hashResult, msg, address);
    }    
}

타원곡선 알고리즘(ECDSA)에 의해 디코딩하여 검증한다!

 

 

참고

https://ethereum.stackexchange.com/questions/55454/recover-address-from-signature-using-web3j

 

Recover address from signature using web3j

I have signed a data string using the following code snippet in web3js var data = "hello world"; web3.personal.sign(web3.fromUtf8(data), web3.eth.accounts[0], function(error, result) { if (!e...

ethereum.stackexchange.com

https://javamana.com/2022/01/202201201454093896.html

 

[DAPP] in DAPP development, Web3 wakes up metamask signature data + java verification signature to realize decentralized and cen

Most usage scenarios are used in DAPP When calling centralization data or manipulating some centralization functions DAPP call MetaMask The wallet signs the data and passes it to the background , The background verifies whether the signature data is the da

javamana.com

 

댓글