WIF 포맷

WIF 포맷

WIF 포맷은 Wallet Import Format의 약자로서 개인키(private key)를 보다 쉽게 표현하기 위한 표준 포맷이다. ECDSA의 private key는 32 바이트(256 비트)의 데이타인데, 여기에서 key가 올바른 키인지 체크하는 간단한 체크섬을 추가한 후, Base58 으로 인코딩한 것이 WIF 포맷이다.

Private Key를 WIF 포맷으로 변경하는 절차는 다음과 같다.

  1. 다음과 같은 private key를 사용한다고 가정하자.
                bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866
  2. private key 앞에 1바이트 버전(version) 정보를 추가한다. 여기서 버전은 사용하는 블럭체인 네트워크가 MainNet인지(0x80), TestNet인지(0xef)를 구별하기 사용된다.
                80 + bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866
  3. 만약 private key가 압축된 공개키(compressed public key)를 사용하면, 마지막에 0x01을 추가한다.
                80 + bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866 + 01
  4. 위의 키값을 SHA256 해시함수로 두번 해싱(double hashing)한다. 더블 해싱 결과는 아래와 같다.
                f9b7646236ceecd59e09bb8cfdeab26a364a72921a1a2039652a52a389ffa0bd
  5. 더블 해싱값의 처음 4바이트를 #3의 키값 뒤에 추가한다. 이는 WIF 결과값이 제대로 인코딩되었는지 체크할 수 있도록 한다.
                80 + bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866 + 01 + f9b76462
  6. 위의 #5의 값을 Base58 으로 인코딩하여 다음과 같은 WIF 포맷 결과를 얻는다. 이것이 일반적으로 Wallet 프로그램에서 사용되는 private key이다.
                L3Wh2WPg21MWqzMFYsVC7PeBXcq1ow32KRccRihnTUnAhJaZUvg1

위의 절차들을 C# 코드로 표현하면 다음과 같다.

enum WifPrefix
{
    MainNet = 0x80,
    TestNet = 0xef
}

public static string PrivateToWif(byte[] privateKey, NetworkType net, 
    bool useCompressedPubKey = true)
{
    List pk = new List();

    byte version = (byte)(net == NetworkType.MainNet ? WifPrefix.MainNet 
                             : WifPrefix.TestNet);
    pk.Add(version);
    pk.AddRange(privateKey);

    if (useCompressedPubKey)
    {
        // if the private key corresponds to a compressed public key,
        // add a 0x01 byte at the end 
        pk.Add(0x01);
    }

    return Base58Check.Encode(pk.ToArray());
}

Base58Check 클래스는 Encode()에서 payload를 더블 해싱하여 뒤에 4바이트 체크섬을 더한 후, Base58 인코딩을 수행한다.

public class Base58Check
{
    public static string Encode(byte[] payload)
    {
        // hash(hash(payload))
        byte[] hash;
        using(var sha = SHA256.Create())
        {
            // double hashing
            hash = sha.ComputeHash(sha.ComputeHash(payload));
        }

        // append first 4 bytes of hash
        List t = new List();
        t.Add(payload);
        t.Add(hash[0..4]); 
        byte[] combined = t.SelectMany(r => r).ToArray();

        return Base58.Encode(combined);
    }
}