数字签名相关知识
数字签名
数字签名
是一种将类似现实世界中物理签名、盖章,在计算机世界中进行实现的技术。是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。使用数字签名可以识别篡改和伪造,还可以防止否认,具有不可抵赖性。
签名以及验证过程过程:
- A 用单向散列函数(Keccak256)求出消息的散列值。
- A 用自己的私钥对散列值进行签名。(使用椭圆加密算法进行签名)。
- A 将消息和签名发送给 B。
- B 用 A 的公钥对收到的签名进行解密。
- B 将解密后得到的散列值,与 A 直接发送的消息的散列值进行对比验证。
以下是对文本进行签名的过程
// Node Example
const signText = (text, privateKey) => {
// 1. 用单向散列函数 keccak256 求出消息的散列值
const hash = keccak256(text)
// 2. 用自己的私钥对散列值进行加密
const key = ec.keyFromPrivate(privateKey, 'hex');
const signature = key.sign(hash);
const sigHex = signature.r.toString(16, 32) +
signature.s.toString(16, 32) +
signature.recoveryParam.toString();
return {
hash,
signature: sigHex
};
};
在 nodejs SDK 中已经封装好了相关函数,加解密部分的实现可以参考prs-utility项目。
Keccak256
单向散列函数
,是一种将长消息转换为短散列值的技术,用于确保消息完整性。
Keccak
是一种符合 SHA-3 标准的单向散列函数,可以生成任意长度的散列值。PRS 项目使用的是 Keccak-256 SHA3
算法,在 JavaScript 中使用的开源库是 js-sha3。
// Node Example
const jsSha3 = require('js-sha3');
let keccak256 = (message) => {
return jsSha3.keccak256(message);
};
椭圆曲线密码
椭圆曲线密码是一种利用椭圆曲线数学来实现的密码技术的统称,是一种建立公钥密码的算法。
Secp256k1
是比特币使用的 ECDSA (椭圆曲线数字签名算法)曲线的参数,并且在高效密码学标准 (Certicom Research) 中进行了定义. PRS 项目也使用该算法来进行数字签名,在 JavaScript 中使用的开源库是 elliptic。
密钥对
在数字签名中,签名的生成和验证使用不同的密钥。生成签名使用的密钥被称为私钥
,验证签名使用的密钥被称为公钥
,任何人都能够使用公钥验证签名。
创建密钥对过程:
- 用随机数生成密钥,因为密钥需要具有不易被他人推测的性质。
- 通过椭圆曲线签名算法,推导出公钥。
- 从公钥经过 Keccak 单向散列函数推导出地址。
// Node Example
const createKeyPair = (options = { dump: true }) => {
// 1. 用随机数生成密钥,因为密钥需要具有不易被他人推测的性质。
let privateKey;
do {
privateKey = randomBytes(32);
} while (!secp256k1.privateKeyVerify(privateKey));
// 2. 通过椭圆曲线签名算法,推导出公钥。
const publicKey = secp256k1.publicKeyCreate(privateKey);
const convertKey = secp256k1.publicKeyConvert(publicKey, false).slice(1);
// 3. 从公钥经过 Keccak 单向散列函数推导出地址。
const publicAdd = ethUtil.pubToAddress(convertKey);
return {
privateKey: dumpBuf(privateKey, options),
publicKey: dumpBuf(publicKey, options),
address: dumpBuf(publicAdd, options)
};
};
// dumpBuf 为工具函数,默认生成的所有 key 均为 Buffer,可根据需要 dump 成为 hex 字符串
在 PRS 中,用户注册以及用户授权都会生成新的密钥对。根据数字签名的特性,address 将作为用户在 PRS 系统中的唯一 ID,privateKey 用于签名操作,系统可以通过公开的 publishKey 来验证用户的权限。
Keystore
Keystore
文件是以太坊钱包存储私钥的一种文件格式(JSON),它使用用户自定义密码加密,以起到一定程度上的保护作用,而保护的程度取决于用户加密该钱包的密码强度。
PRESS.one 使用的 keystore 格式是 eth 标准格式, 在 JavaScript 中使用的是开源库是 keythereum。
具体格式如:
{"address":"c2d7cf95645d33006175b78989035c7c9061d3f9",
"crypto":{
"cipher":"aes-128-ctr",
"ciphertext":"0f6d343b2a34fe571639235fc16250823c6fe3bc30525d98c41dfdf21a97aedb",
"cipherparams":{
"iv":"cabce7fb34e4881870a2419b93f6c796"
},
"kdf":"scrypt",
"kdfparams"{
"dklen":32,
"n":262144,
"p":1,
"r":8,
"salt":"1af9c4a44cf45fe6fb03dcc126fa56cb0f9e81463683dd6493fb4dc76edddd51"
},
"mac":"5cf4012fffd1fbe41b122386122350c3825a709619224961a16e908c2a366aa6"
},
"id":"eddd71dd-7ad6-4cd3-bc1a-11022f7db76c",
"version":3
}
获取私钥
通过 Keystore 文件加上密码,即可解密出用户的私钥,拿到私钥即可进行数字签名。
// Node Example
const recoverPrivateKey = (keystore, password) => {
const privateKey = keythereum.recover(password, JSON.parse(keystore));
return bufToHex(privateKey);
}