Ako vygenerovať svoj vlastný bitcoinový súkromný kľúč

V kryptomenách umožňuje súkromný kľúč používateľovi získať prístup do svojej peňaženky. Osoba, ktorá drží súkromný kľúč, plne ovláda mince v tejto peňaženke. Z tohto dôvodu by ste to mali tajiť. A ak naozaj chcete vygenerovať kľúč sami, má zmysel ho vygenerovať bezpečným spôsobom.

Tu poskytnem úvod do súkromných kľúčov a ukážem vám, ako si môžete vygenerovať vlastný kľúč pomocou rôznych kryptografických funkcií. Poskytnem popis algoritmu a kódu v Pythone.

Musím vygenerovať súkromný kľúč?

Väčšinou nie. Napríklad, ak používate webovú peňaženku ako Coinbase alebo Blockchain.info, vytvoria a spravujú súkromný kľúč za vás. Rovnako je to aj pri výmenách.

Mobilné a stolové peňaženky pre vás zvyčajne tiež generujú súkromný kľúč, aj keď môžu mať možnosť vytvoriť si peňaženku z vlastného súkromného kľúča.

Prečo to teda vôbec generovať? Tu sú dôvody, ktoré mám:

  • Chcete sa ubezpečiť, že nikto nepozná kľúč
  • Iba sa chcete dozvedieť viac o kryptografii a generovaní náhodných čísel (RNG)

Čo je to vlastne súkromný kľúč?

Súkromný kľúč pre bitcoin (a mnoho ďalších kryptomien) je formálne séria 32 bajtov. Teraz existuje veľa spôsobov, ako tieto bajty zaznamenať. Môže to byť reťazec 256 jednotiek a nuly (32 * 8 = 256) alebo 100 kociek kocky. Môže to byť binárny reťazec, reťazec Base64, kľúč WIF, mnemotechnická fráza alebo nakoniec hexadecimálny reťazec. Pre naše účely použijeme hexadecimálny reťazec dlhý 64 znakov.

Prečo presne 32 bajtov? Skvelá otázka! Uvidíte, že na vytvorenie verejného kľúča zo súkromného používa bitcoin ECDSA alebo algoritmus digitálneho podpisu eliptickej krivky. Konkrétnejšie používa jednu konkrétnu krivku nazvanú secp256k1 .

Teraz má táto krivka rádovo 256 bitov, ako vstup berie 256 bitov a na výstupe má 256-bitové celé čísla. A 256 bitov je presne 32 bajtov. Aby sme to povedali inak, potrebujeme 32 bajtov údajov, aby sme sa mohli napájať do tohto algoritmu krivky.

Pre súkromný kľúč existuje ďalšia požiadavka. Pretože používame ECDSA, kľúč by mal byť kladný a mal by byť menší ako poradie krivky. Poradie secp256k1 je FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141, čo je dosť veľké: takmer akékoľvek 32-bajtové číslo bude menšie ako toto.

Naivná metóda

Ako teda vygenerujeme 32-bajtové celé číslo? Prvá vec, ktorá vás napadne, je iba použitie knižnice RNG vo vašom jazyku, ktorý si vyberiete. Python dokonca poskytuje roztomilý spôsob generovania dostatočného množstva bitov:

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

Vyzerá dobre, ale v skutočnosti to tak nie je. Vidíte, bežné knižnice RNG nie sú určené na kryptografiu, pretože nie sú príliš bezpečné. Generujú čísla na základe počiatočnej hodnoty a predvolene je počiatočnou hodnotou aktuálny čas. Takto, ak viete, kedy som generoval vyššie uvedené bity, stačí, ak použijete hrubou silou niekoľko variantov.

Pri generovaní súkromného kľúča chcete byť mimoriadne zabezpečení. Pamätajte, že ak sa niekto naučí súkromný kľúč, môže ľahko ukradnúť všetky mince z príslušnej peňaženky a vy už nemáte šancu ich získať späť.

Skúsme to teda urobiť bezpečnejšie.

Kryptograficky silný RNG

Spolu so štandardnou metódou RNG programovacie jazyky zvyčajne poskytujú RNG špeciálne navrhnutý pre kryptografické operácie. Táto metóda je zvyčajne oveľa bezpečnejšia, pretože čerpá entropiu priamo z operačného systému. Výsledok takéhoto RNG sa reprodukuje oveľa ťažšie. Nemôžete to urobiť tak, že budete poznať generačný čas alebo mať semeno, pretože tu nie je semeno. Prinajmenšom používateľ nezadá semeno - skôr ho vytvára program.

V Pythone je v secretsmodule implementovaný kryptograficky silný RNG . Upravme vyššie uvedený kód, aby bolo zabezpečené generovanie súkromného kľúča!

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

To je úžasné. Stavím sa, že by ste to nedokázali reprodukovať, dokonca ani s prístupom k môjmu počítaču. Môžeme však ísť hlbšie?

Špecializované stránky

Existujú stránky, ktoré pre vás generujú náhodné čísla. Budeme tu uvažovať iba o dvoch. Jedným z nich je random.org, známy generátor náhodných čísel na všeobecné účely. Ďalšou z nich je bitaddress.org, ktorá je navrhnutá špeciálne pre generovanie súkromného kľúča bitcoinu.

Môže nám random.org pomôcť vygenerovať kľúč? Rozhodne, pretože majú službu generovania náhodných bajtov. Tu však nastávajú dva problémy. Random.org tvrdí, že je skutočne náhodným generátorom, ale môžete mu dôverovať? Môžete si byť istí, že je to skutočne náhodné? Môžete si byť istí, že vlastník nezaznamená všetky výsledky generovania, najmä tie, ktoré vyzerajú ako súkromné ​​kľúče? Odpoveď je na vás. Oh, a nemôžete to spustiť lokálne, čo je ďalší problém. Táto metóda nie je stopercentne bezpečná.

Teraz je bitaddress.org úplne iný príbeh. Je to open source, takže môžete vidieť, čo sa nachádza pod jeho kapotou. Je na strane klienta, takže si ho môžete stiahnuť a spustiť lokálne, a to aj bez pripojenia k internetu.

Ako to teda funguje? Používa vás - áno, vy - ako zdroj entropie. Žiada vás, aby ste pohli myšou alebo stlačili náhodné klávesy. Robíte to dosť dlho, aby bolo nemožné reprodukovať výsledky.

Zaujíma vás, ako funguje bitaddress.org? Na vzdelávacie účely sa pozrieme na jeho kód a pokúsime sa ho reprodukovať v jazyku Python.

Krátka poznámka: bitaddress.org vám poskytuje súkromný kľúč v komprimovanom formáte WIF, ktorý sa blíži formátu WIF, o ktorom sme hovorili predtým. Pre naše účely urobíme, aby algoritmus vrátil hexadecimálny reťazec, aby sme ho mohli neskôr použiť na generovanie verejného kľúča.

Bitaddress: špecifiká

Bitaddress vytvára entropiu v dvoch formách: pohybom myši a stlačením klávesu. Povieme si o oboch, ale zameriame sa na stlačenie klávesov, pretože je ťažké implementovať sledovanie myši v jazyku Python lib. Očakávame, že koncový užívateľ bude písať tlačidlá, kým nebudeme mať dostatok entropie, a potom vygenerujeme kľúč.

Bitaddress robí tri veci. Inicializuje bajtové pole, snaží sa získať z vášho počítača čo najviac entropie, vyplní pole vstupom používateľa a potom vygeneruje súkromný kľúč.

Bitaddress používa na ukladanie entropie 256-bajtové pole. Toto pole sa prepisuje v cykloch, takže pri prvom vyplnení poľa sa ukazovateľ vynuluje a proces vyplňovania sa začne znova.

The program initiates an array with 256 bytes from window.crypto. Then, it writes a timestamp to get an additional 4 bytes of entropy. Finally, it gets such data as the size of the screen, your time zone, information about browser plugins, your locale, and more. That gives it another 6 bytes.

After the initialization, the program continually waits for user input to rewrite initial bytes. When the user moves the cursor, the program writes the position of the cursor. When the user presses buttons, the program writes the char code of the button pressed.

Finally, bitaddress uses accumulated entropy to generate a private key. It needs to generate 32 bytes. For this task, bitaddress uses an RNG algorithm called ARC4. The program initializes ARC4 with the current time and collected entropy, then gets bytes one by one 32 times.

This is all an oversimplification of how the program works, but I hope that you get the idea. You can check out the algorithm in full detail on Github.

Doing it yourself

For our purposes, we’ll build a simpler version of bitaddress. First, we won’t collect data about the user’s machine and location. Second, we will input entropy only via text, as it’s quite challenging to continually receive mouse position with a Python script (check PyAutoGUI if you want to do that).

That brings us to the formal specification of our generator library. First, it will initialize a byte array with cryptographic RNG, then it will fill the timestamp, and finally it will fill the user-created string. After the seed pool is filled, the library will let the developer create a key. Actually, they will be able to create as many private keys as they want, all secured by the collected entropy.

Initializing the pool

Here we put some bytes from cryptographic RNG and a timestamp. __seed_int and __seed_byte are two helper methods that insert the entropy into our pool array. Notice that we use 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

Seeding with input

Here we first put a timestamp and then the input string, character by character.

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)

Generating the private key

This part might look hard, but it’s actually very simple.

First, we need to generate 32-byte number using our pool. Unfortunately, we can’t just create our own random object and use it only for the key generation. Instead, there is a shared object that is used by any code that is running in one script.

What does that mean for us? It means that at each moment, anywhere in the code, one simple random.seed(0) can destroy all our collected entropy. We don’t want that. Thankfully, Python provides getstate and setstate methods. So, to save our entropy each time we generate a key, we remember the state we stopped at and set it next time we want to make a key.

Second, we just make sure that our key is in range (1, CURVE_ORDER). This is a requirement for all ECDSA private keys. The CURVE_ORDER is the order of the secp256k1 curve, which is FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141.

Finally, for convenience, we convert to hex, and strip the ‘0x’ part.

def generate_key(self): big_int = self.__generate_big_int() big_int = big_int % (self.CURVE_ORDER — 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

In action

Let’s try to use the library. Actually, it’s really simple: you can generate a private key in three lines of code!

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

You can see it yourself. The key is random and totally valid. Moreover, each time you run this code, you get different results.

Conclusion

As you can see, there are a lot of ways to generate private keys. They differ in simplicity and security.

Generating a private key is only a first step. The next step is extracting a public key and a wallet address that you can use to receive payments. The process of generating a wallet differs for Bitcoin and Ethereum, and I plan to write two more articles on that topic.

If you want to play with the code, I published it to this Github repository.

Robím kurz kryptomien tu na stránkach freeCodeCamp News. Prvá časť je podrobným popisom blockchainu.

Na Twitter tiež zverejňujem náhodné myšlienky o kryptomene, takže si to možno budete chcieť pozrieť.