Windows下使用python-web3.py进行以太坊Dapp开发笔记--第2篇(以太坊公钥加密功能python实现)

以太坊公钥加密功能python实现

1.什么是公钥、私钥、地址

  • 私钥:32字节(256位),私钥需要保管和隐蔽(非公开)(截取《以太坊技术详解与实战》)
  • 公钥:(由私钥生成)64字节(512位),公钥是公开的,不需要保密(截取《以太坊技术详解与实战》)
  • 地址:20字节(160位),要注意,我们使用最多的地址并不等同于公钥

地址的生成过程:
从私钥得到一个512位(64字节)的公钥之后,将公钥进行sha3加密,然后将加密后的前44字节去掉,保留剩下的160位二进制数(20字节)
将该20字节编译成长度为40的十六进制字符串,即地址。
具体如下:
Windows下使用python-web3.py进行以太坊Dapp开发笔记--第2篇(以太坊公钥加密功能python实现)_第1张图片

2.python实现公钥加密

从上面地址的生成过程中可以看到,使用到的两个加密函数主要是secp256k1和sha3(也就是上面的keccak-256),那么要找python中实现这两个函数的模块。
web3.py中已经有keccak-256的实现,故不需要再另寻。
而实现secp256k1的模块是:eth_keys->安装pip install eth_keys
另外,进行公钥加密可以使用模块:eciespy->安装pip install eciespy
所以模块准备
web3.py
pip install eth_keys
pip install eciespy

实现过程:

1.从keystore文件中解出私钥以及私钥->公钥->地址:

(1).keystore文件夹在我们创建创世区块的文件中"./data"下,
在这里插入图片描述
keystore文件夹存放的文件是与账户私钥相关的加密信息,文件由personal.newAccount()函数执行后创建。

所以我们先读取文件信息:

# 读取keystore文件
with open(r"D:\pycharm_code\btc\data\keystore\UTC--2019-09-21T02-01-55.731711200Z--492be64a94551bda23578ebce7ab87aafe61e059","r") as f:
	self.encrypted_key = f.read()

(2).然后从self.encrypted_key中解出私钥:

# 这里需要引入web3.py中管理账户的模块
from web3 import Account
# 从keystore中获取私钥privatekey,需要输入密码(注意这里的密码并不是私钥)
self.privatekey = Account.decrypt(self.encrypted_key,"123")
print(self.privatekey)

(3).引入eth_keys模块中的keys,从私钥中解出公钥

from eth_keys import keys
# 先创建keys模块下的私钥对象
priv_key = keys.PrivateKey(self.privatekey)
print("priv_key:",priv_key)
# 再解出公钥
self.public_key = priv_key.public_key
print("公钥:",self.public_key)
# 这里的公钥是64字节,是压缩的
print("公钥长度:",len(str(self.public_key)))

(4).使用web3.py自带的keccak实现从公钥中得出地址

# 对公钥使用SHA3加密算法
sha3_pub_key = Web3.keccak(hexstr=str(self.public_key))
print("sha3_pub_key",sha3_pub_key)
# 取后20个字节
print('从公钥生成地址:',Web3.toHex(sha3_pub_key[-20:]))

完整代码如下:

import json
from web3 import Web3,Account
# 用于解密
from functools import wraps
from eth_account.messages import encode_defunct

from eth_keys import keys
from eth_utils import decode_hex
# 用于数字加密
import ecies


class ethereumHandler_OP():
    # 类级变量
    myPrivateKey = None
    myPublicAddr = None
    iflogin = None

    """初始化连接以太坊私链"""
    def __init__(self):
        self.web3 = Web3(Web3.WebsocketProvider("ws://127.0.0.1:8546"))
        # 检查是否连接成功
        if self.web3.eth.getBlock(0) is None:
            print("Failed to connect!")
        elif self.web3.isConnected():
            # read the abi
            # r"D:\pycharm_code\contract\myContract_sol_baseContract.abi", 'r'
            with open(r"D:\pycharm_code\contract\myContract_sol_baseContract.abi", 'r') as fo:
                preabi = fo.read()
                # print(self.preabi)
                myabi = json.loads(preabi)
                self.myContractAddr = '0x03E006a0323eE1aE1e24A4A2615c77b7A068abd6'
                self.myContract = self.web3.eth.contract(address=self.myContractAddr, abi=myabi)
                print("Successfully connected")
            #print(self.myContract.all_functions())

    def PrivateKey_PublicKey_Address(self):
        # 读取keystore文件
        with open(r"D:\pycharm_code\btc\data\keystore\UTC--2019-09-21T02-01-55.731711200Z--492be64a94551bda23578ebce7ab87aafe61e059",
                  "r") as f:
            self.encrypted_key = f.read()
        # 可以看到keystore中存储的是json格式的数据
        print("读取到的keystore文件中的信息:",self.encrypted_key)
        # 从keystore中获取私钥privatekey,需要输入密码(注意这里的密码并不是私钥)
        self.privatekey = Account.decrypt(self.encrypted_key,"123")
        print("私钥:",self.privatekey)
        # 先创建keys模块下的私钥对象
        priv_key = keys.PrivateKey(self.privatekey)
        print("priv_key:",priv_key)
        # 再解出公钥
        self.public_key = priv_key.public_key

        print("公钥:",self.public_key)
        # 这里的公钥是64字节,是压缩的
        print("公钥长度:",len(str(self.public_key)))
        # 对公钥使用SHA3加密算法
        sha3_pub_key = Web3.keccak(hexstr=str(self.public_key))
        print("sha3_pub_key",sha3_pub_key)
        # 取后20个字节
        print('从公钥生成地址:',Web3.toHex(sha3_pub_key[-20:]))
        
if __name__ == "__main__":
    eth = ethereumHandler_OP()
    eth.PrivateKey_PublicKey_Address()
    #encrypted_msg = eth.encryptMsg("Hello,world!11")
    #print(eth.decryptMsg(encrypted_msg))
    #eth.test()

结果如图:
Windows下使用python-web3.py进行以太坊Dapp开发笔记--第2篇(以太坊公钥加密功能python实现)_第2张图片

2.利用公钥进行消息加密/解密

使用eciespy模块,在上面class ethereumHandler_OP类中加入函数:

import ecies
class ethereumHandler_OP():
	#...此处省略上面所有函数
	def encryptMsg(self,_msg):
        '''
        params:_msg必须是字节bytes类型,由b''定义的字符串
        '''
        encrypted_msg = ecies.encrypt(self.public_key.to_hex(),bytes(_msg,encoding='utf8'))
        return encrypted_msg

    def decryptMsg(self,_encryptedmsg):
        #private_hex = Web3.toHex(self.privatekey)
        recovered_msg = ecies.decrypt(self.privatekey,_encryptedmsg)
        return recovered_msg

(1).试着对“Hello World!11进行加密”

if __name__ == "__main__":
    eth = ethereumHandler_OP()
    eth.PrivateKey_PublicKey_Address()
    # 调用加密解密函数
    encrypted_msg = eth.encryptMsg("Hello,world!11")

得到加密信息如下:
在这里插入图片描述
(2).然后就是解密:

if __name__ == "__main__":
    eth = ethereumHandler_OP()
    eth.PrivateKey_PublicKey_Address()
    # 调用加密解密函数
    encrypted_msg = eth.encryptMsg("Hello,world!11")
    print("加密信息如下:",encrypted_msg)
    print("解密信息:",eth.decryptMsg(encrypted_msg))
    #eth.test()

结果:
在这里插入图片描述

以上是以太坊实现公钥加密的过程。

完整代码如下:

import json
from web3 import Web3,Account
# 用于解密
from functools import wraps
from eth_account.messages import encode_defunct

from eth_keys import keys
from eth_utils import decode_hex
# 用于数字加密
import ecies


class ethereumHandler_OP():
    # 类级变量
    myPrivateKey = None
    myPublicAddr = None
    iflogin = None

    """初始化连接以太坊私链"""
    def __init__(self):
        self.web3 = Web3(Web3.WebsocketProvider("ws://127.0.0.1:8546"))
        # 检查是否连接成功
        if self.web3.eth.getBlock(0) is None:
            print("Failed to connect!")
        elif self.web3.isConnected():
            # read the abi
            # r"D:\pycharm_code\contract\myContract_sol_baseContract.abi", 'r'
            with open(r"D:\pycharm_code\contract\myContract_sol_baseContract.abi", 'r') as fo:
                preabi = fo.read()
                # print(self.preabi)
                myabi = json.loads(preabi)
                self.myContractAddr = '0x03E006a0323eE1aE1e24A4A2615c77b7A068abd6'
                self.myContract = self.web3.eth.contract(address=self.myContractAddr, abi=myabi)
                print("Successfully connected")
            #print(self.myContract.all_functions())

    def PrivateKey_PublicKey_Address(self):
        # 读取keystore文件
        with open(r"D:\pycharm_code\btc\data\keystore\UTC--2019-09-21T02-01-55.731711200Z--492be64a94551bda23578ebce7ab87aafe61e059",
                  "r") as f:
            self.encrypted_key = f.read()
        # 可以看到keystore中存储的是json格式的数据
        print("读取到的keystore文件中的信息:",self.encrypted_key)
        # 从keystore中获取私钥privatekey,需要输入密码(注意这里的密码并不是私钥)
        self.privatekey = Account.decrypt(self.encrypted_key,"123")
        print("私钥:",self.privatekey)
        #private_hex = Web3.toHex(self.privatekey)
        # 可以看到私钥是一个256位的二进制数字,32字节
        #print("privatekey=>:{}".format(private_hex))
        #print("私钥长度:",len(str(private_hex)))
        #print("从私钥生成地址:",Account.privateKeyToAccount(self.privatekey).address)
        # 从私钥中解出公钥
        #private_key_bytes = decode_hex(str(private_hex))
        #print("private_key_bytes:",private_key_bytes)
        #priv_key = keys.PrivateKey(private_key_bytes)
        # 先创建keys模块下的私钥对象
        priv_key = keys.PrivateKey(self.privatekey)
        print("priv_key:",priv_key)
        # 再解出公钥
        self.public_key = priv_key.public_key

        print("公钥:",self.public_key)
        # 这里的公钥是64字节,是压缩的
        print("公钥长度:",len(str(self.public_key)))
        # 对公钥使用SHA3加密算法
        sha3_pub_key = Web3.keccak(hexstr=str(self.public_key))
        print("sha3_pub_key",sha3_pub_key)
        # 取后20个字节
        print('从公钥生成地址:',Web3.toHex(sha3_pub_key[-20:]))

    def encryptMsg(self,_msg):
        '''
        params:_msg必须是字节bytes类型,由b''定义的字符串
        '''
        encrypted_msg = ecies.encrypt(self.public_key.to_hex(),bytes(_msg,encoding='utf8'))
        return encrypted_msg

    def decryptMsg(self,_encryptedmsg):
        #private_hex = Web3.toHex(self.privatekey)
        recovered_msg = ecies.decrypt(self.privatekey,_encryptedmsg)
        return recovered_msg

    def signtMsg(self,_msg):
        print("original msg:",_msg)
        # 构造加密前的数据体
        premsg = encode_defunct(text=_msg)
        print("预准备的数据体:",premsg)
        signedmsg = Account.sign_message(premsg,self.privatekey)
        print("签名后的信息:",signedmsg)

    def vertifyMsg(self,_signedmsg):
        print(Account.recover_message("Hello,world!",))

    def test(self):
        private_1 = '0x95d3c5e483e9b1d4f5fc8e79b2deaf51362980de62dbb082a9a4257eef653d7d'
        public_1 = '0x98afe4f150642cd05cc9d2fa36458ce0a58567daeaf5fde7333ba9b403011140a4e28911fcf83ab1f457a30b4959efc4b9306f514a4c3711a16a80e3b47eb58b'
        print(len(public_1))
        address_1 = '0x47e801184B3a8ea8E6A4A7A4CFEfEcC76809Da72'
        encryptmsg = ecies.encrypt(public_1,b'hello,world!')
        print("加密的信息:",encryptmsg)
        decryptmsg = ecies.decrypt(private_1,encryptmsg)
        print("解密的信息:",decryptmsg)


if __name__ == "__main__":
    eth = ethereumHandler_OP()
    eth.PrivateKey_PublicKey_Address()
    # 调用加密解密函数
    encrypted_msg = eth.encryptMsg("Hello,world!11")
    print("加密信息如下:",encrypted_msg)
    print("解密信息:",eth.decryptMsg(encrypted_msg))
    #eth.test()

补充:公钥的两种形式(压缩和非压缩编码)

从私钥解出公钥的过程中,公钥具有两种编码方式(压缩编码、非压缩编码),这可能导致公钥的最终显示不同,差了一个04前缀。
在javascript中,引入secp256k1、keccakjs模块,解出来的公钥形式如下:
在这里插入图片描述
而在python中利用eth_keys模块解出的公钥形式如下:
在这里插入图片描述
两者相差了一个十六进制的04前缀,而有04前缀的被称为“非压缩编码”,没有04前缀的称为“压缩编码

补充:公钥认证,参考web3.py在线官方文档例子

链接点击这里

其中sign a message 和 verify a message。

补充:可以实现公钥加密的原理

这是关于椭圆加密算法ECDSA的部分,感兴趣的可以查找关于ECDSA的实现原理进行了解。

各位大佬如果有其他更方便的实现方法可以在讨论区告诉我一下。之前搞这个真的很是头大。
------偷得浮生半日闲,又摘桃花换酒钱!

你可能感兴趣的:(Windows下使用python-web3.py进行以太坊Dapp开发笔记--第2篇(以太坊公钥加密功能python实现))