Android中SHA256withECDSA签名验证失败问题排查
问题分析与解决
首先,我一眼就看到你代码里的致命坑——你用sig.toString()打印签名的时候,完全误解了字节数组的toString()行为!
字节数组直接调用toString(),返回的根本不是签名的实际内容,而是类似[B@6d6f6e48这种对象标识字符串(类名+哈希码)。如果你的实际业务中,是把这个toString()的结果当作签名保存或者传递,然后验证时用这个字符串的字节去校验,那必然会返回false,因为你验证的根本不是原来的签名数据!
正确的签名存储/传递方式
要把字节数组形式的签名转换成可存储的字符串,得用Base64或者十六进制编码。以Android为例,用Base64处理是最方便的:
生成签名并正确编码
val sigBytes = Signature.getInstance("SHA256withECDSA").run { initSign(kp.private) // 明确指定UTF-8编码,避免不同环境下的编码差异 update("foo".toByteArray(Charsets.UTF_8)) sign() } // 把签名转成Base64字符串,这才是可以安全存储/传递的内容 val sigStr = Base64.encodeToString(sigBytes, Base64.DEFAULT) Log.d(tag, "sig: $sigStr")
验证签名
// 把Base64字符串转回字节数组 val sigBytes = Base64.decode(sigStr, Base64.DEFAULT) val verifier = Signature.getInstance("SHA256withECDSA") verifier.initVerify(kp.public) // 同样用UTF-8编码,和生成时保持一致 verifier.update("foo".toByteArray(Charsets.UTF_8)) val isValid = verifier.verify(sigBytes) Log.d(tag, isValid.toString()) // 这下应该输出true了
其他可能的排查点
如果你的代码真的完全像你贴的那样,直接用同一个sig字节数组验证,那唯一的可能就是你的密钥对kp不是同一组!比如你在绑定点击事件的时候,每次都重新生成了新的kp,而不是复用同一个实例。你可以检查下kp的生成逻辑,确保签名和验证时用的是同一对公私钥。
另外补充个小细节:最好明确指定字符串转字节数组的编码(比如Charsets.UTF_8),虽然Android默认是UTF-8,但万一环境编码被修改,也会导致哈希值不一致,验证失败。
内容的提问来源于stack exchange,提问作者user2297550
相关产品推荐
相关产品推荐

