如何让RSACryptoServiceProvider使用公钥与签名完成消息验证
RSA SHA384 签名C#侧验证正确实现
核心踩坑点
- 签名填充模式未对齐:Node.js crypto模块使用
RSA-SHA384算法签名时,默认采用PKCS#1 v1.5填充,C#侧调用VerifyData时必须显式指定相同填充规则 - Hex格式签名未正确转码:不能直接对hex字符串调用编码类转字节,需要先将hex字符串解析为原始二进制字节数组
- 公钥导入方式错误:PKCS1格式的PEM公钥需要正确导入到RSA实例中,不要直接使用未导入公钥的
RSACryptoServiceProvider实例
完整实现代码
.NET 5+ / .NET Core 5.0 及以上版本(支持直接导入PEM)
using System; using System.Security.Cryptography; using System.Text; public static bool VerifyLicense(string jsonContent, string hexSignature, string pkcs1PublicKeyPem) { // 1. 将hex格式签名转为二进制字节数组 byte[] signatureBytes = ConvertHexStringToBytes(hexSignature); // 2. 待验证内容转字节,确认和JS端编码一致(默认UTF-8无BOM) byte[] contentBytes = Encoding.UTF8.GetBytes(jsonContent); // 3. 导入PKCS1格式的PEM公钥 using RSA rsa = RSA.Create(); rsa.ImportFromPem(pkcs1PublicKeyPem); // 4. 验证签名,显式指定哈希算法和填充模式,和JS端对齐 return rsa.VerifyData( data: contentBytes, signature: signatureBytes, hashAlgorithm: HashAlgorithmName.SHA384, padding: RSASignaturePadding.Pkcs1 ); } // Hex字符串转字节数组工具方法 private static byte[] ConvertHexStringToBytes(string hex) { if (hex.Length % 2 != 0) throw new ArgumentException("Hex字符串长度必须为偶数"); byte[] bytes = new byte[hex.Length / 2]; for (int i = 0; i < hex.Length; i += 2) { bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); } return bytes; }
.NET Framework / .NET Core 3.1 及更低版本(不支持直接导入PEM)
需要先手动处理PEM公钥,提取base64内容后导入:
using System; using System.Security.Cryptography; using System.Text; using System.Linq; public static bool VerifyLicense(string jsonContent, string hexSignature, string pkcs1PublicKeyPem) { byte[] signatureBytes = ConvertHexStringToBytes(hexSignature); byte[] contentBytes = Encoding.UTF8.GetBytes(jsonContent); // 手动解析PKCS1 PEM公钥 string publicKeyBase64 = pkcs1PublicKeyPem .Replace("-----BEGIN RSA PUBLIC KEY-----", "") .Replace("-----END RSA PUBLIC KEY-----", "") .Replace("\r", "") .Replace("\n", "") .Trim(); byte[] publicKeyBytes = Convert.FromBase64String(publicKeyBase64); using RSA rsa = RSA.Create(); rsa.ImportRSAPublicKey(publicKeyBytes, out _); return rsa.VerifyData( contentBytes, signatureBytes, HashAlgorithmName.SHA384, RSASignaturePadding.Pkcs1 ); } // Hex字符串转字节数组工具方法 private static byte[] ConvertHexStringToBytes(string hex) { if (hex.Length % 2 != 0) throw new ArgumentException("Hex字符串长度必须为偶数"); byte[] bytes = new byte[hex.Length / 2]; for (int i = 0; i < hex.Length; i += 2) { bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); } return bytes; }
校验点确认
- 若Node.js端签名时显式指定了PSS填充,C#侧需要对应将填充参数改为
RSASignaturePadding.Pss,默认情况下Node.js的crypto.sign('RSA-SHA384', content, privateKey)使用Pkcs1填充 - 若JS端签名时用了
utf16le等非UTF-8编码转换JSON字节,C#侧编码需要同步修改
内容的提问来源于stack exchange,提问作者Kim Sandberg
相关产品推荐
相关产品推荐

