본문 바로가기

Blockchain

BSC Testnet Web3j 스터디 (1) 지갑 주소 생성 (Private Key, Public Key)

1. 지갑 주소 생성 (Private Key, Public Key)

 

이더리움 지갑을 만든다고 하면 흔히 메타마스크 같은 지갑 앱을 떠올리지만, 사실 지갑의 핵심은 단순하다.
지갑은 결국 Private Key, Public Key, Address 이 세 가지 값으로 정의된다. 

자바에서는 web3j 라이브러리를 사용하여 이를 코드로 구현할 수 있다. 


지갑 생성 과정

이더리움 지갑은 기본적으로 다음 과정을 거쳐 만들어진다.

  1. 키 쌍 생성
    • 타원곡선 암호화(SECP-256k1)를 사용해 개인키와 공개키를 생성한다.
    • 개인키는 서명에 사용되고, 공개키는 이를 검증하거나 주소를 만들 때 사용된다.
  2. 주소 도출
    • 공개키를 Keccak-256 해시 함수에 넣는다.
    • 나온 값에서 마지막 20바이트를 추출하고, 앞에 0x를 붙이면 이더리움 주소가 된다.
  3. Credentials 객체 생성
    • web3j는 Credentials라는 객체로 개인키, 공개키, 주소를 한꺼번에 관리한다.
    • 이 객체 하나만 있으면 거래 서명, 잔액 조회 등 다양한 작업을 할 수 있다.

코드 예제

ECKeyPair ecKeyPair = Keys.createEcKeyPair(); // 1. 키 쌍 생성
Credentials credentials = Credentials.create(ecKeyPair); // 2. Credentials 생성

String walletAddress = credentials.getAddress(); // 3. 주소 도출

System.out.println("지갑 주소: " + walletAddress);
System.out.println("Private Key: " + Numeric.toHexStringWithPrefix(ecKeyPair.getPrivateKey()));
System.out.println("Public Key: " + Numeric.toHexStringWithPrefix(ecKeyPair.getPublicKey()));

출력 결과 예시

지갑 주소: 0x658b8a1ae242d0460d4777e17c9af438daab4f77
Private Key: 0x5a1b...
Public Key: 0x376db746fb37...

코드를 실행하면 다음과 같은 값들이 출력된다.

  • 지갑 주소 (Ethereum Address)
    0x 접두사가 붙은 20바이트(40자리 16진수) 주소이다.
    예: 0x658b8a1ae242d0460d4777e17c9af438daab4f77
  • Private Key
    32바이트의 개인키이다. 절대 외부에 공개하면 안 된다.
    개인키를 통해 언제든 동일한 공개키와 주소를 재생성할 수 있다.
  • Public Key
    64바이트의 공개키이다. 주소 생성에 사용된다.

매 실행마다 서로 다른 값이 생성되며, 이는 암호학적 난수를 기반으로 한다.


주요 특징

  • 이더리움 지갑은 데이터베이스에 저장된 것이 아니라, 수학적으로 생성되는 키 쌍이다.
  • 개인키만 있으면 언제든 같은 지갑을 복원할 수 있다.
  • 지갑 주소는 단순히 공개키에서 파생된 값이다.

 

중요한 점

  1. 개인키 관리가 곧 지갑 관리이다
    지갑 파일이나 주소 자체보다 중요한 것은 개인키이다. 개인키만 있으면 같은 지갑을 언제든지 복원할 수 있다.
  2. web3j가 내부 과정을 대신 처리한다
    개발자는 복잡한 암호학 과정을 직접 구현할 필요가 없다.
    Keys.createEcKeyPair()와 Credentials.create()만 호출하면 지갑 생성이 끝난다.
  3. 매번 실행할 때마다 새로운 지갑이 생성된다
    이 과정은 암호학적으로 안전한 난수를 기반으로 하기 때문에, 실행할 때마다 전혀 다른 지갑 주소가 나온다.

정리

  • 이더리움 지갑은 결국 개인키, 공개키, 주소 세 가지 요소로 구성된다.
  • web3j를 사용하면 이 모든 과정을 단 몇 줄의 코드로 구현할 수 있다.
  • 실무에서 가장 중요한 것은 개인키 보관이며, 주소는 공개되어도 상관없다.

 

 

 

package wallet;

import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Keys;
import org.web3j.utils.Numeric;

public class WalletGenerate {

    /**
     * 1. 지갑 주소 생성 (Private Key, Public Key)
     * 
     * 지갑 생성 과정:
     * 1. ECDSA 키페어 생성 (Private Key + Public Key)
     * 2. Public Key에서 Keccak-256 해시로 주소 도출
     * 3. 0x 접두사 + 20바이트 주소 완성
     */

    public static void main(String[] args)
        throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException {

        // [[1. 지갑 주소 셍성 (Private Key, Public Key)]]

        System.out.println("=== 이더리움 지갑 생성 시작 ===");

        // SECP-256k1 타원곡선 암호화로 키페어 생성
        // 비트코인과 동일한 암호화 알고리즘 사용
        ECKeyPair ecKeyPair = Keys.createEcKeyPair();

        // Public Key에서 Keccak-256 해시로 주소 생성
        // 0x + 20바이트(40자리 16진수) 형태
        String walletAddress = Credentials.create(ecKeyPair).getAddress();

        System.out.println("생성된 지갑 주소: " + walletAddress);
        System.out.println("=== 지갑 주소 생성 완료 ===");

        System.out.println("\n=== 키 정보 출력 ===");

        // BigInteger를 0x 접두사가 있는 16진수 문자열로 변환
        String privateKey = Numeric.toHexStringWithPrefix(ecKeyPair.getPrivateKey());
        String publicKey = Numeric.toHexStringWithPrefix(ecKeyPair.getPublicKey());

        // Private Key: 32바이트 (비밀키, 절대 공개 금지)
        System.out.println("Private Key: " + privateKey);
        
        // Public Key: 64바이트 (공개키, 주소 생성용)
        System.out.println("Public Key: " + publicKey);


        /*
         * 실행 결과 예시:
         * 
         * 지갑 주소: 0x658b8a1ae242d0460d4777e17c9af438daab4f77 (20바이트)
         * Private Key: 0x--- (32바이트)
         * Public Key: 0x376db746fb37456556a2c0018d876fbc5e61f9d60dec7f9f21132b2d7a19e71d5a7b81bcd29ded77b7b489f2e0e043998e385809f9779133938065190f351a91 (64바이트)
         * 
         * 주요 특징:
         * - 매번 실행시 다른 값 생성 (암호학적 난수 사용)
         * - Private Key로 언제든 동일한 Public Key와 주소 재생성 가능
         */
    }
}

 

[참고] Numeric 클래스와 toHexStringWithPrefix() 사용하는 이유

Numeric 클래스란?

Web3j의 Numeric 클래스는 블록체인 데이터 형변환을 위한 유틸리티 클래스다. 블록체인에서는 다양한 숫자 형태(BigInteger, byte[], String 등)를 16진수 문자열로 변환하거나 그 반대 작업을 자주 해야 하는데, Numeric 클래스가 이런 번거로운 작업을 간편하게 해준다.

// 이런 변환 작업들을 쉽게 해준다
BigInteger number = new BigInteger("12345");
String hex = Numeric.toHexString(number);                        // "3039" 
String hexWithPrefix = Numeric.toHexStringWithPrefix(number);    // "0x3039"
byte[] bytes = Numeric.hexStringToByteArray(hex);                // [48, 57]

toHexStringWithPrefix()를 쓰는 이유

1. 블록체인 표준 형식 준수

이더리움을 비롯한 대부분의 블록체인에서는 16진수 데이터 앞에 "0x" 접두사를 붙이는 것이 표준이다.

// 표준 형식
"0x1a2b3c4d..."  

// 비표준 형식  
"1a2b3c4d..."

2. RPC 호출 호환성

이더리움 노드와 통신할 때 JSON-RPC API는 16진수 값에 반드시 "0x" 접두사를 요구한다.

{
  "method": "eth_sendTransaction",
  "params": [{
    "from": "0x742d35cc6633c0532925a3b8d1e57b5c4f6b12ab",
    "value": "0x9184e72a000"  // "0x" 접두사 필수!
  }]
}

3. 다른 도구들과의 호환성

MetaMask, Etherscan 등 대부분의 이더리움 도구들이 "0x" 접두사가 있는 형식을 기대한다.

실제 출력 결과 비교

// toHexStringWithPrefix() 사용
System.out.println("Private Key: " + Numeric.toHexStringWithPrefix(ecKeyPair.getPrivateKey()));
// 결과: Private Key: 0x4c08....

// toHexString() 사용 (접두사 없음)
System.out.println("Private Key: " + Numeric.toHexString(ecKeyPair.getPrivateKey()));
// 결과: Private Key: 4c08....

추가 팁: Numeric 클래스 활용법

// BigInteger를 16진수로
BigInteger value = new BigInteger("1000000000000000000"); // 1 ETH in wei
String hexValue = Numeric.toHexStringWithPrefix(value);     // "0xde0b6b3a7640000"

// 16진수를 BigInteger로
String hexString = "0xde0b6b3a7640000";
BigInteger weiAmount = Numeric.toBigInt(hexString);         // 1000000000000000000

// byte 배열과 16진수 변환
byte[] data = "Hello".getBytes();                         // [72, 101, 108, 108, 111]
String hexData = Numeric.toHexString(data);                 // "48656c6c6f"