<p>First, what you defining as public key and private key are actually a <code>bitcoin address</code> and a <code>private key</code> encoded in Wallet Import Format <code>(WIF)</code>.</p>

<p>In order to check that the <code>WIF</code> and the <code>bitcoin addresses</code> are from the same key pair, we will need to decode the <code>private key</code> from its <code>WIF</code> format (checking that the encoding is ok), derive the <code>public key</code> from the <code>private key</code>, and generate the <code>bitcoin address</code> using the <code>public key</code>. If the generated <code>bitcoin address</code> matches with the provided one, then the provided one and the <code>WIF</code> are created from the same key pair.</p>

<p>In order to decode the <code>WIF</code> we will follow the steps from the <a href="https://en.bitcoin.it/wiki/Wallet_import_format#WIF_to_private_key" rel="nofollow noreferrer">bitcoin wiki</a>.</p>

<p>Lets see how we can do this in python: </p>

<pre><code>from binascii import hexlify, unhexlify
from ecdsa import SigningKey, SECP256k1
from hashlib import sha256
from bitcoin_tools.wallet import generate_btc_addr, WIF, TESTNET_WIF


def wif_to_sk(wif, network='main'):
    if network not in ['main', 'test']:
        # Add more networks if needed.
        raise Exception('Bad network')
    else:
        if network is 'main':
            version = WIF
        else:
            version = TESTNET_WIF

    decoded_wif = b58decode(wif)

    c = decoded_wif[-4:]
    v = decoded_wif[:1]

    # The byte defines the version, assert that is correct.
    assert v == chr(version)

    # The four last bytes of the WIF are the four first bytes of the checksum, check that it holds
    checksum = sha256(sha256(decoded_wif[:-4]).digest()).digest()
    assert checksum[:4] == c

    # If the private key in the WIF corresponds to a compressed public key, you must also drop the last byte, that will
    # be 01. We can check that by checking the length of the current key. 32 bytes wil mean uncompressed, while 33 and
    # a leading 01 means compressed.
    sk = hexlify(decoded_wif[1:-4])

    compressed = False

    # Notice that since we have hexlified the sk, the sizes are doubled.
    if len(sk) is 66 and sk[-2:] == '01':
        sk = unhexlify(sk[:-2])
        compressed = True
    else:
        sk = unhexlify(sk)

    return sk, compressed

# Your provided data
wif = 'KwfNqMip1ZdgG2o6wYQUBXv8BqkMQ8VWWeScVU5TLPZp31M5EHeq'
btc_addr = '13YcHBzsBX8SxHoBftb69cXJkdXLfAVQos'
network = 'main'

sk, compressed = wif_to_sk(wif, network=network)

# Derive the public key from the private key
pk = SigningKey.from_string(sk, curve=SECP256k1).get_verifying_key()

# Assert that the computed bitcoin address and the provided one matches.
assert generate_btc_addr(pk, v=network, compressed=compressed) == btc_addr
</code></pre>

<p>To decode the <code>WIF</code> format there is a couple of things that you may know. First, the <code>version</code> of the network (normally either <code>mainnet</code> or <code>testnet</code>) and then, if the <code>private key</code> corresponds to a compressed or uncompressed <code>public key</code>. The version of the network will determine the first byte of the <code>WIF</code> format, while whether the related <code>public key</code> is compressed or uncompressed will determine the last byte before the <code>checksum</code>.</p>

<p>Disclaimer: The provided code uses a function <code>generate_btc_addr</code>, from a python library I've developed, that computes a <code>bitcoin address</code> from a given <code>public key</code>. Such function call a bunch of other simple functions to derive the <code>bitcoin address</code>, but including all on the answer will make it even longer that what it is. You can either get the library from <a href="https://github.com/sr-gi/bitcoin_tools" rel="nofollow noreferrer">GitHub</a>, or get the functions from the <a href="https://github.com/sr-gi/bitcoin_tools/blob/master/bitcoin_tools/wallet.py" rel="nofollow noreferrer">specific file</a>.</p>

CC BY-SA 3.0
2017-10-19T20:10:11.917Z
2017-10-19T20:10:11.917Z
sr_gi
<p>1) A Bitcoin address is between 25 and 34 characters long</p>

<p>2) the address always starts with a 1 (in this case)</p>

<p>3) an address can contain all alphanumeric characters, with the exceptions of 0, O, I, and l.</p>

<p>(ref: <a href="https://thomas.vanhoutte.be/tools/validate-bitcoin-address.php" rel="nofollow noreferrer">https://thomas.vanhoutte.be/tools/validate-bitcoin-address.php</a>)</p>

<p>Now here comes the hard part: The last four bytes of the address are the first four bytes of Sha256(Sha256(2,3,4...21th bytes of the address))</p>

<p>More info: <a href="https://bitcoin.stackexchange.com/a/50879/38618">https://bitcoin.stackexchange.com/a/50879/38618</a></p>

CC BY-SA 3.0
2017-10-19T18:16:15.717Z
2017-10-19T18:16:15.717Z
MCCCS
<blockquote>
  <p>public key:</p>

<pre><code>13YcHBzsBX8SxHoBftb69cXJkdXLfAVQos
</code></pre>
</blockquote>

<p>That is not a public key. That is an address, which is an encoding of the hash of a public key.</p>

<blockquote>
  <p>that the public key is a valid bitcoin address that can receive payments, and</p>
</blockquote>

<p>You can check that the Bitcoin address is valid by decoding the <a href="https://en.bitcoin.it/wiki/Base58Check_encoding" rel="nofollow noreferrer">Base58 Check Encoding</a> of the address. When you decode the base58 string, you should have 25 hex bytes. The last 4 bytes are a checksum. They are the first 4 bytes of the double sha256 hash of the first 21 bytes of what you decoded. So you can hash those 21 bytes and make sure that they match the last 4 bytes. If they do, then the address is valid and coins can be sent to it.</p>

<blockquote>
  <p>that the private key is valid for this particular public key</p>
</blockquote>

<p>Decode the private key to its bytes in the same way as you did for the address, it uses the same encoding scheme. The checksum will be the hash of everything that is not the checksum. </p>

<p>The actual private key will be 32 bytes long, starting from the second byte of the decode Base58 string. Take those 32 bytes and derive the ECDSA public key from them. </p>

<p>If your private key string (the original base58 string you started with) began with a <code>K</code> or <code>L</code>, the public key will need to be compressed, so compress the public key as specified in Section 2.3.3 of the <a href="http://www.secg.org/sec1-v2.pdf" rel="nofollow noreferrer">SEC 1: Elliptic Curve Cryptography standard</a>. Take the public key and hash it first with SHA256 then that result with RIPEMD 160. Compare your resulting bytes to your decoded address from earlier. It should match the 20 bytes starting from the second byte of the decoded address.</p>

CC BY-SA 3.0
2017-10-19T18:22:03.720Z
2017-10-19T18:22:03.720Z
Ava Chow
<p>alternativly, if you don't want to go through the details by yourself, there are web services:</p>

<pre><code>https://www.bitaddress.org/bitaddress.org-v3.2.0-SHA256-ad4fd171c647772aa76d0ce828731b01ca586596275d43a94008766b758e8736.html
</code></pre>

<p>You may want to play with the menu "wallet details". It shows the derived keys as well.</p>

<p>(for sure you wouldn't use a web provided priv key for your production)</p>

CC BY-SA 4.0
2023-12-10T17:09:46.833Z
Unfortunately, that link is broken now :(
Returns a 404 status code
Peter Bergman
CC BY-SA 4.0
2023-12-14T17:08:50.277Z
yup, Internet is a living thing, isn't it? maybe try here and find the way through: https://www.bitaddress.org
pebwindkraft
CC BY-SA 3.0
2017-10-20T08:32:49.857Z
2017-10-20T08:32:49.857Z
pebwindkraft
<p>Let's say some software gave me the following combination:</p>

<p>public key:</p>

<pre><code>13YcHBzsBX8SxHoBftb69cXJkdXLfAVQos
</code></pre>

<p>private key:</p>

<pre><code>KwfNqMip1ZdgG2o6wYQUBXv8BqkMQ8VWWeScVU5TLPZp31M5EHeq
</code></pre>

<p>And let's say that for whatever reason, I want to test the validity:</p>

<ol>
<li><p>that the public key is a valid bitcoin address that can receive payments, and</p></li>
<li><p>that the private key is valid for this particular public key</p></li>
</ol>

<p>What are my options to evaluate the validity of 1 and 2? </p>

CC BY-SA 3.0
2017-10-19T18:14:20.157Z
Key encoding will be useful to give you a precise example. Are the values you are asking actually correct keys, or just made up strings?
sr_gi
CC BY-SA 3.0
2017-10-19T18:21:56.103Z
Alright, it's WIF.
sr_gi
CC BY-SA 3.0
2017-10-20T19:11:53.727Z
@sr-gi Not made up, some software gave them to me. (For others: WIF = wallet import format.)
thanks_in_advance
CC BY-SA 3.0
2017-10-20T19:14:59.090Z
Yep, I got it. Actually I've proved that they match in my answer.
sr_gi
<address><validation>
how to evaluate if this (or any) bitcoin public key / private key combination is valid?
thanks_in_advance
# how to evaluate if this (or any) bitcoin public key / private key combination is valid?

(Asked by: username_0 on 2017-10-19)

Let's say some software gave me the following combination:

public key:

```
13YcHBzsBX8SxHoBftb69cXJkdXLfAVQos

```

private key:

```
KwfNqMip1ZdgG2o6wYQUBXv8BqkMQ8VWWeScVU5TLPZp31M5EHeq

```

And let's say that for whatever reason, I want to test the validity:

1. that the public key is a valid bitcoin address that can receive payments, and
2. that the private key is valid for this particular public key

What are my options to evaluate the validity of 1 and 2?

**Tags:** address, validation

> **Comments:**
> **username_1**: Key encoding will be useful to give you a precise example. Are the values you are asking actually correct keys, or just made up strings?  
> **username_1**: Alright, it's WIF.  
> **username_0**: @username_1 Not made up, some software gave them to me. (For others: WIF = wallet import format.)  
> **username_1**: Yep, I got it. Actually I've proved that they match in my answer.  

---

## 4 answers

### Answer by username_1 on 2017-10-19. Score: 4

First, what you defining as public key and private key are actually a `bitcoin address` and a `private key` encoded in Wallet Import Format `(WIF)`.

In order to check that the `WIF` and the `bitcoin addresses` are from the same key pair, we will need to decode the `private key` from its `WIF` format (checking that the encoding is ok), derive the `public key` from the `private key`, and generate the `bitcoin address` using the `public key`. If the generated `bitcoin address` matches with the provided one, then the provided one and the `WIF` are created from the same key pair.

In order to decode the `WIF` we will follow the steps from the [bitcoin wiki](https://en.bitcoin.it/wiki/Wallet_import_format#WIF_to_private_key).

Lets see how we can do this in python:

```
from binascii import hexlify, unhexlify
from ecdsa import SigningKey, SECP256k1
from hashlib import sha256
from bitcoin_tools.wallet import generate_btc_addr, WIF, TESTNET_WIF

def wif_to_sk(wif, network='main'):
    if network not in ['main', 'test']:
        # Add more networks if needed.
        raise Exception('Bad network')
    else:
        if network is 'main':
            version = WIF
        else:
            version = TESTNET_WIF

    decoded_wif = b58decode(wif)

    c = decoded_wif[-4:]
    v = decoded_wif[:1]

    # The byte defines the version, assert that is correct.
    assert v == chr(version)

    # The four last bytes of the WIF are the four first bytes of the checksum, check that it holds
    checksum = sha256(sha256(decoded_wif[:-4]).digest()).digest()
    assert checksum[:4] == c

    # If the private key in the WIF corresponds to a compressed public key, you must also drop the last byte, that will
    # be 01. We can check that by checking the length of the current key. 32 bytes wil mean uncompressed, while 33 and
    # a leading 01 means compressed.
    sk = hexlify(decoded_wif[1:-4])

    compressed = False

    # Notice that since we have hexlified the sk, the sizes are doubled.
    if len(sk) is 66 and sk[-2:] == '01':
        sk = unhexlify(sk[:-2])
        compressed = True
    else:
        sk = unhexlify(sk)

    return sk, compressed

# Your provided data
wif = 'KwfNqMip1ZdgG2o6wYQUBXv8BqkMQ8VWWeScVU5TLPZp31M5EHeq'
btc_addr = '13YcHBzsBX8SxHoBftb69cXJkdXLfAVQos'
network = 'main'

sk, compressed = wif_to_sk(wif, network=network)

# Derive the public key from the private key
pk = SigningKey.from_string(sk, curve=SECP256k1).get_verifying_key()

# Assert that the computed bitcoin address and the provided one matches.
assert generate_btc_addr(pk, v=network, compressed=compressed) == btc_addr

```

To decode the `WIF` format there is a couple of things that you may know. First, the `version` of the network (normally either `mainnet` or `testnet`) and then, if the `private key` corresponds to a compressed or uncompressed `public key`. The version of the network will determine the first byte of the `WIF` format, while whether the related `public key` is compressed or uncompressed will determine the last byte before the `checksum`.

Disclaimer: The provided code uses a function `generate_btc_addr`, from a python library I've developed, that computes a `bitcoin address` from a given `public key`. Such function call a bunch of other simple functions to derive the `bitcoin address`, but including all on the answer will make it even longer that what it is. You can either get the library from [GitHub](https://github.com/username_1/bitcoin_tools), or get the functions from the [specific file](https://github.com/username_1/bitcoin_tools/blob/master/bitcoin_tools/wallet.py).


---

### Answer by username_2 on 2017-10-19. Score: 3

1) A Bitcoin address is between 25 and 34 characters long

2) the address always starts with a 1 (in this case)

3) an address can contain all alphanumeric characters, with the exceptions of 0, O, I, and l.

(ref: <https://thomas.vanhoutte.be/tools/validate-bitcoin-address.php>)

Now here comes the hard part: The last four bytes of the address are the first four bytes of Sha256(Sha256(2,3,4...21th bytes of the address))

More info: <https://bitcoin.stackexchange.com/a/50879/38618>


---

### Answer by username_3 on 2017-10-19. Score: 2

> public key:
>
> ```
> 13YcHBzsBX8SxHoBftb69cXJkdXLfAVQos
>
> ```

That is not a public key. That is an address, which is an encoding of the hash of a public key.

> that the public key is a valid bitcoin address that can receive payments, and

You can check that the Bitcoin address is valid by decoding the [Base58 Check Encoding](https://en.bitcoin.it/wiki/Base58Check_encoding) of the address. When you decode the base58 string, you should have 25 hex bytes. The last 4 bytes are a checksum. They are the first 4 bytes of the double sha256 hash of the first 21 bytes of what you decoded. So you can hash those 21 bytes and make sure that they match the last 4 bytes. If they do, then the address is valid and coins can be sent to it.

> that the private key is valid for this particular public key

Decode the private key to its bytes in the same way as you did for the address, it uses the same encoding scheme. The checksum will be the hash of everything that is not the checksum.

The actual private key will be 32 bytes long, starting from the second byte of the decode Base58 string. Take those 32 bytes and derive the ECDSA public key from them.

If your private key string (the original base58 string you started with) began with a `K` or `L`, the public key will need to be compressed, so compress the public key as specified in Section 2.3.3 of the [SEC 1: Elliptic Curve Cryptography standard](http://www.secg.org/sec1-v2.pdf). Take the public key and hash it first with SHA256 then that result with RIPEMD 160. Compare your resulting bytes to your decoded address from earlier. It should match the 20 bytes starting from the second byte of the decoded address.


---

### Answer by username_4 on 2017-10-20. Score: 1

alternativly, if you don't want to go through the details by yourself, there are web services:

```
https://www.bitaddress.org/bitaddress.org-v3.2.0-SHA256-ad4fd171c647772aa76d0ce828731b01ca586596275d43a94008766b758e8736.html

```

You may want to play with the menu "wallet details". It shows the derived keys as well.

(for sure you wouldn't use a web provided priv key for your production)

> **Comments:**
> **username_5**: Unfortunately, that link is broken now :( Returns a 404 status code  
> **username_4**: yup, Internet is a living thing, isn't it? maybe try here and find the way through: https://www.bitaddress.org  

---

