生成 HMACSHA256 签名时 key 和 data 必须是字节数组,不能直接传字符串
常见错误是把
key或
data当成纯字符串直接喂给
HMACSHA256.ComputeHash,结果签名总对不上。.NET 的
HMACSHA256只接受
byte[],必须显式编码。默认用 UTF-8,但要和验签端严格一致(比如对方用 ASCII,你就不能用 UTF-8)。
实操建议:
统一用Encoding.UTF8.GetBytes("your-key") 转 key 和待签名原文
如果原文含 Base64、URL 编码等,先解码再签名,别在编码后签名
签名前确认原文无隐藏空格、BOM、换行符——尤其从文件或 HTTP body 读取时
生成的 byte[]签名通常转为十六进制字符串(
BitConverter.ToString(hash).Replace("-", "").ToLower())或 Base64(Convert.ToBase64String(hash)),选哪种取决于对接方要求
验证签名时不能只比对字符串,要防时序攻击
直接用
==或
String.Equals比较两个签名字符串,会暴露时间差异,可能被用于侧信道攻击。.NET 没有内置恒定时间比较字符串的方法,得自己写或用现成安全库。
实操建议:
用CryptographicOperations.FixedTimeEquals(.NET 5+)做字节级恒定时间比较:
CryptographicOperations.FixedTimeEquals(computedHash, receivedHash)如果还在用 .NET Framework 或 .NET Core 3.1,手写恒定时间比较:遍历每个字节异或再或运算,最后判断结果是否全 0 验证前务必把收到的签名(如 Base64 字符串)先解码为
byte[],再和本地计算出的
byte[]比较,不要比字符串 验证失败时统一返回 401,不要透露是密钥错、原文错还是签名格式错
HMACSHA256 构造函数传 null key 会抛 CryptographicException
代码里写
new HMACSHA256(null)看似省事,实际运行直接崩,报错信息是
CryptographicException: Object synchronization method was called from an unsynchronized block of code(误导性很强)。根本原因是
null导致内部 key 初始化失败,并发访问时触发同步异常。
实操建议:
key 必须是非 null、非空的byte[];空 key(长度为 0)也不行,会报
ArgumentException: Key must be at least 64 bits long生产环境 key 应从配置中心或 Azure Key Vault 加载,避免硬编码;本地调试可用
Convert.FromBase64String("...") 安全导入
每次 new 一个新 HMACSHA256实例,别复用——它不是线程安全的,且内部状态会变
跨语言验签失败?重点查这三处编码和填充
和 Python/Java/Node.js 对接时签名总不一致,90% 出在以下三点:
原文编码:Python 默认 str 是 Unicode,hmac.new(key.encode(), data.encode(), 'sha256')中
encode()默认是 UTF-8;C# 也必须用
Encoding.UTF8.GetBytes,不能用
ASCIIEncodingkey 处理:Java 的
SecretKeySpec接收 byte[],但如果 key 是字符串,需确保两边都按相同方式转字节(比如都不额外 Base64 解码) 输出格式:对方返回的是 hex 小写?大写?带冒号?还是 Base64?C# 生成后要用对应方式格式化,别用
ToString()直接打日志看——它输出的是类型名
最稳妥的调试方式:两边都打印原始
byte[]的十六进制(例如 C# 用
BitConverter.ToString(hash).Replace("-", "")),逐字节比对。 