Export Wallets and Keys
Turnkey's export functionality allows your end users to backup or transfer a Wallet by securely viewing the wallet's mnemonic phrase. We engineered this feature to ensure that the user can export their mnemonic or private key without exposing the mnemonic itself to Turnkey or your application.
Follow along with the CLI or Embedded iframe guide.
CLI
Install the latest version of Turnkey CLI to access the new export functionality. You can find detailed instructions for installation here.
Steps
- Generate an encryption key:
turnkey generate encryption-key \
--user $USER_ID \
--organization $ORGANIZATION_ID \
--encryption-key-name "demo-enc"
- The
--user
flag (required) is the id of the user exporting the private key; this is required because the underlying encryption keys used for import are scoped to each user. - The
--encryption-key-name
(optional) is the name of the encryption key that you will use in Step 2. By default, the encryption key's name is "default".
- Export private key:
turnkey wallets export \
--user $USER_ID \
--name "demo key" \
--export-bundle-output "./export_bundle.txt" \
--encryption-key-name "demo-enc" \
--key-name demo
- The
--export-bundle-output
(required) flag is the desired output file path for the "export bundle” that will be sent from Turnkey. - The
--encryption-key--name
(optional) flag specifies the one-time encryption key that you will use to export the wallet or private key. - Reminder: The
--key-name
(optional) flag specifies the name of API key with which to interact with the Turnkey API service. This should be the name of a previously created key. If you do not have one, visit the quickstart guide for help creating one.
- Decrypt without saving plaintext to filesystem. This can be done offline:
turnkey decrypt \
--export-bundle-input "./export_bundle.txt"
- The
--export-bundle-input
(required) flag is the desired input file path for the "export bundle” that was sent from Turnkey in Step 2. The export bundle” contains the ephemeral public key generated by the CLI as part of the shared secret computation with the Turnkey signer enclave. It also contains the ciphertext, which is the plaintext input encrypted by the Turnkey signer’s ephemeral public key. - The
--plaintext-output
(optional) flag is the desired output file path for the private key plaintext. If you don't pass this flag, the plaintext will be written to stdout.
Private Key support
Turnkey CLI also supports exporting private keys. Follow the same steps as exporting a wallet via CLI but use the turnkey private-keys
commands instead. In Step 3 (decrypt
), pass a --key-format
flag for key-specific formatting; the options for private keys are:
hexadecimal
: Used for Ethereum. Examples: 0x13eff5b3f9c63eab5d53cff5149f01606b69325496e0e98b53afa938d890cd2e, 13eff5b3f9c63eab5d53cff5149f01606b69325496e0e98b53afa938d890cd2esolana
: Used for Solana. It’s a base58-encoding of the concatenation of the private key and public key bytes. Example: 2P3qgS5A18gGmZJmYHNxYrDYPyfm6S3dJgs8tPW6ki6i2o4yx7K8r5N8CF7JpEtQiW8mx1kSktpgyDG1xuWNzfsM
turnkey decrypt \
--export-bundle-input "./export_bundle.txt" \
--key-format “hexadecimal”
Embedded iframe
- We have released open-source code to create target encryption keys and decrypt exported wallet mnemonics. We've deployed a static HTML page hosted on
export.turnkey.com
meant to be embedded as an iframe element (see the code here). This ensures the mnemonics are encrypted to keys that the user has access to, but that your organization does not (because they live in the iframe, on a separate domain). - We have also built a package to help you insert this iframe and interact with it in the context of export:
@turnkey/iframe-stamper
In the rest of this guide we'll assume you are using these helpers.
Steps
Here's a diagram summarizing the wallet export flow step-by-step (direct link):
Let's review these steps in detail:
When a user on your application clicks "export", display a new export UI. We recommend setting this export UI as a new hosted page of your application that contains language explaining the security best practices users should follow once they've successfully exported their wallet. Remember: once the wallet has been exported, Turnkey can no longer ensure its security.
While the UI is in a loading state, your application uses
@turnkey/iframe-stamper
to insert a new iframe element:const iframeStamper = new IframeStamper({
iframeUrl: "https://export.turnkey.com",
// Configure how the iframe element is inserted on the page
iframeContainer: yourContainer,
iframeElementId: "turnkey-iframe",
});
// Inserts the iframe in the DOM. This creates the new encryption target key
const publicKey = await iframeStamper.init();
// Set state to not display iframe
let displayIframe = "none";
return (
// The iframe element can be hidden until the wallet is exported
<div style={{ display: displayIframe }} />
);Your code receives the iframe public key. Your application prompts the user to sign a new
EXPORT_WALLET
activity with the wallet ID and the iframe public key in the parameters.Your application polls for the activity response, which contains an export bundle. Remember: this export bundle is an encrypted mnemonic which can only be decrypted within the iframe.
Need help setting up async polling? Checkout our guide and helper here.
Your application injects the export bundle into the iframe for decryption and displays the iframe upon success:
// Inject export bundle into iframe
let success = await iframeStamper.injectWalletExportBundle(exportBundle);
if (success !== true) {
throw new Error("unexpected error while injecting export bundle");
}
// If successfully injected, update the state to display the iframe
iframeDisplay = "block";
Export is complete! The iframe now displays a sentence of words separated by spaces.
The exported wallet will remain stored within Turnkey’s infrastructure. In your Turnkey dashboard, the exported user Wallet will be flagged as “Exported”.
UI customization
Everything is customizable in the export iframe except the sentence of mnemonic words, which is minimally styled: the text is left-aligned and the padding and margins are zero. Here's an example of how you can configure the styling of the iframe.
const iframeCss = `
iframe {
box-sizing: border-box;
width: 400px;
height: 120px;
border-radius: 8px;
border-width: 1px;
border-style: solid;
border-color: rgba(216, 219, 227, 1);
padding: 20px;
}
`;
return (
<div style={{ display: iframeDisplay }} id="your-container">
<style>{iframeCss}</style>
</div>
);
Export as Private Keys
Turnkey also supports exporting Wallet Accounts and Private Keys as private keys.
Wallet Accounts
Follow the same steps above for exporting Wallets as mnemonics, but instead use the EXPORT_WALLET_ACCOUNT
activity and the injectKeyExportBundle
method from the @turnkey/iframe-stamper
.
Private Keys
Follow the same steps above for exporting Wallets as mnemonics, but instead use the EXPORT_PRIVATE_KEY
activity and the injectKeyExportBundle
method from the @turnkey/iframe-stamper
. You can pass an optional keyFormat
to injectKeyExportBundle(keyFormat)
that will apply either hexadecimal
or solana
formatting to the private key that is exported in the iframe. The default key format is hexadecimal
, which is used by MetaMask, MyEtherWallet, Phantom, Ledger, and Trezor for Ethereum keys. For Solana keys, you will need to pass the solana
key format.
At the end of a successful private key export, the iframe displays a private key.
Cryptographic details
Turnkey's export functionality ensures that neither your application nor Turnkey can view the wallet mnemonic or private key.
It works by anchoring export in a target encryption key (TEK). This target encryption key is a standard P-256 key pair and can be created in many ways: completely offline, or online inside of script using the web crypto APIs.
The following diagram summarizes the flow:
The public part of this key pair is passed as a parameter inside of a signed EXPORT_WALLET
, EXPORT_PRIVATE_KEY
, or EXPORT_WALLET_ACCOUNT
activity.
Our enclave encrypts the wallet's mnemonic or private key to the user's TEK using the Hybrid Public Key Encryption standard, also known as HPKE or RFC 9180.
Once the activity succeeds, the encrypted mnemonic or private key can be decrypted by the target public key offline or in an online script.
Solana notes
Solana paths do not include an index
. Creating a wallet account with an index specified could lead to unexpected behavior when exporting and importing into another wallet.
When importing into a multichain wallet such as Phantom, see this guide on matching private keys across Solana, Ethereum, and Polygon.