Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
rusldv
7 лет назад

Основы блкчейн разработки: Генерация ключей, Эллиптическая кривая с примерами на Node.js

Основы блкчейн разработки: Генерация ключей, Эллиптическая кривая с примерами на Node.js



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

Njde.js в качестве среды разработке я выбрал, поскольку считаю, что примеры будут выглядить более наглядно, а также на голосе есть достаточно много уроков по JavaScript - языку программирования, лежащему в основе Node.

Введение

Особеностью блокчейна является его децентрализованность, а безопасность переводов - криптографией.
На самом деле: для генерации адреса кошелька вовсе не обязательно подключаться к интернету. Более того, создание транзакции для перевода средств (токенов) может происходить локально. Однако, чтобы транзакцию все же проверили узлы, а также
могли добавить ее в блокчейн (распределенную базу транзакций), подключиться к интернету все же придется. Об этом поговорим позже.

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

В основе лежит криптография с открытым ключем, рассмотренная мною ранее тут.
Но как же это происходит математически? Давайте рассмотрим это по-порядку.

Генерация закрытого ключа

Для создания открытого ключа, в первую очередь нам понадобиться закрытый, так как открытый создается на его основе, и в последствии из закрытого всегда можно воссоздать открытый.
Что же из себя представляет закрытый ключ. А простое число. Например в Биткоин это 256-битное число (состоящие из произвольной последовательности 0 и 1). На самом деле оно чуть меньше - так как должно проектироваться на эллиптической кривой
(см.
далее).

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

Для примера мы будем использовать встроенную по умолчанию в Node.js библиотеку crypto, которая основывается на распространенной C библиотеке OpenSSL.
Вот таким вот образом, используя Node можно сгенерировать подобный закрытый ключ:

const crypto = require('crypto');

const rand = crypto.randomBytes(256);
console.log(rand.toString('hex'));

Мы добавляем к буферу rand функцию toString('hex'), чтобы посмотреть свое число в виде 16-ричного хеша, а не бинарного буфера.
Выглядит оно может, например так:

252e0d0354426c445496888c72876a0de48e88928f5390d73dbfbee02e87b2a511d5fe62dfbc18b0537430ec760c667197a80b8c1ce883c1dfb27f1ab514a47c9afff028415ebd554e7fd527cf8d1c169146f920edabde56a38e74ad3214e9a4eb83a50459eed17039766e550cf182f898edd1
ab3d93ecd4e0c094cdac2c4db925f2af3622bcbca7dfd4da4e4b21e1d6da486d6e57abbb7f8e5b5be61e50592019122f3c0d9398cfba2a4910b264e8b0cdd4f525bc5ffadb68d4453502f835c8eab7a803d156645b4fe8e30b94e221b2a8c2cb9c7383664a319e6fcee6177aa82d28c10acdd4
7aa83ebd6ec087bb25ab7d57e694ea143127e5419ad94527784d

Число не самое, коротенькое, а в знакомой всем 10-ной системе счисления оно и того длиннее, поэтому его часто кодируют дополнительно в base64, или в Биткоин в base58 (мы о них также вскоре поговорим). Этот способ кодирования предоставляет
большее сжатье, так как в отличии от 16-ричного представления имеет больше символов, а именно помимо цифр еще и прописные и заглавные буквы английского алфавита. Однако вне зависимости от того как число закодировано, оно одно и тоже.

Генерация открытого ключа

Давайте теперь выясним, как же из этого числа появляется открытый (публичный) ключ

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


картинка

Нам достаточно иметь представление, что эллиптическая кривая это не просто визуально отчерченная на оси координат симметричная дугообразная ваза, а функция, которая позволяет производить расчеты на этой кривой.
Формально дуга эта состоит из огромного количества точек, которые расположены на оси координат, а следоватенльно, каждая точка может быть представлена координатами (x, y). Так как точек много, то и числа эти достаточно большие.
По оси Х кривая симметрична, благодаря чему точка может быть расчитана по некоторому уровнению, что позволяет сжимать публичный ключ, до числа - значения одной координаты Х.
Пока давайте остановимся на процессе создания открытого ключа:

Сперва нам нужно выбрать какую-нибудь точку на эллиптической кривой. Как правило какую именно не так уж и важно, поэтому используют точки из спецификации, предложенной специальным стандартизирующим агенством, и в Биткоин, н6апример, эта
тогчка определена заранее. Нам нужно лишь знать название кривой, чтобы просмотреть их полный доступный в вашей системе список, пишем и запускаем следующий код:

const crypto = require('crypto');

console.log(crypto.getCurves());

В Биткоин используется кривая secp256k1

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

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

Однако эти координаты х и y представляют достаточно длинное число (по 256 бит каждая координата), а так как кривая семмитрична, мы можем отбросить значение по оси Y, сохранив только знак (выше нуля или ниже) по оси X.

Это вычисляется по специальной формуле, которая встроена в ПО кошелька. Таким образом мы получаем готкрытый ключ в виде координаты Х со знаком. В двоичной системе знак определяется четностью первого разряда (бита), для того, чтобы добавить
информацию о четности к публичному ключу, используются обозначения 02 и 03 перед координатой х для обозначения нечетности или четности соответственно (т.е знака координаты по оси Х).

Посмотрим, как мы можем реализовать подобное, с помощью Node.js:

const crypto = require('crypto');

// Задаем эллиптическую кривую
const ecdh = crypto.createECDH('secp256k1');

// Генерируем пару ключей - открытый и закрытый
const keys = ecdh.generateKeys('hex'); 

console.log('+++ ecdh +++');

// Результат работы generateKeys() - это публичный ключ (координаты X и Y на эллиптической кривой, в формате 04xy)
console.log(keys); // PubK: 04cc50bd2a7a377cdd8bfa70a2f3286b7492645838970773e3c2f82203e0e42adeab7df5d975768c1ad4c1e8b1839317fd5fc79d6cd03a8a5cd9c93088b0825fc1
// Можно получить сжатый публичный ключ о котором шла речь выше
console.log(ecdh.getPublicKey('hex', 'compressed')); // 03cc50bd2a7a377cdd8bfa70a2f3286b7492645838970773e3c2f82203e0e42ade

console.log('____');

// Функция getPublicKey() возвращает тот же публичный ключ
console.log('Public Key: ');
console.log(ecdh.getPublicKey('hex')); // 04cc50bd2a7a377cdd8bfa70a2f3286b7492645838970773e3c2f82203e0e42adeab7df5d975768c1ad4c1e8b1839317fd5fc79d6cd03a8a5cd9c93088b0825fc1

// А с помощью getPrivateKey() мы можем получить сгенерированный соответствующий приватный ключ
console.log('Private Key: ');
console.log(ecdh.getPrivateKey('hex')); // 6c9c783b7e0af29a6a47fb391bf5092211d66e54333f68008612f40c0af538a1

Здесь в const keys = ecdh.generateKeys('hex'); мы генерируем в том числе и приватный ключ. Однако можем задать свой, сгенерированный ранее, для получения соответствующего публичного с помощью setPrivateKey():
Я задал приватный ключ из предыдущего примера, чтобы показать, что оинаковому приватному ключу (числу) всегда будет соответствовать одинаковый публичный (если мы конечно не изменим точку генерации на эллиптической кривой).

const crypto = require('crypto');
const ecdh = crypto.createECDH('secp256k1');
ecdh.setPrivateKey('6c9c783b7e0af29a6a47fb391bf5092211d66e54333f68008612f40c0af538a1', 'hex');
console.log('+++ ecdh +++');

// PubK: 04cc50bd2a7a377cdd8bfa70a2f3286b7492645838970773e3c2f82203e0e42adeab7df5d975768c1ad4c1e8b1839317fd5fc79d6cd03a8a5cd9c93088b0825fc1
console.log('pubkey:');
console.log(ecdh.getPublicKey('hex'));
console.log('privkey:');
console.log(ecdh.getPrivateKey('base64')); 
// bJx4O34K8ppqR/s5G/UJIhHWblQzP2gAhhL0DAr1OKE=
// 6c9c783b7e0af29a6a47fb391bf5092211d66e54333f68008612f40c0af538a1

`
Необходимо также иметь ввиду, что адреса - не тоже самое, что публичные ключи. Например Биткоин-адреса - это кодированные в Base58Check открытые ключи, к тому же они разные для сжатых и несжатых публичных ключах, хотя и соответствуют
одному и тому же закрытому ключу.

Ну а на сегодня пока, что хватит :)

108
0.548 GOLOS
На Golos с November 2016
Комментарии (3)
Сортировать по:
Сначала старые