Hogyan készíthet saját Bitcoin titkos kulcsot

A kriptovalutákban egy privát kulcs lehetővé teszi a felhasználó számára, hogy hozzáférjen a pénztárcájához. Az a személy, aki a magánkulcsot birtokolja, teljes mértékben ellenőrzi az adott pénztárca érméit. Ezért titkolnia kell. És ha valóban maga akarja előállítani a kulcsot, akkor van értelme biztonságos módon előállítani.

Itt bemutatom a privát kulcsokat, és bemutatom, hogyan hozhatja létre saját kulcsát különféle kriptográfiai funkciók segítségével. Adok egy leírást az algoritmusról és a kódról a Pythonban.

Létre kell hoznom egy magánkulcsot?

Legtöbbször nem. Például, ha olyan webes pénztárcát használ, mint a Coinbase vagy a Blockchain.info, akkor ők létrehozzák és kezelik az Ön számára a privát kulcsot. Ugyanez vonatkozik a cserékre is.

A mobil és asztali pénztárcák általában privát kulcsot is generálnak az Ön számára, bár lehetőségük van arra, hogy pénztárcát hozzanak létre saját privát kulcsukból.

Szóval miért generálja mégis? Itt vannak az okaim:

  • Biztosítani akarja, hogy senki ne tudja a kulcsot
  • Csak többet szeretne megtudni a rejtjelezésről és a véletlenszám-előállításról (RNG)

Mi is az a privát kulcs?

Formálisan a Bitcoin (és sok más kriptovaluta) titkos kulcsa 32 bájtos sorozat. Most sokféleképpen rögzíthetjük ezeket a bájtokat. Ez lehet 256 egyből álló zéró és nulla (32 * 8 = 256) vagy 100 kockadobás. Ez lehet egy bináris karakterlánc, Base64 karakterlánc, egy WIF kulcs, emlékeztető kifejezés vagy végül egy hex karakterlánc. Céljainkhoz 64 karakter hosszú hex sztringet fogunk használni.

Miért pont 32 bájt? Remek kérdés! Látja, hogy egy nyilvános kulcs létrehozásához egy magánkulcsból a Bitcoin az ECDSA -t vagy az elliptikus görbe digitális aláírás algoritmust használja. Pontosabban egy secp256k1 nevű sajátos görbét használ .

Ez a görbe 256 bit nagyságrendű, 256 bitet vesz be bemenetként, és 256 bites egész számokat ad ki. A 256 bit pedig pontosan 32 bájt. Tehát, másképpen fogalmazva, 32 bájt adatra van szükségünk ahhoz, hogy betöltsük ezt a görbe algoritmust.

A privát kulcsra további követelmény vonatkozik. Mivel ECDSA-t használunk, a kulcsnak pozitívnak kell lennie, és kisebbnek kell lennie, mint a görbe sorrendje. A secp256k1 sorrendje megegyezik FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141, ami elég nagy: szinte minden 32 bájtos szám kisebb lesz nála.

Naiv módszer

Szóval, hogyan generálunk egy 32 bájtos egész számot? Az első dolog, ami eszembe jut, hogy csak egy RNG könyvtárat használjon a kívánt nyelven. A Python még egy aranyos módszert is kínál a kellő bitek előállításához:

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

Jól néz ki, de valójában nem az. Látja, a normál RNG könyvtárakat nem titkosításra szánják, mivel nem túl biztonságosak. Számokat generálnak egy mag alapján, és alapértelmezés szerint a mag az aktuális idő. Így, ha hozzávetőlegesen tudja, mikor generáltam a fenti biteket, akkor csak annyit kell tennie, hogy néhány opciót nyers erővel alkalmaz.

Amikor létrehoz egy privát kulcsot, akkor rendkívül biztonságosnak kell lennie. Ne feledje, hogy ha valaki megtanulja a magánkulcsot, könnyen ellophatja az összes érmét a megfelelő pénztárcából, és esélye sincs arra, hogy valaha is visszakapja őket.

Próbáljuk tehát biztonságosabb módon megtenni.

Kriptográfiailag erős RNG

A szabványos RNG módszerrel együtt a programozási nyelvek általában kifejezetten kriptográfiai műveletekhez tervezett RNG-t biztosítanak. Ez a módszer általában sokkal biztonságosabb, mert egyenesen az operációs rendszerből vonja le az entrópiát. Az ilyen RNG eredményét sokkal nehezebb reprodukálni. Nem teheti meg, ha ismeri a keletkezés idejét, vagy megkapja a magot, mert nincs mag. Nos, legalább a felhasználó nem ír be magot - inkább a program hozza létre.

A Pythonban kriptográfiailag erős RNG van megvalósítva a secretsmodulban. Módosítsuk a fenti kódot, hogy biztonságossá tegyük a privát kulcs előállítását!

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

Ez fantasztikus. Fogadok, hogy ezt még PC-jemhez való hozzáféréssel sem tudná reprodukálni. De elmehetünk-e mélyebben?

Speciális oldalak

Vannak olyan webhelyek, amelyek véletlenszerű számokat generálnak az Ön számára. Itt csak kettőt veszünk figyelembe. Az egyik a random.org, egy jól ismert általános célú véletlenszám-generátor. Egy másik a bitaddress.org, amelyet kifejezetten a Bitcoin magánkulcs-generálására terveztek.

Segíthet a random.org kulcs létrehozásában? Határozottan, mivel van szolgáltatásuk véletlenszerű bájtok előállítására. De két probléma merül fel itt. A Random.org azt állítja, hogy valóban véletlenszerű generátor, de bízhat benne? Biztos lehet benne, hogy valóban véletlenszerű? Biztos lehet benne, hogy a tulajdonos nem rögzíti az összes generációs eredményt, különösen azokat, amelyek privát kulcsoknak tűnnek? A válasz rajtad múlik. Ja, és nem futtathatja helyben, ami további probléma. Ez a módszer nem 100% -osan biztonságos.

Most a bitaddress.org egy teljesen más történet. Nyílt forráskódú, így láthatja, mi van a motorháztete alatt. Ügyféloldali, így letöltheti és futtathatja helyben, internetkapcsolat nélkül is.

Tehát hogyan működik? Téged - igen, téged - az entrópia forrásaként használ. Megkéri, hogy mozgassa az egeret, vagy nyomja meg a véletlenszerű gombokat. Elég sokáig csinálod, hogy megvalósíthatatlanná tegye az eredmények reprodukálását.

Érdekli, hogy működik a bitaddress.org? Oktatási célokra megnézzük a kódját, és megpróbáljuk Pythonban reprodukálni.

Rövid megjegyzés: A bitaddress.org a privát kulcsot tömörített WIF formátumban adja meg, amely közel áll ahhoz a WIF formátumhoz, amelyet korábban tárgyaltunk. Céljainkra az algoritmust visszaküldjük egy hex sztringre, hogy később felhasználhassuk azt egy nyilvános kulcs előállításához.

Bitaddress: a sajátosságok

A Bitaddress kétféle formában hozza létre az entrópiát: az egér mozgatásával és a billentyű nyomásával. Beszélünk mindkettőről, de a kulcsnyomásra összpontosítunk, mivel az egérkövetést nehéz megvalósítani a Python lib-ben. Arra számítunk, hogy a végfelhasználó írja be a gombokat, amíg elegendő entrópiánk nem lesz, majd létrehozunk egy kulcsot.

A Bitaddress három dolgot csinál. Inicializálja a bájt tömböt, megpróbálva a lehető legtöbb entrópiát megszerezni a számítógépről, kitölti a tömböt a felhasználó bemenetével, majd létrehoz egy magánkulcsot.

A Bitaddress a 256 bájtos tömböt használja az entrópia tárolására. Ezt a tömböt ciklusokban írják át, így a tömb első kitöltésekor a mutató nullára megy, és a kitöltési folyamat újra kezdődik.

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.

I am making a course on cryptocurrencies here on freeCodeCamp News. The first part is a detailed description of the blockchain.

I also post random thoughts about crypto on Twitter, so you might want to check it out.