회사에서 온보딩으로 진행한 Web3j 학습 내용을 정리한 글입니다.
- 네트워크: BSC Testnet
- 체인 ID: 97
---
1. 지갑 주소 생성 (Private Key, Public Key)
이더리움 지갑을 만든다고 하면 흔히 메타마스크 같은 지갑 앱을 떠올리지만, 사실 지갑의 핵심은 단순하다.
지갑은 결국 Private Key, Public Key, Address 이 세 가지 값으로 정의된다.
자바에서는 web3j 라이브러리를 사용하여 이를 코드로 구현할 수 있다.
지갑 생성 과정
이더리움 지갑은 기본적으로 다음 과정을 거쳐 만들어진다.
- 키 쌍 생성
- 타원곡선 암호화(SECP-256k1)를 사용해 개인키와 공개키를 생성한다.
- 개인키는 서명에 사용되고, 공개키는 이를 검증하거나 주소를 만들 때 사용된다.
- 주소 도출
- 공개키를 Keccak-256 해시 함수에 넣는다.
- 나온 값에서 마지막 20바이트를 추출하고, 앞에 0x를 붙이면 이더리움 주소가 된다.
- 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바이트의 공개키이다. 주소 생성에 사용된다.
매 실행마다 서로 다른 값이 생성되며, 이는 암호학적 난수를 기반으로 한다.
주요 특징
- 이더리움 지갑은 데이터베이스에 저장된 것이 아니라, 수학적으로 생성되는 키 쌍이다.
- 개인키만 있으면 언제든 같은 지갑을 복원할 수 있다.
- 지갑 주소는 단순히 공개키에서 파생된 값이다.
중요한 점
- 개인키 관리가 곧 지갑 관리이다
지갑 파일이나 주소 자체보다 중요한 것은 개인키이다. 개인키만 있으면 같은 지갑을 언제든지 복원할 수 있다. - web3j가 내부 과정을 대신 처리한다
개발자는 복잡한 암호학 과정을 직접 구현할 필요가 없다.
Keys.createEcKeyPair()와 Credentials.create()만 호출하면 지갑 생성이 끝난다. - 매번 실행할 때마다 새로운 지갑이 생성된다
이 과정은 암호학적으로 안전한 난수를 기반으로 하기 때문에, 실행할 때마다 전혀 다른 지갑 주소가 나온다.
정리
- 이더리움 지갑은 결국 개인키, 공개키, 주소 세 가지 요소로 구성된다.
- 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"
'Blockchain' 카테고리의 다른 글
BSC Testnet Web3j 스터디 (5) 단위 변환 (wei, gwei, eth) (0) | 2025.10.12 |
---|---|
BSC Testnet Web3j 스터디 (4) Private Key로 서명 생성 (0) | 2025.10.11 |
BSC Testnet Web3j 스터디 (3) 생성된 주소 메타마스크에 등록해보기 (1) | 2025.10.09 |
BSC Testnet Web3j 스터디 (2) Private Key로 Public Key 추출 (0) | 2025.09.13 |
블록체인의 이해 (1) | 2025.08.24 |