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()