비트코인 서명과 검증
비트코인 메시지 디지탈 서명과 검증
비트코인은 ECDSA(Elliptic Curve Digital Signature Algorithm)라 불리우는 디지탈 서명 알고리즘을 사용하고 있다.
ECDSA를 통해 비트코인은 Private Key를 사용하여 메시지에 대한 디지탈 서명을 생성하고, Public Key를 사용하여
그 디지탈 서명을 검증하게 된다.
이러한 비트코인 서명과 검증은 비트코인 프로토콜의 중요한 메인 프로세스 중의 하나이다.
메시지를 서명하기 위해 ECDSA는 메시지와 Privat Key 라는 2개의 입력을 받아들여 서명(signature)을 생성한다.
그리고 메서지를 검증하기 위해서 3개의 입력 즉 메시지, 서명, 그리고 Public Key를 사용하여 해당 메시지가
Key 소유자에 의해 서명된 것인지를 체크하게 된다.
메시지는 오직 Private Key 소유자에 의해서만 서명된다.
예를 들어, Bob이 Alice에게 비트코인을 보내려고 할 때, 그는 메시지 (트랜잭션 메시지)를 그의 Private Key 로 사인하여
디지탈 서명을 생성한다. 이어 Bob은 그의 Public Key, 메시지 그리고 디지탈 서명을 Peer Network에 보내게 된다.
Peer Network은 이어 디지탈 서명을 검사하여 그 메시지가 Bob에 의해 올바르게 사인된 것인지 체크하게 된다.
다음 샘플 코드는 Private Key를 사용하여 메시지를 사인하는 방법과 Public Key를 메시지를 검증하는 방법을
예시하고 있다. 여기서 메시지를 사인하거나 검증하기 위해, BouncyCastle 라이브러리의 ECDsaSigner 클래스를 사용하고 있다.
메시지를 사인하기 위해서,
(1) 먼저 ECDsaSigner 클래스의 객체를 생성한다
(2) 다음 ECDsaSigner 객체로부터 Init(true, privateKeyParams) 메서드를 호출하는데 여기서 true는 디지탈 서명을 위한 것이라는 의미이며
privateKeyParams는 Private Key 정보를 가진 ECPrivateKeyParameters 객체를 의미한다.
만약 Private Key가 문자열이면, 아래 코드에서와 같이 이 문자열로부터 ECPrivateKeyParameters 객체를 생성해야 한다.
(3) 마지막으로 ECDsaSigner 객체의 GenerateSignature() 메서드를 호출하여 메시지를 서명한다.
이 메서드로부터 리턴되는 서명은 두개의 BigInteger 인데, 첫번째는 r, 두번째는 s 값을 의미한다.
디지탈 서명은 (r, s) 두개의 값을 갖는다.
/*
using System;
using System.Text;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Crypto.Signers;
*/
// Sample Keys
string privateKey = "A470CA126FC665F0774EA10405CA126F1594B836B5FCEB0A7281FA3442FE5B48";
string publicKey = "0374420A244B12F1FA59AF17186103107C0EA4599ED78B9EEA222F3A6186112CBF";
// Rebuild Private/Public KeyParameters from Keys
BigInteger priv = new BigInteger(privateKey, 16);
X9ECParameters ec = SecNamedCurves.GetByName("secp256k1");
ECDomainParameters domainParams = new ECDomainParameters(ec.Curve, ec.G, ec.N, ec.H);
ECPrivateKeyParameters privateKeyParams = new ECPrivateKeyParameters(priv, domainParams);
BigInteger pub = new BigInteger(publicKey, 16);
ECPoint q = domainParams.Curve.DecodePoint(pub.ToByteArray());
ECPublicKeyParameters publicKeyParams = new ECPublicKeyParameters(q, domainParams);
// Create Signature with private key. ECDSA Signing.
ECDsaSigner privSigner = new ECDsaSigner();
privSigner.Init(true, privateKeyParams);
var msg = Encoding.ASCII.GetBytes("Send 1.0 BTC");
BigInteger[] signature = privSigner.GenerateSignature(msg);
// signature = (r, s)
BigInteger r = signature[0];
BigInteger s = signature[1];
// Verify Signature with public key
ECDsaSigner pubSigner = new ECDsaSigner();
pubSigner.Init(false, publicKeyParams);
bool verified = pubSigner.VerifySignature(msg, r, s);
Console.WriteLine(verified); // true
디지탈 서명 검증 프로세스는 사인 프로세스와 비슷하다.
서명 검증을 위해서는
(1) 먼저 ECDsaSigner 클래스의 객체를 생성한다
(2) 다음 ECDsaSigner 객체로부터 Init(false, publicKeyParams) 메서드를 호출하는데 여기서 false는
디지탈 서명을 위해 쓰지 않을 것이라는 의미이며
publicKeyParams는 Public Key 정보를 가진 ECPublicKeyParameters 객체를 의미한다.
만약 Public Key가 문자열이면, 아래 코드에서와 같이 이 문자열로부터 ECPublicKeyParameters 객체를 생성해야 한다.
(3) 마지막으로 ECDsaSigner 객체의 VerifySignature() 메서드를 메시지와 서명(r과 s)와 함께 호출하여 서명을 검증한다.
이 메서드는 메시지 서명이 Key 소유자에 의해 올바르게 생성된 것이라면 true를 리턴한다.