Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python 2.7 Converting Bitcoin Privkey into WIF Privkey

I just went through a tutorial as a coding newbie. The tutorial was this one: https://www.youtube.com/watch?v=tX-XokHf_nI. And I wanted to generate my Bitcoin addresses/privkeys with 1 single easy to read (not cryptic) Python file - just in the style the code is written right now.

The tutorial got to the part where I got the Bitcoin address starting with a "1" but not the privkey starting with a "5". Plus I am missing how to BIP38 encrypt the private key (starting with a "6"). It's as you can see for the main Bitcoin network.

Was using https://en.bitcoin.it/wiki/Wallet_import_format as a step by step guide after the tutorial. In the end I commented out my tries to do it myself because it all was rubbish. (The part with "SHA256 HASHED EXTENDED PRIVATE KEY THIS IS WRONG ON SO MANY LEVELS") I think the part where I added the 80 bytes to the Private Key could be correct.

PS: I'm using a static private key for now until everything works, which is why I commented out the non-static private key part. It has been generated via the part I commented out "non static private key usage". I also commented out the signed message code lines (at the bottom of the code) because they were shown in the tutorial, not important for the key/address generating. I also tried to "beautify" the code a bit by putting prints etc. only at the bottom of the file and sorting things a bit different etc. but it turned out Python 2.7 didn't like that.

I am using Python 2.7, installed everything successfully, the code is working as it should right now with the commented out parts. I verified the results it printed with bitaddress.org, just like the uploader from the tutorial did. Tried searching to find a solution, but I could not get anything useful out of my search results.

If you could help me out with the few missing lines of code, I'd be happy! Also maybe explain / comment in the code what does what. Especially for the yet missing BIP38 Privkey password encryption. So I can see what is what and can understand.

Running the .py script returns valid results, except for the 80 bytes I added - no Idea if this has been done correct by me. Adding 80 bytes is a needed step for getting the final private key starting with a "5" later.

Running it prints:

This is my Private Key: 29a59e66fe370e901174a1b8296d31998da5588c7e0dba860f11d65a3adf2736
This is my 80 Byte Private Key: 8029a59e66fe370e901174a1b8296d31998da5588c7e0dba860f11d65a3adf2736
This is my Public Key: 04d370b77a4cf0078ab9e0ba3c9e78e8dd87cc047fa58d751b3719daa29ac7fbf2c3ba8338f9a08f60a74a5d3a2d10f26afa2f703b8c430eecad89d59a9df00ec5
This is my Bitcoin Address: 1B3wS8dQHtfMpFMSmtT5Fy4kHCYvxejtVo

Here you can see my code, commented here and there as good as I could according to the tutorial: (Forgot to comment out the "This is my hashed ext priv key checksum" part, sorry for confusion. This is the code I need help now with.)

import os
import ecdsa
import hashlib
import base58

##  STATIC KEY USAGE
private_key_static = "29a59e66fe370e901174a1b8296d31998da5588c7e0dba860f11d65a3adf2736"
##  PRINTOUT FROM STATIC PRIVATE KEY
print "This is my Private Key: " + private_key_static

## NON STATIC PRIVATE KEY USAGE
#private_key = os.urandom(32).encode("hex")
#print "this is my private key: " + private_key

##  80-BYTE EXTENDED PRIVATE KEY
private_key_plus_80byte = (('80') + private_key_static)

##  PRINTOUT 80-BYTE EXTENDED PRIVATE KEY
print "This is my 80 Byte Private Key: " + private_key_plus_80byte

## SHA256 HASHED EXTENDED PRIVATE KEY
## THIS IS WRONG ON SO MANY LEVELS
#hashed_ext_priv_key_checksum = hashlib.sha256(hashlib.sha256(private_key_plus_80byte).digest()).digest()[:4]
#hashed_ext_priv_key_checksum = hashed_ext_priv_key_checksum.decode("hex")
#print "This is my hashed ext priv key checksum: " + hashed_ext_priv_key_checksum

##  PRIVATE! SIGNING KEY ECDSA.SECP256k1
sk = ecdsa.SigningKey.from_string(private_key_static.decode("hex"),
                        curve = ecdsa.SECP256k1)

##  PUBLIC! VERIFYING KEY (64 BYTE LONG, MISSING 04 BYTE AT THE BEGINNING)
vk = sk.verifying_key

##  PUBLIC KEY
public_key = ('\04' + vk.to_string()).encode("hex")
##  PRINTOUT PUBLIC KEY
print "This is my Public Key: " + public_key

##  PUBLIC KEY ENCODING (2x RIPEMD160)
ripemd160 = hashlib.new('ripemd160')

ripemd160.update(hashlib.sha256(public_key.decode('hex')).digest())

middle_man = ('\00') + ripemd160.digest()

checksum = hashlib.sha256(hashlib.sha256(middle_man).digest()).digest()[:4]

binary_addr = middle_man + checksum

addr = base58.b58encode(binary_addr)

print "This is my Bitcoin Address: " + addr

##  MESSAGE CONTENT
#msg = "hello world"

##  SIGN MESSAGE CONTENT
#signed_msg = sk.sign(msg)

##  VERIFY MESSAGE CONTENT
#assert vk.verify(signed_msg, "hello world")

##  PRINTOUT SIGNED MESSAGE ENCODED TO HEX
#print "This is a HEX encoded signed Message: " + signed_msg.encode("hex")
like image 899
BloodyPythonNewbie Avatar asked Jul 15 '17 04:07

BloodyPythonNewbie


1 Answers

What you probably misunderstood from the Bitcoin Wiki's steps is that all the hashing and stuff must be done on the keys as bytes, not as strings.

This means that if you want to derive the WIF key from your Private key "29a59..." you don't have to hash the string "8029a59..." but the binary data that corresponds to it instead.

Here the missing snippet that works

# importing binascii to be able to convert hexadecimal strings to binary data
import binascii

# Step 1: here we have the private key
private_key_static = "29a59e66fe370e901174a1b8296d31998da5588c7e0dba860f11d65a3adf2736"
# Step 2: let's add 80 in front of it
extended_key = "80"+private_key_static
# Step 3: first SHA-256
first_sha256 = hashlib.sha256(binascii.unhexlify(extended_key)).hexdigest()
# Step 4: second SHA-256
second_sha256 = hashlib.sha256(binascii.unhexlify(first_sha256)).hexdigest()
# Step 5-6: add checksum to end of extended key
final_key = extended_key+second_sha256[:8]
# Step 7: finally the Wallet Import Format is the base 58 encode of final_key
WIF = base58.b58encode(binascii.unhexlify(final_key))
print (WIF)

where binascii.unhexlify(...) tells us the binary data represented by the hexadecimal string.

The rest of your code works just fine ;)

like image 77
Marco Avatar answered Oct 03 '22 07:10

Marco