# From Private Key to Bitcoin Address

By [kootsZhin](https://paragraph.com/@kootszhin) · 2021-11-11

---

![](https://storage.googleapis.com/papyrus_images/c62576b4e3e6c6e62b320acf9339ef2c9ce4647230d2d69f5c42b9e9d5f8c5a0.jpg)

Okay, so you have heard of the fact that Bitcoin is using a signature algorithm which contains a pair of public key and private key in verifying the identity of the owners. Simply speaking, the public key is just the account name of your wallet and private key the password. At first glance, you may think that the public key is referring to your Bitcoin address, but in fact that is not the case. This article is a step-by-step guide to getting the exact Bitcoin address by looking at its private key.

Importing Dependencies
----------------------

In this study, we will need to make use of the below Python libraries.

    import hashlib
    import base58
    import codecs
    import ecdsa
    

Converting Private Key to Address
---------------------------------

Step 0
------

First of all, we will have to start with a private ECDSA key (basically any series of 32 bytes) and this will be our private key. In this example, we will use the same key from the [Bitcoin Wiki](https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses).

**Input**

    private_key = "18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725"private_key
    

**Output**

    '18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725'
    

Step 1
------

Next we will have to generate an uncompressed public key from the private key that we have

**Input**

    # Hex decoding the private key to bytes using codecs library
    private_key_bytes = codecs.decode(private_key, 'hex')# Generating a public key in bytes using SECP256k1 & ecdsa library
    public_key_raw = ecdsa.SigningKey.from_string(private_key_bytes, curve=ecdsa.SECP256k1).verifying_key
    public_key_bytes = public_key_raw.to_string()# Hex encoding the public key from bytes
    public_key_hex = codecs.encode(public_key_bytes, 'hex')# Bitcoin public key begins with bytes 0x04 so we have to add the bytes at the start
    public_key = (b'04' + public_key_hex).decode("utf-8")public_key
    

**Output**

    '0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6'
    

Step 2
------

Next we have to compressed our uncompressed public key

**Input**

    # Checking if the last byte is odd or even
    if (ord(bytearray.fromhex(public_key[-2:])) % 2 == 0):
        public_key_compressed = '02'
    else:
        public_key_compressed = '03'
    
        # Add bytes 0x02 to the X of the key if even or 0x03 if odd
    public_key_compressed += public_key[2:66]
    
    public_key_compressed
    

**Output**

    '0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352'
    

Step 3
------

Perform SHA-256 hashing on the compressed public key

**Input**

    # Converting to bytearray for SHA-256 hashing
    hex_str = bytearray.fromhex(public_key_compressed)
    sha = hashlib.sha256()
    sha.update(hex_str)sha.hexdigest() # .hexdigest() is hex ASCII
    

**Output**

    '0b7c28c9b7290c98d7438e70b3d3f7c848fbd7d1dc194ff83f4f7cc9b1378e98'
    

**Step 4**
----------

Perform RIPMED-160 hashing on the result of SHA-256

**Input**

    rip = hashlib.new('ripemd160')
    rip.update(sha.digest())
    key_hash = rip.hexdigest()key_hash # Hash160
    

**Output**

    'f54a5851e9372b87810a8e60cdd2e7cfd80b6e31'
    

Step 5
------

Add version byte in front of RIPEMD-160 hash (0x00 for Main Network)

**Input**

    modified_key_hash = "00" + key_hashmodified_key_hash
    

**Output**

    '00f54a5851e9372b87810a8e60cdd2e7cfd80b6e31'
    

Step 6
------

Perform SHA-256 hash on the extended RIPEMD-160 result _(Below steps are called Base58Check encoding)_

**Input**

    sha = hashlib.sha256()
    hex_str = bytearray.fromhex(modified_key_hash)
    sha.update(hex_str)sha.hexdigest()
    

**Output**

    'ad3c854da227c7e99c4abfad4ea41d71311160df2e415e713318c70d67c6b41c'
    

**Step 7**

Perform SHA-256 hash on the result of the previous SHA-256 hash

**Input**

    sha_2 = hashlib.sha256()
    sha_2.update(sha.digest())sha_2.hexdigest()
    

**Output**

    'c7f18fe8fcbed6396741e58ad259b5cb16b7fd7f041904147ba1dcffabf747fd'
    

Step 8
------

Take the first 4 bytes of the second SHA-256 hash, this is the address checksum

**Input**

    checksum = sha_2.hexdigest()[:8]checksum
    

**Output**

    'c7f18fe8'
    

Step 9
------

Add the 4 checksum bytes from stage 8 at the end of extended RIPEMD-160 hash from stage 5, this is the 25-byte binary Bitcoin Address

**Input**

    byte_25_address = modified_key_hash + checksumbyte_25_address
    

**Output**

    '00f54a5851e9372b87810a8e60cdd2e7cfd80b6e31c7f18fe8'
    

**Step 10 (Final Result)**

Convert the result from a byte string into a base58 string using Base58Check encoding, this is the most commonly used Bitcoin Address format

**Input**

    address = base58.b58encode(bytes(bytearray.fromhex(byte_25_address))).decode('utf-8')address
    

**Output**

    '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs'
    

Alternative Ways in Converting Hash160 (key hash) into base58
-------------------------------------------------------------

Making use of b58.encode\_check from base58 library directly at Step 6

**Input**

    key_bytes = codecs.decode(modified_key_hash, 'hex')
    address = base58.b58encode_check(key_bytes).decode('utf-8')address
    

**Output**

    '1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs'

---

*Originally published on [kootsZhin](https://paragraph.com/@kootszhin/from-private-key-to-bitcoin-address)*
