C#

비트코인 Key 생성

비트코인 Private/Public Key 생성하는 방법

비트코인은 한 쌍의 Private Key와 Public Key를 기반으로 한 Elliptic Curve Digital Signature Algorithm (ECDSA) 암호화 방식을 사용한다. DSA는 RSA와 비슷한 방식으로 Private/Public 키를 사용하는데, 일반적으로 DSA는 RSA에 비해 Signature 생성이 빠르지만 Validation은 더 느리다. DSA의 한 유형으로 Elliptic Curve 를 사용하는 방식을 ECDSA라 하는데, 비트코인은 여러 Elliptic Curve 중 secp256k1 커브를 사용하고 있다. secp256k1 는 아래와 같은 수학적 함수를 통해 생성된 그래프를 사용한다.

secp256k1

.NET Framework은 자체적으로 ECDSA 구현을 제공하고 있지 않으므로, ECDSA를 사용하기 위해서 3rd Party 라이브러리를 사용하게 되는데, 여기서는 이 중 BouncyCastle 라이브러리를 사용해 본다. BouncyCastle를 사용하기 위해서는 다음과 같이 Nuget 패키지를 설치하면 된다.
PM> Install-Package BouncyCastle

BouncyCastle에서 SecNamedCurves.GetByName() 메서드를 사용하면 Well Known Elliptic Curve를 불러올 수 있는데, 예를 들어, GetByName("secp256k1")와 같이 지정하여 secp256k1 커브에 대한 파라미터들을 가져올 수 있다.

ECKeyPairGenerator 클래스는 Private Key와 Public Key 쌍을 생성하는 기능을 가지고 있는데, 이 클래스의 초기화(Init) 메서드에 Elliptic Curve 정보와 Random 생성을 위한 객체를 넣어 주고, 이어 키생성 메서드인 GenerateKeyPair()을 호출하여 키쌍을 생성한다. 생성된 키쌍은 AsymmetricCipherKeyPair의 객체로서 이 객체의 Private 속성에는 Private 키 정보가, Public 속성에는 Public 키 정보가 각각 들어 있다.

/*
using System;
using System.Linq;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
*/

// Elliptic Curve
X9ECParameters ec = SecNamedCurves.GetByName("secp256k1");
ECDomainParameters domainParams = new ECDomainParameters(ec.Curve, ec.G, ec.N, ec.H);
SecureRandom random = new SecureRandom();

// Generate EC KeyPair
ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
ECKeyGenerationParameters keyParams = new ECKeyGenerationParameters(domainParams, random);
keyGen.Init(keyParams);
AsymmetricCipherKeyPair keyPair = keyGen.GenerateKeyPair();

ECPrivateKeyParameters privateKeyParams = keyPair.Private as ECPrivateKeyParameters;
ECPublicKeyParameters publicKeyParams = keyPair.Public as ECPublicKeyParameters;

// Get Private Key
BigInteger privD = privateKeyParams.D;
byte[] privBytes = privD.ToByteArray();

if (privBytes.Length == 33)
{
	var temp = new byte[32];
	Array.Copy(privBytes, 1, temp, 0, 32);
	privBytes = temp;
}
else if (privBytes.Length < 32)
{
	var temp = Enumerable.Repeat<byte>(0x00, 32).ToArray();
	Array.Copy(privBytes, 0, temp, 32 - privBytes.Length, privBytes.Length);
	privBytes = temp;
}   
string privKey = BitConverter.ToString(privBytes).Replace("-", "");

// Get Compressed Public Key
ECPoint q = publicKeyParams.Q;
FpPoint fp = new FpPoint(ec.Curve, q.AffineXCoord, q.AffineYCoord);
byte[] enc = fp.GetEncoded(true);
string compressedPubKey = BitConverter.ToString(enc).Replace("-", "");

// Get Uncompressed Public Key
enc = fp.GetEncoded(false);
string uncompressedPubKey = BitConverter.ToString(enc).Replace("-", "");

// Output
Console.WriteLine(privKey);
Console.WriteLine(compressedPubKey);
Console.WriteLine(uncompressedPubKey);

비트코인에서 Private Key는 32 바이트이다 (주: 일부 최신 지갑에서 128 ~ 512 비트 사용하기도 함). Public Key는 압축된 키와 압축이 되지 않은 키 두 종류가 있다. Public Key는 EC 커브 상의 한 점 (X, Y) 좌표로 구성되는데, X와 Y 좌표는 각각 32 바이트 숫자이다. 압축되지 않은 경우는 이 X, Y 좌표값과 1 바이트의 Prefix를 붙여 65 바이트가 되며, 압축된 경우는 X 좌표값과 1 바이트의 Prefix를 붙여 33바이트가 된다 (이때 Y 좌표는 계산에 의해 산출됨). Public Key의 첫번째 Prefix 바이트는 압축된 경우 0x02 혹은 0x03 이 되며, 압축되지 않은 경우 0x04 값을 갖는다. Public Key가 압축된 경우 Y 좌표값이 짝수이면 0x02 가 앞에 붙게되고, 홀수이면 0x03 이 붙게된다.

BouncyCastle에서 FpPoint.GetEncoded(bool) 메서드에 true를 넣으면 키가 압축되고, false 이면 압축이 되지 않는다.

Bitcoin Private Key와 Public Key가 올바르게 생성되었는지 체크하기 위해 bitaddress.org 웹사이트의 [Wallet Details] 기능을 사용할 수 있다. 이곳에서 위에서 생성된 Private Key를 넣으면 상응하는 Public Key를 비롯하여 여러 주소 정보를 얻을 수 있다.

본 웹사이트는 광고를 포함하고 있습니다. 광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.

Previous Next Print