미리 알아야 할 내용들
'비트코인/암호학' 카테고리의 글 목록
평범한 대학생의 블록체인 기술 관련 블로그 입니다.
kwjdnjs.tistory.com
[비트코인 암호학] 3.1 공개키 암호화
타원곡선을 이용해 디지털 서명을 하고 검증하기 위해서는 공개키(Public key)와 개인키(Private key)가 필요합니다. 공개키와 개인키로 디지털 서명을 하는 방법에 대해 설명하기 전에 먼저 공개키
kwjdnjs.tistory.com
[비트코인 암호학] 3.2 공개키와 개인키
지금까지 유한체 타원곡선을 다루면서 다음과 같은 변수들을 정리했었습니다. a와 b는 타원곡선 방정식의 값으로 지금까지 a=0, b=7을 사용했습니다. p는 유한체의 위수입니다. G는 타원곡선 위
kwjdnjs.tistory.com
[비트코인 구조] 빅 엔디안(Big endian)과 리틀 엔디안(Little endian)
빅 엔디안(Big endian)과 리틀 엔디안(Little endian) 엔디안(Endian)은 바이트가 저장되는 순서입니다. 빅 엔디안은 일반적으로 사람이 읽는 순서로, 리틀 엔디안 빅 엔디안의 역순으로 저장합니다. 예
kwjdnjs.tistory.com
[비트코인 구조] 비트코인 해시 함수(sha-256, hash256, hash160)
비트코인 해시 함수(sha-256, hash256, hash160) 해시 함수는 입력 데이터를 고정된 길이의 해시값으로 변환하는 함수를 말합니다. 비트코인에서는 기본적인 해시 함수인 sha-256과 이를 응용한 hash256, h
kwjdnjs.tistory.com
[비트코인 구조] 인코딩(Encoding) - Base58Check, Bech32
인코딩(Encoding) - Base58Check, Bech32 인코딩(Encoding)은 데이터를 저장할 때 데이터의 양을 줄이기 위해 특별한 규칙에 따라 압축하는 것을 말합니다. 인코딩된 데이터를 불러와 원본으로 변환하는
kwjdnjs.tistory.com
개인키(Private key), 공개키(Public key), 주소(Address) 생성
이번 글에서는 개인키와 공개키 그리고 비트코인 주소를 생성해보겠습니다. 비트코인의 계좌번호라고 할 수 있는 주소를 생성하기 위해서는 공개키가 필요합니다. 그리고 공개키를 생성하기 위해서는 비트코인 계좌의 비밀번호라고 할 수 있는 개인키가 필요합니다. 따라서 개인키를 가장 먼저 생성해보겠습니다.
1. 개인키
개인키는 비트코인 계좌의 비밀번호로 비트코인 주소(계좌)에 접근할 수 있게 해주는 유일한 도구입니다. 따라서 생성된 개인키를 안전하게 보관하는 것이 중요합니다. 실제 사용할 용도의 개인키는 보안을 위해 반드시 무작위로 생성해야 합니다. 이 글에서는 오로지 테스트를 목적으로 개인키를 생성하기 때문에 특정한 키워드를 통해 개인키를 생성해보겠습니다.
개인키는 256비트로 이뤄져 있습니다. 따라서 sha-256 함수를 통해 개인키를 생성해보겠습니다. 이 글에서 사용할 생성 키워드는 start bitcoin입니다. 파이썬을 이용한 코드는 다음과 같습니다.
import hashlib
s = b'start bitcoin'
h = hashlib.sha256(s)
print(h.hexdigest())
이를 통해 얻어진 개인키는 다음과 같습니다.
42d219444915828585a35b44849417b6eeb604bd0c7afda058c78319481d0055
2. 공개키
이번에는 생성된 개인키를 이용해 공개키를 생성해보겠습니다. 공개키를 생성하는 가장 기본적인 규칙은 다음과 같이 타원곡선의 스칼라 곱셈을 이용하는 것입니다.

P는 생성된 공개키, d는 개인키, G는 secp256k1에 정의되어 있는 생성점 G입니다. secp256k1에 정의된 값들과 유한체 타원곡선에서의 스칼라 곱셈 공식을 이용하면 공개키 P를 구할 수 있습니다. 다음은 공개키를 구하는 파이썬 코드입니다.
# 개인키
d = 0x42d219444915828585a35b44849417b6eeb604bd0c7afda058c78319481d0055
#secp256k1
p = 2**256 - 2**32 - 977 # 타원곡선의 위수 p (공개키 아님)
n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
g = [gx, gy]
# 점 덧셈 함수
def add(dot1, dot2, p):
# 한 점이 무한원점일 경우
if dot1 == None:
return dot2
elif dot2 == None:
return dot1
x1 = dot1[0]
x2 = dot2[0]
y1 = dot1[1]
y2 = dot2[1]
# 점 덧셈 계산
if x1==x2 and y1!=y2:
return None # 무한원점
elif x1!=x2 and y1!=y2:
m = (((y2-y1)%p) * power((x2-x1)%p,(p-2),p))%p
x3 = (m**2-x1-x2)%p
y3 = (m*(x1-x3)-y1)%p
return [x3,y3]
elif x1==x2 and y1==y2 and y1!=0:
m = (((3*x1**2)%p) * power((2*y1)%p,p-2,p))%p
x3 = (m**2-2*x1)%p
y3 = (m*(x1-x3)-y1)%p
return [x3,y3]
else:
return None
# 빠른 거듭제곱 계산
def power(a,b,p):
result = 1
while b > 0:
if b % 2 != 0:
result = (result*a) % p
b //= 2
a = (a*a) % p
return result
# 공개키 p 구하기
i = 0
g_list = []
# 개인키 d는 G의 군의 위수 n에 대하여 순환하므로 n에 대한 나머지 구하기
d = d % n
while True:
g_list.append(g)
g = add(g,g,p)
# 계산속도를 빠르게 하기 위한 방법 사용
# G*G => 2G * 2G => ... => nG * nG + (n-i)G + ...
i += 1
if(pow(2,i+1) > d):
k = d - pow(2,i)
i -= 1
while k != 0:
if k >= pow(2,i):
k = k - pow(2,i)
g = add(g,g_list[i],p)
i -= 1
break
# 결과 출력
print('공개키 Px:',hex(g[0]))
print('공개키 Py:',hex(g[1]))
출력 결과는 다음과 같습니다.
공개키 Px: 0xeb6033f9e7446731e50e75b099ed5439698f3d5a59e89b1f64c2445c2a245bf0
공개키 Py: 0xb1eee1bef6c81f6118de9fda1da4d85a728579563556b402aa75728d97da2598
공개키 P는 타원곡선 위의 한 점으로 x좌표와 y좌표의 값을 갖습니다. 따라서 P를 공개키로 사용하기 위해 x, y값을 하나로 묶을 필요가 있습니다. 이 과정에서 비압축 방식과 압축 방식 2가지가 사용됩니다.
1. 비압축 방식
비압축 방식은 단순하게 x와 y를 한 줄로 묶어서 저장하는 방식입니다. 해당 공개키가 비압축 방식이라는 사실을 알려주기 위해 맨 앞에 '04'를 붙이고 이어서 Px와 Py를 붙입니다. 다음은 비압축 방식의 공개키입니다.
04eb6033f9e7446731e50e75b099ed5439698f3d5a59e89b1f64c2445c2a245bf0b1eee1bef6c81f6118de9fda1da4d85a728579563556b402aa75728d97da2598
2. 압축 방식
압축 방식은 Px와 Py가 타원곡선 위의 한 점이라는 사실을 이용합니다. 타원곡선의 방정식은 secp256k1에 미리 정의되어 있기 때문에 x좌표만 가진 상태에서도 y좌표를 도출해 낼 수 있습니다. 따라서 압축 방식은 x좌표 만을 저장합니다. 타원곡선의 특성상 하나의 x좌표에 대한 y좌표의 값이 2개씩 존재하기 때문에 y좌표가 홀수인지 짝수인지를 표시해야 합니다. 따라서 y좌표가 짝수인 경우 맨 앞에 '02'를 붙이고, 홀수인 경우 맨 앞에 '03'을 붙입니다. 이후 x좌표를 뒤에 붙이면 압축 방식의 공개키가 생성됩니다. 압축 방식의 공개키는 다음과 같습니다.
02eb6033f9e7446731e50e75b099ed5439698f3d5a59e89b1f64c2445c2a245bf0
3. 주소
마지막으로 압축 방식의 공개키를 이용하여 비트코인의 주소를 생성해보겠습니다.
1. hash160 해시 함수 사용해 해시값 구하기
먼저 공개키 값을 hash160 해시 함수를 이용하여 160비트의 고정된 길이의 해시값으로 변환합니다. 파이썬 코드는 다음과 같습니다.
import hashlib
p = '02eb6033f9e7446731e50e75b099ed5439698f3d5a59e89b1f64c2445c2a245bf0'
h1 = hashlib.sha256(bytes.fromhex(p))
h2 = hashlib.new('ripemd160', h1.digest())
print(h2.hexdigest())
결괏값은 다음과 같습니다.
4d2f18c566944ff7ce000b657d237e8a2fecbf87
2. 메인넷과 테스트넷 주소 구분하기
비트코인에는 실제 비트코인이 사용되고 있는 블록체인인 메인넷과 개발과 테스트를 목적으로 한 블록체인인 테스트넷으로 구분되어 있습니다. 두 블록체인은 다른 블록체인이기 때문에 주소도 구별해 사용합니다. 기본적으로 메인넷은 해시값 앞에 16진수 '00'을 붙이고, 테스트넷은 해시값 앞에 16진수 '6f'를 붙입니다. 세그윗 등 다른 방식의 주소는 다른 값이 앞에 붙습니다.
- 메인넷: 004d2f18c566944ff7ce000b657d237e8a2fecbf87
- 테스트넷: 6f4d2f18c566944ff7ce000b657d237e8a2fecbf87
3. 체크섬 구하기
메인넷과 테스트넷으로 구분된 값을 hash256 함수를 이용하여 해시값을 계산합니다. 그 후 첫 4바이트의 값을 얻습니다. 이 4바이트의 값이 체크섬입니다. 파이썬 코드는 다음과 같습니다.
import hashlib
main = '004d2f18c566944ff7ce000b657d237e8a2fecbf87'
test = '6f4d2f18c566944ff7ce000b657d237e8a2fecbf87'
def hash256(p):
h1 = hashlib.sha256(p)
h2 = hashlib.sha256(h1.digest())
print(h2.hexdigest()[:8])
hash256(bytes.fromhex(main))
hash256(bytes.fromhex(test))
체크섬 값은 다음과 같습니다.
메인넷: f03c65b1
테스트넷: 18a8f31a
구한 체크섬 값은 각 해시값 맨 뒤에 붙입니다.
- 메인넷: 004d2f18c566944ff7ce000b657d237e8a2fecbf87f03c65b1
- 테스트넷: 6f4d2f18c566944ff7ce000b657d237e8a2fecbf8718a8f31a
4. Base58Check 인코딩으로 주소 구하기
마지막으로 체크섬까지 포함된 값을 Base58Check로 인코딩합니다. 파이썬 코드는 다음과 같습니다.
import hashlib
main = '004d2f18c566944ff7ce000b657d237e8a2fecbf87f03c65b1'
test = '6f4d2f18c566944ff7ce000b657d237e8a2fecbf8718a8f31a'
BASE58_CODE = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def base58_check(p):
prefix = ''
for c in p:
if c == 0:
prefix += '1'
else:
break
num = int.from_bytes(p,'big') # 'big'은 빅엔디안
result = ''
while num > 0:
num, mod = divmod(num, 58)
result = BASE58_CODE[mod] + result
print(prefix + result)
base58_check(bytes.fromhex(main))
base58_check(bytes.fromhex(test))
결과는 다음과 같습니다.
- 메인넷: 1837T7TYua4iekZQ8d6jYMoMVpCqQ6AoMS
- 테스트넷: mnZ4kAYXibVyRs31rC57NH1gMooYDaKQMw
위 결과 값이 바로 비트코인 메인넷, 테스트넷 주소입니다. 맨 처음 생성했던 개인키를 이용하면 위 주소에 접근할 수 있습니다.
[주의! 위 주소로 비트코인을 전송하지 마세요. 개인키가 공개된 주소이기 때문에 비트코인을 잃어버릴 수 있습니다.]
지금까지 개인키와 공개키 그리고 비트코인 주소의 생성 방식에 대해 알아봤습니다. 감사합니다.
이어지는 글들
[비트코인 구조] DER 형식 서명(Signature) 생성
미리 알아야 할 내용들 '비트코인/암호학' 카테고리의 글 목록 평범한 대학생의 블록체인 기술 관련 블로그 입니다. kwjdnjs.tistory.com [비트코인 암호학] 4.1 디지털 서명 디지털 서명은 공개키 암
kwjdnjs.tistory.com
'비트코인 > 비트코인 구조' 카테고리의 다른 글
[비트코인 구조] 잠금 스크립트(scriptPubkey)와 해제 스크립트(scriptSig) 기초 (0) | 2022.08.29 |
---|---|
[비트코인 구조] DER 형식 서명(Signature) 생성 (0) | 2022.08.26 |
[비트코인 구조] 인코딩(Encoding) - Base58Check, Bech32 (2) | 2022.08.22 |
[비트코인 구조] 비트코인 해시 함수(sha-256, hash256, hash160) (0) | 2022.08.20 |
[비트코인 구조] 빅 엔디안(Big endian)과 리틀 엔디안(Little endian) (0) | 2022.08.19 |