2018-06-30

比特币使用的加密算法是椭圆加密算法 。该算法是非对称加密算法

挖矿使用的算法是sha-256算法,该算法也是一种非对称加密算法。

我们在比特币的bitcoin_cli getnewaddress 可以生成一个新的比特币钱包地址。

生成地址的过程是


2018-06-30_第1张图片

从比特币私钥得到我们日常转账所用的比特币钱包地址总共需要九个步骤,中间用到了SHA256加密、RIPEMD160加密和BASE58编码。

下面,我们以实际案例来模拟一下整个流程:

第一步:生成随机私钥

私钥是一个随机数,随机选取一个32字节的数,这个数的范围大小是介于1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141之间的一个数,为了方便后面的计算,我们随机生成一个合法的私钥:

8F72F6B29E6E225A36B68DFE333C7CE5E55D83249D3D2CD6332671FA445C4DD3

第二步:椭圆曲线算公钥

生成了私钥之后,我们使用椭圆曲线加密算法(ECDSA-secp256k1)计算私钥所对应的非压缩公钥,生成的公钥共65字节, 其中一个字节是0x04,其中32个字节是X坐标,另外32个字节是Y坐标:

公钥P.X:

06CCAE7536386DA2C5ADD428B099C7658814CA837F94FADE365D0EC6B1519385

公钥P.Y:

FF83EC5F2C0C8F016A32134589F7B9E97ACBFEFD2EF12A91FA622B38A1449EEB

第三步:计算公钥的SHA-256哈希值

将上述公钥地址拼合,得到标准地址:

0406CCAE7536386DA2C5ADD428B099C7658814CA837F94FADE365D0EC6B1519385FF83EC5F2C0C8F016A32134589F7B9E97ACBFEFD2EF12A91FA622B38A1449EEB

对齐进行SHA-256哈希计算,得到结果:

2572e5f4a8e77ddf5bb35b9e61c61f66455a4a24bcfd6cb190a8e8ff48fc097d

第四步:计算 RIPEMD-160哈希值

取上一步结果,进行RIPEMD-160计算,得到结果:

0b14f003d63ab31aef5fedde2b504699547dd1f6

第五步:加入地址版本号(比特币主网版本号“0x00”)

取上一步结果,在前面加上16进制的00,即:

000b14f003d63ab31aef5fedde2b504699547dd1f6

第六步:计算 SHA-256 哈希值

取上一步结果,进行SHA-256计算,可得:

ddc2270f93cc84cc6869dd373f3c340bbf5cb9a8f5559297cc9e5d947aab2536

然后,对以上结果再次计算 SHA-256 哈希值,得到:

869ac57b83ccf75ca9da8895823562fffb611e3c297d9c2d4612aeeb32850078

第七步:取上一步结果的前4个字节(8位十六进制)

869ac57b

第八步:把这4个字节加在第五步的结果后面

作为校验位,将这4个字节加载第五步的结果后面,这就是比特币地址的16进制形态了:

869ac57b000b14f003d63ab31aef5fedde2b504699547dd1f6

第九步:用Base58编码变换一下地址

对上一步的结果进行Base58编码,得到:

1QCXRuoxWo5Bya9NxHaVBArBQYhatHJrU7

这就是我们经常看到的传统意义上的比特币钱包地址了。



如果大家感兴趣,可以去 查一下椭圆曲线和sha256加密的过程原理:为了深刻理解加密算法,当然这对于理解比特币机制也没太大意义。因为地址生成。在比特币代码中就是一个函数

CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal)

{

    AssertLockHeld(cs_wallet); // mapKeyMetadata

    bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets

    CKey secret;

    // Create new metadata

    int64_t nCreationTime = GetTime();

    CKeyMetadata metadata(nCreationTime);

    // use HD key derivation if HD was enabled during wallet creation

    if (IsHDEnabled()) {

        DeriveNewChildKey(batch, metadata, secret, (CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false));

    } else {

        secret.MakeNewKey(fCompressed);

    }

    // Compressed public keys were introduced in version 0.6.0

    if (fCompressed) {

        SetMinVersion(FEATURE_COMPRPUBKEY);

    }

    CPubKey pubkey = secret.GetPubKey();

    assert(secret.VerifyPubKey(pubkey));

    mapKeyMetadata[pubkey.GetID()] = metadata;

    UpdateTimeFirstKey(nCreationTime);

    if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) {

        throw std::runtime_error(std::string(__func__) + ": AddKey failed");

    }

    return pubkey;

}

    比特币的私钥用类CKey来封装,从代码中可以看到先通过CKey.MakeNewKey生成私钥:

void CKey::MakeNewKey(bool fCompressedIn) {

    do {

        GetStrongRandBytes(keydata.data(), keydata.size());

    } while (!Check(keydata.data()));

    fValid = true;

    fCompressed = fCompressedIn;

}

    私钥起始就是一串随机生成的字节。有了私钥,在通过CKey::GetPubKey生成公钥:

CPubKey CKey::GetPubKey() const {

    assert(fValid);

    secp256k1_pubkey pubkey;

    size_t clen = CPubKey::PUBLIC_KEY_SIZE;

    CPubKey result;

    int ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, begin());

    assert(ret);

    secp256k1_ec_pubkey_serialize(secp256k1_context_sign, (unsigned char*)result.begin(), &clen, &pubkey, fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);

    assert(result.size() == clen);

    assert(result.IsValid());

    return result;

}

    这里用libsecp256k1库中的接口,对私钥进行椭圆曲线加密处理,得到的公钥封装在CPubKey中。

之前笔者遇到有一个最基础的问题,就是我作为挖矿者。怎么知道这笔交易是你的呢。意思是,比特币地址是公开的。我可以用你的地址来发送一笔交易。发送到我的地址。这样我就能拿到钱、

在比特币的机制里,我不能够使用你的地址发币。是因为比特币有个签名机制。你转账是得在转账上边签名的,然后我为了确认这笔转账是你的签名,我得使用你的公钥来解密。这样就能保证这笔交易是你发的了。

这就是非对称加密的另一种使用,消息签名。、

比特币消息签名源代码在这里:

bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const

{

    CKey key;//上文已经说过ckey中已经是保存了公钥和私钥等信息

    if (!provider.GetKey(address, key))

        return false;

    // Signing with uncompressed keys is disabled in witness scripts

    if (sigversion == SigVersion::WITNESS_V0 && !key.IsCompressed())

        return false;

    uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); //这里生成交易的摘要

    if (!key.Sign(hash, vchSig))    //用私钥生成签名

        return false;

    vchSig.push_back((unsigned char)nHashType);

    return true;

}

    先通过SignatureHash生成交易的摘要,然后通过CKey::Sign来生成签名,CKey是私钥的封装。

bool CKey::Sign(const uint256 &hash, std::vector& vchSig, uint32_t test_case) const {

    if (!fValid)

        return false;

    vchSig.resize(CPubKey::SIGNATURE_SIZE);

    size_t nSigLen = CPubKey::SIGNATURE_SIZE;

    unsigned char extra_entropy[32] = {0};

    WriteLE32(extra_entropy, test_case);

    secp256k1_ecdsa_signature sig;

    int ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, test_case ? extra_entropy : nullptr);

    assert(ret);

    secp256k1_ecdsa_signature_serialize_der(secp256k1_context_sign, vchSig.data(), &nSigLen, &sig);

    vchSig.resize(nSigLen);

    return true;

}

    之前已经提到过,比特币的非对称加密实现使用的是libsecp256k1实现的椭圆曲线算法,这里直接调用库提供的接口用私钥生成了hash摘要的签名。

    之后交易被广播到比特币网络中,收到此交易的节点将会对交易进行验证,包括交易签名的验证,我们以比特币中最常见的P2PKH标准交易为例,其最后一步就是OP_CHECKSIG,即验证签名,实现可参考TransactionSignatureChecker::CheckSig:    

template

bool GenericTransactionSignatureChecker::CheckSig(const std::vector& vchSigIn, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const

{

    CPubKey pubkey(vchPubKey);

    if (!pubkey.IsValid())

        return false;

    // Hash type is one byte tacked on to the end of the signature

    std::vector vchSig(vchSigIn);

    if (vchSig.empty())

        return false;

    int nHashType = vchSig.back();

    vchSig.pop_back();

    uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata);    //交易签名

    if (!VerifySignature(vchSig, pubkey, sighash)) //用公钥验证签名

        return false;

    return true;

}

    这里同样用SignatureHash生成交易摘要,然后通过VerifySignature来验证签名:

template

bool GenericTransactionSignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash) const

{

    return pubkey.Verify(sighash, vchSig);

}

    最终调用了CPubKey::Verfy,CPubKey封装了公钥,即用公钥验证签名:

bool CPubKey::Verify(const uint256 &hash, const std::vector& vchSig) const {

    if (!IsValid())

        return false;

    secp256k1_pubkey pubkey;

    secp256k1_ecdsa_signature sig;

    if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) {

        return false;

    }

    if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, vchSig.data(), vchSig.size())) {

        return false;

    }

    /* libsecp256k1's ECDSA verification requires lower-S signatures, which have

    * not historically been enforced in Bitcoin, so normalize them first. */

    secp256k1_ecdsa_signature_normalize(secp256k1_context_verify, &sig, &sig);

    return secp256k1_ecdsa_verify(secp256k1_context_verify, &sig, hash.begin(), &pubkey);

}

    关于比特币的交易及其签名脚本,后续再分析比特币交易部分的源码时会进行更详细的分析,现在只需要知道比特币里通过数字签名的方式可以证明一笔UTXO的合法所有人就可以啦!

你可能感兴趣的:(2018-06-30)