區塊鏈中的DCDSA:橢圓曲線數字簽名

既然咱們已經能夠產生橢圓曲線密鑰對,咱們接下來就用使用它來進行消息的簽名和驗證。我所指的消息是任何形式,不管是文本仍是二進制形式,只要它們有被驗證合法性的須要。特別的是,bitcoin客戶端經過簽名來證實交易的有效性,反之,礦工則是經過驗證這樣的簽名,來批准並廣播合法的交易。git

ECDSA 簽名

橢圓曲線簽名算法就是ECDSA(Elliptic-Curve Digital Signature Algorithm).在ECDSA中,各方必須約定一個共同的哈希函數H, 由於咱們將要簽名的對象是H(消息),而不是消息自己。值得注意的是,只有簽名方S有私鑰的權限,驗證方V只須要拿到相應的公鑰就能夠進行驗證。本文中,我將使用上一章所建立的密鑰對。算法

下面的案例中,咱們簽名的對象是SHA-256摘要。但bitcoin中指定的H函數是HASH256,也就是指雙重SHA-256網絡

簽名

第一步把咱們的消息存入文件,命名ex-message.txtide

This is a very confidential message

以後,咱們用私鑰對其SHA-256摘要進行簽名。函數

$ openssl dgst -sha256 -sign ec-priv.pem ex-message.txt >ex-signature.der

ex-signature.der文件是簽名的DER格式。OpenSSL使用DER編碼任何二進制輸出,但這裏咱們忽略這個細節。你不須要了解ECDSA簽名的語法,只須要記住它僅僅是一組的大數對(r,s)區塊鏈

你可能會注意到,每一次你執行程序,簽名都發生變化,也就是說默認的簽名過程是不具備肯定性的。這就給序列化區塊鏈交易時帶來了問題,由於簽名是交易字節序列中的一部分,而且你必定知道txid是對交易進行哈希得來的。所以,每當你簽名一筆交易txid就會隨之變化。這種行爲也是形成交易可塑性的緣由之一。ui

爲了顯示十六進制編碼的簽名,只需添加-hex參數。編碼

$ openssl dgst -sha256 -hex -sign ec-priv.pem ex-message.txt

爲了重用剛剛輸出的結果,最好使用hexdup已生成的DER文件。命令行

$ hexdump ex-signature.der

驗證

不管何時將合法消息發佈到網絡,接收者都但願可以獲得一個附件的簽名。在假設咱們已經獲得做者公鑰的狀況下,不管是原消息仍是簽名,都必須做爲驗證流程的輸入數據:code

$ openssl dgst -sha256 -verify ec-pub.pem -signature ex-signature.der ex-message.txt

代碼版本

咱們使用代碼來完成上文中在命令行中完成的一樣的工做。

簽名

OpenSSL使簽名流程變得簡單,這一部分能夠在 ex-ecdsa-sign.c中查看。

uint8_t priv_bytes[32] = { ... };
const char message[] = "This is a very confidential message\n";

EC_KEY *key;
uint8_t digest[32];
ECDSA_SIG *signature;
uint8_t *der, *der_copy;
size_t der_len;

...

key = bbp_ec_new_keypair(priv_bytes);
bbp_sha256(digest, (uint8_t *)message, strlen(message));
signature = ECDSA_do_sign(digest, sizeof(digest), key);

ECDSA_SIG是一個簡單的結構,用於存儲上文所說的(r,s)對:

struct {
    BIGNUM *r;
    BIGNUM *s;
} ECDSA_SIG;

使用i2d_ECDSA_SIG函數,咱們也能夠獲得DER編碼的簽名:

der_len = ECDSA_size(key);
der = calloc(der_len, sizeof(uint8_t));
der_copy = der;
i2d_ECDSA_SIG(signature, &der_copy);

驗證

驗證一樣很簡單,能夠在ex-ecdsa-verify.c中查看:

uint8_t pub_bytes[33] = { ... };
uint8_t der_bytes[] = { ... };
const char message[] = "This is a very confidential message\n";

EC_KEY *key;
const uint8_t *der_bytes_copy;
ECDSA_SIG *signature;
uint8_t digest[32];
int verified;

...

key = bbp_ec_new_pubkey(pub_bytes);
der_bytes_copy = der_bytes;
signature = d2i_ECDSA_SIG(NULL, &der_bytes_copy, sizeof(der_bytes));

由於沒法獲得私鑰,咱們利用使用下面的輔助函數將pub_bytes解碼爲壓縮形式。

EC_KEY *bbp_ec_new_pubkey(const uint8_t *pub_bytes, size_t pub_len);

另外一方面,der_bytes是簽名程序返回的DER格式的簽名。咱們將解碼DER簽名到更方便的ECDSA_SIG結構中,而後與消息摘要比較進行驗證。

ECDSA_do_verify函數的返回值:

  • 1,簽名合法
  • 0,簽名不合法
  • -1,出現未知錯誤

注意:使用ECDSA_verify能夠跳過簽名的解碼過程,由於它須要的輸入值是DER形式的簽名。

相關文章
相關標籤/搜索