签名验证失败求助:存入数据库前成功取出后失败
你遇到的问题不是MySQL排序规则导致的,核心原因是直接对elliptic库生成的签名对象做了JSON.stringify序列化,从数据库取出JSON.parse后得到的只是普通JavaScript对象,并非原始的Signature实例,这就导致verify方法无法正确验证签名。
为什么会这样?
ec.sign()返回的是elliptic库的Signature类实例,这个对象不仅包含r、s等属性,还带有库内部的方法和类型标识。当你用JSON.stringify()序列化它时,只会保留可枚举的普通属性(比如r和s的字符串形式),但丢失了对象的原型链和内部结构。而publicKeyPair.verify()方法需要的是一个完整的Signature实例,不是普通的JSON对象,所以验证自然失败。
正确的解决方案:序列化签名为标准格式字符串
椭圆曲线签名通常可以序列化为DER格式的十六进制字符串(密码学领域的标准格式),而不是直接序列化对象。修改你的代码如下:
1. 存入数据库时:将签名转为DER十六进制字符串
// 替换原来的JSON.stringify(signature) var signatureHex = signature.toDER('hex'); var post = { host: host, type: select, value: value, signature: signatureHex, // 存储十六进制字符串 public : publicKey, hash: message };
2. 从数据库取出时:将十六进制字符串还原为Signature实例
// 替换原来的JSON.parse(result[0].signature) var signatureHex = result[0].signature; var signature = ec.signatureFromDER(signatureHex, 'hex'); // 还原为Signature对象 const isVerified = verify(hash, public, signature); console.log('> Is verified: ', isVerified); // 现在应该返回true了
额外验证:排除MySQL影响
如果还是担心MySQL的问题,可以确认你的signature字段类型是TEXT或者VARCHAR(255)(DER格式的椭圆曲线签名一般在70字节左右,转成十六进制是140字符,完全足够),排序规则用utf8mb4_general_ci或者ascii_general_ci都可以——因为十六进制字符都是ASCII范围内的,不会有编码问题。
补充说明
如果你不想用DER格式,也可以用signature.toString('hex')来序列化(这会把r和s拼接成一个长字符串),取出时用ec.signatureFromHex(signatureHex)来还原,但DER格式是更通用的标准,推荐使用。
内容的提问来源于stack exchange,提问作者Peter Štieber

