3、区块链之比特币的私钥、公钥、地址

我们之前说到比特币具有很强的安全性和匿名性,这两点的基础就在于比特币的私钥、公钥和地址。

私钥是一串随机数字,由256位 0 和 1 组成,通常用 16 进制表示,一共有64位。

公钥则是由私钥通过椭圆曲线算法生成的,而此过程是不可逆,也即无法从公钥推出私钥。

比特币地址是一个由数字和字母组成的字符串,可以与任何想给你比特币的人分享。由公钥生成的比特币地址以数字“1”开头。

比特币地址与公钥不同。比特币地址是由公钥经过单向的哈希函数生成的。

这里写图片描述

接下来我们来看一下中本聪先生是如何生成密钥和将公钥生成地址的代码

//实际代码有很多错误处理,我们这里只将流程代码贴上来
/* * NID_secp256k1 是椭圆曲线 * EC_KEY_new_by_curve_name 是OpenSSL的函数, */
EC_KEY* pkey = EC_KEY_new_by_curve_name(NID_secp256k1);

//获取private key
CPrivKey GetPrivKey() const
{
        unsigned int nSize = i2d_ECPrivateKey(pkey, NULL);
        if (!nSize)
            throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed");
        CPrivKey vchPrivKey(nSize, 0);
        unsigned char* pbegin = &vchPrivKey[0];
        if (i2d_ECPrivateKey(pkey, &pbegin) != nSize)
            throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size");
        return vchPrivKey;
}

//获取public key
vector<unsigned char> GetPubKey() const
{
        unsigned int nSize = i2o_ECPublicKey(pkey, NULL);
        if (!nSize)
            throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed");
        vector<unsigned char> vchPubKey(nSize, 0);
        unsigned char* pbegin = &vchPubKey[0];
        if (i2o_ECPublicKey(pkey, &pbegin) != nSize)
            throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size");
        return vchPubKey;
}

// public key to address
inline string PubKeyToAddress(const vector<unsigned char>& vchPubKey)
{
    return Hash160ToAddress(Hash160(vchPubKey));
}
inline string Hash160ToAddress(uint160 hash160)
{
    // add 1-byte version number to the front
    vector<unsigned char> vch(1, ADDRESSVERSION);
    vch.insert(vch.end(), UBEGIN(hash160), UEND(hash160));
    return EncodeBase58Check(vch);
}

inline string EncodeBase58Check(const vector<unsigned char>& vchIn)
{
    // add 4-byte hash check to the end
    vector<unsigned char> vch(vchIn);
    uint256 hash = Hash(vch.begin(), vch.end());
    vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4);
    return EncodeBase58(vch);
}

inline string EncodeBase58(const vector<unsigned char>& vch)
{
    return EncodeBase58(&vch[0], &vch[0] + vch.size());
}

inline string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
{
    CAutoBN_CTX pctx;
    CBigNum bn58 = 58;
    CBigNum bn0 = 0;

    // Convert big endian data to little endian
    // Extra zero at the end make sure bignum will interpret as a positive number
    vector<unsigned char> vchTmp(pend-pbegin+1, 0);
    reverse_copy(pbegin, pend, vchTmp.begin());

    // Convert little endian data to bignum
    CBigNum bn;
    bn.setvch(vchTmp);

    // Convert bignum to string
    string str;
    str.reserve((pend - pbegin) * 138 / 100 + 1);
    CBigNum dv;
    CBigNum rem;
    while (bn > bn0)
    {
        if (!BN_div(&dv, &rem, &bn, &bn58, pctx))
            throw bignum_error("EncodeBase58 : BN_div failed");
        bn = dv;
        unsigned int c = rem.getulong();
        str += pszBase58[c];
    }

    // Leading zeroes encoded as base58 zeros
    for (const unsigned char* p = pbegin; p < pend && *p == 0; p++)
        str += pszBase58[0];

    // Convert little endian string to big endian
    reverse(str.begin(), str.end());
    return str;
}

这里有一篇文章是讲OpenSSL如何生成密钥对的,可以一看
小王的尴尬日常(二)—Openssl 实现国密算法(基础介绍和产生秘钥对)

参考

精通比特币 第4章 密钥、地址、钱包

阅读更多

更多精彩内容