Azure API Management中HMAC-SHA256签名验证异常排查
APIM入站请求HMAC签名验证不一致的排查及请求体正确获取方式
一、请求体获取的核心问题
你当前使用context.Request.Body.As<string>(preserveContent: true)读取请求体,存在以下潜在问题:
- APIM可能对请求体做自动处理(如JSON格式化、编码转换),导致读取到的不是原始字节对应的字符串
- 若请求采用分块传输(Chunked),
As<string>可能无法完整读取原始内容 - 硬编码UTF-8转换,若客户端使用其他编码(如GBK),会导致待签名字节数组不一致
正确获取原始请求体的方式
直接读取原始字节数组,避免编码转换带来的差异,推荐两种实现:
- 先读取原始字节,再按需转字符串:
<set-variable name="rawBodyBytes" value="@(context.Request.Body.As<byte[]>(preserveContent: true))" /> <!-- 仅当确认客户端用UTF-8编码时,转成字符串;否则直接用字节数组参与签名计算 --> <set-variable name="body" value="@(System.Text.Encoding.UTF8.GetString((byte[])context.Variables["rawBodyBytes"]))" />
- 直接用字节数组拼接计算签名(最可靠,避免编码差异):
<!-- 先读取各部分原始数据 --> <set-variable name="messageId" value="@(context.Request.Headers.GetValueOrDefault("messageId", ""))" /> <set-variable name="timestamp" value="@(context.Request.Headers.GetValueOrDefault("timestamp", ""))" /> <set-variable name="rawBodyBytes" value="@(context.Request.Body.As<byte[]>(preserveContent: true))" /> <!-- 计算签名时直接拼接字节数组 --> <set-variable name="calculatedSignature" value="@{\n var key = (byte[])context.Variables["secretKeyEncoded"];\n var messageIdBytes = System.Text.Encoding.UTF8.GetBytes((string)context.Variables["messageId"]);\n var timestampBytes = System.Text.Encoding.UTF8.GetBytes((string)context.Variables["timestamp"]);\n var rawBody = (byte[])context.Variables["rawBodyBytes"];\n \n // 拼接所有待签名数据的字节数组\n var combinedBytes = new byte[messageIdBytes.Length + timestampBytes.Length + rawBody.Length];\n System.Buffer.BlockCopy(messageIdBytes, 0, combinedBytes, 0, messageIdBytes.Length);\n System.Buffer.BlockCopy(timestampBytes, 0, combinedBytes, messageIdBytes.Length, timestampBytes.Length);\n System.Buffer.BlockCopy(rawBody, 0, combinedBytes, messageIdBytes.Length + timestampBytes.Length, rawBody.Length);\n \n var hmac = new System.Security.Cryptography.HMACSHA256(key);\n var hashBytes = hmac.ComputeHash(combinedBytes);\n return System.BitConverter.ToString(hashBytes).ToLower().Replace("-", string.Empty);\n}" />
二、其他可能导致签名不一致的原因
- 密钥解码逻辑不匹配:当前代码将
{{secret}}按十六进制字符串解码,但如果客户端是将Base64格式的密钥转成字节数组,需改为Convert.FromBase64String(key),而非自定义十六进制解码逻辑 - 待签名数据拼接规则不一致:确认客户端是否在
messageId、timestamp、body之间添加了分隔符(如空格、换行),而你的代码直接无分隔拼接,导致待签名数据完全不同 - 数据预处理差异:客户端是否对
messageId/timestamp做了trim、大小写转换,或者对body做了JSON格式化(如排序键、缩进),而你的代码直接读取原始值 - 编码不一致:客户端计算签名时使用的字符编码(如UTF-16、GBK)与你代码中的UTF-8不匹配,导致字节数组差异
内容的提问来源于stack exchange,提问作者mburm
相关产品推荐
相关产品推荐

