Как создать свой собственный частный ключ биткойн

1656588256 kak sozdat svoj sobstvennyj chastnyj klyuch bitkojn

В криптовалютах закрытый ключ позволяет пользователю получить доступ к своему кошельку. Человек, держащий закрытый ключ, полностью контролирует монеты в этом кошельке. По этой причине вы должны держать это в секрете. И если вы действительно хотите сгенерировать ключ самостоятельно, имеет смысл создать его безопасным способом.

Здесь я ознакомлюсь с приватными ключами и покажу вам, как вы можете создать собственный ключ с помощью различных криптографических функций. Я представлю описание алгоритма и кода на Python.

Нужно ли генерировать приватный ключ?

Чаще всего вы этого не делаете. К примеру, если вы используете веб-кошелек, такой как Coinbase или Blockchain.info, они создают частный ключ и управляют им. То же самое для обмена.

Мобильные и настольные кошельки обычно также генерируют приватный ключ для вас, хотя они могут иметь возможность создать кошелек из вашего собственного частного ключа.

Так зачем же его вообще генерировать? Вот причины, которые у меня есть:

  • Вы хотите убедиться, что никто не знает ключа
  • Вы просто хотите узнать больше о криптографии и генерации случайных чисел (RNG)

Что такое частный ключ?

Формально приватный ключ для биткойна (и многих других криптовалют) – это серия из 32 байтов. Сейчас существует множество способов записи этих байтов. Это может быть ряд из 256 единиц и нулей (32*8=256) или 100 бросков кубиков. Это может быть двоичная строка, строка Base64, ключ WIF, мнемоническая фраза или, наконец, шестнадцатеричная строка. Для наших целей мы будем использовать шестнадцатеричную строку длиной 64 символа.

lyrhBKkIKdFsCCrBdSXnaRYJrwj67NUaMXNy
Тот же закрытый ключ, написанный в разных форматах.

Почему именно 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 не предназначены для криптографии, потому что они не очень безопасны. Они генерируют числа на основе начального числа, и по умолчанию начальным является текущее время. Таким образом, если вы примерно знаете, когда я сгенерировал приведенные выше биты, все, что вам нужно сделать, это подготовить несколько вариантов.

Когда вы создаете закрытый ключ, вы хотите быть чрезвычайно защищенным. Помните, если кто узнает секретный ключ, он может легко украсть все монеты из соответствующего кошелька, и у вас нет шансов когда-нибудь вернуть их.

Попробуем сделать это безопаснее.

Криптографически надежный ГСЧ

Наряду со стандартным методом ГСЧ, языки программирования обычно обеспечивают ГСЧ, специально разработанный для криптографических операций. Этот метод, как правило, гораздо более безопасен, поскольку извлекает энтропию непосредственно из операционной системы. Результат такого ГСЧ воспроизвести гораздо труднее. Вы не можете этого сделать, зная время зарождения или имея семена, потому что нет семян. Ну, по крайней мере, пользователь не вводит семена – скорее, они создаются программой.

В 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 – это совсем другая история. Это открытый исходный код, поэтому можно увидеть, что находится под его капотом. Он находится на стороне клиента, поэтому вы можете загрузить его и запустить локально, даже без подключения к Интернету.

Итак, как это работает? Он использует вас – да, вы – как источник энтропии. Он просит переместить мышь или нажать произвольные клавиши. Вы делаете это достаточно долго, чтобы сделать невозможным воспроизведение результатов.

y5eDywvm3A2NMywdEk2u6pQYUORSP42gtWr2
Процесс генерации энтропии путём случайного перемещения мыши. Большая часть символов показывает бассейн.

Вам интересно посмотреть, как работает bitaddress.org? Для обучающих целей мы рассмотрим его код и попытаемся воспроизвести его на Python.

Краткое примечание: bitaddress.org предоставляет вам частный ключ в сжатом формате WIF, который близок к формату WIF, который мы обсуждали ранее. Для наших целей мы заставим алгоритм возвращать шестнадцатеричную строку, чтобы мы могли использовать его позже для генерации открытого ключа.

Битадрес: особенности

Bitaddress создает энтропию в двух формах: движение мыши и нажатие клавиши. Мы обсудим оба, но сосредоточимся на нажатии клавиш, поскольку в библиотеку Python трудно реализовать отслеживание мыши. Мы ожидаем, что конечный пользователь будет вводить кнопки, пока у нас не будет достаточно энтропии, а затем мы сгенерируем ключ.

Bitaddress выполняет три вещи. Он инициализирует массив байтов, пытаясь получить как можно больше энтропии от компьютера, он заполняет массив введенными пользователями, а затем генерирует закрытый ключ.

Bitaddress использует 256-байтный массив для хранения энтропии. Этот массив переписывается циклами, поэтому при первом заполнении массива указатель возвращается на ноль и процесс заполнения начинается снова.

Программа инициирует массив с 256 байтами из window.crypto. Затем он записывает метку времени, чтобы получить дополнительные 4 байта энтропии. Наконец, он получает такие данные, как размер экрана, ваш часовой пояс, информацию о плагинах браузера, ваш язык и т.д. Это дает ему еще 6 б.

После инициализации программа постоянно ожидает ввода пользователя для перезаписи начальных байтов. Когда пользователь перемещает курсор, программа записывает положения курсора. Когда пользователь нажимает кнопку, программа записывает символовый код нажатой кнопки.

Наконец, bitaddress употребляет скопленную энтропию для сотворения личного ключа. Он должен сгенерировать 32 байта. Для этой задачи битадрес использует алгоритм RNG под названием ARC4. Программа инициализирует ARC4 в настоящее время и собранной энтропией, затем получает байты по одному 32 раза.

Это все упрощение работы программы, но я надеюсь, что вы поняли идею. Вы можете полностью ознакомиться с алгоритмом Github.

Делая это самостоятельно

Для наших целей мы построим более простую версию бит-адреса. Во-первых, мы не будем собирать данные о компьютере и местонахождении пользователя. Во-вторых, мы будем вводить энтропию только посредством текста, поскольку постоянно получать позицию мыши с помощью скрипта Python достаточно сложно (проверьте PyAutoGUI, если хотите это сделать).

Это приводит нас к формальной спецификации нашей библиотеки генераторов. Сначала он инициализирует массив байтов с помощью криптографического ГСЧ, затем заполнит метку времени и, наконец, заполнит созданную пользователем строку. После заполнения исходного пула библиотека позволит разработчику создать ключ. На самом деле, они смогут создать столько частных ключей, сколько захотят, и все они будут защищены собранной энтропией.

Инициализация пула

Здесь мы помещаем несколько байтов из криптографического ГСЧ и метку времени. __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. The 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

Вы сами это видите. Ключ случайный и полностью действительный. Более того, каждый раз, когда вы запускаете этот код, вы получаете разные результаты.

Вывод

Как видите, существует множество способов создания частных ключей. Они отличаются простотой и сохранностью.

Создание приватного ключа – это только первый шаг. Следующим шагом является получение открытого ключа и адреса кошелька, которые можно использовать для получения платежей. Процесс создания кошелька отличается Bitcoin и Ethereum, и я планирую написать еще две статьи на эту тему.

Если вы хотите поиграть с кодом, я опубликовал его в этом репозитории Github.

Я делаю курс о криптовалютах здесь на FreeCodeCamp News. Первая часть – это подробное описание блокчейна.

Я также публикую случайные мысли о криптовалюте в Twitter, так что вы можете проверить это.

Добавить комментарий

Ваш адрес email не будет опубликован.