Bitcoin Forum
September 09, 2025, 03:38:31 AM *
News: Latest Bitcoin Core release: 29.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Wallet encryption process  (Read 850 times)
whanau (OP)
Member
**
Offline Offline

Activity: 131
Merit: 45


View Profile
April 18, 2021, 05:55:28 AM
Merited by ABCbits (1)
 #1

I had been wondering about the process of encrypting a wallet but could not find a simple enough explanation of how it works (I am a newbie!) So I thought I would do some investigating. The result was that I put together a little python script which explains it to me. I thought I would post it here in case it is of use to others. The base code came from
https://stackoverflow.com/questions/61378005/decrypting-aes-256-cbc-cipher-with-a-passcode-built-using-pbkdf2-in-python. The rest of the information I pieced together from various places on the internet. The explanation is in the code. Install the dependencies, run it, look at the results and then read through the heavily commented code to understand the process. If I have made any mistakes, please let me know.

Code:
from base64 import b64encode
import hashlib
import binascii
import pyaes
import os
import random

# Wallet software generates a random 32 byte master key. (How this is done is well documented and is not
# explained here). The master key needs to be encrypted to protect your funds. The one below is made up.
# You can substitute your own key below to try the software but DO NOT publish your own key anywhere.
mkey = binascii.unhexlify("5c5692da0f165d3d32e5c05a56dde9b2d0ebc05f100f8d0616941e9abe7e0fb0")

# Wallet software will ask you for a password to encrypt the master key.
# You can change the password below for your own, but don't lose the b in front.
password = b'password123'

# Also needed is some 'randomness' in the form of a salt and an iteration count. These are generated by the wallet.
# Use the # to comment out the salt and iteration count below if testing your own values.

# create a random 8 byte salt and iteration count.
salt = os.urandom(8)
iterationcount = random.randint(2500, 50000)
# These two values are saved in wallet.dat so that the wallet key can be recreated (not decrypted) if you know the password.

# here you can substitute your own salt and iteration count from a wallet. Remove the # in front
#salt = binascii.unhexlify('a49e804e25740714')
#iterationcount = 91005

# Now if you run the program several times, you will see you get different wallet keys for the same password.
# The random salt and iterations help prevent duplicate wallet keys for the same password.

# Hashing with SHA512. This process generates the wallet key and IV. It is one-way. You cannot go back from
# here to recover the password, which is why it is so important you remember it.
# It is also essential the password is strong because this is where dictionary attacks can take place.
# Remember the salt & iterations are recoverable from the wallet.dat files!
hash512 = hashlib.pbkdf2_hmac('sha512', password, salt, iterationcount)

# The wallet key is the first 32 bytes of the hash.
wallet_key = hash512[0:32]
# The IV (initialization vector) is the next 16 bytes of the hash
iv = hash512[32:48]

# We now have all we need to encrypt the master key
# Encryption with AES-256-CBC
encrypter = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(wallet_key, iv))
ciphertext = encrypter.feed(mkey)
ciphertext += encrypter.feed()
# The cipher text is what you see in the wallet.dat files as Mkey followed by 48 bytes
# There are also Ckey's which hold the private spend keys for each address.
#######################################################################################
# Your master private key is now encrypted!!
#######################################################################################
# Decryption is the reverse process using the same wallet key and IV to process the ciphertext (Mkey)

# Decryption with AES-256-CBC
decrypter = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(wallet_key, iv))
decryptedData = decrypter.feed(ciphertext)
decryptedData += decrypter.feed()
# The master key is now decrypted for all the world to see.
# However, before we can spend any funds we need to decrypt the Ckeys which hold the private keys to the addresses
# As you can see with no Ckey's, wallet recovery services cannot access your funds. It is safe to send Mkey ciphertext
# iteration count and salt but not the wallet itself as this has ALL the keys.
# Decrypting Ckeys will be the subject of another post.

# If you have found this useful please consider a small BTC tip to bc1qg82720gvdyvnc3jycnun348fhs3qam44nsmeqm

# Display results
def printData(text, data):
    print(text + " (hex)   :" + data.hex())
    print(text + " (Base64):" + b64encode(data).decode('utf8') + "\n")


printData("SHA512    ", hash512)
printData("Wallet Key", wallet_key)
printData("16 bytes IV", iv)
printData("Master Key ", mkey)
printData("Ciphertext ", ciphertext)

# Decrypted data
printData("Decrypted  ", decryptedData)

 
NotATether
Legendary
*
Offline Offline

Activity: 2086
Merit: 8904


Search? Try talksearch.io


View Profile WWW
April 18, 2021, 07:14:22 AM
 #2

Quote
Code:
# Hashing with SHA512. This process generates the wallet key and IV. It is one-way. You cannot go back from
# here to recover the password, which is why it is so important you remember it.
# It is also essential the password is strong because this is where dictionary attacks can take place.
# Remember the salt & iterations are recoverable from the wallet.dat files!

Not just strong, it's got to be absurdly long like >40 characters or a shorter number of completely random characters.

I am currently brute forcing a Bitcoin hash for somebody and can go through 300 million combinations in about half a day using Hashcat. The word list was assembled by combining other short words, symbols and separators (like space ' ', period and exclamation mark) with each other to make groups of 1-4 words stuck together.

So, somebody with a list of tokens that you might use can exhaust a frighteningly large space of passwords it could've been, which is why it's important to use a random password or a very long password for wallet.dat files especially.

██
██
██
██
██
██
██
██
██
██
██
██
██
... LIVECASINO.io    Play Live Games with up to 20% cashback!...██
██
██
██
██
██
██
██
██
██
██
██
██
pooya87
Legendary
*
Offline Offline

Activity: 3934
Merit: 11905



View Profile
April 18, 2021, 07:39:45 AM
 #3

I am currently brute forcing a Bitcoin hash for somebody and can go through 300 million combinations in about half a day using Hashcat.
Do you know what the steps are? I mean is it just SHA2 hash/hmac + KDF or is there more including AES decryption and maybe address creation because I'm trying to figure out why you get ~7000 combinations per second only (it seems too low if the steps are the former).

NotATether
Legendary
*
Offline Offline

Activity: 2086
Merit: 8904


Search? Try talksearch.io


View Profile WWW
April 18, 2021, 08:28:04 AM
Merited by hugeblack (2), pooya87 (1), ABCbits (1)
 #4

I am currently brute forcing a Bitcoin hash for somebody and can go through 300 million combinations in about half a day using Hashcat.
Do you know what the steps are? I mean is it just SHA2 hash/hmac + KDF or is there more including AES decryption and maybe address creation because I'm trying to figure out why you get ~7000 combinations per second only (it seems too low if the steps are the former).

I'm actually getting 4500 combinations/second so my numbers are slightly off. 300 million words was a really bad estimate off by a few dozen million.

It's PBKDF2-HMAC-SHA512 key stretching only, the input hash does not contain the encryption data for AES.

I believe a custom iteration count is used for each wallet file, for example my hash uses 207301 iterations.

██
██
██
██
██
██
██
██
██
██
██
██
██
... LIVECASINO.io    Play Live Games with up to 20% cashback!...██
██
██
██
██
██
██
██
██
██
██
██
██
HCP
Legendary
*
Offline Offline

Activity: 2086
Merit: 4382

<insert witty quote here>


View Profile
April 18, 2021, 09:45:43 AM
 #5

I had been wondering about the process of encrypting a wallet but could not find a simple enough explanation of how it works (I am a newbie!) So I thought I would do some investigating.
It should be noted that this is just one method of encrypting a wallet... Not all wallets employ this particular method of encryption.

Indeed it seems that this is the method specifically employed by Bitcoin Core for encrypting private key data in the wallet.dat file.

█████████████████████████
████▐██▄█████████████████
████▐██████▄▄▄███████████
████▐████▄█████▄▄████████
████▐█████▀▀▀▀▀███▄██████
████▐███▀████████████████
████▐█████████▄█████▌████
████▐██▌█████▀██████▌████
████▐██████████▀████▌████
█████▀███▄█████▄███▀█████
███████▀█████████▀███████
██████████▀███▀██████████
█████████████████████████
.
BC.GAME
▄▄░░░▄▀▀▄████████
▄▄▄
██████████████
█████░░▄▄▄▄████████
▄▄▄▄▄▄▄▄▄██▄██████▄▄▄▄████
▄███▄█▄▄██████████▄████▄████
███████████████████████████▀███
▀████▄██▄██▄░░░░▄████████████
▀▀▀█████▄▄▄███████████▀██
███████████████████▀██
███████████████████▄██
▄███████████████████▄██
█████████████████████▀██
██████████████████████▄
.
..CASINO....SPORTS....RACING..
█░░░░░░█░░░░░░█
▀███▀░░▀███▀░░▀███▀
▀░▀░░░░▀░▀░░░░▀░▀
░░░░░░░░░░░░
▀██████████
░░░░░███░░░░
░░█░░░███▄█░░░
░░██▌░░███░▀░░██▌
░█░██░░███░░░█░██
░█▀▀▀█▌░███░░█▀▀▀█▌
▄█▄░░░██▄███▄█▄░░▄██▄
▄███▄
░░░░▀██▄▀


▄▄████▄▄
▄███▀▀███▄
██████████
▀███▄░▄██▀
▄▄████▄▄░▀█▀▄██▀▄▄████▄▄
▄███▀▀▀████▄▄██▀▄███▀▀███▄
███████▄▄▀▀████▄▄▀▀███████
▀███▄▄███▀░░░▀▀████▄▄▄███▀
▀▀████▀▀████████▀▀████▀▀
radioactive73
Newbie
*
Offline Offline

Activity: 1
Merit: 0


View Profile
October 31, 2022, 01:11:17 AM
 #6


# Decrypting Ckeys will be the subject of another post.


I'd be interested in the process for decrypting ckeys for Bitcoin
NotATether
Legendary
*
Offline Offline

Activity: 2086
Merit: 8904


Search? Try talksearch.io


View Profile WWW
November 02, 2022, 10:28:25 PM
 #7


# Decrypting Ckeys will be the subject of another post.


I'd be interested in the process for decrypting ckeys for Bitcoin

There's no way to do that without the AES key. There are no known flaws in the algorithm you could exploit.

Besides, these steps usually do not encrypt the wallet file per se, they encrypt specific values within the wallet (usually private keys), because wallet files are often just databases such as sqlite and BDB.

██
██
██
██
██
██
██
██
██
██
██
██
██
... LIVECASINO.io    Play Live Games with up to 20% cashback!...██
██
██
██
██
██
██
██
██
██
██
██
██
whanau (OP)
Member
**
Offline Offline

Activity: 131
Merit: 45


View Profile
January 28, 2023, 07:36:31 AM
 #8

As NotATether says first you need the AES key. Without that, forget it.
If you have the AES key, then take the public key of the Ckey and perform a SHA256 on it twice. (The public key is in the wallet dump below the Ckey)

The first 16 bytes of the hash are the IV for the AES. Then you do an AES decrypt with the Ckey, IV and AES key.
The first 32 bytes of the result is the private key for that Ckey. Example using the dummy AES key from the original post:

master key    5c5692daff165d3d32e5c05a56dde3b2d0ebc05f133f8d0616941e9abe7e0fb0
Ckey              316787cf83a3c9caeca2a2b20f0879edc2a8c60ed127453c00330d1ac009402d78eff292b03e588 a3b858410ea2628ed
public key      027a098dbada15a831c66491cb20dee174d4971fdd9766a3b6c3dc64562cbb6524

SHA256 x 2   e8fd42dda81c80d956998c05238a12d7ea58c971d31a1640f1ef51df47277b90

AES key    5c5692daff165d3d32e5c05a56dde3b2d0ebc05f133f8d0616941e9abe7e0fb0
IV             e8fd42dda81c80d956998c05238a12d7 and the Ckey

Result        e1f08760a5dbfa7fc0b7f2d1864fbd8c31180b7cc8f87d2e8c1c892bc1ced5a0101010101010101 01010101010101010
Private key e1f08760a5dbfa7fc0b7f2d1864fbd8c31180b7cc8f87d2e8c1c892bc1ced5a0
bakd247
Newbie
*
Offline Offline

Activity: 13
Merit: 0


View Profile
March 19, 2025, 04:30:12 AM
 #9

the double sha256 in this post is wrong and the decryption of the provided keys does not work as a result.....
the private key and public keys are correct but the double sha256 of that public key '027a098dbada15a831c66491cb20dee174d4971fdd9766a3b6c3dc64562cbb6524' is '9b6f2b7c70b80818e4706970dcdc03b66b2f1b1199a03619e8df17b013b0a4e9'
NOT what you have listed above...

Also if I run decrypt with the key and iv you have listed above...decrypt fails....WHY?!?!? is the question...apparently I am doing something wrong here...
All I did was copy and paste the code above and while it gives correct out for decrypt of the master key....
decrypt fails with both hashes...the one you have listed and the correct one....
so this means only that the encrypted private key is wrong or your hashing method is different...

is there any way someone out there could please confirm  EXACTLY how the wallet encryption process works?!?!?!

I feel like I should be able to just keep the list of my encrypted private keys where ever I want and decrypt them at anytime.

form all my searching online no one has made a simple app that allows you to provide the mkey, encrypted private key, salt , iteration count, and passphrase.

I am attempting to write a python script that will allow for this...so if someone had a corrupted wallet but was able to get this information using pywallet...they would still have access to their keys.

Please any help with this is greatly appreciated.  Thanks in Advance

I intend on updating this post once I have this figured out..
nc50lc
Legendary
*
Offline Offline

Activity: 2898
Merit: 7568


Self-proclaimed Genius


View Profile
March 21, 2025, 05:14:32 AM
Merited by ABCbits (1), apogio (1)
 #10

the double sha256 in this post is wrong and the decryption of the provided keys does not work as a result.....
the private key and public keys are correct but the double sha256 of that public key '027a098dbada15a831c66491cb20dee174d4971fdd9766a3b6c3dc64562cbb6524' is '9b6f2b7c70b80818e4706970dcdc03b66b2f1b1199a03619e8df17b013b0a4e9'
NOT what you have listed above...
The SHA256x2 hash: e8fd42dda81c80d956998c05238a12d7ea58c971d31a1640f1ef51df47277b90 in the post above is correct.
The result of your script is wrong because the public key and its first SHA256 hash must have been input as "text" instead of "hex".

bakd247
Newbie
*
Offline Offline

Activity: 13
Merit: 0


View Profile
March 27, 2025, 11:17:37 PM
Last edit: April 05, 2025, 05:40:51 AM by hilariousandco
 #11

you are correct about that the input method...for the sha256...I did confirm that....sorry for my ignorance....
In the process of writing the version on my github page...I figured that out the hard way as I was getting wrong answers for decrypt left and right...
very frustrating.....

thank you for clarifying that

I corrected the double sha256 and am getting the same output as you have displayed but decrypt is failing when decrypting the ckey you have shown... when I decrypt using the first 32 bytes of the decrypted key as the aes key and the same iv you have listed above...decrypt fails....
I am using the same method shown above just changed the key, iv and key to be decrypted to the ones you have listed above and decrypt fails.
I made sure that both the key and iv are changed to bytes from hex using the unhexlify method just like above also...so I really dont see why this would fail...
can you shed any light on this? I see you must have gotten some code working in the past in order to get the output you have shown.
Thanks in advance
bakd247
Newbie
*
Offline Offline

Activity: 13
Merit: 0


View Profile
March 31, 2025, 04:34:41 AM
Last edit: March 31, 2025, 05:06:59 AM by bakd247
 #12

for whatever reason...the ckey you have listed above is not decrypting properly using the code you have provided above:

I have a working version of this aes-256-cbc on my github page here:

https://github.com/bakd247/bitcoinCoreDecrypt

this version has been confirmed using core wallet dump from pywallet and has been checked against multiple core wallets and in fact works flawlessly if the correct information is entered accordingly
nc50lc
Legendary
*
Offline Offline

Activity: 2898
Merit: 7568


Self-proclaimed Genius


View Profile
March 31, 2025, 06:04:21 AM
 #13

for whatever reason...the ckey you have listed above is not decrypting properly using the code you have provided above:
Do you mean you used the "ckey" in the last example shared by @whanau with the code in the OP?
That certainly wont work since the code is for encrypting/decrypting the "mkey" which is required to decrypt the "ckey".
He didn't provided a code to decrypt the ckey, just example values.

whanau (OP)
Member
**
Offline Offline

Activity: 131
Merit: 45


View Profile
May 18, 2025, 12:03:33 AM
Merited by ABCbits (5), nc50lc (1)
 #14

For reference, here is the whole thing. It works for me and I will not be changing anything but bugs.
There are more elegant and slick ways of doing this, but this is the simplest way I could break it down
I Suggest you make your own wallet with a known password to experiment with.

Code:
import hashlib
import binascii
import pyaes
import os
import random

# Wallet software generates a random 32 byte master key. (How this is done is well documented and is not
# explained here). The master key needs to be encrypted to protect your funds. The one below is made up.
# You can substitute your own key below to try the software but DO NOT publish your own key anywhere.
mkey = binascii.unhexlify("5c5692daff165d3d32e5c05a56dde3b2d0ebc05f133f8d0616941e9abe7e0fb0")

# Wallet software will ask you for a password to encrypt the master key.
# You can change the password below for your own, but don't lose the b in front.
pw_txt = "alberto" #Thank you AlbertoBSD for Keyhunt!
password = b'alberto'

# Also needed is some 'randomness' in the form of a salt and an iteration count. These are generated by the wallet.
# Use the # to comment out the salt and iteration count below if testing your own values.

# create a random 8 byte salt and iteration count.
# salt = os.urandom(8)
# iterationcount = random.randint(2500, 50000)

# Now if you run the program several times, you will see you get different wallet keys for the same password.
# The random salt and iterations help prevent duplicate wallet keys for the same password.
# These two values are saved in wallet.dat so that the wallet key can be recreated (not decrypted) if you know the password.

# here you can substitute your own salt and iteration count from a wallet. Remove the # in front
salt = binascii.unhexlify('a49e804e25740714')
iterationcount = 91705

# Hashing with SHA512. This process generates the wallet key and IV. It is one-way. You cannot go back from
# here to recover the password, which is why it is so important you remember it.
# It is also essential that the password is strong because this is where dictionary attacks can take place.
# Remember the salt & iterations are recoverable from the wallet.dat files!

hash512 = password + salt
i = 0
while i < iterationcount:
    hash512 = hashlib.sha512(hash512).digest()
    i = i + 1

# The wallet key is the first 32 bytes of the hash result.
wallet_key = hash512[0:32]
# The IV (initialization vector) is the next 16 bytes of the hash
iv = hash512[32:48]

# We now have all we need to encrypt the master key
# Encryption with AES-256-CBC
encrypter = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(wallet_key, iv))
ciphertext = encrypter.feed(mkey)
ciphertext += encrypter.feed()
# The cipher text is what you see in the wallet.dat files as Mkey followed by 48 bytes
# There are also Ckey's which hold the private spend keys for each address.
#######################################################################################
# Your master private key is now encrypted!!
#######################################################################################
# Decryption is the reverse process using the same wallet key and IV to process the ciphertext (Mkey)

# Decryption with AES-256-CBC
decrypter = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(wallet_key, iv))
decryptedData = decrypter.feed(ciphertext)
decryptedData += decrypter.feed()
# The master key is now decrypted for all the world to see.
# Before we can spend any funds we need to decrypt the Ckeys which hold the private spend keys to the addresses.
# As you can see with no Ckey's, wallet recovery services cannot access your funds. It is safe to send Mkey ciphertext
# iteration count and salt but not the wallet itself as this has ALL the keys.
# ======================================================================================================================
# Now to decrypt a Ckey which holds the private spend key to an address. Here's one I made earlier.
# public address: 18sGovZjm83NhxXQb1bsxogCqXiYE5nXn7

ckey = binascii.unhexlify("316787cf83a3c9caeca2a2b20f0879edc2a8c60ed127453c00330d1ac009402d78eff292b03e588a3b858410ea2628ed")
public_key = binascii.unhexlify("027a098dbada15a831c66491cb20dee174d4971fdd9766a3b6c3dc64562cbb6524")
# Hash the public key twice
hash1 = hashlib.sha256(public_key).digest()
hash2 = hashlib.sha256(hash1).digest()
# The IV to the private key is the first 16 bytes of the hash result.
pk_iv = hash2[0:16]

# Decrypt the ciphertext Ckey with the master key and IV
decrypter = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(decryptedData, pk_iv))
decryptedckey = decrypter.feed(ckey)

# The plain text 32 byte raw key is now visible to the world!
# You can put the key into one of the online services and see the result is the compressed public address above.


# Display results
def printData(text, data):
    print(text + "(hex)   :" + data.hex())


print("Password      ", pw_txt)
printData("SHA512        ", hash512)
printData("Wallet Key    ", wallet_key)
printData("16 bytes IV   ", iv)
printData("Encrypting    ", mkey)
printData("Ciphertext    ", ciphertext)

# Decrypted data
printData("Master key    ", decryptedData)

# Ckey output
printData("\nCkey               :", ckey)
printData("Public Double hash :", hash2)
printData("PK IV              :", pk_iv)
printData("Private Key        :", decryptedckey)
printData("to public Key      :", public_key)

# If you have found this useful please consider a small BTC tip to bc1qg82720gvdyvnc3jycnun348fhs3qam44nsmeqm


Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!