使用Crypto++验证Windows Hello公钥签名失败问题咨询
方案可行性结论
- 你设计的验签方案完全可行:Windows Hello生成的RSA密钥对中,私钥存储在系统安全隔离区域(TPM或系统级安全容器)不对外暴露,调用
RequestSignAsync()接口获取签名值、搭配导出的公钥自行完成验签是官方支持的标准使用方式,无需强制依赖官方示例提供的服务端验证逻辑。
现有代码验签失败的常见原因
你配置的PKCS1v1.5填充、SHA256哈希算法参数和官方要求完全匹配,验证失败基本是以下三个环节的疏漏导致:
公钥加载方式错误
你当前代码直接调用keyPublic.Load()加载Base64解码后的公钥,该方法默认读取Crypto++私有的BER编码密钥格式,但Windows Hello导出的公钥是标准ASN.1 DER编码的X.509 SubjectPublicKeyInfo格式,直接加载会得到错误的公钥参数,且多数情况下不会抛出显性异常。
正确的公钥加载代码如下:CryptoPP::RSA::PublicKey keyPublic; CryptoPP::StringSource pubkeySource(pubkey, true, new CryptoPP::Base64Decoder( new CryptoPP::X509PublicKeyLoader(keyPublic) ) );注意:如果拿到的公钥字符串带
-----BEGIN PUBLIC KEY-----/-----END PUBLIC KEY-----的PEM头尾,需要先将这两行标识删除后再做Base64解码,否则会引入冗余数据导致公钥解析失败。验签流程未传入待签名原文
你当前的验签逻辑仅向SignatureVerificationFilter传入了解码后的签名值,完全缺失调用RequestSignAsync()时提交的待签名原始数据。RSA验签需要同时持有原文、签名、公钥三个要素,缺少原文必然返回验证失败。注意:Windows Hello签名时不会对待签名数据做任何额外封装(不会加前缀、盐值、结构头),传入验签逻辑的原文必须和调用签名接口时传入的二进制内容完全一致,字节序、编码、换行符任意一个字节存在差异都会导致验签失败。
签名输入顺序错误
Crypto++的签名验证过滤器要求先输入完整的待验签原文,再输入签名值,最后标记消息结束,直接把签名作为唯一输入无法完成校验。
修正后的完整验签代码如下:bool bSignatureVerified = false; std::string sigBin; // 先对签名做Base64解码 CryptoPP::StringSource sigDecoder(sig, true, new CryptoPP::Base64Decoder( new CryptoPP::StringSink(sigBin) ) ); // 替换为你调用RequestSignAsync时传入的原始二进制数据 std::string originalData = "待签名原文的二进制内容"; CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA256>::Verifier verifier(keyPublic); CryptoPP::SignatureVerificationFilter sigFilter( verifier, new CryptoPP::ArraySink((CryptoPP::byte*)&bSignatureVerified, sizeof(bSignatureVerified)), CryptoPP::SignatureVerificationFilter::THROW_EXCEPTION ); // 按顺序传入原文、签名,标记流结束 sigFilter.Put((const CryptoPP::byte*)originalData.data(), originalData.size()); sigFilter.Put((const CryptoPP::byte*)sigBin.data(), sigBin.size()); sigFilter.MessageEnd(); if (bSignatureVerified) { return 0; } else { return -2; }
内容的提问来源于stack exchange,提问作者A. Beam

