PowerShell实现HMAC-SHA256 API授权遇401错误求助
问题解决:PowerShell调用API的HMAC-SHA256授权401错误
问题概述
接手API调用任务,用PowerShell实现数据保存,已解决约束语言模式错误,但当前出现401未授权错误,核心问题出在HMAC-SHA256授权逻辑的实现偏差。
API授权规则(整理后)
生成签名密钥:
- 生成格式为“yyyyMMddTHHmmss”的UTC时间戳,与服务器时间差超10分钟请求会被拒绝;
- 使用私钥对上述时间戳生成HMAC-SHA256;
- 以上一步的二进制结果为密钥,对小写的
API组件+公钥生成HMAC-SHA256,得到签名密钥。请求签名:
- 用
|拼接小写HTTP方法、小写请求URI绝对路径(不含域名和查询参数,含开头斜杠)、内容长度(GET请求填0),生成请求字符串;- 用签名密钥对请求字符串生成HMAC-SHA256;
- 将结果Base64编码得到签名。
脚本中的核心错误及修正
错误1:时间戳未使用UTC时间
原脚本用本地时间生成时间戳,不符合API要求,会导致时间差校验失败。
修正:
$timestamp = (Get-Date -Utc -Format "yyyyMMddTHHmmss").ToString()
错误2:请求字符串中的URI错误
原脚本使用完整URL(包含域名),但API要求仅使用URI绝对路径(不含域名)。
修正:
提取URL中的路径部分,转小写:
# 提取URI路径部分 $uriPath = ([Uri]$uri).AbsolutePath.ToLower() $requesttosign = $method.ToLower() + $seperator + $uriPath + $seperator + $contentlength
错误3:HMAC对象引用错误
原脚本使用未定义的$hmacsha1、$hmacsha3变量,会导致运行错误,且重复使用同一个HMAC对象可能存在状态污染。
修正:
每次计算哈希时,重新创建HMACSHA256对象,避免状态问题:
# step1key:用私钥对UTC时间戳生成HMAC-SHA256 $hmacshaStep1 = New-Object System.Security.Cryptography.HMACSHA256 $hmacshaStep1.Key = [Text.Encoding]::UTF8.GetBytes($apisecretkey) $step1key = $hmacshaStep1.ComputeHash([Text.Encoding]::UTF8.GetBytes($timestamp)) # step2key:用step1key(二进制)作为密钥,对小写的component+apikey生成HMAC-SHA256 $hmacshaStep2 = New-Object System.Security.Cryptography.HMACSHA256 $hmacshaStep2.Key = $step1key # 直接用二进制数组作为密钥,不要转UTF8 $step2key = $hmacshaStep2.ComputeHash([Text.Encoding]::UTF8.GetBytes(($component + $apikey).ToLower())) # 生成签名:用step2key对请求字符串生成HMAC-SHA256后Base64编码 $hmacshaSign = New-Object System.Security.Cryptography.HMACSHA256 $hmacshaSign.Key = $step2key $signatureBytes = $hmacshaSign.ComputeHash([Text.Encoding]::UTF8.GetBytes($requesttosign)) $signature = [Convert]::ToBase64String($signatureBytes)
错误4:Authorization头格式可能缺失必要信息
部分API要求Authorization头包含公钥、时间戳等额外信息,如果修正上述问题后仍报错,需确认API的头格式要求,比如添加时间戳到请求头:
$headers = @{ Authorization = "Signature $signature" # 或按API要求的格式调整 "X-Timestamp" = $timestamp # 若API要求传递时间戳 }
修正后的完整脚本
# Variables $uri = "https://www.somewhere.com/api/uat/places/v1.1/lh/public/Properties" $method = "GET" $contentlength = "0" $seperator = "|" $timestamp = (Get-Date -Utc -Format "yyyyMMddTHHmmss").ToString() $component = "keyword" # API keys $apikey = "thisisthekey" $apisecretkey = "thisisthesecretkey" # 生成请求字符串:提取URI路径,转小写 $uriPath = ([Uri]$uri).AbsolutePath.ToLower() $requesttosign = $method.ToLower() + $seperator + $uriPath + $seperator + $contentlength # 生成签名密钥 step1:私钥+时间戳 $hmacshaStep1 = New-Object System.Security.Cryptography.HMACSHA256 $hmacshaStep1.Key = [Text.Encoding]::UTF8.GetBytes($apisecretkey) $step1key = $hmacshaStep1.ComputeHash([Text.Encoding]::UTF8.GetBytes($timestamp)) # 生成签名密钥 step2:step1key + 小写component+apikey $hmacshaStep2 = New-Object System.Security.Cryptography.HMACSHA256 $hmacshaStep2.Key = $step1key $step2key = $hmacshaStep2.ComputeHash([Text.Encoding]::UTF8.GetBytes(($component + $apikey).ToLower())) # 生成最终签名 $hmacshaSign = New-Object System.Security.Cryptography.HMACSHA256 $hmacshaSign.Key = $step2key $signatureBytes = $hmacshaSign.ComputeHash([Text.Encoding]::UTF8.GetBytes($requesttosign)) $signature = [Convert]::ToBase64String($signatureBytes) # 构建请求头(若API要求,添加时间戳等额外字段) $headers = @{ Authorization = $signature # 按API实际要求调整格式,比如"ApiKey $apikey:$signature" "X-Timestamp" = $timestamp } # 调用API并保存数据 try { $data = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers $data | Export-Csv "\\server\location\data.csv" -NoTypeInformation Write-Host "数据保存成功" } catch { Write-Error "请求失败:$_" }
学习资料参考
- PowerShell中
System.Security.Cryptography.HMACSHA256类用法:执行[System.Security.Cryptography.HMACSHA256] | Get-Member查看可用方法,或通过Get-Help了解.NET类文档。 - UTC时间处理:使用
Get-Date -Utc生成UTC时间,确保与服务器时间一致性。 - URI路径提取:利用
[Uri]类的AbsolutePath属性快速获取URL路径,避免手动截取出错。 - HMAC哈希原理:理解二进制哈希值的处理逻辑,直接使用二进制数组作为密钥,不要随意转字符串再编码。
内容的提问来源于stack exchange,提问作者BarelySurviving
相关产品推荐
相关产品推荐

