Skip to content

Key Import and Export

← Back to KMD Client

This example demonstrates how to import and export keys using the KMD importKey() and exportKey() methods. Key concepts:

  • Importing externally generated keys into a wallet
  • Exporting private keys from a wallet
  • Understanding that imported keys are NOT backed up by the master derivation key
  • LocalNet running (via algokit localnet start)
  • Covered operations:
  • importKey() - Import an external private key into the wallet
  • exportKey() - Export a private key from the wallet

From the repository root:

Terminal window
cd examples
npm run example kmd_client/05-key-import-export.ts

View source on GitHub

05-key-import-export.ts
/**
* Example: Key Import and Export
*
* This example demonstrates how to import and export keys using the KMD
* `importKey()` and `exportKey()` methods.
*
* Key concepts:
* - Importing externally generated keys into a wallet
* - Exporting private keys from a wallet
* - Understanding that imported keys are NOT backed up by the master derivation key
*
* Prerequisites:
* - LocalNet running (via `algokit localnet start`)
*
* Covered operations:
* - importKey() - Import an external private key into the wallet
* - exportKey() - Export a private key from the wallet
*/
import nacl from 'tweetnacl'
import {
cleanupTestWallet,
createKmdClient,
createTestWallet,
printError,
printHeader,
printInfo,
printStep,
printSuccess,
} from '../shared/utils.js'
/**
* Format a byte array for display, showing first and last few bytes for security
*/
function formatBytesForDisplay(bytes: Uint8Array, showFirst = 4, showLast = 4): string {
const hex = Buffer.from(bytes).toString('hex')
if (bytes.length <= showFirst + showLast) {
return hex
}
const firstBytes = hex.slice(0, showFirst * 2)
const lastBytes = hex.slice(-(showLast * 2))
return `${firstBytes}...${lastBytes}`
}
/**
* Compare two Uint8Arrays for equality
*/
function arraysEqual(a: Uint8Array, b: Uint8Array): boolean {
if (a.length !== b.length) return false
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false
}
return true
}
async function main() {
printHeader('KMD Key Import and Export Example')
const kmd = createKmdClient()
let walletHandleToken = ''
const walletPassword = 'test-password'
try {
// =========================================================================
// Step 1: Create a Test Wallet
// =========================================================================
printStep(1, 'Creating a test wallet for key import/export')
const testWallet = await createTestWallet(kmd, walletPassword)
walletHandleToken = testWallet.walletHandleToken
printSuccess(`Test wallet created: ${testWallet.walletName}`)
printInfo(`Wallet ID: ${testWallet.walletId}`)
// =========================================================================
// Step 2: Create a Random Account Using nacl
// =========================================================================
printStep(2, 'Creating a random account to get a private key')
// Generate a random ed25519 keypair using tweetnacl
// - publicKey: 32 bytes (used to derive the Algorand address)
// - secretKey: 64 bytes (the private key used for signing)
const keypair = nacl.sign.keyPair()
const originalPrivateKey = keypair.secretKey
printSuccess('Random keypair generated!')
printInfo('')
printInfo('Keypair details:')
printInfo(` Public key (32 bytes): ${formatBytesForDisplay(keypair.publicKey)}`)
printInfo(` Private key (64 bytes): ${formatBytesForDisplay(originalPrivateKey)}`)
printInfo('')
printInfo('Note: The ed25519 private key is 64 bytes because it contains')
printInfo(' both the 32-byte private seed AND the 32-byte public key.')
// =========================================================================
// Step 3: Import the Private Key into the Wallet
// =========================================================================
printStep(3, 'Importing the private key with importKey()')
const importResult = await kmd.importKey({
walletHandleToken,
privateKey: originalPrivateKey,
})
printSuccess('Key imported successfully!')
printInfo('')
printInfo('ImportKeyResponse fields:')
printInfo(` address: ${importResult.address}`)
printInfo('')
printInfo('The imported address is derived from the public key portion')
printInfo('of the private key that was imported.')
const importedAddress = importResult.address
// =========================================================================
// Step 4: Verify the Key is in the Wallet
// =========================================================================
printStep(4, 'Verifying the key is in the wallet')
const listResult = await kmd.listKeysInWallet({
walletHandleToken,
})
const keyFound = listResult.addresses.some((addr) => addr.toString() === importedAddress.toString())
if (keyFound) {
printSuccess('Imported key found in wallet!')
} else {
printError('Imported key not found in wallet list')
}
printInfo(`Wallet contains ${listResult.addresses.length} key(s)`)
// =========================================================================
// Step 5: Export the Private Key
// =========================================================================
printStep(5, 'Exporting the private key with exportKey()')
const exportResult = await kmd.exportKey({
walletHandleToken,
address: importedAddress,
walletPassword,
})
const exportedPrivateKey = exportResult.privateKey
printSuccess('Key exported successfully!')
printInfo('')
printInfo('ExportKeyResponse fields:')
printInfo(` privateKey (64 bytes): ${formatBytesForDisplay(exportedPrivateKey)}`)
printInfo('')
printInfo('Note: Exporting private keys requires the wallet password for security.')
// =========================================================================
// Step 6: Verify the Exported Key Matches the Original
// =========================================================================
printStep(6, 'Verifying the exported key matches the original')
const keysMatch = arraysEqual(originalPrivateKey, exportedPrivateKey)
if (keysMatch) {
printSuccess('Exported key matches the original key!')
printInfo('')
printInfo('Verification details:')
printInfo(` Original key length: ${originalPrivateKey.length} bytes`)
printInfo(` Exported key length: ${exportedPrivateKey.length} bytes`)
printInfo(` Keys are identical: true`)
} else {
printError('Keys do not match!')
printInfo('')
printInfo(`Original key: ${formatBytesForDisplay(originalPrivateKey)}`)
printInfo(`Exported key: ${formatBytesForDisplay(exportedPrivateKey)}`)
}
// =========================================================================
// Important Note About Imported Keys
// =========================================================================
printStep(7, 'Important note about imported keys')
printInfo('')
printInfo('IMPORTANT: Imported keys are NOT backed up by the master derivation key!')
printInfo('')
printInfo('When you import a key:')
printInfo(' - The key is stored in the wallet database')
printInfo(' - It can be used for signing transactions')
printInfo(' - It can be exported using exportKey()')
printInfo('')
printInfo('However, imported keys CANNOT be recovered by:')
printInfo(' - Restoring the wallet from its mnemonic/MDK')
printInfo(' - Using generateKey() (which only regenerates derived keys)')
printInfo('')
printInfo('To backup imported keys, you must either:')
printInfo(' 1. Export the key and store it securely')
printInfo(' 2. Backup the entire wallet database file')
// =========================================================================
// Cleanup
// =========================================================================
printStep(8, 'Cleaning up test wallet')
await cleanupTestWallet(kmd, walletHandleToken)
walletHandleToken = '' // Mark as cleaned up
printSuccess('Test wallet handle released')
// =========================================================================
// Summary
// =========================================================================
printHeader('Summary')
printInfo('This example demonstrated key import and export in KMD:')
printInfo('')
printInfo(' 1. importKey() - Import an external private key into a wallet')
printInfo(' Parameters: walletHandleToken, privateKey (64-byte Uint8Array)')
printInfo(' Returns: address of the imported key')
printInfo('')
printInfo(' 2. exportKey() - Export a private key from a wallet')
printInfo(' Parameters: walletHandleToken, address, walletPassword')
printInfo(' Returns: privateKey (64-byte Uint8Array)')
printInfo('')
printInfo('Key takeaways:')
printInfo(' - Private keys are 64 bytes (32-byte seed + 32-byte public key)')
printInfo(' - Importing returns the corresponding Algorand address')
printInfo(' - Exporting requires the wallet password for security')
printInfo(' - Imported keys are NOT protected by the wallet mnemonic/MDK')
printInfo(' - Always backup imported keys separately!')
printInfo('')
printInfo('Note: The test wallet remains in KMD (wallets cannot be deleted via API).')
} catch (error) {
printError(`Error: ${error instanceof Error ? error.message : String(error)}`)
printInfo('')
printInfo('Troubleshooting:')
printInfo(' - Ensure LocalNet is running: algokit localnet start')
printInfo(' - If LocalNet issues occur: algokit localnet reset')
printInfo(' - Check that KMD is accessible on port 4002')
// Cleanup on error
if (walletHandleToken) {
await cleanupTestWallet(kmd, walletHandleToken)
}
process.exit(1)
}
}
main().catch((error) => {
console.error('Fatal error:', error)
process.exit(1)
})