3

So I have already the public key. My question is if I'm applying correctly the b58 encoding, since bitcoin uses b58check, and also adds x00 prefix. Can somebody explain me that? Thanks.

import hashlib
import base58

# ECDSA Public Key
base64_str = 'MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE7P0EE5amecnYKMlq96RUL3Q+mZJCQrta6iHyjQWtsbbgcBMayhR/CTzDi5j4Fb/wD9EclHt3dpYRyJcl9Rtmug=='

hex_str = base64_str.decode('base64').encode('hex')

sha = hashlib.sha256()
rip = hashlib.new('ripemd160')

sha.update(base64_str.decode('base64'))

rip.update(sha.hexdigest())

# Get address
print base58.b58encode(rip.hexdigest())
John Smith
  • 271
  • 2
  • 10

2 Answers2

2

If the public key were correct (see Mark's answer), the code is not complete (lacks signature) and contains a hash of an ASCII hexdigest which is not correct.
I'd like to contribute with this implementation of bitcoin address from public key.
It covers the case of both uncompressed and compressed bitcoin addresses (just changing compress_pubkey boolean value).

#!/usr/bin/env python2
# https://en.bitcoin.it/wiki/Protocol_documentation#Addresses

import hashlib
import base58

# ECDSA bitcoin Public Key
pubkey = '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6'
# See 'compressed form' at https://en.bitcoin.it/wiki/Protocol_documentation#Signatures
compress_pubkey = False


def hash160(hex_str):
    sha = hashlib.sha256()
    rip = hashlib.new('ripemd160')
    sha.update(hex_str)
    rip.update( sha.digest() )
    print ( "key_hash = \t" + rip.hexdigest() )
    return rip.hexdigest()  # .hexdigest() is hex ASCII


if (compress_pubkey):
    if (ord(pubkey[-2:].decode('hex')) % 2 == 0):
        pubkey_compressed = '02'
    else:
        pubkey_compressed = '03'
    pubkey_compressed += pubkey[2:66]
    hex_str = pubkey_compressed.decode('hex')
else:
    hex_str = pubkey.decode('hex')

# Obtain key:

key_hash = '00' + hash160(hex_str)

# Obtain signature:

sha = hashlib.sha256()
sha.update( key_hash.decode('hex') )
checksum = sha.digest()
sha = hashlib.sha256()
sha.update(checksum)
checksum = sha.hexdigest()[0:8]

print ( "checksum = \t" + sha.hexdigest() )
print ( "key_hash + checksum = \t" + key_hash + ' ' + checksum )
print ( "bitcoin address = \t" + base58.b58encode( (key_hash + checksum).decode('hex') ) )
circulosmeos
  • 614
  • 1
  • 4
  • 10
  • AttributeError: 'str' object has no attribute 'decode' – Stakedex.io Aug 04 '22 at 19:15
  • Unfortunately the script is only valid with Python **2**, to match the code in the question - for Python 2&3 scripts, you can check: https://github.com/circulosmeos/bitcoin-in-tiny-pieces/ – circulosmeos Aug 04 '22 at 19:50
1

No.

A public key (the data you're going to hash) looks like this:

04 xx xx xx xx xx ... (uncompressed, 65 hex bytes)

or

(02 or 03) xx xx xx xx xx ... (compressed, 33 hex bytes)

It's not a DER format public key.

  • "It's not a DER format public key." It is a base64 string extracted from a PEM certificate generated with: openssl ec -in private_key.pem -pubout -out pub_key.pem So, decoding the base64 string with base64_str.decode('base64') does not help? In such case, How can I generate a private/public key suitable for bitcoin using openssl ? – John Smith Jul 21 '17 at 20:46
  • I haven't found a reference that explains how to actually generate a ECDSA keypair without the PEM format. – John Smith Jul 21 '17 at 20:59
  • This involves rolling your own crypto, which you shouldn't. You might want to use pyecdsa, and you'll not need any external libraries to use it. In the documentation it says that VerifyingKey.to_string() returns the x and y coordinates as a hex string. You just prepend "04" to it and decode it from hex. Additionally, you might want to see pybitcointools, but that library is buggy and I don't recommend using it. –  Jul 22 '17 at 06:38
  • 1
    (1) compressed point begins **02 or** 03. (2) the point is exactly the 'value' (after removing unused_bits) of the BIT STRING at the end of the DER-encoded SubjectPublicKeyInfo; since the AlgorithmIdentifier for EC,secp256k1 is always the same, you can simply unbase64, drop the first 23 octets and keep the rest, which is easy in Python. ... – dave_thompson_085 Sep 20 '17 at 07:45
  • ... @JohnSmith: `openssl ec` or `openssl pkey` can create binary DER as well as PEM, which saves the trim-and-unbase64, but it's still SPKI and not actually what you want. `openssl asn1parse – dave_thompson_085 Sep 20 '17 at 07:46