验签代码示例
Last updated
Last updated
MYKEY使用账号的Reserved Key签名。服务端验签,需要先从链上找出Reserved Key,再验证。
ETH:
查询用的AccountStorage合约文件为:
第三方应用传递timestamp, account, uuID, ref给MYKEY,MYKEY按如下规则进行签名: let message = hex(timestamp + account + uuID + ref)
let hashedMessage = crypto.Keccak256Hash(common.FromHex(unsignData)).Bytes()
let unsignedData = "\x19Ethereum Signed Message:\n" + hashedMessage.length + hashedMessage
认证时,MYKEY还会返回mykeyUID和mykeyUIDSignature字段,其签名规则如下:
let messageForMykeyUID = hex(timestamp + account + uuID + ref + mykeyUID)
let unsignedDataForMykeyUID = "\x19Ethereum Signed Message:\n" + message.length + message
package main
import (
"encoding/hex"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"log"
"strings"
"testing"
)
const (
signFormat = "\x19Ethereum Signed Message:\n%d%s"
)
func TestVerifty(t *testing.T) {
ETHPrv := "87f9bc3c6cd57f18a0143b6863d6b47d6364f1a1ba86eae0ce411097704d5d0d"
ETHAdd := "0x296Fd341F90508B6742701F4341D2027CdF5c86d"
//待签名的数据
data := "0x1234"
prvKey, _ := hex.DecodeString(ETHPrv)
sig, _ := SignWithEthWeb3(data, prvKey)
log.Println("sig:", sig)
unsignDataHashByte := crypto.Keccak256Hash(common.FromHex(data)).Bytes()
// 开始服务端签名验证
log.Println("verify sign:", verifySig(strings.ToLower(ETHAdd), sig, unsignDataHashByte))
}
func SignWithEthWeb3(unsignData string, privateByte []byte) (signature string, err error) {
// 需要先做一次Hash
unsignDataHashByte := crypto.Keccak256Hash(common.FromHex(unsignData)).Bytes()
return SignWithEth(unsignDataHashByte, privateByte)
}
func SignWithEth(unsignData, privateKeyByte []byte) (signature string, err error) {
// web3签名需要加盐后做Hash,对hash数据进行签名
newUnsignData := fmt.Sprintf(signFormat, len(unsignData), unsignData)
unsignDataHash := crypto.Keccak256([]byte(newUnsignData))
key, err := crypto.ToECDSA(privateKeyByte)
if err != nil {
log.Println("sign ToECDSA err:", err.Error())
return "", err
}
signatureByte, err := crypto.Sign(unsignDataHash, key)
if err != nil {
log.Println("sign Sign err:", err.Error())
return "", err
}
// web逻辑
signatureByte[64] += 27
return hexutil.Encode(signatureByte), nil
}
// 验证签名
func verifySig(from, sigHex string, msg []byte) bool {
fromAddr := common.HexToAddress(from)
sig := hexutil.MustDecode(sigHex)
if sig[64] != 1 && sig[64] != 0 && sig[64] != 27 && sig[64] != 28 {
log.Println("in hexutil.MustDecode error.")
return false
}
if sig[64] != 1 && sig[64] != 0 {
sig[64] -= 27
}
pubKey, err := crypto.SigToPub(createSignHash(msg), sig)
if err != nil {
log.Println("in crypto.SigToPub error:", err.Error())
return false
}
recoveredAddr := crypto.PubkeyToAddress(*pubKey)
log.Println("recoveredAddr:", recoveredAddr.String())
return fromAddr == recoveredAddr
}
// 待签名数据生成符合格式的hash
func createSignHash(data []byte) []byte {
msg := fmt.Sprintf(signFormat, len(data), data)
return crypto.Keccak256([]byte(msg))
}
let sigUtil = require('eth-sig-util')
let Web3 = require('web3');
let web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/56444e75b6a24070a374f791bd25f811"));
let json = require('./AccountStorage.abi.json');
let AccountStorageABI = json.abi
let AccountStorageAddr = '0xADc92d1fD878580579716d944eF3460E241604b7'
let AccountStorageIns = new web3.eth.Contract(AccountStorageABI, AccountStorageAddr);
// 1. get mykey account Reserved key
// https://docs.mykey.org/dive-into-mykey/mykey-on-eos#keydata%E8%A1%A8%E4%B8%AD%E7%9A%84%E5%AF%86%E9%92%A5
let account = '0x67913A00a459fCd41CbF4124a887e8d8dE0742c0'
// account proxy
let reservedKeyAddr = await AccountStorageIns.methods.getKeyData(account, 3).call();
console.log(account, "reserved key:", reservedKeyAddr) // should be 0xd2F9b4652D80FA870207C2b421B8437d7D54a484
// 2. 签名消息。使用mykey时,直接调用web3.personal.sign签名即可。以下提供私钥签名,仅为了测试签名和验签。
web3.personal.sign("hello", web3.eth.coinbase, console.log);
let message = 'hello'
let messageHash = web3.utils.soliditySha3(message);
let privKeyHex = '78e22x19400da88318b74649ec8ff0d6aa9x7f8062950276f28xc65b9d569f32f84' // prvkey of '0xd2F9b4652D80FA870207C2b421B8437d7D54a484'
let privKey = Buffer.from(privKeyHex, 'hex')
let msgParams = { data: messageHash }
let signed = sigUtil.personalSign(privKey, msgParams)
console.log("signature:", signed)
// 3. recover
msgParams.sig = signed
let recovered = sigUtil.recoverPersonalSignature(msgParams)
console.log("recovered:", recovered)
console.log(reservedKeyAddr.toLowerCase() === recovered.toLowerCase())
// 验证签名:message是待处理的data;sig是已签名的数据;expectedKey是期望的ReservedKey
async function recoverOnly() {
// recover
let message = "xxxxx"
let messageHash = web3.utils.soliditySha3(message);
let sig = '0x1dc17f7413edaa2d696a40a2298cca98cfff59f489af7d7e82a7e5184b65036b6a6147734b17728796a5932aa0c1f1455cafc80e208d55adeb2a8400777c1fa01'
let recovedKey = await web3.eth.accounts.recover(messageHash, sig)
let expectedKey = "0x0b2144B2c8430ecde7d4ED7xxxxxx"
console.log( expectedKey.toLowerCase() == recovedKey.toLowerCase())
}
var Web3 = require('web3')
// var Contract = require('web3-eth-contract')
var Accounts = require('web3-eth-accounts');
var accounts = new Accounts('ws://localhost:8546');
var assert = require("assert")
var message = "test string"
var unsignedData = "\x19Ethereum Signed Message:\n" + message.length + message
var pk = "0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"
var address = accounts.privateKeyToAccount(pk).address
// sign
var signature = accounts.sign(unsignedData, pk).signature;
console.log("signature ", signature)
// recover
var recoveredAddress = accounts.recover(unsignedData, signature)
if ( address == recoveredAddress ) {
console.log("the signature is correct")
}
var web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/9dced93dfa714682b768ce813867a4d3"));
var account = '0x67913A00a459fCd41CbF4124a887e8d8dE0742c0' //test account
var json = require('./AccountStorage.abi.json')
var addr = '0xADc92d1fD878580579716d944eF3460E241604b7'
var mykeyAccountsContract = new web3.eth.Contract(json.abi, addr)
var keyDataCall = mykeyAccountsContract.methods.getKeyData(account, 3).call();
keyDataCall
.then(console.log)
.catch((error) => {
throw new Error(error)
})