EOA Link to heading

(EOA)钱包核心功能 Link to heading

  • 账户管理: 创建账号、导入导出
    • 私钥、助记词、Keystore、MPC
  • 构造交易、签名、发送交易
  • 其他:展示资产价值、交易记录、钱包浏览器、资产交易等

EOA 账号创建 Link to heading

  • 本质是一个 32 字节的私钥(随机数)

• 找到一个安全的熵源(不可预测、不可重复)作为私钥,如掷硬币256次。

• 使用 secp256k1椭圆曲线算法计算出公钥

• secp256k1 : 比特币和以太坊

• Secp256r1: Passkey SSH

• Ed25519 : Solana

• 对公钥进行keccak256 hash运算再取后40位得到

分层确定性推倒 Link to heading

  • 随机生成的私钥,备份麻烦,不易管理

  • BIP32 提案(HD钱包):由同一个种子,就可以生成无数个私钥

和地址

  • BIP44 提案: 给bip32的路径赋予意义来支持做多币种、多地址

  • BIP39: 使用助记词的方式,生成种子

KEYSTORE 加密私钥 Link to heading

  • Keystore 文件就是一种以加密的方式存储密钥的文件(加强安全性),签名前先解析出私钥。

  • 原理: 使用 scrypt 算法从密码派生加密密钥, 用加密密钥使用 AES-128-CTR 算法加密私钥。

Keystore 生成及校验

import crypto from 'crypto'
import fs from 'fs/promises'
import path from 'path'

class KeystoreUtils {
    /**
     * 加密私钥并生成 KeyStore 文件
     * @param {string} privateKey - 私钥(带0x前缀)
     * @param {string} password - 密码
     * @returns {Object} KeyStore 对象
     */
    static async encryptPrivateKey(privateKey, password) {
        try {
            // 1. 生成随机 salt
            const salt = crypto.randomBytes(32)
            
            // 2. 使用 scrypt 派生密钥
            const derivedKey = crypto.scryptSync(password, salt, 32, {
                N: 8192,
                r: 8,
                p: 1
            })
            
            // 3. 生成 IV
            const iv = crypto.randomBytes(16)
            
            // 4. 加密私钥
            // 使用派生密钥的前16字节作为AES-128的密钥
            const cipher = crypto.createCipheriv('aes-128-ctr', derivedKey.slice(0, 16), iv)
            const privateKeyBuffer = Buffer.from(privateKey.slice(2), 'hex')
            const ciphertext = Buffer.concat([
                cipher.update(privateKeyBuffer),
                cipher.final()
            ])
            
            // 5. 计算 MAC
            const mac = crypto.createHash('sha3-256')
                .update(Buffer.concat([derivedKey.slice(16, 32), ciphertext]))
                .digest()
            
            // 6. 返回 KeyStore 对象
            return {
                crypto: {
                    cipher: 'aes-128-ctr',
                    cipherparams: { iv: iv.toString('hex') },
                    ciphertext: ciphertext.toString('hex'),
                    kdf: 'scrypt',
                    kdfparams: {
                        dklen: 32,
                        n: 8192,
                        p: 1,
                        r: 8,
                        salt: salt.toString('hex')
                    },
                    mac: mac.toString('hex')
                },
                id: crypto.randomUUID(),
                version: 3
            }
        } catch (error) {
            console.error('加密私钥失败:', error)
            throw error
        }
    }

    /**
     * 从 KeyStore 文件解密私钥
     * @param {Object} keystore - KeyStore 对象
     * @param {string} password - 密码
     * @returns {string} 解密后的私钥(带0x前缀)
     */
    static async decryptPrivateKey(keystore, password) {
        try {
            const { crypto: cryptoData } = keystore
            
            // 1. 从 KeyStore 获取参数
            const { kdfparams, cipherparams, ciphertext, mac } = cryptoData
            const { salt } = kdfparams
            const { iv } = cipherparams
            
            // 2. 使用 scrypt 派生密钥
            const derivedKey = crypto.scryptSync(
                password,
                Buffer.from(salt, 'hex'),
                kdfparams.dklen,
                {
                    N: kdfparams.n,
                    r: kdfparams.r,
                    p: kdfparams.p
                }
            )
            
            // 3. 验证 MAC
            const calculatedMac = crypto.createHash('sha3-256')
                .update(Buffer.concat([derivedKey.slice(16, 32), Buffer.from(ciphertext, 'hex')]))
                .digest()
            
            if (calculatedMac.toString('hex') !== mac) {
                throw new Error('密码错误或 KeyStore 文件已损坏')
            }
            
            // 4. 解密私钥
            const decipher = crypto.createDecipheriv(
                'aes-128-ctr',
                derivedKey.slice(0, 16),  // 使用派生密钥的前16字节
                Buffer.from(iv, 'hex')
            )
            
            const decrypted = Buffer.concat([
                decipher.update(Buffer.from(ciphertext, 'hex')),
                decipher.final()
            ])
            
            return '0x' + decrypted.toString('hex')
        } catch (error) {
            console.error('解密私钥失败:', error)
            throw error
        }
    }

    /**
     * 保存 KeyStore 文件
     * @param {Object} keystore - KeyStore 对象
     * @param {string} filePath - 保存路径
     */
    static async saveKeystore(keystore, filePath) {
        try {
            // 确保目录存在
            await fs.mkdir(path.dirname(filePath), { recursive: true })
            
            await fs.writeFile(
                filePath,
                JSON.stringify(keystore, null, 2),
                'utf8'
            )
            console.log('KeyStore 文件已保存到:', filePath)
        } catch (error) {
            console.error('保存 KeyStore 文件失败:', error)
            throw error
        }
    }

    /**
     * 加载 KeyStore 文件
     * @param {string} filePath - KeyStore 文件路径
     * @returns {Object} KeyStore 对象
     */
    static async loadKeystore(filePath) {
        try {
            const data = await fs.readFile(filePath, 'utf8')
            return JSON.parse(data)
        } catch (error) {
            console.error('加载 KeyStore 文件失败:', error)
            throw error
        }
    }
}

export default KeystoreUtils 
import KeystoreUtils from './keystore_utils.js'
import path from 'path'
import dotenv from 'dotenv'

dotenv.config()

async function demo() {
    try {
        // 1. 从环境变量获取私钥
        const privateKey = process.env.PRIVATE_KEY
        if (!privateKey) {
            throw new Error('请在 .env 文件中设置 PRIVATE_KEY')
        }
        console.log('原始私钥:', privateKey)

        // 2. 设置密码和文件路径
        const password = '123456'
        const keystorePath = path.join(process.cwd(), '.keys', 'keystore.json')

        // 3. 加密私钥并生成 KeyStore
        console.log('\n开始加密私钥...')
        const keystore = await KeystoreUtils.encryptPrivateKey(privateKey, password)
        console.log('KeyStore 生成成功')

        // 4. 保存 KeyStore 文件
        await KeystoreUtils.saveKeystore(keystore, keystorePath)

        // 5. 加载 KeyStore 文件
        console.log('\n开始加载 KeyStore 文件...')
        const loadedKeystore = await KeystoreUtils.loadKeystore(keystorePath)
        console.log('KeyStore 文件加载成功')

        // 6. 解密私钥
        console.log('\n开始解密私钥...')
        const decryptedPrivateKey = await KeystoreUtils.decryptPrivateKey(loadedKeystore, password)
        console.log('解密后的私钥:', decryptedPrivateKey)
        console.log('私钥是否一致:', privateKey === decryptedPrivateKey)

    } catch (error) {
        console.error('演示过程出错:', error)
    }
}

// 执行演示
demo()