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 mainimport ("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) {// 需要先做一次HashunsignDataHashByte := 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] += 27return 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}// 待签名数据生成符合格式的hashfunc 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.abilet 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%A5let account = '0x67913A00a459fCd41CbF4124a887e8d8dE0742c0'// account proxylet 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. recovermsgParams.sig = signedlet recovered = sigUtil.recoverPersonalSignature(msgParams)console.log("recovered:", recovered)console.log(reservedKeyAddr.toLowerCase() === recovered.toLowerCase())// 验证签名:message是待处理的data;sig是已签名的数据;expectedKey是期望的ReservedKeyasync function recoverOnly() {// recoverlet 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 + messagevar pk = "0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"var address = accounts.privateKeyToAccount(pk).address// signvar signature = accounts.sign(unsignedData, pk).signature;console.log("signature ", signature)// recovervar 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 accountvar 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)})