如何用python生成自己的比特币私钥 - Go语言中文社区

如何用python生成自己的比特币私钥


在加密货币中,私钥允许用户访问其钱包。持有私钥的人完全控制该钱包中数字货币。出于这个原因,你应该保守秘密。如果你真的想自己生成密钥,那么以安全的方式生成密钥是有意义的。在这里,我将介绍私钥,并向你展示如何使用各种加密函数生成自己的密钥。我将在Python中提供算法和代码的描述。

我需要生成私钥吗?

大多数时候你没有。例如,如果你使用Coinbase或Blockchain.info等网络钱包,他们会为你创建和管理私钥。交易所也是如此。

移动和桌面钱包通常也会为你生成私钥,但他们可以选择使用你自己的私钥创建钱包。

那么为什么要生成呢?以下是我的原因:

  • 你想确保没有人知道密钥。
  • 你只想了解有关加密和随机数生成(RNG)的更多信息。

什么是私钥?

形式上,比特币(以及许多其他加密货币)的私钥是一系列32字节。现在,有很多方法可以记录这些字节。它可以是256个1和0(32*8=256)或100个骰子所组成的字符串。它可以是二进制字符串,Base64字符串,WIF密钥,助记符短语,或最后是十六进制字符串。出于我们的目的,我们将使用64个字符长的十六进制字符串。

为什么正好是32字节?好问题!你可以看到,要从私有密钥创建公钥,比特币使用 ECDSA 或椭圆曲线数字签名算法。更具体地说,它使用一个称为 secp256k1 的特定曲线。

现在,该曲线具有256位的量级,以256位作为输入,并输出256位整数。256位正好是32个字节。因此,换句话说,我们需要32字节的数据来提供给这种曲线算法。

私钥还有一个额外的要求。因为我们使用ECDSA,所以关键应该是正数,并且应该小于曲线的顺序。secp256k1的顺序是 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 ,它非常大:几乎任何32字节的数字都会比它小。

简单粗暴的方法

那么,我们如何生成一个32字节的整数? 首先想到的是只使用你选择的语言的RNG库。Python甚至提供了一种生成足够位的友好方式:

import random
bits = random.getrandbits(256)
# 30848827712021293731208415302456569301499384654877289245795786476741155372082
bits_hex = hex(bits)
# 0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32
private_key = bits_hex[2:]
# 4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32
PYthon学习企鹅裙:88198-2657  领取python自动化编程资料教程

看起来很好,但实际上,它不是。你看,普通的RNG库不适用于加密,因为它们不是很安全。它们根据种子生成数字,默认情况下,种子是当前时间。这样,如果你大致知道我上面生成的那些位,你需要做的就是暴力破解一些变种。

生成私钥时,你希望非常安全。请记住,如果有人学习私钥,他们可以轻松地从相应的钱包中窃取所有硬币,而你没有机会将其取回。

所以让我们尝试更安全地做到这一点。

密码学上强大的RNG

除了标准的RNG方法,编程语言通常还提供专门用于加密操作的RNG。这种方法通常更加安全,因为它直接从操作系统中提取熵。这种RNG的结果很难再现。你不能通过知道生成时间或种子来做到这一点,因为没有种子。好吧,至少用户不输入种子;相反,它是由程序创建的。

在Python中,密码强的RNG在 secrets 模块中实现。让我们修改上面的代码,使私钥生成安全!

import secrets
bits = secrets.randbits(256)
# 46518555179467323509970270980993648640987722172281263586388328188640792550961
bits_hex = hex(bits)
# 0x66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31
private_key = bits_hex[2:]
# 66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31
PYthon学习企鹅裙:88198-2657  领取python自动化编程资料教程

这是惊人的。我敢打赌,即使访问我的电脑,你也无法重现这一点。但我们可以更深入了吗?

专业的网站

有些网站会为你生成随机数。我们在这里只考虑两个。一个是random.org ,一个众所周知的通用随机数发生器。另一个是bitaddress.org ,专门用于比特币私钥生成。

random.org可以帮助我们生成密钥吗? 当然,因为他们有生成随机字节的服务 。但是这里出现了两个问题。Random.org声称是一个真正的随机发生器,但你能相信吗? 你能确定它确实是随机的吗? 你能确定所有者不记录所有代的结果,特别是那些看起来像私钥的结果吗? 答案取决于你。哦,你不能在本地运行它,这是一个额外的问题。此方法不是100%安全。

现在,bitaddress.org是一个完全不同的故事。它是开源的,所以你可以看到它的内部代码。它是客户端,因此即使没有Internet连接,你也可以下载并在本地运行它。

那么它是怎样工作的? 它使用你——是的,你自己——作为熵的来源。它会要求你移动鼠标或按随机键。你做得足够长,使得重现结果变得不可行。

你是否有兴趣了解bitaddress.org的工作原理?出于学习目的,我们将查看其代码并尝试在Python中重现它。

快速说明:bitaddress.org为你提供压缩WIF格式的私钥,该格式接近我们之前讨论过的WIF格式。出于我们的目的,我们将使算法返回一个十六进制字符串,以便我们以后可以使用它来生成公钥。

Bitaddress:具体细节

Bitaddress以两种形式创建熵:通过鼠标移动和按键压力。我们将讨论两者,但我们将重点关注按键,因为很难在Python lib中实现鼠标跟踪。我们希望最终用户在我们有足够的熵之前键入按钮,然后我们将生成一个密钥。

Bitaddress做了三件事。它初始化字节数组,试图从你的计算机获得尽可能多的熵,它用用户输入填充数组,然后生成一个私钥。

Bitaddress使用256字节数组来存储熵。这个数组是循环重写的,所以当第一次填充数组时,指针变为零,并且填充过程再次开始。

程序从window.crypto启动一个256字节的数组。然后,它写入时间戳以获得额外的4个字节的熵。最后,它获取的数据包括屏幕大小,时区,浏览器插件信息,区域设置等。这给了它另外6个字节。

初始化之后,程序不断等待用户输入以重写初始字节。当用户移动光标时,程序会写入光标的位置。当用户按下按钮时,程序会写入按下的按钮的字符代码。

最后,bitaddress使用累积的熵来生成私钥。它需要生成32个字节。对于此任务,bitaddress使用名为ARC4的RNG算法。程序用当前时间初始化ARC4并收集熵,然后逐个获取32次字节。

这完全是对程序运作方式的过度简化,但我希望你能得到这个想法。你可以在 Github 上详细查看算法。

自己动手吧

为了我们的目的,我们将构建一个更简单的bitaddress版本。首先,我们不会收集有关用户机器和位置的数据。其次,我们将仅通过文本输入熵,因为使用Python脚本持续接收鼠标位置非常具有挑战性(如果你想这样做,请检查 PyAutoGUI )。

这将我们带到了我们的生成器库的正式规范。首先,它将使用加密RNG初始化一个字节数组,然后它将填充时间戳,最后它将填充用户创建的字符串。种子池填满后,库将让开发人员创建一个密钥。实际上,他们将能够创建任意数量的私钥,所有私钥都由收集的熵保护。

初始化池

这里我们从加密RNG和时间戳中放入一些字节。 __seed_int 和 __seed_byte 是两个将熵插入池数组的辅助方法。请注意,我们使用 secrets 。

def __init_pool(self):
    for i in range(self.POOL_SIZE):
        random_byte = secrets.randbits(8)
        self.__seed_byte(random_byte)
    time_int = int(time.time())
    self.__seed_int(time_int)
def __seed_int(self, n):
    self.__seed_byte(n)
    self.__seed_byte(n >> 8)
    self.__seed_byte(n >> 16)
    self.__seed_byte(n >> 24)
def __seed_byte(self, n):
    self.pool[self.pool_pointer] ^= n & 255
    self.pool_pointer += 1
    if self.pool_pointer >= self.POOL_SIZE:
        self.pool_pointer = 0

输入种子

这里我们首先放置一个时间戳,然后输入字符串。

def seed_input(self, str_input):
    time_int = int(time.time())
    self.__seed_int(time_int)
    for char in str_input:
        char_code = ord(char)
        self.__seed_byte(char_code)

生成私钥

这部分可能看起来很难,但实际上非常简单。

首先,我们需要使用我们的池生成32字节的数字。不幸的是,我们不能只创建自己的 random对象,只能用于密钥生成。相反,有一个共享对象,由在一个脚本中运行的任何代码使用。

这对我们意味着什么?

这意味着在每个时刻,代码中的任何地方,一个简单的 random.seed(0) 都可以破坏我们收集的所有熵。我们不希望这样。值得庆幸的是,Python提供了 getstate 和 setstate 方法。因此,为了在每次生成密钥时保存我们的熵,我们记住我们停下来的状态,并在下次我们想要创建密钥时设置它。

其次,我们只确保我们的键在范围内(1, CURVE_ORDER )。这是所有ECDSA私钥的要求。 CURVE_ORDER 是secp256k1曲线的顺序,它是 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 。

最后,为方便起见,我们转换为十六进制,并剥离 '0x' 部分。

def generate_key(self):
    big_int = self.__generate_big_int()
    big_int = big_int % (self.CURVE_ORDER — 1) # key < curve order
    big_int = big_int + 1 # key > 0
    key = hex(big_int)[2:]
    return key
def __generate_big_int(self):
    if self.prng_state is None:
    seed = int.from_bytes(

在加密货币中,私钥允许用户访问其钱包。持有私钥的人完全控制该钱包中数字货币。出于这个原因,你应该保守秘密。如果你真的想自己生成密钥,那么以安全的方式生成密钥是有意义的。在这里,我将介绍私钥,并向你展示如何使用各种加密函数生成自己的密钥。我将在Python中提供算法和代码的描述。

我需要生成私钥吗?

大多数时候你没有。例如,如果你使用Coinbase或Blockchain.info等网络钱包,他们会为你创建和管理私钥。交易所也是如此。

移动和桌面钱包通常也会为你生成私钥,但他们可以选择使用你自己的私钥创建钱包。

那么为什么要生成呢?以下是我的原因:

  • 你想确保没有人知道密钥。
  • 你只想了解有关加密和随机数生成(RNG)的更多信息。

什么是私钥?

形式上,比特币(以及许多其他加密货币)的私钥是一系列32字节。现在,有很多方法可以记录这些字节。它可以是256个1和0(32*8=256)或100个骰子所组成的字符串。它可以是二进制字符串,Base64字符串,WIF密钥,助记符短语,或最后是十六进制字符串。出于我们的目的,我们将使用64个字符长的十六进制字符串。

为什么正好是32字节?好问题!你可以看到,要从私有密钥创建公钥,比特币使用 ECDSA 或椭圆曲线数字签名算法。更具体地说,它使用一个称为 secp256k1 的特定曲线。

现在,该曲线具有256位的量级,以256位作为输入,并输出256位整数。256位正好是32个字节。因此,换句话说,我们需要32字节的数据来提供给这种曲线算法。

私钥还有一个额外的要求。因为我们使用ECDSA,所以关键应该是正数,并且应该小于曲线的顺序。secp256k1的顺序是 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 ,它非常大:几乎任何32字节的数字都会比它小。

简单粗暴的方法

那么,我们如何生成一个32字节的整数? 首先想到的是只使用你选择的语言的RNG库。Python甚至提供了一种生成足够位的友好方式:

import random
bits = random.getrandbits(256)
# 30848827712021293731208415302456569301499384654877289245795786476741155372082
bits_hex = hex(bits)
# 0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32
private_key = bits_hex[2:]
# 4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32

看起来很好,但实际上,它不是。你看,普通的RNG库不适用于加密,因为它们不是很安全。它们根据种子生成数字,默认情况下,种子是当前时间。这样,如果你大致知道我上面生成的那些位,你需要做的就是暴力破解一些变种。

生成私钥时,你希望非常安全。请记住,如果有人学习私钥,他们可以轻松地从相应的钱包中窃取所有硬币,而你没有机会将其取回。

所以让我们尝试更安全地做到这一点。

密码学上强大的RNG

除了标准的RNG方法,编程语言通常还提供专门用于加密操作的RNG。这种方法通常更加安全,因为它直接从操作系统中提取熵。这种RNG的结果很难再现。你不能通过知道生成时间或种子来做到这一点,因为没有种子。好吧,至少用户不输入种子;相反,它是由程序创建的。

在Python中,密码强的RNG在 secrets 模块中实现。让我们修改上面的代码,使私钥生成安全!

import secrets
bits = secrets.randbits(256)
# 46518555179467323509970270980993648640987722172281263586388328188640792550961
bits_hex = hex(bits)
# 0x66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31
private_key = bits_hex[2:]
# 66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31

这是惊人的。我敢打赌,即使访问我的电脑,你也无法重现这一点。但我们可以更深入了吗?

专业的网站

有些网站会为你生成随机数。我们在这里只考虑两个。一个是random.org ,一个众所周知的通用随机数发生器。另一个是bitaddress.org ,专门用于比特币私钥生成。

random.org可以帮助我们生成密钥吗? 当然,因为他们有生成随机字节的服务 。但是这里出现了两个问题。Random.org声称是一个真正的随机发生器,但你能相信吗? 你能确定它确实是随机的吗? 你能确定所有者不记录所有代的结果,特别是那些看起来像私钥的结果吗? 答案取决于你。哦,你不能在本地运行它,这是一个额外的问题。此方法不是100%安全。

现在,bitaddress.org是一个完全不同的故事。它是开源的,所以你可以看到它的内部代码。它是客户端,因此即使没有Internet连接,你也可以下载并在本地运行它。

那么它是怎样工作的? 它使用你——是的,你自己——作为熵的来源。它会要求你移动鼠标或按随机键。你做得足够长,使得重现结果变得不可行。

你是否有兴趣了解bitaddress.org的工作原理?出于学习目的,我们将查看其代码并尝试在Python中重现它。

快速说明:bitaddress.org为你提供压缩WIF格式的私钥,该格式接近我们之前讨论过的WIF格式。出于我们的目的,我们将使算法返回一个十六进制字符串,以便我们以后可以使用它来生成公钥。

Bitaddress:具体细节

Bitaddress以两种形式创建熵:通过鼠标移动和按键压力。我们将讨论两者,但我们将重点关注按键,因为很难在Python lib中实现鼠标跟踪。我们希望最终用户在我们有足够的熵之前键入按钮,然后我们将生成一个密钥。

Bitaddress做了三件事。它初始化字节数组,试图从你的计算机获得尽可能多的熵,它用用户输入填充数组,然后生成一个私钥。

Bitaddress使用256字节数组来存储熵。这个数组是循环重写的,所以当第一次填充数组时,指针变为零,并且填充过程再次开始。

程序从window.crypto启动一个256字节的数组。然后,它写入时间戳以获得额外的4个字节的熵。最后,它获取的数据包括屏幕大小,时区,浏览器插件信息,区域设置等。这给了它另外6个字节。

初始化之后,程序不断等待用户输入以重写初始字节。当用户移动光标时,程序会写入光标的位置。当用户按下按钮时,程序会写入按下的按钮的字符代码。

最后,bitaddress使用累积的熵来生成私钥。它需要生成32个字节。对于此任务,bitaddress使用名为ARC4的RNG算法。程序用当前时间初始化ARC4并收集熵,然后逐个获取32次字节。

这完全是对程序运作方式的过度简化,但我希望你能得到这个想法。你可以在 Github 上详细查看算法。

自己动手吧

为了我们的目的,我们将构建一个更简单的bitaddress版本。首先,我们不会收集有关用户机器和位置的数据。其次,我们将仅通过文本输入熵,因为使用Python脚本持续接收鼠标位置非常具有挑战性(如果你想这样做,请检查 PyAutoGUI )。

这将我们带到了我们的生成器库的正式规范。首先,它将使用加密RNG初始化一个字节数组,然后它将填充时间戳,最后它将填充用户创建的字符串。种子池填满后,库将让开发人员创建一个密钥。实际上,他们将能够创建任意数量的私钥,所有私钥都由收集的熵保护。

初始化池

这里我们从加密RNG和时间戳中放入一些字节。 __seed_int 和 __seed_byte 是两个将熵插入池数组的辅助方法。请注意,我们使用 secrets 。

def __init_pool(self):
    for i in range(self.POOL_SIZE):
        random_byte = secrets.randbits(8)
        self.__seed_byte(random_byte)
    time_int = int(time.time())
    self.__seed_int(time_int)
def __seed_int(self, n):
    self.__seed_byte(n)
    self.__seed_byte(n >> 8)
    self.__seed_byte(n >> 16)
    self.__seed_byte(n >> 24)
def __seed_byte(self, n):
    self.pool[self.pool_pointer] ^= n & 255
    self.pool_pointer += 1
    if self.pool_pointer >= self.POOL_SIZE:
        self.pool_pointer = 0

输入种子

这里我们首先放置一个时间戳,然后输入字符串。

def seed_input(self, str_input):
    time_int = int(time.time())
    self.__seed_int(time_int)
    for char in str_input:
        char_code = ord(char)
        self.__seed_byte(char_code)

生成私钥

这部分可能看起来很难,但实际上非常简单。

首先,我们需要使用我们的池生成32字节的数字。不幸的是,我们不能只创建自己的 random对象,只能用于密钥生成。相反,有一个共享对象,由在一个脚本中运行的任何代码使用。

这对我们意味着什么?

这意味着在每个时刻,代码中的任何地方,一个简单的 random.seed(0) 都可以破坏我们收集的所有熵。我们不希望这样。值得庆幸的是,Python提供了 getstate 和 setstate 方法。因此,为了在每次生成密钥时保存我们的熵,我们记住我们停下来的状态,并在下次我们想要创建密钥时设置它。

其次,我们只确保我们的键在范围内(1, CURVE_ORDER )。这是所有ECDSA私钥的要求。 CURVE_ORDER 是secp256k1曲线的顺序,它是 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 。

最后,为方便起见,我们转换为十六进制,并剥离 '0x' 部分。

def generate_key(self):
    big_int = self.__generate_big_int()
    big_int = big_int % (self.CURVE_ORDER — 1) # key < curve order
    big_int = big_int + 1 # key > 0
    key = hex(big_int)[2:]
    return key
def __generate_big_int(self):
    if self.prng_state is None:
    seed = int.from_bytes(self.pool, byteorder=’big’, signed=False)
    random.seed(seed)
    self.prng_state = random.getstate()
    random.setstate(self.prng_state)
    big_int = random.getrandbits(self.KEY_BYTES * 8)
    self.prng_state = random.getstate()
    return big_int

行动

我们试着使用这个库。实际上,它非常简单:你可以使用三行代码生成私钥!

kg = KeyGenerator()
kg.seed_input(‘Truly random string. I rolled a dice and got 4.’)
kg.generate_key()
# 60cf347dbc59d31c1358c8e5cf5e45b822ab85b79cb32a9f3d98184779a9efc2

你可以自己看看。密钥是随机的,完全有效。而且,每次运行此代码时,都会得到不同的结果。

结论

如你所见,有很多方法可以生成私钥。它们的简单性和安全性不同。

生成私钥只是第一步。下一步是提取可用于接收付款的公钥和钱包地址。

self.pool, byteorder=’big’, signed=False)
    random.seed(seed)
    self.prng_state = random.getstate()
    random.setstate(self.prng_state)
    big_int = random.getrandbits(self.KEY_BYTES * 8)
    self.prng_state = random.getstate()
    return big_int

行动

我们试着使用这个库。实际上,它非常简单:你可以使用三行代码生成私钥!

kg = KeyGenerator()
kg.seed_input(‘Truly random string. I rolled a dice and got 4.’)
kg.generate_key()
# 60cf347dbc59d31c1358c8e5cf5e45b822ab85b79cb32a9f3d98184779a9efc2

你可以自己看看。密钥是随机的,完全有效。而且,每次运行此代码时,都会得到不同的结果。

结论

如你所见,有很多方法可以生成私钥。它们的简单性和安全性不同。

生成私钥只是第一步。下一步是提取可用于接收付款的公钥和钱包地址。

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/ajian6/article/details/94645024
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-06-12 19:33:10
  • 阅读 ( 1174 )
  • 分类:区块链

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢