BE CAREFUL: Use this at your own risk, and keep an eye on this thread for updates. Check out POST 18 for the latest fixes.
Honestly, I was surprised at how few resources there are online for making your own Bitcoin address. Sure, there's bitaddress.org, which even offers an offline version, but it doesn’t support bech32 addresses (which have lower fees), and I’m not sure if they let you generate a seed phrase.
I wanted to have complete control over generating my Bitcoin keys because it’s tough to trust that non-custodial wallets really don’t have access to your private keys. To verify that, you’d need to use a packet sniffer like Wireshark while accessing your wallet with your seed phrase to monitor the data sent to the wallet provider. Even then, it’s tricky to figure out what info they actually receive. Sure, you might have to use wallet software when you want to cash out, but having the ability to create your own valid address could be handy as a paper wallet in the meantime.
Another issue I want to tackle is "Entropy." People warn against generating your own addresses because your method for entropy could be weak and might get compromised.
I’m definitely not a Bitcoin or Python whiz, so part of why I’m sharing my code is to get feedback on any potential vulnerabilities or inefficiencies. I ran this code using Spyder with the Anaconda interpreter (got the necessary libraries through Anaconda).
1) Generate your own entropy
import hashlib
import os
def generate_entropy_from_input(user_input):
salt =
As long as the wallet software you are using is 100% open source, you don't need complicated ways, you can just take a look at its source code to see what it is doing in the background like what data is being sent out to other nodes/servers.
For example you could have just gone though Electrum's source code which happens to be in python to see what it does.
Same as above.
Well you need to have a good understanding of both Bitcoin and python to be able to write something like that. In any case read my comment here to see how its being done
https://bitcointalk.org/index.php?topic=5461279.msg62621270#msg62621270
I have found an error with my code. The seed phrase had no connection with the private key, which it would make it impossible to restore it with the seed phrase it printed.
An AI search helped me detect it.
"Incorrect Derivation of the Master Key:
In BIP32, the master key is derived from the seed using HMAC-SHA512 with a specific key (b"Bitcoin seed"), not hashlib.pbkdf2_hmac as you are using.
Your code uses hashlib.pbkdf2_hmac to derive the master key, which is not part of the BIP32 standard.
Private Key Derivation:
The private key is not directly derived from the master key. Instead, BIP32 defines a hierarchical deterministic (HD) wallet structure where private keys are derived from the master key using child key derivation (CKD) functions.
Bech32 Address Generation:
The Bech32 address generation in your code is not following the correct derivation path. For a valid Bech32 address, you need to derive the public key from the private key and then hash it using SHA256 and RIPEMD-160 before encoding it into Bech32 format."
Here is the corrected code:
import hashlib
from mnemonic import Mnemonic
from bip32utils import BIP32Key
import bech32
# Input your entropy code
entropy_code = "" # IMPORTANT: After running the program, erase your code
# Convert the entropy code to bytes
entropy = bytes.fromhex(entropy_code)
# Generate a BIP39 mnemonic phrase
mnemonic = Mnemonic("english")
words = mnemonic.to_mnemonic(entropy) # 12-word phrase
# Derive a seed from the mnemonic phrase
seed = mnemonic.to_seed(words)
# Derive the master key from the seed using BIP32
bip32_master_key = BIP32Key.fromEntropy(seed)
# Get the private key (hexadecimal) from the master key
private_key = bip32_master_key.PrivateKey()
# Convert private key to WIF format
wif_private_key = bip32_master_key.WalletImportFormat()
# Derive the public key
public_key = bip32_master_key.PublicKey()
# Hash the public key to generate the Bech32 address
hashed_public_key = hashlib.new('ripemd160', hashlib.sha256(public_key).digest()).digest()
# Generate the Bech32 address
hrp = "bc" # Human-readable part for Bitcoin mainnet addresses
witver = 0 # Witness version (0 for Pay-to-Witness-Public-Key-Hash addresses)
bech32_address = bech32.encode(hrp, witver, hashed_public_key)
# Print the Bitcoin private key (WIF format), mnemonic phrase, and Bech32 address
print("Bitcoin Private Key (WIF format):", wif_private_key)
print("Bitcoin Private Key (Hexadecimal):", private_key.hex())
print("BIP39 Mnemonic Phrase:", words)
print("Bech32 Address:", bech32_address)
Good effort, but you should, at least, take pooya87's response into consideration.
The coding aspect is perfect for research and you can gain significant knowledge implementing stuff like that.
But for real usage, I strongly suggest using well established open-source projects.
And I am a person who develops a lot, just for fun, but when it comes to real money, I only use thoroughly tested software.
all this is plain wrong stuff
first of all bip32key is eth thing
https://pypi.org/project/bip32key/
second
this 'master private key' is not supposed to be in wif format. it's root key of hd wallet where key in wif is single wallet key. And if you derive address (single wallet address) from this key it'll be completely unrelated to this hd wallet root key belongs to. hd root key itself is 78 bytes long, not 32. most of the time you can trunk it to 32 yeah but you have no guarantee you'll get original key back.
It's fked up project .
BIP32 is applicable to both Bitcoin and Ethereum: BIP32 is a standard for hierarchical deterministic (HD) key generation and is used in both Bitcoin and Ethereum, among other cryptocurrencies.
Bech32 is used for SegWit addresses. If you're generating a Legacy or P2SH address, you'll need to use Base58Check encoding instead.
Correcting the code.
import hashlib
from mnemonic import Mnemonic
from bip32utils import BIP32Key, BIP32_HARDEN
import bech32
# Input your entropy code
entropy_code = "e531b2d5f61c22579bade7bc41e6a13e" # IMPORTANT: After running the program, erase your code
# Check if the entropy code is valid (16 bytes or 32 hex characters)
if len(entropy_code) != 32 or not entropy_code.isalnum():
raise ValueError("Invalid entropy code. Ensure it is a 32-character hexadecimal string.")
# Convert the entropy code to bytes
entropy = bytes.fromhex(entropy_code)
# Generate a BIP39 mnemonic phrase
mnemonic = Mnemonic("english")
words = mnemonic.to_mnemonic(entropy) # 12-word phrase
# Derive a seed from the mnemonic phrase
seed = mnemonic.to_seed(words)
# Derive the master key from the seed using BIP32
bip32_master_key = BIP32Key.fromEntropy(entropy) # Corrected method name
# Print the HD root key (extended key)
hd_root_key = bip32_master_key.ExtendedKey() # Use ExtendedKey() instead of Serialize()
print("HD Root Key (Extended Key):", hd_root_key)
# Derive a single wallet key from the master key (e.g., m/44'/0'/0'/0/0)
derived_key = bip32_master_key.ChildKey(44 + BIP32_HARDEN).ChildKey(0 + BIP32_HARDEN).ChildKey(0 + BIP32_HARDEN).ChildKey(0).ChildKey(0)
# Get the private key (hexadecimal) from the derived key
private_key = derived_key.PrivateKey()
# Convert private key to WIF format
def private_key_to_wif(private_key, compressed=True):
# Add version byte (0x80 for mainnet)
versioned_key = b'\x80' + private_key
if compressed:
versioned_key += b'\x01' # Append 0x01 for compressed keys
# Calculate the checksum
checksum = hashlib.sha256(hashlib.sha256(versioned_key).digest()).digest()[:4]
# Create the final byte string
final_key = versioned_key + checksum
# Convert to Base58
alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
num = int.from_bytes(final_key, 'big')
wif = ''
while num > 0:
num, rem = divmod(num, 58)
wif = alphabet[rem] + wif
return wif
# Get the WIF private key
wif_private_key = private_key_to_wif(private_key)
# Derive the public key
public_key = derived_key.PublicKey()
# Hash the public key to generate the Bech32 address
hashed_public_key = hashlib.new('ripemd160', hashlib.sha256(public_key).digest()).digest()
# Generate the Bech32 address
hrp = "bc" # Human-readable part for Bitcoin mainnet addresses
witver = 0 # Witness version (0 for Pay-to-Witness-Public-Key-Hash addresses)
bech32_address = bech32.encode(hrp, witver, hashed_public_key)
# Print the Bitcoin private key (WIF format), mnemonic phrase, and Bech32 address
print("Bitcoin Private Key (WIF format):", wif_private_key)
print("Bitcoin Private Key (Hexadecimal):", private_key.hex())
print("BIP39 Mnemonic Phrase:", words)
print("Bech32 Address:", bech32_address)
output:
HD Root Key (Extended Key): xprv9s21ZrQH143K2iWCf6km9a3mJ1vnFhe6DmJT8h9d7FoVzmTnpK1owZZoK9SVj8ppnv4drno77CL RDjFrXCwnpDHdmthpLmjQBqz1SX89BZv
Bitcoin Private Key (WIF format): KwqbXPC6yAPDfQQUUXJbA9csN9mQU9hKyhNG71jSwAxU2VqmKtef
Bitcoin Private Key (Hexadecimal): 1275eff28bafa4211a98812cba059d764caef67af1d51288e6d9ddb8dd6aae4e
BIP39 Mnemonic Phrase: topple mirror rely umbrella season cloth huge rude rough august portion laptop
Bech32 Address: bc1q4tyxydpv08refa2r64vdga32muznfwh7mmpx9l
------------
Do you like the code now better?
So you 've posted some code which, if used by someone, would lead to them losing their money.
You clearly state that you want to learn, so please, at the top of the original post, write a warning. Something like "educational purposes only". Just to make sure that anyone won't be lazy enough to skip your intro and go straight to your code and use it.
You should edit urgently your first post, it's not safe at all for users to leave code with such behavior on the forum. You should always test critical code before publishing it publicly. You don't need any AI tool for that.
Besides that, it could be interesting if it works properly now but on which device can we execute this script? Do you know if some calculators(especially not connected ones) are able to execute that in 2024? Or with some minor changes at least? I guess some libraries you use are not included in Python standard distributions.
Nice work mate but a few point to mention
First :
Handling private keys and seed phrases in plaintext is inherently risky. You stand at a very high risk if an attacker gets hold of your system.
Using user_variable for entropy is highly risky. Assuming a user provides a weak or predictable input?. Despite introducing randomness via salting understanding the strength of the entropy greatly relies on the quality of the user input
Lastly using RIPEMD-160 hash of the private key for the witness program is risky. This can lead to an invalid address or one that cannot be recovered .
It is best to use a proper public key derived from the private key when creating the witness program.
It is not just risky, it is impossible since there won't be any way to spend those coins. The hash must be computed from the public key. The code OP posted in first post was wrong, they fixed it in the last comment:
What do you mean by that? How could he do otherwise? All wallets display in their GUI, in the console or even in plain text files sometimes, the private keys and the HD seed of the wallet if you request it. You think it should be encoded with a scheme or even encrypted with a key or a password ? But how and where the user could decypher it safely then?
He just shifts this responsibility onto the user, taking it on. An explicit warning message would certainly be more cautious, but if you don't trust or don't want to trust reliability of some libraries or environnement behaviors on all equipments and hardware configurations, it's simpler and safer to delegate the burden of this task and its responsibility directly to the user IMO. He sould be informed of what this implies though.
It's not that complex. The script could ask user to input the variable value when running the program and enter password before storing encrypted key on disk.
Average user wouldn't run OP script in first place.
I have this code:
# pip install mnemonic bip32
from mnemonic import Mnemonic
from bip32 import BIP32
def generar_semilla_y_bip32():
mnemo = Mnemonic("english")
mnemonic_words = mnemo.generate(strength=192)
print("Frase mnemónica generada (18 palabras):")
print(mnemonic_words)
seed = mnemo.to_seed(mnemonic_words)
bip32 = BIP32.from_seed(seed)
root_key = bip32.get_xpriv_from_path("m")
print("\nBIP32 Root Key generada a partir de la frase mnemónica:")
print(root_key)
def obtener_bip32_desde_semilla():
mnemo = Mnemonic("english")
mnemonic_words = input("Introduce tu frase mnemónica: ")
seed = mnemo.to_seed(mnemonic_words)
bip32 = BIP32.from_seed(seed)
root_key = bip32.get_xpriv_from_path("m")
print("\nBIP32 Root Key generada a partir de la frase mnemónica:")
print(root_key)
def main():
print("Elige una opción:")
print("1. Crear nueva semilla")
print("2. Ya tengo una semilla (dame la BIP32 Root Key)")
opcion = input("Introduce el número de la opción que quieres: ")
if opcion == "1":
generar_semilla_y_bip32()
elif opcion == "2":
obtener_bip32_desde_semilla()
else:
print("Opción no válida")
if __name__ == "__main__":
main()
It creates a 18 words seed phrase, and shows its BIP32 Root Key. With this, you can use the seed words in Electrum (or any BIP39 compatible wallet) and with the BIP32 Root Key you can import the same wallet in Bitcoin core (using importdescriptors command).
If you already have one seed phrase, it returns just the BIP32 Root Key.
In case you want to mantain a wallet for spendings with Electurm in your mobile, and just watch-only in Bitcoin core, you can use de importdescriptors with the xpub instead of the BIP32 Root Key (extracting the public descriptors with listdescriptors first).
Yes, first, i stated at the fist post to turn off the internet on the system before running the code.
And i know that if the user chooses a weak entropy it runs risk of the private key getting cracked. That's why i also stated the entropy should be a large text, mix languages, roll your face over the keyboard, all together.
It looks fine, but gotta be careful that you are trusting a third-party for the entropy and you will have to use the output of that code on wallets that actually support it, like Electrum. Not all wallets support other than 12 or 24 words. Or other limits.
UPDATE. The Previous code had some issues.
The Entropy generator first:
BTC address code
-------------
Now, this will create a BIP39 seed phrase that you should import into a good wallet. To make it work on Electrum Wallet you need to enable BIP39 mode.
i'm just curious why I need to create my own Bitcoin address instead of using the ones generated by Electrum. Also, would this BTC address generated by your code be of any special benefit to me? As a non-expert in Bitcoin coding, how am I sure that following your strategy wouldn't result in some losses. As in, something going wrong and my newly generated wallet failing to receive sent fund? Doesn't it make sense to stick with what is proven to work and proven to be safe than going for this?
If you are not sure. Keep using what everyone does.
I just found it annoying that there isn't an easy way to find how to make your own BTC address without having to trust anyone.
I have seen the weak link in BTC addresses generation is the entropy. Sometimes hackers find out how the entropy was being generated and that way they could generate your addresses. I know that the most known wallets use a very good way of generating entropy. But I still think the option to create your own BTC address should be available to all.