前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >创建数字钱包(一)

创建数字钱包(一)

作者头像
lambeta
发布2019-01-28 17:03:37
1.1K0
发布2019-01-28 17:03:37
举报
文章被收录于专栏:编舟记

椭圆曲线数字签名算法生成私钥

Secp256k1 通过椭圆曲线数字签名算法生成私钥和公钥,其中SEC(Standards for Efficient Cryptography)是专门利用ECDSA或者其可选项Schnorr算法来产生高效的加密方法。 特点是生成密钥很快。

Scep256k1 基本特性

  • secp256k1 ECDSA signing/verification and key generation.
  • Adding/multiplying private/public keys.
  • Serialization/parsing of private keys, public keys, signatures.
  • Constant time, constant memory access signing and pubkey generation.
  • Derandomized DSA (via RFC6979 or with a caller provided function.)
  • Very efficient implementation.

讲解代码

步骤

  1. 生成私钥
  2. 加密私钥
  3. 生成 keyObject 对象
  4. 从keyObject对象中恢复私钥

生成私钥

下面利用 keythereum[1] 产生符合以太坊的密钥,并产生keyObject文件

代码语言:javascript
复制
const params = { keyBytes: 32, ivBytes: 16 };
let {privateKey, salt, iv} = keythereum.create(params);

keythereum可以产生私钥,以及后面加密私钥所用的PBKDF2算法需要的salt,和加密aes-128-ctr私钥的iv值。

得到私钥之后,我们可以通过私钥生成公钥。

代码语言:javascript
复制
let privateKeyBuffer = Buffer.from(privateKey, "hex") // or "base64"
let publicKey = secp256k1.publicKeyCreate(privateKeyBuffer, false).slice(1);
let address = "0x" + keccak256(publicKey).slice(-20).toString("hex");

加密私钥

利用KDF算法基于password派生出密钥,然后利用这个密钥加密我们的私钥。

代码语言:javascript
复制
const password = "Hello,Ethereum"
const options = {
    kdf: "pbkdf2",
    cipher: "aes-128-ctr",
    kdfparams: {
        c: 262144,
        dklen: 32,
        prf: "hmac-sha256"
    }
};
const keyObject = keythereum.dump(password, privateKey, salt, iv, options);

这就是产生keyObject基本思路。我们在看看dump函数到底做了什么

代码语言:javascript
复制
this.marshal(this.deriveKey(password, salt, options), privateKey, salt, iv, options);

deriveKey(...) 的源码如下:

代码语言:javascript
复制
this.crypto.pbkdf2Sync(
        password,
        salt,
        options.kdfparams.c || this.constants.pbkdf2.c,
        options.kdfparams.dklen || this.constants.pbkdf2.dklen,
        prf //hmac-sha256
      );

这里基于password生成的derivedKey,这个密钥并不是我们要用的私钥,而是用来加密先前生成的privateKey的,加密的过程在marshal函数中调用的encrypt函数里。

代码语言:javascript
复制
let ciphertext = this.encrypt(privateKey, derivedKey.slice(0, 16), iv, algo).toString("hex");

encrypt函数,如下:

代码语言:javascript
复制
var cipher, ciphertext;
algo = algo || this.constants.cipher;
if (!this.isCipherAvailable(algo)) throw new Error(algo + " is not available");

//加密过程
cipher = this.crypto.createCipheriv(algo, this.str2buf(key), this.str2buf(iv));
ciphertext = cipher.update(this.str2buf(plaintext));

return Buffer.concat([ciphertext, cipher.final()]);

此处的ciphertext代表的是privateKey,而key则是derivedKey

生成 keyObject 对象

得到了加密后的ciphertext之后,开始组装keyObject对象并返回。

代码语言:javascript
复制
keyObject = {
      address: this.privateKeyToAddress(privateKey).slice(2),
      crypto: {
        cipher: options.cipher || this.constants.cipher,
        ciphertext: ciphertext,
        cipherparams: { iv: iv.toString("hex") },
        mac: this.getMAC(derivedKey, ciphertext)
      },
      id: uuid.v4(), // random 128-bit UUID
      version: 3
    };
keyObject.crypto.kdf = "pbkdf2";
      keyObject.crypto.kdfparams = {
        c: options.kdfparams.c || this.constants.pbkdf2.c,
        dklen: options.kdfparams.dklen || this.constants.pbkdf2.dklen,
        prf: options.kdfparams.prf || this.constants.pbkdf2.prf,
        salt: salt.toString("hex")
      };    

privateKeyToAddress(...)方法里首先通过privateKey产生publicKey,然后使用keccak256哈希publicKey得到地址。

具体实现如下:

代码语言:javascript
复制
let privateKeyBuffer = Buffer.from(privateKey);
let publicKey = secp256k1.publicKeyCreate(privateKeyBuffer, false).slice(1);
let address = "0x" + keccak256(publicKey).slice(-20).toString("hex");

keccak256(publicKey) 产生了32bytes,截取尾部20bytes转换成十六进制之后就是40字符,加上前导0x之后,就是42个字符的以太坊地址,比如:0x0f645438395206b408e52be4fcf4bc21c330bfa2

从keyObject对象中恢复私钥

有了keyObject和密码就可以恢复原来的私钥

代码语言:javascript
复制
let privateKey = keythereum.recover(password, keyObject)

可以想到,recover方法中,首先会利用password和keyObject中的salt派生出当初的密钥derivedKey,然后把加密过的私钥ciphertext和derivedKey, iv作为原来加密算法aes-128-ctr的输入参数,成功解密后返回明文的私钥。

具体代码如下:

代码语言:javascript
复制
verifyAndDecrypt(this.deriveKey(password, salt, keyObjectCrypto), salt, iv, ciphertext, algo)

这里首先得到了derivedKey,然后验证并解密kyeObject中的ciphertext,如下:

代码语言:javascript
复制
function verifyAndDecrypt(derivedKey, salt, iv, ciphertext, algo) {
  var key;
  if (self.getMAC(derivedKey, ciphertext) !== keyObjectCrypto.mac) {
    throw new Error("message authentication code mismatch");
  }
  if (keyObject.version === "1") {
    key = keccak256(derivedKey.slice(0, 16)).slice(0, 16);
  } else {
    key = derivedKey.slice(0, 16);
  }
  return self.decrypt(ciphertext, key, iv, algo);
}

注意这里的mac值比较,确保了ciphertext没有被人篡改才有解密的必要。

参考实现

  1. NodeJS
  2. Bitcoin-core

  1. Keythereum
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019.01.23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 椭圆曲线数字签名算法生成私钥
  • 讲解代码
    • 步骤
      • 生成私钥
        • 加密私钥
          • 生成 keyObject 对象
            • 从keyObject对象中恢复私钥
            • 参考实现
            相关产品与服务
            全站加速网络
            全站加速网络(Enterprise Content Delivery Network,以下简称 ECDN)为您提供稳定高效的网络加速服务,适用于动静混合、纯动态、跨国、上传等多种加速场景。ECDN 网络资源丰富,同时融合静态缓存、智能路由、协议优化、多路传输、抗抖动等自研技术,加速效果更加显著;接入便捷,功能配置灵活多样,可满足您个性化的业务需求。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档