[Bitcoin 4장] 키와 주소

Mong22·2023년 3월 14일
0
post-thumbnail

[해당 글은 '비트코인, 공개블록체인 프로그래밍(안드레아스 M. 안토노풀로스 저, 최은실 김도훈 송주한 옮김, 코인플러그 기술 감수)'을 참고했다.]

[해당 글에 https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch04.asciidoc에 포함된 사진과 코드, 표들을 사용했다.]


들어가기 앞서서

  비트코인은 암호학을 기반으로 한다. 암호학을 통해 비밀 정보를 입증(디지털 서명)하고 데이터의 진위 여부를 입증(디지털 지문)할 수 있다. 이번 장에서는 자금의 소유권을 관리하기 위해 키, 주소, 지갑의 형태로 비트코인에서 사용하는 암호학에 대해 살펴볼 것이다.

암호학은 컴퓨터 보안 분야에서 광범위하게 사용하는 수학의 한 분야로 '비밀 글쓰기'라는 의미를 담고 있다.


들어가기

  비트코인의 소유권은 디지털 키, 비트코인 주소, 디지털 서명 등을 통해 성립된다.

  디지털 키는 사용자의 지갑 속에 생성되어 저장된다. 디지털 키는 비트코인 프로토콜과 완전히 독립되어 있고, 블록체인에 대한 참조나 인터넷 접근 없이 지갑 소프트웨어가 생성하고 관리한다. 키는 분산화된 신뢰 및 관리, 소유권 입증, 암호증명 보안 모델 등의 비트코인 특성을 가능하게 한다.

  키는 개인키와 공개키 한 쌍으로 구성되어 있다. 대부분의 경우 개인키와 공개키는 지갑 파일 내부에 저장되어 지갑 소프트웨어를 통해 관리한다.


  디지털 서명은 비트코인 거래에 필요하다. 디지털 서명은 개인키만 보유하고 있으면 생성될 수 있다. 금액을 소비하기 위해 사용되는 디지털 서명은 암호학적 용어로 증인이라고 한다. 증인 데이터는 소비되는 자금의 실제 소유권을 증명한다.


  수취인의 공개키는 비트코인 주소라고 불리는 디지털 지문으로 표현된다. 대부분의 경우 비트코인 주소는 공개키로부터 생성되며 공개키에 대응된다.


  이제 암호 기법부터 키 생성, 저장, 관리 방법, 키를 대신하기 위해 사용되는 인코딩 방법, 그리고 키와 주소의 고급 기능에 대해 알아볼 것이다.


공개키의 암호학과 암호화폐

  공개키 암호는 한 방향으로는 계산하기 쉽지만 반대 방향으로는 계산이 불가능한 특성을 지닌다. 이 특성으로 디지털 비밀번호와 위조가 불가능한 디지털 서명을 가능하게 한다. 비트코인의 경우 타원곡선 곱셈 함수를 공개키 암호의 원리로 사용한다.

  비트코인에서 사용하는 한 쌍의 키는 공개키 암호학을 활용해 개인키와 개인키로 부터 파생된 공개키를 생성한다. 공개키는 비트코인을 전송받을 때 사용되며, 개인키는 전송받은 비트코인을 소비하기 위해 서명할 때 이용된다. 이 때, 공개키와 개인키의 수학적 관계 때문에 비밀키의 노출 없이 공개키만으로 서명의 유효성을 검증할 수 있다.

  비트코인을 소비할 때, 현재 소유주가 공개키와 서명을 제시하면 비트코인이 소비되며, 제시된 공개키와 서명을 통해 비트코인 네트워크 내에 있는 모든 사람이 거래를 검증한다. 이 때, 전송하려는 사람이 전송 당시 비트코인을 소유하고 있는지의 여부도 확인 가능하다.


개인키와 공개키

  앞서 비트코인 지갑에는 쌍으로 구성된 여러 키들이 들어있다고 설명했다. 이 때, 개인키(k)는 숫자로 구성되어 있으며 무작위로 숫자를 추출한다. 공개키(K)는 일방 암호함수인 타원곡선 곱셈함수를 이용해 개인키로부터 생성한다. 이제 키의 생성 과정과 공개키로부터 비트코인 주소를 생성하는 과정을 자세히 살펴보자. 아래의 그림은 개인키, 공개키, 그리고 비트코인 주소의 관계이다. 참고바란다.

[Figure 1. Private key, public key, and Bitcoin address]


개인키

  개인키는 무작위로 추출한 단순한 숫자로 구성된다. 비트코인 거래에 사용되는 돈의 소유권을 증명하기 위해 필요한 서명을 하기 위해 개인키가 사용된다. 이는 비트코인을 소비할 수 있다는 것을 의미하므로 개인키가 절대 노출돼선 안된다. 개인키 분실 시 개인키로 묶여 있는 돈을 찾을 수 없으므로 백업이 필요하다.

무작위로 뽑은 숫자를 이용해서 개인키 생성하기

  키를 생성하는 과정 중 가장 중요한 단계는 보안이 철저한 엔트로피소스(무작위성)을 찾는 일이다. 비트코인 소프트웨어는 기본적인 OS 난수생성기를 이용해 256비트의 엔트로피(무작위성)를 만들어 낸다.

  개인키는 1부터 n-1까지의 어떤 숫자로 구성될 수 있다. 무작위로 256비트의 숫자를 선택하고 이 숫자가 n-1보다 작은지 체크한다. 결과값이 n-1보다 작다면 성공적으로 개인키가 생성됐다고 보면 된다. n-1보다 크다면 다른 난수를 생성하게 된다.

개인키 생성에서 사용된 n은 타원곡선의 위수(order)라고 정의된 상수(n = 1.158*10^77, 2^256보다 약간 작은 값)이다.

  난수를 만들기 위해서 충분한 엔트로피를 가지고 있는 소스로부터 얻은 종자(seed)를 이용해야 한다. 즉 보안 측면에서 암호학적으로 안전한 난수생성기를 이용해야 한다. 암호학적으로 안전한 난수생성기에는 유사난수생성기(CSRPNG)가 있다.

  생성된 개인키를 16진수로 표현하면 다음과 같다.

1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD

공개키

  공개키는 타원곡선 곱셈 함수를 이용해 개인키로부터 계산된다. 그 역은 성립될 수 없다. K = k*G라는 식에서 k는 개인키, G는 생성 포인트라는 이름의 상수이며 K는 계산 결과로 나온 공개키다. 이 때, k를'이산로그 찾기'로 계산할 수 있으나 이는 브루트포싱과 동일하므로 의미가 없다. 이러한 특성을 가진 함수를 트랩 도어 수라고 부른다

브루트포싱이란 가능한 값들을 모두 대입해보는 공격 방식이다. 비트코인의 개인키의 경우 2의 256승의 경우의 수가 있으므로 2의 256승의 대입을 모두 해봐야 한다.

트랩 도어 함수는 한 방향으로의 계산은 쉬우나 반대 방향으로의 계산은 불가능한 함수이다. 즉, 역함수를 구하기 불가능하다는 말이다. 타원곡선 함수의 경우 곱셈은 쉬우나 나눗셈은 어렵다.

타원곡선 암호학

  이산로그 문제를 기반으로 하는 타원곡선 암호학은 비대칭 형식이거나 공개키 암호 형식으로, 타원곡선을 구성하는 여러 점들 위에서 덧셈 함수나 곱셈 함수로 표현된다. 다음 예시를 참고하자.


[Figure 2. An elliptic curve]

  비트코인은 특정한 타원곡선과 여러 개의 수학 상수를 함께 사용한다. NIST에서 개발한 secp256k1이라는 표준을 통해 타원곡선을 만들어 내는 함수를 아래와 같이 정의한다.

y2=(x3+7)over(Fp)y^2 = (x^3 + 7)over(\mathbb {F} _{p})

또는

y2modp=(x3+7)modpy^2 \mod p = (x^3 + 7) \mod p

  mod p(모듈로 소수 p)는 위 곡선이 소수위수(prime order) p의 유한체 체제에 존재한다는 사실을 보여 준다. 여기에서 p는 2^256 - 2^32 - 2^9 - 2^8 - 2^7- 2^6 - 2^4 - 1로 매우 큰 소수다.

유한체(Fp)(\mathbb {F} _{p})는 임의의 소수 p에 대하여 0, 1, ... , p-1만으로 이루어진 원소의 개수가 p인 집합을 의미한다. 이 집합 속에서는 사칙연산을 포함한 정해진 연산이 이루어져야 한다.

  아래의 그림은 소수위수가 17인 소규모 유한체에서 secp256k1을 기준으로 만든 함수의 그래프가 있다. 실제 비트코인에서 이용되는 함수의 그래프는 이보다 좀 더 복잡한 유형의 점들이 찍혀 있는 형태라고 생각하면 된다.


[Figure 3. Elliptic curve cryptography: visualizing an elliptic curve over F(p), with p=17]

  실제 사용되는 함수에서 어떤 점이 타원 곡선 위에 존재하는지 확인하려면 수식에 대입해보면 된다. 어떤 점(P)가 다음과 같다고 했을 때, 파이썬을 이용해 한번 확인해보자.

P = (55066263022277343669578718895168534326250603453777594175500187360389116729240, 
32670510020758816978083085130507043184471273380659243275938904335757337482424)

  타원곡선에서의 덧셈 연산은 우리가 아는 실수의 사칙연산의 덧셈과 유사하지만 살짝 다르다. 기본적으로 P1과 P2라는 두 개의 점이 주어졌을 때, 곡선상에는 제3의 포인트인 P3 = P1 + P2가 존재한다. 이 때, P3는 P1과 P2사이에 직선을 그어서 계산한다. 이 직선은 정확히 P1과 P2가 아닌 또 다른 한 점에서 이 곡선과 교차하는데, 이 포인트를 P3' = (x, y)라고 하자. 그 후 x축에 대칭시키면 P3 = (x, -y)를 구할 수 있다. 이해하기 힘들다면 위에서 제시한 타원곡선 그래프에서 직접 연산을 해보며 이용해보길 바란다.

  타원곡선 수학에서는 '무한원점'이라는 점 하나가 있다. 이는 덧셈에서 0의 역할과 유사하다. 이 무한원점의 개념이 필요한 경우가 몇 가지 존재한다. 예를 들어 P1과 P2의 x값은 같지만 y값만 다른 경우를 생각해보자. 이 두 점을 지나는 접선은 X축에 평행하게 되고, P1과 P2의 합인 P3는 무한원점이 된다. 또, P1이 무한원점일 경우, P1 + P2 = P2가 되고, P2가 무한원점일 경우, P1 + P2 = P1이 된다.

  타원 곡선상의 덧셈은 결합성을 지니고 있다. 이는 실수의 사칙연산의 결합법칙과 동일하다고 생각하면 된다.

  이번엔 타원곡선 수학에서의 곱셈을 정의해보자. 타원곡선상에 위치한 점 P에 대해 k가 정수인 경우 kP = P + P + ... + P(k배)가 된다. 이 때, k를 지수라고 부른다.

공개키 생성하기

  공개키는 무작위 값인 개인키 k값에 곡선 위에서 미리 정해진 값인 생성 포인트 G를 곱해서 곡선상의 다른 곳에 위치한 포인트를 얻는다. 이 포인트가 공개키인 K가 되는 것이다. 생성 포인트는 secp256k1의 일부로 명시되어 있고 비트코인의 모든 키에 대해 동일하다. 공개키 연산식은 다음과 같다.

K=kGK = k * G

  이 때, k와 K값의 관계는 고정되어 있지만, k값에서 K값 쪽으로 일방향으로만 계산할 수 있다. 다음 그림을 통해 G를 k번 더하는 과정을 이해해보자.

[Figure 4. Elliptic curve cryptography: visualizing the multiplication of a point G by an integer k on an elliptic curve]


비트코인 주소

  비트코인 주소는 숫자와 문자로 구성된 문자열로 공개키로부터 생성된 주소는 숫자 1로 시작한다. 아래 문자열이 주소의 예시다.

1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy

  비트코인 주소는 개인키 및 공개키 쌍을 보유한 소유주를 나타낼 수도 있고, 지불 스크립트를 통해 여러 다른 대상을 나타낼 수도 있다.

  비트코인 주소를 공개키로부터 생성할 때는 일방 암호화 해싱을 사용한다. 이 때 사용하는 알고리즘은 SHA와 RIPEMD이다. 공개키 K를 가지고 SHA256 해시를 산출한 후 그 결과값의 RIPEMD16 해시를 산출하면 160비트(20바이트) 크기의 숫자가 생성된다.


$$ A = RIPEMD160(SHA256(K)) $$
> _**암호화 해싱**_은 알고리즘으로, 임의 크기의 입력값으로부터 일정한 크기의 해시값을 생성해 내는 일방 함수다.

  해싱 과정을 거친 공개키는 사람이 입력할 때의 오류를 예방하기 위해 거의 항상 Base58Check로 인코딩되어 비트코인 주소가 된다. 이에 대해선 다음 챕터에서 정확히 알아보자.

  대략적인 비트코인 주소 형성 과정은 아래의 그림을 참고 바란다.


[Figure 5. Public key to Bitcoin address: conversion of a public key into a Bitcoin address]


Base58과 Base58Check 인코딩

  Base58은 문자열을 (0, O, l, I)를 제외한 대문자, 소문자, 숫자를 사용해 압축하는 텍스트 기반 인코딩 포맷이다. 아래에 있는 문자들이 Base58 알파벳이다

123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

  Base58Check는 비트코인에서 주로 사용되는 오류의 검사 코드를 지닌 Base58의 인코딩 포맷이다. 인코딩된 데이터의 해시로부터 검사합을 생성해 데이터 입력 및 타이핑 오류를 감지하고 방지할 수 있다.

검사합은 인코딩되고 있는 데이터의 끝부분에 추가되는 4바이트 크기의 중복 검사다.

  Base58Check 코드와 디코딩 소프트웨어가 함께 존재하는 경우, 해당 데이터의 검사합을 계산한 후 Base58Check 코드와 비교한다. 두 검사합 값이 불일치하면 에러가 발생하고 데이터가 유효하지 않게 된다.

  데이터를 Base58Check 포맷으로 전환하기 위해서 데이터에 '버전 바이트'라고 불리는 접두부를 붙여야 한다. 버전 바이트는 인코딩된 데이터의 유형을 쉽게 찾아볼 수 있도록 해 준다. 아래의 표가 접두부 목록이다.


[Table 1. Base58Check version prefix and encoded result examples]

  데이터에 접두부를 붙인 후 '이중-SHA' 검사합을 산출한다. 결과값으로 나온 32비트의 해시에서 첫 4바이트만을 따온다. 이 4바이트가 검사합이 되고, 검사합은 데이터의 끝부분에 연결된다.

checksum=SHA256(SHA256(prefix+data))checksum = SHA256(SHA256(prefix+data))

  결과값은 접두부, 데이터, 검사합 이렇게 세 부분으로 구성된다. 이 결과값을 Base58 알파벳을 이용해 인코딩한다. 접두부는 포맷을 구분하기 쉽도록 하는 역할을 한다. 아래의 그림으로 정리해보자.


[Figure 6. Base58Check encoding]


키 포맷


  개인키와 공개키는 여러 개의 다른 포맷으로 표현될 수 있으며, 이는 사람들이 키를 읽고 실수 없이 입력하게 하기 위함이다.

개인키 포맷

  아래는 여러 개인키 포맷을 나타내며, 각각의 포맷은 다른 환경에서 사용된다. 16진법 포맷과 가공하지 않은 2진법 포맷은 소프트웨어 내부적으로 상ㅇ되며, WIF 포맷은 지갑 간 액스포트/임포트, 또는 QR 코드에 사용된다.

  WIF 포맷의 경우 Base58Checksum을 이용한다. 이 때, 접두부는 128(0x80)으로 하여 인코딩된다.


공개키 포맷

  공개키는 압축 공개키비압축 공개키로 표현될 수 있다. 공개키는 한 쌍의 (x, y) 좌표로 구성된 점으로, 타원곡선상에 존재한다. 공개키는 접두부 04가 있는데 이는 비압축 공개키와 02 혹은 03으로 시작하는 압축 공개키를 구분하기 위해 사용된다.


압축 공개키

  압축 공개키는 디스크 공간을 절약하기 위해 도입되었다. 공개키가 타원곡선 위의 점이라는 것을 이용해 x좌표만 저장하는 방식이다. 타원곡선 함수를 통해 x값을 알면 y값을 알 수 있다. 그런데 방정식의 왼쪽에는 y2y^2이 존재하므로 타원곡선 유한체 상 y값이 짝수와 홀수 두 가지가 나온다. 이후 공개키의 정확한 추정을 위해 접두부를 통해 y값이 짝수인 경우 02, y값이 홀수인 경우 03을 사용해 이를 표시한다.

[Figure 7. Public key compression]

  압축 공개키는 동일한 개인키에 대응한다. 하지만, 압축 공개키로 만든 비트코인 주소는 비압축 공개키로 만든 비트코인 주소와 달라진다. 이 문제를 해결하기 위해 WIF를 통한 압축 개인키가 사용된다.


압축 개인키

  압축 개인키는 압축 공개키만의 생성 출처가 돼야 하는 개인키를 의미한다. 여기서 엑스포트 포맷을 WIF-압축이나 WIF라고 말한다. 비압축 개인키는 압축 개인키와 달리 앞의 장에서 본 비압축 공개키만의 생성 출처가 돼야 하는 개인키를 의미한다. 압축 개인키라고 해서 개인키가 압축되었다고 생각하면 안된다. 아래의 표는 Hex, WIF 형태의 개인키와 압축형으로 표현된 개인키들을 나타낸 표다.

  16진법으로 되어 있는 압축 개인키는 포맷은 끝단에 1바이트(0x01)가 추가된다. Base58 인코딩 버전의 접두부인 WIF와 WIF-압축 포맷 모두에 접두부는 128(0x80)으로 동일하지만, 숫자의 끝부분에 1바이트가 추가됨으로써 WIF와 WIF-압축형의 첫 글자가 5에서 K혹은 L로 바뀌게 된다.

  WIF는 압축 공개키 기능을 가지고 있는 새로운 지갑에서 개인키를 추출하면, 개인키에 1바이트 크기의 접미부 01이 추가된 형태로 WIF가 변경된다. 이 WIF의 Base58Check로 인코딩된 개인키 결과값을 당연히 '압축형 WIF'라고 한다.

  결론적으로 압축 개인키는 공개키의 압축 여부에 따라 비트코인 주소가 달라지는 점을 고려해 개인키로 공개키를 생성할 때 공개키의 종류를 밝히기 위해서 존재한다.


고급키와 주소

암호화된 개인키(BIP-38)

  개인키는 유출을 대비하여 암호화해야 안전하다. 개인키로 비트코인 거래를 마음대로 할 수 있기 때문이다. BIP-38은 WIF 형태의 비트코인 개인키를 입력값으로 한다. 또한, 패스프레이즈라는 패스워드를 이용할 수도 있다. 패스프레이즈를 사용하면 입력값이 (개인키 + 패스프레이즈) 형태가 된다. BIP-38 암호화 기법의 결과값은 Base58Check로 인코딩된 암호화 개인키로 접두부 6P로 시작한다.


Pay-to-Script Hash(P2SH)와 다중서명 주소

  PS2H 주소는 비트코인 거래의 수령인을 공개키 소유주가 아닌 스크립트의 해시로 지정한다. 이 주소는 '3'으로 시작하며 공개키 해시와 하나의 개인키 서명 외에 더 많은 것이 있어야 소유권이 증명된다. 소유권 증명 요건은 주소 생성시에 스크립트 내에 지정된다. P2SH 주소를 인코딩하는 과정은 기존 비트코인 주소 생성과 동일하다.

scripthash=RIPEMD160(SHA256(script))script hash = RIPEMD160(SHA256(script))

  위의 수식의 결과값의 첫 글자는 5가 되며, Base58Check로 인코딩되어 결과적으로 3으로 시작하는 주소를 얻는다.

다중서명 주소와 P2SH

  P2SH 함수의 가장 흔한 구현은 다중서명 주소 스크립트다. M-of-N 다중서명이라고도 한다. N개의 키 중에서 M개으이 서명이 필요하다는 것이다. 따라서 M은 N보다 항상 작거나 같다.


꾸미기 주소

  꾸미기 주소는 사람이 읽을 수 있는 특정 단어가 속한 주소를 의미한다. 생성은 브루트 포싱을 이용하여 생성하며 여러 알고리즘이 존재한다. 채굴자들에게 비트코인을 주고 외주를 줘서 꾸미기 주소를 구매할 수 도 있다.

꾸미기 주소 보안

  사용자가 꾸미기 주소에 포함된 단어에만 주목할 경우, 비슷한 유형의 꾸미기 주소를 만들어 주소를 바꿔치기 한다면 다른 곳에 코인이 전달될 수 있다. 따라서 사용자로 하여금 단어 뒤에 몇 글자를 더 확인하게 함으로써 정확한 주소로 코인을 전달할 수 있게 해야 한다.


종이지갑

  종이지갑은 개인키를 종이에 저장해 놓는 방식이다. 종이지갑에만 개인키가 저장된 경우, 온라인 상에 어떠한 곳에서도 정보를 찾을 수 없게 되므로 어떠한 온라인 공격에도 안전하게 된다. 이 때, 종이에 대한 도난은 막을 수 없는데, BIP-38 암호화를 이용하거나 조작 방지 스티커 등을 통해 대비할 수 있다. bitaddress.org의 html을 복사해 놓으면 오프라인 상태에서도 개인키를 프린트할 수 있다.



  이번 장에선 비트코인 거래에 필요한 개인키, 공개키와 비트코인 주소가 생성되는 원리와 유형에 대해 알아봤다. 다음 장에선 비트코인 지갑 기술에 대해 알아보도록 하자.

profile
Wah!

0개의 댓글