Browse Source

지갑과 주소 완료

iwanhae 3 years ago
parent
commit
be73d45915
4 changed files with 105 additions and 4 deletions
  1. 105 4
      4_지갑과_비트코인_주소.md
  2. BIN
      img/4_3.jpg
  3. BIN
      img/4_4.png
  4. BIN
      img/4_5.PNG

+ 105 - 4
4_지갑과_비트코인_주소.md

@@ -36,7 +36,7 @@
 
 >  초기 비트코인의 경우 1에서 2^256 사이의 숫자를 고를때 운영체제 자체에서 제공하는 난수발생기를 이용하던가 자신이 직접 아무숫자나 고를 수 도 있었지만 요즘 대부분의 비트코인 특정 규칙에따라 난수를 발생시키는데 이는 지갑파트에서 자세히 다룰것이다.
 
- 비밀키가 만들어졌으면, 다음으로 필요한 것은 공개키 이다. 공개키를 구하는 과정은 앞쪽에서 ECDSA를 설명하면서 그 과정만을 설명한 책이 있을정도로 분량이 많고 복잡하다면서 건너뛰었다. 사실 이 개념은 필자 본인도 대략적으로 이해만 할뿐 정확히 수학적으로 어떻게 구현되는지는 모르고, 이건 비트코인 개발자들도 마찬가지여서 원조 비트코인 프로그램인 Bitcoin Core도 개발 초기에는 OpenSSL에 구현된 secp256k1을 사용했다. 현재는 OpenSSL 대신 비트코인을 위해 개발된 libsecp256k1을 사용해 ECDSA관련 연산을 행하고 있는데 libsecp256k1 자체가 `sipa`라는 Github유저에 의해 2013년 중순에 개발되기 시작해 2015년 11월에 와서야 완성되고 Bitcoin Core에 통합되었다는 점을 생각해보면 그 복잡도를 상상해 볼 수 있다.
+ 비밀키가 만들어졌으면, 다음으로 필요한 것은 공개키 이다. 공개키를 구하는 과정은 앞쪽에서 ECDSA를 설명하면서 그 과정만을 설명한 책이 있을정도로 분량이 많고 복잡하다면서 건너뛰었으나, 사실 이 개념은 필자 본인도 대략적으로 이해만 할뿐 정확히 수학적으로 어떻게 구현되는지는 모른다. 다만 이건 비트코인 개발자들도 마찬가지였는지 원조 비트코인 프로그램인 Bitcoin Core도 개발 초기에는 직접 구현하지 않고 OpenSSL라는 다른 오픈소스 프로젝트에 구현된 secp256k1을 그대로 가져와 사용했다. 현재는 OpenSSL 대신 비트코인을 위해 개발된 libsecp256k1을 사용해 ECDSA관련 연산을 행하고 있는데 libsecp256k1 자체가 `sipa`라는 Github유저에 의해 2013년 중순에 개발되기 시작해 2015년 11월에 와서야 완성되고 Bitcoin Core에 통합되었다는 점을 생각해보면 그 복잡도를 간접접으로 상상해 볼 수 있을꺼라 믿는다.
 
  이제 이 완성된 공개키를 이용해서 비트코인 주소를 만들어볼 차례이다. 공개키로부터 비트코인 주소를 만드는 과정은 아래와 같은 그림으로 간단히 표현할 수 있다. 
 
@@ -48,14 +48,115 @@ graph TD
 Public_Key_Hash-->|Base58Check|비트코인주소
 ```
 
-먼저 공개키를 SHA2 해시함수에 넣고 돌려서 256bits크기의 해시값 A를 얻어낸다. 그 후 얻어낸 해시값A를 RIPEMD160이라는 해시함수에 넣고 돌려서 160bits크기의 해시값 B를 얻어낸다. 이 해시값 B는 `Public Key Hash`라 불리며 비트코인 주소 대신에 이 값으로 송금을 할 수도 있다. (거래 생성부분에서 `P2PKH` 참고) 근데  `Public Key Hash`만으로도 거래가 가능하면 왜 비트코인 주소가 따로있는 것일까? 그 이유는 미관에 있다. 공개키를 해시함수를 두번돌려서 나온 160bits크기의 값은 0과 1로만 구성되어있는 2진수 데이터 이고 이를 그나마 보기쉬운 16진수표기법으로 바꾸면 `005F9D6E944D5B968DADE7EFABFA02395A8733F6086E43E710`이런 형태로 나타나는데 미관상 보기 좋지 않을뿐더러 입력하다가 실수하기 좋은 형태이기도 하다. 이렇게 실수하기 좋은 형태를한 2진수 데이터를 사람이 입력할때 실수가 잘 발생하지 않도록 보기좋은 형태로 바꿔주는 것이 바로 Base58 인코딩이다.
+먼저 공개키를 SHA2 해시함수에 넣고 돌려서 256bits크기의 해시값 A를 얻어낸다. 그 후 얻어낸 해시값A를 RIPEMD160이라는 해시함수에 넣고 돌려서 160bits크기의 해시값 B를 얻어낸다. 이 해시값 B는 `Public Key Hash`라 불리며 비트코인 주소 대신에 이 값으로 송금을 할 수도 있다. (거래 생성부분에서 `P2PKH` 참고) 근데  `Public Key Hash`만으로도 거래가 가능하면 왜 비트코인 주소가 따로있는 것일까? 그 이유는 미관에 있다. 공개키를 해시함수를 두번돌려서 나온 160bits크기의 값은 0과 1로만 구성되어있는 2진수 데이터 이고 이를 그나마 보기쉬운 16진수표기법으로 바꾸면 `005F9D6E944D5B968DADE7EFABFA02395A8733F6086E43E710`이런 형태로 나타나는데 미관상 보기 좋지 않을뿐더러 입력하다가 실수하기 좋은 형태이기도 하다. 이렇게 실수하기 좋은 형태를한 2진수 데이터를 사람이 입력할때 실수가 잘 발생하지 않도록 보기좋은 형태로 바꿔주는 것이 바로 Base58 인코딩이다. 개념은 아스키코드랑 똑같다. 어떤 비트조합이 있으면 해당 비트열을 그에 해당하는 문자로 치환시켜주는 것이다.
 
 ![img](./img/4_3.jpg)
 
-이미 잘 알려진 Base64인코딩과 다른점은 글꼴에 따라 혼동하기 쉬운 0(숫자), O(알파벳), l(소문자 L), I(대문자 i), +, / 등의 글자가 빠져 사람이 옮겨적을때 실수할 가능성을 낮춰졌다는 정도이다. 다만 비트코인 주소가 실제 자산이 옮겨가는 주소이고 사소한 실수가 
+이미 잘 알려진 Base64인코딩과 다른점은 글꼴에 따라 혼동하기 쉬운 0(숫자), O(알파벳), l(소문자 L), I(대문자 i), +, / 등의 글자가 빠져 사람이 옮겨적을때 실수할 가능성을 낮춰졌다는 정도이다. 
+
+ 다만, 비트코인 주소가 실제 자산이 옮겨가는 주소이고 사소한 실수로 주소 입력과정에서 오타 하나 나온것 때문에 송금하는 금액이 모두 증발해버리면 매우 슬플것이다. (은행과 다르게 비트코인은 중앙관리기관이 없어서 송금취소도 못한다!) 여기서 그 존재감을 들어내는게 Base58Check라는 존재이다. Base58Check는 그냥 Base58로 변환된 문자열도 쓰기 복잡해서 솔직히 실수가 일어날 수 있으니 문자열 자체에 자신의 오류를 감지하는 정보를 조금 넣어두어 오타가 났을때 감지할 수 있게하자! 라는 아이디어를 실현시킨 방법이다. 그 구현법은 간단하여 일단 변환하려는 데이터 앞쪽에 이 데이터의 정체를 알려주는 정보를 붙힌 데이터를 SHA2 를 두번 돌려 나온 해시값을 변환하려는 데이터 뒤에다 붙히고 Base58 인코딩을 하자!, 아 잠만, 해시값이 256bit=32Byte는 너무 크다 앞쪽 4바이트만 남기고 나머지는 버리자! 라는 느낌이다. 이렇게 하므로써 우리는 Base58Check로 인코딩된 어떤 문자열을 봤을때 이 문자열이 공개키에 관한건지, 주소에 관한건지, 비밀키에 관한건지 같은 이 데이터의 정체를 짐작할 수 있게 되며, 그뿐 아니라 만약 오타가 났을시에는 입력한 데이터가 제대로 되어있지 않음을 높은 확률로 즉각 판단할 수 있게된다. (물론 천문학적인 확률로 두번 해싱한값의 첫 4바이트가 같은값이 나오게 오타가날경우 판단 못한다.)
+
+```mermaid
+graph TD
+How_Base58Check_Works
+Public_Key_Hash-->|Adding Prefix|Prefix+Public_Key_Hash
+Prefix+Public_Key_Hash-->|Adding first 4bytes of double SHA2|Prefix+Public_Key_Hash+Hash
+Prefix+Public_Key_Hash+Hash-->|Base58Encoding|비트코인주소
+```
+
+ Base58Check의 경우 비트코인 주소 말고도 몇가지 용도로 사용되는데 그에따른 Prefix는 아래 표와 같이 표현된다.
+
+| Decimal version | Leading symbol | Use                                     |
+| --------------- | -------------- | --------------------------------------- |
+| 0               | 1              | Bitcoin pubkey hash                     |
+| 5               | 3              | Bitcoin script hash                     |
+| 21              | 4              | Bitcoin (compact) public key (proposed) |
+| 128             | 5              | Private key (uncompressed pubkey)       |
+| 128             | K or L         | Private key (compressed pubkey)         |
+| 98              | 6P             | Encrypted Private Key                   |
+
+ 비트코인 주소에서 사용하는 Public Key Hash의 경우 Prefix에 의해 항상 맨앞에는 1이 오게되는데 실제 거래에 사용되는 비트코인 주소들을 보면 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa(첫 비트코인 채굴자의 주소) 나 19iZnngPRx7Tcf5ydAyiFGp97SavWCPg7y (필자의 비트코인 주소) 처럼 항상 처음은 1로 시작하는 것을 알 수 있다.
+
+> 비밀키 하나에 비트코인 주소가 두개?
+>
+> 비트코인 주소를 만들때는 공개키를 두번 해시함수에 돌린값을 이용한다. 하지만 앞서 밝혔다시피 공개키는 같은 정보를 가지고 있지만 압축형과 기본형 두가지 형태가 있다. 그럼 여기서 문제가 생긴다. 기본형을 사용할때랑 압축키를 사용할때랑 같은 해시값이 나올리가 없다. 그러므로 결국 비트코인 주소는 두개가 탄생할 것이다. 과연 그럴까? 정답은 그렇다! 이다. 다만 본래 기본형 공개키를 사용했으며 압축형 공개키를 사용한건 비교적 최근일이여서 구형 비트코인 클라이언트가 오류를 내는것을 방지하고 최신형 비트코인 클라이언트와 호환성을 유지하기 위해 압축형 공개키를 사용하는 비밀키 양식과 기본형 공개키를 사용하는 비밀키 양식을 나눠 놓았다. 그 양식은 위 표에 나와있으며 간단한 확인법은 비트코인 클라이언트에 비트코인 비밀키를 출력하게해 5로 시작하면  해당 비밀키는 기본형 공개키를 사용하며 K혹은 L로 시작하게되면 압축형 공개키를 사용해 수수료를 지불할때 조금이나마 이득을 볼 수 있게된다.
+
+### 암호화된 비밀키
+
+ 비밀키는 삭제되면 영영 해당 비밀키에 해당되는 비트코인 주소는 사용하지 못하게 된다. 또한 하드디스크나 SSD는 언제나 정보가 삭제될 위험이 존재한다. 그럼 가장 안전한 매체는 무엇일까? 라는 의문에대한 답은 바로 `종이`이다. 종이는 고대 이집트 파피루스가 현재까지 그 정보와 함께 보관되어 올 정도로 그 안정성이 보장되어 왔으며 내용을 적기 쉽고, 우리 주변에서 쉽게 구할 수 있으며, 가지고다니기 종이만큼 쉬운것도 없다. 하지만 한가지 문제가 있는데 훔쳐지거나 카메라 촬영과 같은 형태로 그 정보가 도난당할 위험이 있는 것이다. 이 때 만약 종이에 비밀키가 그대로 적혀있다면? 그 돈은 도난당한거나 마찬가지이다.
+
+ 이러한 불편한 상황으로부터 벗어나기위해 BIP38에 비트코인 비밀키를 암호화 해서 보관할 것이 제안되었다. AES라는 대칭 암호화 기법을 사용하여 자신이 정한 비밀번호로 암호화 되며, 암호화된 비밀키는 Base58Check인코딩으로 항상 "6P"로 시작하는것으로 정해져 있다.
+
+예시: `6PnYPn11mffTbYcp21YBhLjN8B9wPruaWeLcctDft9RHaH5Lu21pG23beG`
+
+ 이렇게 암호된 비밀키는 남에게 노출되어도 비밀번호를 모르면 쓸모없는 정보이므로 종이와같은 형태로 출력할때는 암호화된 비밀키를 사용할것을 권장한다.
+
+> 사소한 팁으로 https://bitaddress.org 에서 지갑을 생성하고  그에 해당하는 비밀키와 비트코인 주소를 멋있는 형태로 출력할 수 있다. ![](./img/4_5.png)
 
 ## 지갑
 
 ### 개요
 
-  비트코인에서는 모든 거래내역이 투명하게 공개된다.  하지만 동시에 익명성을 보장하기위해 비트코인이 접근한 방법은 송금받기위한 비트코인 주소를 1회용으로 사용하게 하는 것이다. 비트코인 주소를 한번쓰고 버리게 하여 결과적으로 해당 주소의 소유주가 현실세계에서 누군지를 특정하는데 방해하는 것이다. 다만 이런과정에서 문제가 발생하는데 현재 내가 보유하고있는 잔액을 파악하기위해 필요한 비트코인 주소가 따로따로 관리하기에는 너무 많아진다는 것이다. 이를 위해 비트코인은 지갑이란 개념을 도입하였다. 초기 비트코인 지갑은 무식하게 모든 주소를 저장하는 방법을 택했지만 그 후 몇가지 트릭을 더하여 지금와서는 사람이 몇개의 단어만 기억해두면 언제 어디서든 여태껏 사용해온 모든 주소를 불러올 수 있게 되었다.
+  비트코인에서는 모든 거래내역이 투명하게 공개된다.  하지만 동시에 익명성을 보장하기위해 비트코인이 접근한 방법은 송금받기위한 비트코인 주소를 1회용으로 사용하게 하는 것이다. 비트코인 주소를 한번쓰고 버리게 하여 결과적으로 해당 주소의 소유주가 현실세계에서 누군지를 특정하는데 방해하는 것이다. 다만 이런과정에서 문제가 발생하는데 현재 내가 보유하고있는 잔액을 파악하기위해 필요한 비트코인 주소가 따로따로 관리하기에는 너무 많아진다는 것이다. 이를 위해 비트코인은 사용한 주소들을 통합관리 해주는 지갑이란 개념을 도입하였다. 초기 비트코인 지갑은 무식하게 모든 주소를 저장하는 방법을 택했지만 그 후 몇가지 트릭을 더하여 지금와서는 사람이 몇개의 단어만 기억해두면 언제 어디서든 여태껏 사용해온 모든 주소를 불러올 수 있게 되었다. 이 파트에서는 대표적인 지갑양식에 대하여 간략히 소개해 주겠다.
+
+```mermaid
+graph TD
+
+지갑-->비결정적지갑
+지갑-->결정적지갑
+결정적지갑-->BIP32
+```
+
+### 비결정적 지갑
+
+ 비트코인에서 가장 처음으로 구현된 지갑 양식이며 가장 단순한 방법이다. 그 원리는 너무 간단하여 딱 한마디로 정리 할 수 있는데 "여태껏 사용한 모든 비밀키를 그냥 저장한다."이다. 다만 단순무식한만큼 몇가지 문제가 발생하게 되는데 비록 수백바이트에 불과하긴 하지만 거래를 할때마다 계속 지갑의 용량이 커지게되며, 언제나 디지털상태로 어딘가에 저장되어 있어야하다 보니깐 실수로 삭제라도 하게되다간.....다음은 상상에 맡기겠다. 물론 그런만큼 결정적지갑대비 장점도 존재하는데, 결정적지갑은 최초의 단서가 유출되면 그동안 사용한 모든 비밀키도 같이 유출되는 반면 비결정적지갑은 지갑전체가 유출되어야 모든 비밀키가 유출된다는점을 생각해볼때 보안이 조금더 강하다고 할 수 있다. 다만 생각보다 그렇게 큰 보안적 이득이 생기는것이 아니며, 실수로 삭제하게되어 모든 비밀키를 날려버릴 개연성이 더 큼으로 비결정적 지갑보다는 결정적지갑을 사용하는것을 권장한다.
+
+### 결정적 지갑
+
+ 결정적 지갑은 다음과 같은 아이디어를 생각해보면 쉽다. 일단 자신이 기억하기 쉽지만 남은 추측하는게 어려운 어떤 문자열을 정한다. (여러분의 네이버 비밀번호정도를 상상하면 좋을것 같다.) 그리고 이 문자열의 해시값을 구하고 그 해시값을 비밀키로 정한다. 그 다음은 이전 비밀키의 해시값을 비밀키로 정한다. 그 다음은..... 이걸 계속 반복하다보면 우리는 어떤 문자열 하나만 기억해두면 특별히 백업을 고민할 필요도 없이 우리가 사용하기에 충분한 비밀키를 생성할 수 있으며, 컴퓨터가 랜섬웨어에 걸려 모든 비밀키에대한 정보가 암호화되어 결국 복원 못하게 삭제되게 되어도 어떤 문자열만 알고있으면 쉽게 복원할 수 있게된다. 
+
+```mermaid
+graph LR
+
+어떤문자열-->|해시함수|비밀키1
+비밀키1-->|해시함수|비밀키2
+비밀키1-->공개키1
+공개키1-->비트코인주소1
+비밀키2-->|해시함수|비밀키3
+비밀키2-->공개키2
+공개키2-->비트코인주소2
+비밀키3-->공개키3
+공개키3-->비트코인주소3
+비밀키3-->...이하생략
+```
+
+ 이게 결정적 지갑에대한 기초적인 아이디어 이다. 다만 편한만큼 보안적으로 위험하기도 한데, 위 방법의 경우 처음 문자열만 알면, 아니 중간에 사용되는 비밀키 하나만 알아내는데 성공하면 다른 비밀키도 유출될 수 있다는 것이다. 이런 상황을 조금이나마 방지하기위해 다음과 같은 방법들이 제시되고 일부는 실제 사용되고 있다. 다만 이 방법을 사용하는것은 강제사항이 아니기에 비트코인 클라이언트마다 그 구현방법이 다를수도 있고 다른 비트코인 클라이언트간에 호환이 되지 않을 수도 있으니 주의 바란다.
+
+#### 연상기호(mnemonic)
+
+ 이부분은 처음 "어떤문자열"을 어떻게 정할까?의 부분과 상관이 있다. 개개인이 모두 알아서 정말 예측불가능해서 컴퓨터로 일일히 대입해봐도 수백년뒤에 뚫릴만한 문자열을 사용한다면 최고지만, 아쉽게도 대부분의 경우 "0000"같은 간단한 문자열을 사용하거나 다른 사이트에서 비밀번호로 사용했던 문자열을 그대로 가져오는것이 현실이다. 다른 웹사이트의 경우 비밀번호 유출이 의심될때는 단순히 비밀번호 바꾸기를 통해서 이를 해결하면 되지만 비트코인은 문자열 자체로부터 단서를 획득하는 것이기에 비밀번호 바꾸듯이 쉽게 바꿀 수 없고 기존 지갑을 버리고 새로운 지갑을 사용해야만 한다.(이 과정에서 송금 수수료가 발생한다.)  그럼 처음부터 예측 불가능하지만 기억할만한 문자열을 보편적으로 선택 가능하게 해주어야 하는데 이를 위해 생겨난 방법이 "연상기호"이다.
+
+ 사실 이는 비트코인에서 처음 제시된 방법은 아니고 기존에 쓰던방법을 비트코인에서 채용한것에 가까운데, 일단 128bits~256bits 사이의 임의의 데이터를 난수발생기를 통해 발생시킨다. 그 후 만들어진 데이터 뒤에 나중에 입력 실수했을때 잡아내기 위해 데이터의 해시값의 첫 몇바이트를 뒤에다 붙힌다. (이는 Base58Check에서 행한 과정과 목적이 비슷하다.)(이를 검사합이라 부른다.) 그리고 이를 11비트 단위로 나눈다. 그럼 각 11비트는 1에서 2048=2^11 사이의 숫자로 표현 가능한데 이를 미리 정해둔 기억하기 쉬운 영어단어와 매칭시킨다. 그럼 상황에 따라 12단어에서 24단어 사이의 영어단어가 나타나게 되는데 사용자는 이것만 기억하면 된다. 만약 잘못기억해도 맨뒤에 포함된 오류보정 데이터가 확인해주기 때문에 몇번 시행착오를 거듭하다보면된다. 이렇게 완성된 영어단어는 대략 다음과 같은 모습을 지닌다. 
+
+`scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump`
+
+이걸 어떻게 왜울지는 각자의 재량에 달렸고, 개인적으로는 그냥 나온 영어단어를 외우기보다는 연상해서 외우기 쉬운 문자배치가 나올때까지 여러번 돌려보는걸 추천한다. 다만 아쉽게도 이 방법은 영어 이외에도 일본어, 스페인어, 중국어, 프랑스어, 이탈리아어 버전이 있지만 한국어는 현재 없는 상태이니 참고 바란다.
+
+#### BIP32
+
+ 일단 BIP란 Bitcoin Improvement Proposals 의 약자로 한국어로는 대략 "비트코인 개선을 위한 제안사항"정도 되는 존재이다. 일반적으로 비트코인 포럼에서 논의되고 결정된 사항이 BIP문서로 작성되고 각 제안에대해서는 순서대로 번호가 붙게된다. BIP32의 경우 32번째로 등록된 BIP정도의 의미를 가지며 구글에 검색해보면 그 원본문서를 쉽게 관람할 수 있다. 
+
+ BIP32는 BIP로서는 처음으로 결정적인 지갑에 대한 제시와 그 구현방법에 대해 제시를 해준 문서이다. 자세히 들어가면 길고 복잡하며 위에서 해시함수를 예로들어 설명한 방법과 개념적으로 유사하기에 이에대한 설명은 넘어가도록 한다. ![](./img/4_4.png)
+
+  다만 처음에 제안될때 그 방법이 꽤 느슨하고 열려있게 제안되어서 같은 문서를 보고 작성한 프로그램간에 서로 호환이 안될 여지가 보였기에 BIP43과 BIP44에서 추가적인 문서가 작성되었다는점을 참고해두자.
+
+ 현재 대부분의 비트코인 결정적 지갑은 BIP32에 따르거나 따를 예정이라고 하며 다음과 같은 지갑들은 서로 호환이 된다고 한다.
+
+- [Mycelium Bitcoin Wallet (Android)](https://play.google.com/store/apps/details?id=com.mycelium.wallet) ([source](https://github.com/mycelium-com/wallet))
+- [Copay](https://copay.io/) ([source](https://github.com/bitpay/copay))
+- [CoinVault](https://www.coinvault.io/) ([source](https://github.com/CoinVault/dotblock))
+- [Samourai Wallet](https://samouraiwallet.com/) ([source](https://github.com/Samourai-Wallet/samourai-wallet-android))
+- [TREZOR](https://trezor.io/) ([source](https://github.com/trezor/))
+- [KeepKey](https://www.keepkey.com/) ([source](https://github.com/keepkey/))
+- [Ledger Wallet](https://www.ledgerwallet.com/) ([source](https://github.com/LedgerHQ))
+- [21 Machine Wallet](https://21.co/learn/21-lib-wallet/) ([source](https://github.com/21dotco))

BIN
img/4_3.jpg


BIN
img/4_4.png


BIN
img/4_5.PNG