如何在浏览器前端验证.NET生成的RSASSA-PKCS1-v1_5签名?
RSA签名跨.NET与JavaScript验证失败问题排查
任务与问题
- 在.NET 6 Web服务中对payload的哈希值进行签名(简化描述为对payload签名)
- 前端Web应用通过JavaScript获取payload与签名
- 需要在浏览器中用JavaScript验证签名
- 核心问题:签名验证始终失败
.NET签名核心代码
using (var rsa = new RSACryptoServiceProvider(2048)) { var privateKeyPem = File.ReadAllText(pemPath, Encoding.UTF8); rsa.ImportFromPem(privateKeyPem.ToCharArray()); rsa.PersistKeyInCsp = false; var rsaFormatter = new RSAPKCS1SignatureFormatter(rsa); rsaFormatter.SetHashAlgorithm("SHA256"); var SIGNATURE = rsaFormatter.CreateSignature(PAYLOAD); }
JavaScript验证代码
注1:JWK结构由签名所用密钥对的公钥PEM转换而来
注2:曾尝试将crypto.subtle.importKey第一个参数设为pkcs8,传入公钥PEM,仍未成功
const publicKey = crypto.subtle.importKey( "jwk", { "kty": "RSA", "n": "<lots of chars>", "e": "AQAB" }, { name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" }, }, false, ["verify"] ); const isSignatureValid = await crypto.subtle.verify({ name: "RSASSA-PKCS1-v1_5", hash: { name: "SHA-256" }, }, publicKey, SIGNATURE, PAYLOAD); // isSignatureValid === false
问题排查与解决思路
验证失败的核心原因大概率是数据编码/格式不匹配,以下是几个关键检查点:
签名的字节传递一致性
- .NET中
CreateSignature返回的是原始字节数组,传递到前端时需确保用Base64编码(或其他二进制安全的编码方式)传输,前端接收后再解码为ArrayBuffer/TypedArray。如果直接传递字符串或使用错误编码,会导致签名字节内容失真。
- .NET中
Payload的哈希处理逻辑
- .NET的
RSAPKCS1SignatureFormatter.CreateSignature如果传入的是原始payload字节,会自动计算SHA256哈希并签名;如果传入的是预先计算好的SHA256哈希值,JavaScript端也需要先对payload计算SHA256哈希,再传入verify方法。需确认两端的输入逻辑一致。
- .NET的
公钥转换的正确性
- JWK格式要求模数
n和指数e使用URL安全的Base64编码(去掉+//,替换为-/_,去掉末尾=),如果转换时用了普通Base64,会导致公钥导入错误。 - 可尝试直接导入SPKI格式的公钥PEM:去掉头部
-----BEGIN PUBLIC KEY-----和尾部-----END PUBLIC KEY-----,将剩余内容Base64解码为ArrayBuffer,然后用crypto.subtle.importKey("spki", ...)导入。
- JWK格式要求模数
算法名称的兼容性
- .NET中使用
"SHA256",JavaScript中使用"SHA-256",底层逻辑兼容,但部分场景下.NET需使用完整OID路径"http://www.w3.org/2001/04/xmldsig-more#sha256"作为哈希算法名称,可尝试替换后重新签名测试。
- .NET中使用
内容的提问来源于stack exchange,提问作者Heinrich Ulbricht
相关产品推荐
相关产品推荐

