什么是数字签名算法(DSA)
全称Digital Signature Algorithm,即数字签名算法,它主要用于数字签名的生成和验证。
只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。
一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。
数字签名主要是为了完成两个目的:
(1)确认信息是由签名者发送的;
(2)确认信息自签名后到收到为止,未被修改过。
DSA算法的工作原理
主要基于离散对数问题,它包含三个阶段:密钥生成、签名生成和签名验证。
在密钥生成阶段,DSA会生成一对公钥和私钥。公钥用于验证签名,而私钥则用于生成签名。
在签名生成阶段,DSA会使用私钥和一些随机数来生成一个数字签名。这个签名是独一无二的,并且与消息内容紧密相关。
在签名验证阶段,DSA会使用公钥和原始消息来验证签名的有效性。如果签名是有效的,那么就可以确认这条消息是由私钥的持有者发送的,并且在传输过程中没有被篡改。
DSA分类
- RSA
- DSA
- ECDSA
- Schnorr
什么是椭圆曲线数字签名算法(ECDSA)
椭圆曲线数字签名算法(英语:Elliptic Curve Digital Signature Algorithm,缩写作 ECDSA)是一种基于椭圆曲线密码学的公开金钥加密算法。1985年,Koblitz和Miller把数字签名算法移植到椭圆曲线上,椭圆曲线数字签名算法由此诞生
具体说明可以参考
椭圆曲线数字签名算法(ECDSA)
ECDSA处理过程:
1.参与数字签名的所有通信方都使用相同的全局参数,用于定义椭圆曲线以及曲线上的基点
2.签名者首先生成一对公私钥。对于私钥,选择一个随机数或者伪随机数作为私钥,利用随机数和基点算出另一点,作为公钥
3.对消息计算Hash值,用私钥、全局参数和Hash值生成签名
4.验证者用签名者的公钥、全局参数等验证。
原文链接:https://blog.csdn.net/weixin_43586667/article/details/122766815
———————————————
ECDSA作用:
比特币和以太坊使用ECDSA来生成账户的公私钥以及对交易和区块进行验证。
1.在已知公钥的情况下,无法推导出该公钥对应的私钥。
2.可以通过某些方法来证明某人拥有一个公钥所对应的私钥,而此过程不会暴露关于私钥的任何信息。
Secp256k1:
Secp256k1是指比特币、以太坊中使用的ECDSA(椭圆曲线数字签名算法)曲线的参数,并且在高效密码学标准(Certicom Research,http://www.secg.org/sec2-v2.pdf)中进行了定义。
Secp256k1基于Fp有限域上的椭圆曲线,由于其特殊构造的特殊性,其优化后的实现比其他曲线性能上可以特高30%。这种算法具有许多优点,例如占用很少的带宽和存储资源,密钥的长度很短,让所有的用户都可以使用同样的操作完成域运算。
各加密货币采用的签名算法的现状
ECDSA分类:
-
spec256k1(特定的椭圆曲线称为secp256k1,即曲线 y² = x³ + 7 在有限域 (⼜名伽罗瓦域)
-
spec256r1(secp256k1和secp256r1都是ECDSA(椭圆曲线数字签名算法)曲线的参数,区别是他们所使用的随机质数不同,目前行业内对于r1算法的安全性存疑。)
-
ed25519(ed25519是基于扭曲爱德华曲线Edwards25519和SHA-512的EdDSA签名机制。)
ECDSA使用:
在EVM中预编译一个方法
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)
从椭圆曲线签名中恢复与公钥关联的地址或在错误时返回零。函数参数对应于签名的 ECDSA 值:
-
r
= 签名的前 32 个字节 -
s
= 签名的第二个 32 字节 -
v
= 签名的最后 1 个字节
为什么需要ecrecover函数?
1.实现广义元交易
2.避免使用进行两步交易:授权+ transferFrom!
作用实质为验证一个签名数据,而这些数据不一定来自交易签署者
如何使用ecrecover函数
有些业务场景只需要用户签名交易数据,让商户付费来执行交易并付手续费,就得确保用户清楚地知道他们在签署什么,所以提供用户签名服务时就得符合 EIP-712的规范
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
contract VerifySignature {
function getMessageHash(
address _to,
uint _amount,
string memory _message,
uint _nonce
) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_to, _amount, _message, _nonce));
}
function getEthSignedMessageHash(bytes32 _messageHash)
public
pure
returns (bytes32)
{
return
keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", _messageHash)
);
}
function verify(
address _signer,
address _to,
uint _amount,
string memory _message,
uint _nonce,
bytes memory signature
) public pure returns (bool) {
bytes32 messageHash = getMessageHash(_to, _amount, _message, _nonce);
bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash);
return recoverSigner(ethSignedMessageHash, signature) == _signer;
}
function recoverSigner(bytes32 _ethSignedMessageHash, bytes memory _signature)
public
pure
returns (address)
{
(bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
return ecrecover(_ethSignedMessageHash, v, r, s);
}
function splitSignature(bytes memory sig)
public
pure
returns (
bytes32 r,
bytes32 s,
uint8 v
)
{
require(sig.length == 65, "invalid signature length");
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := byte(0, mload(add(sig, 96)))
}
}
}
使用Hardhat进行测式
const { expect } = require("chai")
const { ethers } = require("hardhat")
describe("VerifySignature", function () {
it("Check signature", async function () {
const accounts = await ethers.getSigners(2)
const VerifySignature = await ethers.getContractFactory("VerifySignature")
const contract = await VerifySignature.deploy()
await contract.deployed()
// const PRIV_KEY = "0x..."
// const signer = new ethers.Wallet(PRIV_KEY)
const signer = accounts[0]
const to = accounts[1].address
const amount = 999
const message = "Hello"
const nonce = 123
const hash = await contract.getMessageHash(to, amount, message, nonce)
const sig = await signer.signMessage(ethers.utils.arrayify(hash))
const ethHash = await contract.getEthSignedMessageHash(hash)
console.log("signer ", signer.address)
console.log("recovered signer", await contract.recoverSigner(ethHash, sig))
// Correct signature and message returns true
expect(
await contract.verify(signer.address, to, amount, message, nonce, sig)
).to.equal(true)
// Incorrect message returns false
expect(
await contract.verify(signer.address, to, amount + 1, message, nonce, sig)
).to.equal(false)
})
})
运行结果