C#使用RSA验证Node.js生成的签名失败问题咨询
核心问题1:填充模式不匹配
你最初版本的C#代码全部使用RSASignaturePadding.Pkcs1(即PKCS#1 v1.5填充),但Node.js端明确指定了RSA_PKCS1_PSS_PADDING,两种填充模式完全不兼容,不可能互相验证。
核心问题2:PSS参数未对齐
你修改代码使用PSS填充后仍验证失败的原因是PSS填充的附属参数没有对齐:
- Node.js crypto模块默认PSS填充的
saltLength等于哈希算法的输出长度,SHA256对应盐长度为32字节,同时MGF1摘要算法默认和签名哈希一致(即SHA256) - 如果你使用BouncyCastle库,不能直接用
SHA256WITHRSA作为算法名,这个是对应PKCS#1 v1.5填充的算法标识,PSS模式的算法标识为SHA256WITHRSAANDMGF1,还需要显式指定参数。
核心问题3:早期JS签名逻辑错误
你最开始的JS签名函数存在逻辑错误:没有调用update传入待签名数据,误写了keyLike参数,等于你最初生成的签名本身就是错误的,修改后加了cSign.update(verifyData)才是正确的签名逻辑。
正确可互操作的代码示例
1. Node.js 签名逻辑(固定版本)
function signature(verifyData) { const sign = crypto.createSign('sha256'); sign.update(verifyData, 'utf8'); return sign.sign({ key: privateKey, padding: crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST // 显式指定盐长度等于哈希长度,避免环境默认值差异 }).toString('base64'); }
2. .NET 原生验证逻辑
public bool VerifyData(string rawData, string base64Signature) { byte[] dataBytes = Encoding.UTF8.GetBytes(rawData); byte[] signatureBytes = Convert.FromBase64String(base64Signature); using var rsa = RSA.Create(); string publicKeyPem = File.ReadAllText("public.txt"); rsa.ImportFromPem(publicKeyPem); // 显式指定PSS填充,盐长度默认等于哈希长度,和Node.js配置对齐 return rsa.VerifyData(dataBytes, signatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pss); }
3. BouncyCastle 签名/验证逻辑
// 签名 public string SignDataBouncyCastle(string rawData, AsymmetricKeyParameter privateKey) { byte[] dataBytes = Encoding.UTF8.GetBytes(rawData); // PSS模式算法标识 var signer = SignerUtilities.GetSigner("SHA256WITHRSAANDMGF1"); signer.Init(true, new ParametersWithRandom(privateKey, new SecureRandom())); signer.BlockUpdate(dataBytes, 0, dataBytes.Length); return Convert.ToBase64String(signer.GenerateSignature()); } // 验证 public bool VerifyDataBouncyCastle(string rawData, string base64Signature, AsymmetricKeyParameter publicKey) { byte[] dataBytes = Encoding.UTF8.GetBytes(rawData); byte[] signatureBytes = Convert.FromBase64String(base64Signature); var signer = SignerUtilities.GetSigner("SHA256WITHRSAANDMGF1"); signer.Init(false, publicKey); signer.BlockUpdate(dataBytes, 0, dataBytes.Length); return signer.VerifySignature(signatureBytes); }
额外检查项
- 确保两边待签名的原始数据完全一致,没有多余的换行、空格、UTF-8 BOM头
- 确保密钥读取正确,公钥/私钥没有混用,Pem文件的首尾标识没有被截断
内容的提问来源于stack exchange,提问作者Leandro Battochio
相关产品推荐
相关产品推荐

