wallet.aes.json is the file a Blockchain.info or Blockchain.com "My Wallet" backup lives in. Almost every "decrypt failed" message people bring us starts with a misunderstanding of what that file is, because one filename covers several different wrapper formats and two separate layers of encryption. This is a reference page: what the file is, how to tell which version you are holding, how it is encrypted, and what each record inside a decrypted wallet means. If you want the diagnostic steps for a backup that will not open, they are in Blockchain.com backup file won't decrypt, and the service page is Blockchain.com wallet recovery.
What is a wallet.aes.json file?
Locked out of a wallet like this one?
I repair hardware wallets, crack forgotten passwords, and rebuild broken seed phrases — on my own bench in Denver.
A wallet.aes.json file is one AES-encrypted JSON payload that holds an entire Blockchain wallet: its private keys, its HD seed, its address labels, and its transaction notes. It is not a text file you can read, it does not contain your balance, and it is not tied to Blockchain's servers once you have it. Your balance lives on the blockchain; the file holds the keys that prove the coins are yours, which is why a copy of the file is a copy of the money.
Unlike a desktop wallet's database, this file was designed for a hosted wallet. Blockchain kept the encrypted blob and handed it back to your browser, which decrypted it locally with your password. wallet.aes.json is that same blob saved to disk, either through the wallet's own backup export or pulled from Blockchain's servers with your Wallet ID. That origin explains the file's two most confusing traits: it is often a single unbroken line of base64 with no structure a person can see, and it can carry a second password that the login password never touches.
Which format do I have? Check the outer shape
You do not need the password to answer this, and the answer changes which tool will open the file. There are three outer shapes, all called wallet.aes.json.
| Shape | Era | What it looks like | How it is read |
|---|---|---|---|
| Raw (v0 / v1) | Earliest Blockchain.info wallets | One long run of base64, no { and no JSON around it |
Treated as raw encrypted data |
| Config wrapper | Some server exports | JSON with a payload field but no version field |
The real wallet is nested inside payload |
| Versioned (v2 / v3 / v4) | Modern wallets | JSON {"version":N,"pbkdf2_iterations":I,"payload":"<base64>"} |
payload is the encrypted blob |
Blockchain's own open-source wallet decides this the same way: it tries to parse the file as JSON, and "if parsing fails, treats as version 1 (raw encrypted string)"; if a wrapper is present it reads wrapper.version, wrapper.pbkdf2_iterations, and wrapper.payload (My-Wallet-V3 wallet-crypto.js, decryptWalletSync). A config file has no version attribute and instead "encapsulate[s] the wallet file plus some detritus", so the encrypted wallet has to be pulled out of its payload field first (btcrecover btcrpass.py, _parse_encrypted_blockchain_wallet).
This is the single most common reason a "good tool" reports a good file as broken: a decoder built for a versioned wallet is handed a raw v1 blob, or handed a config wrapper it does not know to unwrap, and fails on a file whose coins are perfectly intact.
If you would rather not judge this by eye, our free Blockchain.com backup file checker reports the shape, the version, and the iteration count, and, most usefully, whether the encrypted payload is complete or has lost bytes. It runs entirely in your browser, nothing is uploaded, and it never needs your password.
The version numbers, and what they actually mean
The version field inside a modern wrapper is the encryption version, not the wallet's feature set. Blockchain's public library defines SUPPORTED_ENCRYPTION_VERSION = 3, and its decoder "throws error if version > SUPPORTED_ENCRYPTION_VERSION" (My-Wallet-V3 wallet-crypto.js). Recovery tooling goes one further: btcrecover raises NotImplementedError only when data["version"] > 4, and its second-password handling reads a v4+ format: one xpriv per derivation/script-type, so a version 4 layout with per-derivation extended keys does appear in newer exports (btcrecover btcrpass.py).
For recovery, only two things about the version matter:
- The iteration count travels with the file, not the version. The wrapper carries its own
pbkdf2_iterations, and that number, not the version label, sets how fast passwords can be tested. - A v0 / v1 raw file states no iteration count at all. The earliest wallets used a fixed low count, so a recovery run has to assume it. btcrecover tries
10iterations first for these, then falls back to older schemes.
If you know roughly what year the wallet was created, that alone usually tells you whether you are dealing with a raw early file or a modern versioned one.
How wallet.aes.json is encrypted
Every versioned Blockchain backup is encrypted the same way, and the details are worth knowing because they explain both why the file is recoverable and why generic tools mishandle it.
From Blockchain's wallet-crypto.js:
- The password is stretched with PBKDF2 using SHA1 (
stretchPassword), producing a 256-bit key (KEY_BIT_LEN = 256). - The cipher is AES-256-CBC (
AES.CBC = 'aes-256-cbc'). - The padding is ISO 10126 (
Iso10126): random filler bytes with a final byte that carries the padding length. - The first 16 bytes of the decoded payload are the salt/IV (
SALT_BYTES = 16), and the code notes the "AES initialization vector is also used as the salt in password stretching."
That last point is the one to remember: the first sixteen bytes of the decoded payload are simultaneously the PBKDF2 salt and the AES-CBC initialization vector. btcrecover splits the file exactly this way, iv, encrypted = decoded[:16], decoded[16:], and derives the key with pbkdf2_hmac("sha1", password, salt_and_iv, iter_count, 32) (btcrecover btcrpass.py, decrypt_current).
The default for a new modern wallet is 5,000 PBKDF2-SHA1 iterations (defaultPbkdf2Iterations, from the merged getDefaultWalletOptions() in blockchain-wallet.js). Five thousand SHA1 rounds per guess is what makes a GPU the practical tool for a forgotten password and what makes the wallet's stored iteration count, rather than password length alone, set the pace. The full brute-force reasoning is in how I brute-force Blockchain.info wallets.
The ISO 10126 padding is why a "decrypt failed" message is so often misleading. Generic AES libraries expect PKCS#7 padding and reject the real final block even when the password is correct, so a tool that validates PKCS#7 will call a perfectly good decryption a failure. A wrong password and a padding-scheme mismatch produce the identical error, which is a large part of why people spend weeks attacking a password that was never the problem.
The oldest v0 / v1 raw files predate this settled scheme. Blockchain's own code tries several methods in order for them: CBC with ISO 10126 at 10 iterations, then OFB with no padding at 1 iteration, then two more variants (wallet-crypto.js, the decryptFns list). If a very old backup will not open in a modern tool, this is usually why: the tool never tries the old modes.
What is inside a decrypted wallet
Once the outer password decrypts the payload, the plaintext is a JSON wallet object. Recovery tools recognise a correct decryption by looking for these field names in the first block, which is also the cleanest list of what a Blockchain wallet contains. btcrecover matches on: guid, sharedKey, double_encryption, dpasswordhash, metadataHDNode, options, address_book, tx_notes, tx_names, keys, hd_wallets, paidTo, and tag_names (btcrecover btcrpass.py, matchStrings).
The ones that matter for recovery:
| Field | Meaning |
|---|---|
guid |
The wallet identifier, the same GUID as your Wallet ID |
sharedKey |
A per-wallet secret used as the salt for the second-password hash. Not your password |
double_encryption |
Boolean. true means a second password is set and the keys are locked behind it |
dpasswordhash |
The stored verifier for the second password, a 32-byte hex hash |
options |
Wallet settings, including pbkdf2_iterations, fee_per_kb, logout_time |
keys |
The array of imported / legacy addresses, each with a priv private key |
hd_wallets |
The HD wallet(s): each has accounts, each account an xpriv (v3) or per-derivation xpriv (v4) |
address_book |
Saved send-to addresses and their labels |
tx_notes / tx_names |
Your private notes and names on transactions |
metadataHDNode |
The key for Blockchain's metadata service |
Two practical points follow from this list.
The keys array and the hd_wallets array are two different pots of coins. Old Blockchain wallets held a flat list of individually generated keys; the HD upgrade added hd_wallets with a seed behind it. A wallet can hold both, and a restore that recovers one but not the other will show a balance that is too low. The version marker Blockchain uses internally is exactly this distinction, "version 2.0 (legacy, non-HD) versus 3.0 (HD-upgraded)" (wallet.js).
A note on where the fields live. btcrecover's own comment records that the layout has drifted: the guid "no longer appears in the first block" as of May 2020, and tx_notes appears there instead. This is harmless for recovery but explains why a check that hunts for one specific field name can miss a good decryption of a newer file.
The second password, and "I can log in but can't spend"
This is the trap that catches more Blockchain users than any other, and it is a genuinely separate layer of encryption.
When double_encryption is true, your main password decrypts the wallet structure, but every private key stays encrypted behind a second password. You can log in, see your addresses and balance, and still be unable to move a single satoshi, because the priv, xpriv, and seedHex fields are individually encrypted (blockchain-wallet.js, the encrypt() method).
The second password is verified, not stored. Blockchain computes passwordHash = hashNTimes(sharedKey + inputString, iterations) and compares it to the saved dpasswordhash (blockchain-wallet.js, validateSecondPassword). That hash is iterated SHA-256 of the shared key concatenated with your second password, using the wallet's options.pbkdf2_iterations count; older wallets used a single SHA-256 hash or exactly ten (btcrecover btcrpass.py, WalletBlockchainSecondpass). Note that the second-password verifier uses plain iterated SHA-256, while the per-key field encryption uses the same PBKDF2-SHA1 and AES-CBC as the outer layer, keyed on sharedKey + password.
Three consequences matter if this is you:
- The second password is recoverable independently. Because
dpasswordhashis a verifier, a search can test candidate second passwords against it offline without ever touching Blockchain, exactly as an outer-password search does. sharedKeyis the salt, not a secret you set. It comes out of the wallet once the outer password is known, which is why the outer password has to be recovered first.- Two forgotten passwords is two jobs, in order. The outer one unlocks the structure; the second one unlocks the coins. Neither substitutes for the other.
The legacy mnemonic is not a modern seed phrase
Blockchain's older HD wallets used a recovery phrase that does not behave like a standard BIP-39 seed, which is why one so often "restores an empty or wrong wallet" in another app. That is a distinct problem with its own reference: recovering a Blockchain.com Wallet ID and the legacy mnemonic, and the general "right seed, wrong derivation path" case is covered in seed phrase restored an empty wallet. The short version: a phrase that restores nothing is usually a derivation-path or format problem, not proof the coins are gone.
What wallet.aes.json is not
It is not a downloadable balance. Searches for a "wallet.aes.json with funds" reach files that are bait: encrypted with a password you will never have, empty of spendable coins, carrying malware, or set up so any coins you move are swept away by a script watching the address. There is no honest version of this. If you are looking because you are trying to recover your own money, the pages above are the real path, and how to spot a fake recovery service covers what happens to people who go looking in that part of the internet.
It is not readable without the password. "Decrypt failed" is the same message for a wrong password, a truncated file, and a wrong-format tool. Settling which of the three you have is the whole first hour of a real case, and it is what the backup file checker exists to answer without a password.
It is not safe to hand to a stranger. A copy of the decrypted wallet is a copy of the coins. Nobody legitimate needs your wallet file to give you a quote, and nobody legitimate contacts you first. That commitment, and what to check before trusting anyone with a wallet file, is on the scam warning page.
If you are stuck
A wallet.aes.json written years ago still holds spendable keys today. The reasons it will not open are almost always a recoverable password, a wrong-format tool, or a file that lost bytes when it was copied out of an email or a chat, and those are different jobs with different answers. Identify the shape and version first, work only on a copy, and never let the original be overwritten.
If you would rather not do that yourself, Blockchain.com wallet recovery is a service we run, and the wallet and key recovery tools survey lists the software worth trying first if you want to attempt it on your own.
Sources
- Blockchain
My-Wallet-V3src/wallet-crypto.js, forSUPPORTED_ENCRYPTION_VERSION = 3,SALT_BYTES = 16,KEY_BIT_LEN = 256,BLOCK_BIT_LEN = 128, the version-detection logic indecryptWalletSync, PBKDF2-SHA1 instretchPassword, AES-256-CBC with ISO 10126 padding, the IV doubling as the PBKDF2 salt, and the v0/v1decryptFnsfallback list: https://github.com/blockchain/My-Wallet-V3/blob/master/src/wallet-crypto.js (retrieved 2026-09-15) - Blockchain
My-Wallet-V3src/blockchain-wallet.js, for the top-level wallet fields, theoptionssub-fields, the default of 5,000 PBKDF2 iterations for new wallets, and the second-password mechanism (validateSecondPassword,hashNTimes(sharedKey + password, iterations),dpasswordhash, and theencrypt()/decrypt()ofpriv/xpriv/seedHex): https://github.com/blockchain/My-Wallet-V3/blob/master/src/blockchain-wallet.js (retrieved 2026-09-15) - Blockchain
My-Wallet-V3src/wallet.js, for the version-2.0 (legacy, non-HD) versus version-3.0 (HD-upgraded) distinction: https://github.com/blockchain/My-Wallet-V3/blob/master/src/wallet.js (retrieved 2026-09-15) - btcrecover
btcrecover/btcrpass.py,WalletBlockchainandWalletBlockchainSecondpass, for_parse_encrypted_blockchain_wallet(JSON wrapper vs config vs raw v0, theversion > 4limit, thepbkdf2_iterationsread, the base64 decode and length/entropy checks),decrypt_current(PBKDF2-HMAC-SHA1, salt/IV split at 16 bytes, AES-256-CBC, ISO 10126), the default of 10 iterations for v0 files, thematchStringsfield list with the note thatguidleft the first block in May 2020, and the second-password hash (iterated SHA-256 ofsharedKey + password, or a single / ten-round scheme in older wallets) with per-derivationxprivin v4: https://github.com/3rdIteration/btcrecover/blob/master/btcrecover/btcrpass.py (retrieved 2026-09-15)
