Реалізація імпорту приватного ключа в мобільний криптокошелек
Імпорт приватного ключа — операція, яку користувач виконує один раз, але яка повинна бути реалізована безупречно. Ключ передається в hex або Base58, проходить через UI та оседає в сховище. Саме у цей момент частіше за все допускають помилки з пам'яттю та валідацією.
Що потрібно валідувати перед збереженням
Ethereum-ключ у hex: 32 байти, рівно 64 символи без префікса 0x або 66 з ним. Діапазон значення — від 1 до 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140 (порядок групи secp256k1). Ключ рівний нулю або що перевищує порядок групи — невалідний. Бібліотека @noble/secp256k1 виб'є виключення при спробі отримати публічний ключ з невалідного приватного — це гарне місце для перевірки.
Bitcoin WIF-формат: Base58Check з версійним байтом 0x80 (mainnet) або 0xEF (testnet), опціональний байт стиснення 0x01 в кінці. bitcoinjs-lib або WalletCore декодують та валідують checksum.
Solana: Base58 без checksum, 32 байти (Ed25519 scalar). Будь-яке значення від 1 до порядку Ed25519 групи валідне.
Безпека введення
TextField для приватного ключа повинен мати secureTextEntry = true (iOS) / inputType="textPassword" (Android). Це запобігає показу клавіатури з автокорекцією та запису у кеш клавіатури. autocorrect = false та spellCheck = false обов'язкові — інакше ключ потрапляє у словник клавіатури.
Після копіювання ключа з буфера через paste — негайно обнулити UITextField (показати ***...***) та запланувати очистку clipboard. В SwiftUI:
.onChange(of: keyInput) { value in
guard value.count >= 64 else { return }
processImport(value)
keyInput = "" // очищаємо поле
UIPasteboard.general.string = "" // чистимо clipboard
}
Після валідації — прямо до Secure Storage
Приватний ключ не можна залишати в пам'яті довше необхідного. Паттерн:
func importPrivateKey(_ hexKey: String) throws -> String {
let keyData = try validateAndDecodeHex(hexKey)
defer { keyData.withUnsafeMutableBytes { $0.baseAddress?.initializeMemory(as: UInt8.self, repeating: 0, count: keyData.count) } }
let address = try deriveAddress(from: keyData)
try keychain.store(keyData, identifier: "pk_\(address)")
return address
}
defer з обнуленням буфера — мінімальний TTL ключа в пам'яті. На Android аналогічно: Arrays.fill(keyBytes, 0) в finally.
Процес
Реалізація займає 1–3 дня: валідація формату під потрібні блокчейни, безпечний введення, збереження в Keychain/Keystore, деривація адреси для підтвердження користувачу. Окремо тестуємо граничні випадки: ключ поза діапазоном, WIF з невірною checksum, введення з пробілами.







