Mumbler

基础---很多人并没有搞明白

以太坊私钥

eg:
fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19

由256位;不考虑0x前缀,友32个字节,64个字符构成。

以太坊公共地址

eg:

0x20F8D42FB0F667F2E53930fed426f225752453b3

由160位组成;不考虑0x前缀,由20个字节,40个字符构成。

公钥与以太坊公共地址是一回事吗

一句话,公共地址是公钥的Keccak-256哈希。

具体在以太坊中,用keccak哈希算法来计算公钥的256位哈希,再截取这256位哈希的后160位哈希作为地址值。

所以,在以太坊中它俩个不是一回事。

以太坊地址的大小写---到底有没有区别

一个现象是我们用钱包的时候,比如ImToken/Metamask/交易所,钱包地址都是大小写混杂的,但是在etherscan和opensea上查看交易信息和NFT信息时却可以不区分大小写的正确进行查询,这是为什么呢?

还得从以太坊历史说起,起初以太坊内部实际并不区分大小写的,但是用户在钱包的实际使用过程中,以太坊的用户发现,由于缺乏类似比特币的自带地址校验机制,以太坊的公开地址在当朋友之间转账时,经常因为误输入字符而转错了账号。

于是社区用户在以太坊很早期的时候,就已经提出该问题并思考改进的方法,经过整理成文,形成了 EIP-55 提议。该提议按照一定逻辑,将地址中的部分字母大写,与剩余的小写字母来形成校验和,让地址拥有自校验的能力,具体JavaScript实现代码如下:

const createKeccakHash = require('keccak')

function toChecksumAddress (address) {
  address = address.toLowerCase().replace('0x', '')
  var hash = createKeccakHash('keccak256').update(address).digest('hex')
  var ret = '0x'

  for (var i = 0; i < address.length; i++) {
    if (parseInt(hash[i], 16) >= 8) {
      ret += address[i].toUpperCase()
    } else {
      ret += address[i]
    }
  }

  return ret
}

即符合EIP55规范的软件或APP应该对传入的地址进行一次检验,以防止人为书写错误。进一步实际上对账户地址进行keccak256哈希运算,根据运算结果值的二进制位图决定地址十六进制字符串(0-9,a-f)的大写或小些。

带校验地址 (大小写区分)0x7c52e508C07558C287d5A453475954f6a547eC41 普通地址 (全小写)0x7c52e508c07558c287d5a453475954f6a547ec41

优点:

  • 向后兼容许多接受混合大小写的十六进制解析器,允许它随着时间的推移轻松引入
  • 保持长度为40个字符
  • 平均每个地址将有15个校验位,如果输入错误,随机生成的地因抄写错误意外通过检查的净概率为0.0247%。 这比ICAP提高了约50倍,但不如4字节检查代码好。

BIP39助记词与私钥---有怎样的转换为关系

首先要说明的是BIP39助记词有12、15、18、24个区分,但实际原理是一样的,一般钱包更常见的是采用12个助记词。下面以12个助记词来讲解原理:

在实际应用中,助记词往往不是匹配到一个私钥的,常常被使用的是层级钱包,也就是HD钱包,hierarchical deterministic wallet。这种模式中,助记词对应的是原始密码,原始密码可以生成种子秘钥,种子秘钥可以能再生成多级的子公私钥对。这种模式的好处在于,有了助记词,就能获得所有子账户的控制权。同时,子账户中的私钥可以从未生成的和传播,有利于钱包安全。

那么助记词的原理是什么呢?

我们都知道,以太坊的私钥,看起来是256位的二进制字符串。不便于记忆,也不便于输入。为了便于记忆,我们可以把特定的字符组合映射到某个单词,这样就便于记忆了。

例如,3个字符的组合有2的3次方,一共8种。我们可以设定一种关系,000用moon表示,001用sun表示,010用zero表示,011用zoom表示。这样一个12个字符的串,000010011001我们就可以及意义成 moon sun zero zoom 了,这样输入时不容易出错,也容易记忆。具体原理图如下:

以太坊私钥到公钥---具体实现原理

首先从一段代码看起,这样整个过程会更清晰

package main

import (
	"crypto/ecdsa"
	"fmt"
	"log"
	"github.com/ethereum/go-ethereum/common/hexutil"
	"github.com/ethereum/go-ethereum/crypto"
	"golang.org/x/crypto/sha3"
)

func main() {
	privateKey, err := crypto.GenerateKey()
	if err != nil {
		log.Fatal(err)
	}
	privateKeyBytes := crypto.FromECDSA(privateKey)
	fmt.Println(hexutil.Encode(privateKeyBytes)[2:]) 

	publicKey := privateKey.Public()
	publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
	if !ok {
		log.Fatal("cannot assert type: publicKey is not of type *ecdsa.PublicKey")
	}
	
	publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA)
	fmt.Println(hexutil.Encode(publicKeyBytes)[4:]) 

	address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
	fmt.Println(address)

	//以下是使用 golang.org/x/crypto/sha3 的 Keccak256函数手动完成的方法。会在keccak环节深入讲解。
	hash := sha3.NewLegacyKeccak256()
	hash.Write(publicKeyBytes[1:])
	fmt.Println(hexutil.Encode(hash.Sum(nil)[12:])) // 0x96216849c49358b10257cb55b28ea603c874b05e
}

解析:

  • crypto.GenerateKey() //生成随机私钥

  • crypto.FromECDSA(privateKey) //将其转换为字节, 再次强调一个字节等于2个字符,所以普通的以太坊地址除了0x外是40个字符,20个字节。这个过程完成的ECDSA签名,具体实现的功能在下一节讲解。

  • fmt.Println(hexutil.Encode(privateKeyBytes)[2:]) // 转换为十六进制字符串,该包提供了一个带有字节切片的Encode方法,然后我们在十六进制编码之后删除“0x”; 私钥是256位的二进制字符串,也就是32个字节,64个字符。

  • publicKey := privateKey.Public() //由于公钥是从私钥派生的,加密私钥具有一个返回公钥的Public方法

  • publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) //这一步就是EIP55做的事情,生成经过ECDSA校验的Public

  • publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA) //将其转换为字节

  • fmt.Println(hexutil.Encode(publicKeyBytes)[4:]) // 9a7df67f79246283fdc93af76d4f8cdd62c4886e8cd870944e817dd0b97934fdd7719d0810951e03418205868a5c1b40b192451367f28e0088dd75e15de40c05 将其转换为十六进制的过程与我们使用转化私钥的过程类似。 我们剥离了0x和前2个字符04,它始终是EC前缀,不是必需的。

  • address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() //接受一个ECDSA公钥,并返回公共地址; 公共地址其实就是公钥的Keccak-256哈希,然后我们取最后40个字符(20个字节)并用“0x”作为前缀

  • fmt.Println(address) // 0x96216849c49358B10257cb55b28eA603c874b05E

    最终,打印出来的0x96216849c49358B10257cb55b28eA603c874b05E的地址就是我们钱包所见的公钥地址了。

不得不说的ECDSA

上面的章节中我们看到了通过代码crypto.FromECDSA(privateKey) 来执行ECDSA签名,那么ECDSA是什么?到底完成了什么功能?

ECDSA定义

ECDSA又名椭圆曲线数字签名算法,是使用椭圆曲线密码(ECC)对数字签名算法(DSA)的模拟。数字签名算法(DSA)是联邦信息处理标准FIPS中的数字签名标准,它的安全性基于素域上的离散对数问题。椭圆曲线密码(ECC)由Neal Koblitz和Victor Miller于1985年发明。它可以看作是椭圆曲线对先前基于离散对数问题(DLP)的密码系统的模拟,只是群元素由素域中的元素数换为有限域上的椭圆曲线上的点。

一句话总结,ECDSA是ECC与DSA的结合,整个签名过程与DSA类似,所不一样的是签名中采取的算法为ECC,最后签名出来的值也是分为r,s。

ECDSA功能

  • 初始化化秘钥组,生成ECDSA算法的公钥和私钥

  • 执行私钥签名, 使用私钥签名,生成私钥签名

  • 执行公钥签名,生成公钥签名

  • 使用公钥验证私钥签名 

    Tips:ECDSA属于非对称加密。公钥与私钥匙成对出现。 遵从的原则就是“私钥签名、公钥验证”。

ECDSA原理(签名+验签)

  • 签名原理:
    1、选择一条椭圆曲线Ep(a,b),和基点G;
    2、选择私有密钥k(k<n,n为G的阶),利用基点G计算公开密钥K=kG;
    3、产生一个随机整数r(r<n),计算点R=rG;
    4、将原数据和点R的坐标值x,y作为参数,计算SHA1做为hash,即Hash=SHA1(原数据,x,y);
    5、计算s≡r - Hash * k (mod n)
    6、r和s做为签名值,如果r和s其中一个为0,重新从第3步开始执行
  • 验证原理:
    1、接受方在收到消息(m)和签名值(r,s)后,进行以下运算
    2、计算:sG+H(m)P=(x1,y1), r1≡ x1 mod p。
    3、验证等式:r1 ≡ r mod p。
    4、如果等式成立,接受签名,否则签名无效。

不得不说的Keccak

上述代码进行手动生成时调用了hash := sha3.NewLegacyKeccak256()函数 ,其中用到Keccak256算法。

Keccak是一种被选定为SHA-3标准的单向散列函数算法。Keccak可以生成任意长度的散列值,但为了配合SHA-2的散列值长度,SHA-3标准中规定了SHA3-224、SHA3-256、SHA3-384、SHA3-512这4种版本。在输入数据的长度上限方面,SHA-1为2的64次方-1比特,SHA-2为2的128次方-1比特,而SHA-3则没有长度限制。

在以太坊中,用keccak哈希算法来计算公钥的256位哈希,再截取这256位哈希的后160位哈希作为地址值。

keccak和sha3的区别

sha3由keccak标准化而来,在很多场合下Keccak和SHA3是同义词,但在2015年8月SHA3最终完成标准化时,NIST调整了填充算法:SHA3-256(M) = KECCAK [512] (M || 01, 256)。所以标准的NIST-SHA3就和keccak计算的结果不一样。

以太坊在开发的时候sha3还在标准化中,所以采用了keccak,所以Ethereum和Solidity智能合约代码中的SHA3是指Keccak256,而不是标准的NIST-SHA3,为了避免混淆,直接在合约代码中写成Keccak256是最清晰的

在早期的Ethereum相关代码中,普遍使用SHA3代指Keccak256,为了避免和NIST标准的SHA3混淆,现在的代码直接使用Keccak256作为函数名。

SHA3采用Keccak算法,在很多场合下Keccak和SHA3是同义词,但在2015年8月SHA3最终完成标准化时,NIST调整了填充算法,标准的SHA3和原先的Keccak算法就有所区别了。

为何推出sha3

推出sha3不是因为sha2出现了漏洞,只是当时学术界对于sha1被成功碰撞的担忧,但目前基于NIST的建议,sha2和sha3都是属于可以安全商用的哈希算法,sha3相当于多了一种安全选择,比特币选用的就是sha2(SHA256)。

参考

1.https://ethbook.abyteahead.com/ch2/eip55.html

2.https://learnblockchain.cn/docs/eips/eip-55.html#%E8%A7%84%E8%8C%83 EIP55

3.https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md EIP55

4.https://zhuanlan.zhihu.com/p/150125861 助记词到私钥

5.https://blog.csdn.net/m0_37458552/article/details/80250258 ECDSA

6.https://blog.csdn.net/weixin_39860583/article/details/111584269 Keccak256

Subscribe to Renaissance Labs
Receive the latest updates directly to your inbox.
Verification
This entry has been permanently stored onchain and signed by its creator.
Author Address
0x9B5b7b8290c23dD…cCBad3661786f3a
Content Digest
KgD26BedXy8yUk6…WtRQfST0R6rpsBg