Технология создания биткоин кошелька

В этой статье рассматриваются секреты создания приватных ключей для Биткоина, а также способы получения адреса кошелька. Приводятся практические примеры на языке bash. Статья является результатом анализа информации исходного кода нескольких проектов на GitHub, а также ресурсов зарубежных авторов.

Создание приватного ключа.

Приватный ключ содержит 256 бит (32 байта) значений, начинающихся с 0х1 до 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF BAAE DCE6 AF48 A03B BFD2 5E8C D036 4140. В результате всего может существовать 2^256 или 1.16 x 10^77 приватных ключей.

Для создания приватного ключа используется хэш SHA256. Хэш может быть сгенерирован какой-либо программой либо получен на основе заложенной человеком определенная комбинация символов, которую знает только он. Это делается с целью того, чтобы человек всегда смог восстановить свой приватный ключ из своей памяти. Такие кошельки принято называть Brain Wallets. Т.е. в них заложена какая-то смысловая информация. Но всегда есть риск забыть ее и потерять доступ к своему кошельку. После преобразования смысловой строки в хэш SHA256, на выходе всегда получается срока имеющая 256 битное значение. Учитывая то, что каждые 2 символа представляют собой 8 бит или 1 байт, то в итоговое значение содержит 64 символа.

Ниже показан пример, как исходная строка преобразуется в хэш SHA256 используя bash.

$ echo "this is a group of words .... generate a private key" | openssl sha256
# out: a966eb6058f8ec9f47074a2faadd3dab42e2c60ed05bc34d39d6c0e1d32b8bdf

Создание публичного ключа.

Публичный ключ генерируется из приватного ключа используя эллиптическую кривую secp256k1 по следующей формуле K = k * G, где K — это публичный ключ, k — приватный ключ, а G это константа называемая точкой генератора. Этот алгоритм не имеет обратного преобразования, т.е. нельзя по формуле восстановить приватный ключ имея публичный.

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

На выходе в примере мы получаем открытый ключ «043cba1f4d12d1ce0bced725373769…….dfe47e5dce2e08601d6f11f5a4», который включает префикс 0х04, а также X и Y координаты на элиптической кривой secp256k1 (для наглядности ниже в примере они отмечены).

$ openssl ec -inform DER -text -noout -in <(cat <(echo -n "302e0201010420") <(echo -n "a966eb6058f8ec9f47074a2faadd3dab42e2c60ed05bc34d39d6c0e1d32b8bdf") <(echo -n "a00706052b8104000a") | xxd -r -p) 2>/dev/null | tail -6 | head -5 | sed 's/[ :]//g' | tr -d '\n' && echo

# out:
# PREFIX: 04
# X: 3cba1f4d12d1ce0bced725373769b2262c6daa97be6a0588cfec8ce1a5f0bd09
# Y: 2f56b5492adbfc570b15644c74cc8a4874ed20dfe47e5dce2e08601d6f11f5a4

Получение сжатого публичного ключа.

Большинство кошельков реализуют сжатый публичный ключ для экономии пространства блокчейна. Для конвертации не сжатого публичного ключа в сжатый, вы можете не учитывать Х, потому что его можно расчитать имея значение Y, решив уравнение элиптической кривой Y² = X³ + 7. Префикс 0x02 добавляется для положительных значений Y, а 0x03 для отрицательных. Если последняя двоичная цифра координаты равна 0, то число положительное, а если 1 — отрицательное. В итоге получаем сжатую версию публичного ключа:

02 3cba1f4d12d1ce0bced725373769b2262c6daa97be6a0588cfec8ce1a5f0bd09

Префикс 0x02 добавлен потому, что Y координата содержала в конце значение 0xa4, что говорит о ее положительном значении.

Генерация bitcoin-адреса.

Биткоин использует 2 типа адресов, один из них называется P2SH-P2WPKH (pay-to-script hash & pay-to-witness-public-key-hash) который сейчас исползуется по умолчанию во всех кошельках.

Второй называется P2PKH (Pay to Public Key Hash) и был предшественником первого. Скрипты дают больше функциональности, поэтому они более популярны сейчас.

Получаем хеш публичного адреса.

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

$ echo 023cba1f4d12d1ce0bced725373769b2262c6daa97be6a0588cfec8ce1a5f0bd09 | xxd -r -p | openssl sha256
# out: (stdin)= 8eb001a42122826648e66005a549fc4b4511a7ad3fc378221aa1c73c5efe77ef

$ echo 8eb001a42122826648e66005a549fc4b4511a7ad3fc378221aa1c73c5efe77ef | xxd -r -p | openssl ripemd160
# out: (stdin)= 3a38d44d6a0c8d0bb84e0232cc632b7e48c72e0e

Обратите внимание, т.к. на входе у нас строка, то команда xxd -r -p будет конвертировать hex строку в бинарную а затем выведет ее в ascii, которую openssl ожидает на входе.

Кодирование в base58.

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

Формат адреса P2PKH.

Формат P2PKH адреса Биткоина начинаются со значения байта версии 0х00 и заканчиваются 4 байтовой контрольной суммой. Сначала мы добавляем байт с номером версии к нашему хэшу открытого ключа и вычисляем его. А затем добавляем контрольную сумму, после чего кодируем результат в base58.

$ echo 003a38d44d6a0c8d0bb84e0232cc632b7e48c72e0e | xxd -p -r | base58 -c && echo
# out: 16JrGhLx5bcBSA34kew9V6Mufa4aXhFe9X

Примечание: опция -c означает, что была применена чексумма. Чексумма расчитывается как SHA256(SHA256(prefix+data)) после чего беруется только первые 4 байта и добавляются в конец хеша ключа.

В результате значение адреса P2PKH для Bitcoin примет вид:

19P1LctLQmH6tuHCRkv8QznNBGBvFCyKxi

Формат адреса P2SH-P2WPKH.

Похожие шаги используются для генерации P2SH-P2WPKH адреса за исключением того, что хэш выполняется в сценарии транзакции, вместо открытого ключа.

У Биткоина есть язык сценариев. В основном это позволяет устанавливать требования к сигнатуре для отправки биткоинов, задержки времени перед отправкой и т.д. Обычно используемый сценарий является транзакцией с несколькими сигналами: OP_0 0x14 <PubKey Hash>, где PubKey Hash это RIPEMD160 от SHA256 от открытого ключа (как определялось для P2WPKH) и 0х14 байтов в PubKey Hash. Таким образом, чтобы превратить этот скрипт в адрес, вы просто применяете BASE58CHECK к RIPEMD160 от SHA256 от OP_0 0x14 <PubKey Hash>, за исключением того, что вы добавляете 0х05 к хэш-скрипту вместо 0х00 для обозначения типа адреса P2SH.

$ echo 00143a38d44d6a0c8d0bb84e0232cc632b7e48c72e0e | xxd -r -p | openssl sha256
# out: (stdin)= 1ae968057eaef06c3e13439695edd7a54982fc99f36c3aa41d8cc41340f30195

$ echo 1ae968057eaef06c3e13439695edd7a54982fc99f36c3aa41d8cc41340f30195 | xxd -r -p | openssl ripemd160
# out: (stdin)= 1d521dcf4983772b3c1e6ef937103ebdfaa1ad77

$ echo 051d521dcf4983772b3c1e6ef937103ebdfaa1ad77 | xxd -p -r | base58 -c && echo
# out: 34N3tf5m5rdNhW5zpTXNEJucHviFEa8KEq
Рейтинг
( 5 оценок, среднее 4.2 из 5 )
Понравилась статья? Поделиться с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: