Sådan genereres din helt egen Bitcoin private nøgle

I kryptokurver giver en privat nøgle brugeren adgang til deres tegnebog. Den person, der har den private nøgle, kontrollerer fuldt ud mønterne i den tegnebog. Af denne grund skal du holde det hemmeligt. Og hvis du virkelig vil generere nøglen selv, er det fornuftigt at generere den på en sikker måde.

Her vil jeg give en introduktion til private nøgler og vise dig, hvordan du kan generere din egen nøgle ved hjælp af forskellige kryptografiske funktioner. Jeg vil give en beskrivelse af algoritmen og koden i Python.

Skal jeg generere en privat nøgle?

Det meste af tiden gør du det ikke. For eksempel, hvis du bruger en web-tegnebog som Coinbase eller Blockchain.info, opretter og administrerer de den private nøgle for dig. Det er det samme for udvekslinger.

Mobil- og desktop-tegnebøger genererer normalt også en privat nøgle til dig, selvom de muligvis har mulighed for at oprette en tegnebog fra din egen private nøgle.

Så hvorfor generere det alligevel? Her er de grunde, jeg har:

  • Du vil sikre dig, at ingen kender nøglen
  • Du vil bare lære mere om kryptografi og generering af tilfældigt tal (RNG)

Hvad er en privat nøgle nøjagtigt?

Formelt er en privat nøgle til Bitcoin (og mange andre kryptovalutaer) en serie på 32 byte. Nu er der mange måder at optage disse bytes på. Det kan være en streng på 256 ener og nuller (32 * 8 = 256) eller 100 terningkast. Det kan være en binær streng, Base64-streng, en WIF-nøgle, mnemonic-sætning eller endelig en hex-streng. Til vores formål bruger vi en 64 tegn lang hex streng.

Hvorfor netop 32 byte? Fantastisk spørgsmål! For at oprette en offentlig nøgle fra en privat bruger Bitcoin ECDSA eller Elliptic Curve Digital Signature Algorithm. Mere specifikt bruger den en bestemt kurve kaldet secp256k1 .

Nu har denne kurve en rækkefølge på 256 bit, tager 256 bit som input og udsender 256-bit heltal. Og 256 bits er nøjagtigt 32 byte. Så for at sige det på en anden måde har vi brug for 32 byte data for at føje til denne kurvealgoritme.

Der er et yderligere krav til den private nøgle. Fordi vi bruger ECDSA, skal nøglen være positiv og være mindre end kurvens rækkefølge. Rækkefølgen af ​​secp256k1 er FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141, hvilket er ret stort: ​​næsten ethvert 32-byte nummer vil være mindre end det.

Naiv metode

Så hvordan genererer vi et 32-byte heltal? Den første ting, der kommer til at tænke på, er at bruge et RNG-bibliotek på dit valgte sprog. Python giver endda en sød måde at generere lige nok bits på:

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

Ser godt ud, men faktisk er det ikke. Ser du, normale RNG-biblioteker er ikke beregnet til kryptografi, da de ikke er særlig sikre. De genererer tal baseret på et frø, og som standard er frøet det aktuelle tidspunkt. På den måde, hvis du ved omtrent, hvornår jeg genererede bitene ovenfor, er alt hvad du skal gøre, at brute-force et par varianter.

Når du genererer en privat nøgle, vil du være ekstremt sikker. Husk, at hvis nogen lærer den private nøgle, kan de nemt stjæle alle mønterne fra den tilsvarende tegnebog, og du har ingen chance for nogensinde at få dem tilbage.

Så lad os prøve at gøre det mere sikkert.

Kryptografisk stærk RNG

Sammen med en standard RNG-metode giver programmeringssprog normalt en RNG, der er specielt designet til kryptografiske operationer. Denne metode er normalt meget mere sikker, fordi den trækker entropi direkte fra operativsystemet. Resultatet af en sådan RNG er meget sværere at reproducere. Du kan ikke gøre det ved at kende generationens tid eller have frøet, fordi der ikke er noget frø. Nå, i det mindste indtaster brugeren ikke et frø - snarere er det oprettet af programmet.

I Python implementeres kryptografisk stærk RNG i secretsmodulet. Lad os ændre koden ovenfor for at gøre genereringen af ​​den private nøgle sikker!

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

Det er fantastisk. Jeg vedder på, at du ikke ville være i stand til at reproducere dette, selv med adgang til min pc. Men kan vi gå dybere?

Specialiserede websteder

Der er sider, der genererer tilfældige tal til dig. Vi vil kun overveje to her. Den ene er random.org, en velkendt generator for tilfældige tal til generelle formål. En anden er bitaddress.org, som er designet specielt til generering af private private nøgler.

Kan random.org hjælpe os med at generere en nøgle? Absolut, da de har service til at generere tilfældige byte. Men der opstår to problemer her. Random.org hævder at være en virkelig tilfældig generator, men kan du stole på det? Kan du være sikker på, at det faktisk er tilfældigt? Kan du være sikker på, at ejeren ikke registrerer alle generationsresultater, især dem der ligner private nøgler? Svaret er op til dig. Åh, og du kan ikke køre det lokalt, hvilket er et yderligere problem. Denne metode er ikke 100% sikker.

Nu er bitaddress.org en helt anden historie. Det er open source, så du kan se, hvad der er under emhætten. Det er klientsiden, så du kan downloade det og køre det lokalt, selv uden en internetforbindelse.

Så hvordan fungerer det? Det bruger dig - ja, du - som en kilde til entropi. Det beder dig om at flytte musen eller trykke på tilfældige taster. Du gør det længe nok til at gøre det umuligt at gengive resultaterne.

Er du interesseret i at se, hvordan bitaddress.org fungerer? Til uddannelsesmæssige formål vil vi se på dens kode og forsøge at gengive den i Python.

Hurtig note: bitaddress.org giver dig den private nøgle i et komprimeret WIF-format, der er tæt på WIF-formatet, som vi diskuterede før. Til vores formål får vi algoritmen til at returnere en hex-streng, så vi senere kan bruge den til generering af en offentlig nøgle.

Bitadresse: detaljerne

Bitaddress skaber entropien i to former: ved musebevægelse og ved nøgletryk. Vi taler om begge dele, men vi fokuserer på tastetryk, da det er svært at implementere musesporing i Python lib. Vi forventer, at slutbrugeren skriver knapper, indtil vi har nok entropi, og derefter genererer vi en nøgle.

Bitaddress gør tre ting. Det initialiserer byte-array, forsøger at få så meget entropi som muligt fra din computer, det fylder arrayet med brugerindgangen, og derefter genererer det en privat nøgle.

Bitaddress bruger 256-byte-array til at gemme entropi. Denne matrix omskrives i cyklusser, så når matrixen udfyldes for første gang, går markøren til nul, og processen med udfyldning starter igen.

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.

Jeg laver et kursus om kryptokurver her på freeCodeCamp News. Den første del er en detaljeret beskrivelse af blockchain.

Jeg sender også tilfældige tanker om krypto på Twitter, så du vil måske tjekke det ud.