You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell中使用证书认证Microsoft Graph API遇AADSTS50027错误

解决PowerShell 5.1中JWT断言无效的问题

你的脚本生成的JWT不符合Azure AD的要求,导致返回AADSTS50027错误,以下是具体问题和修正方案:

核心问题分析

  • JWT结构不完整:标准JWT由Header.Payload.Signature三部分组成,原脚本仅生成了Payload和Signature,缺少必须包含算法与类型声明的Header部分。
  • Base64编码不符合规范:JWT要求使用URL安全的Base64编码(替换+为-、/为_,移除末尾=),原脚本使用普通Base64会导致令牌格式错误。
  • 私钥处理不当:PowerShell 5.1中$cert.PrivateKey可能返回RSACng对象,直接用RSACryptoServiceProvider导入参数会引发兼容性问题。
  • Audience参数错误:v2.0令牌端点的aud值应为https://login.microsoftonline.com/{tenantId}/v2.0,而非带租户域名的格式。
  • 手动拼接请求体易出错:手动构建URL编码的请求体可能导致参数编码错误,应直接传递哈希表给Invoke-RestMethod。

修正后的完整脚本

$thumbprint = "<证书指纹>"
$clientId = "<客户端ID>"
$tenantId = "<租户ID>"

# 1. 生成JWT Header(必须包含alg和typ)
$header = @{
    alg = "RS256"
    typ = "JWT"
}

# 2. 生成JWT Payload
$expUnixTime = [math]::truncate(((Get-Date).AddMinutes(30).ToUniversalTime()).Subtract((Get-Date "1970-01-01").ToUniversalTime()).TotalSeconds)
$payload = @{
    iss = $clientId  # 发行者应为客户端ID,而非证书CN
    sub = $clientId  # 主体与发行者一致,应为客户端ID
    aud = "https://login.microsoftonline.com/$tenantId/v2.0"  # 正确的v2.0端点aud值
    exp = $expUnixTime
    nbf = [math]::truncate(((Get-Date).ToUniversalTime()).Subtract((Get-Date "1970-01-01").ToUniversalTime()).TotalSeconds)  # 添加生效时间
}

# 3. 实现URL安全的Base64编码函数
function ConvertTo-Base64Url {
    param([string]$InputString)
    $bytes = [System.Text.Encoding]::UTF8.GetBytes($InputString)
    $base64 = [Convert]::ToBase64String($bytes)
    # 替换为URL安全字符并移除末尾等号
    return $base64.Replace('+', '-').Replace('/', '_').TrimEnd('=')
}

# 4. 编码Header和Payload
$encodedHeader = ConvertTo-Base64Url ($header | ConvertTo-Json -Compress)
$encodedPayload = ConvertTo-Base64Url ($payload | ConvertTo-Json -Compress)

# 5. 准备签名的原始数据(Header.Payload)
$signatureInput = "$encodedHeader.$encodedPayload"

# 6. 使用证书私钥签名
$cert = Get-Item -Path Cert:\CurrentUser\My\$thumbprint
$rsa = $cert.PrivateKey
if ($rsa -is [System.Security.Cryptography.RSACng]) {
    $rsa = [System.Security.Cryptography.RSA]::Create($rsa.ExportParameters($true))
}
$signatureBytes = $rsa.SignData([System.Text.Encoding]::UTF8.GetBytes($signatureInput), [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
$encodedSignature = ConvertTo-Base64Url $signatureBytes

# 7. 构建完整JWT断言
$jwtAssertion = "$encodedHeader.$encodedPayload.$encodedSignature"

# 8. 请求令牌
$tokenUrl = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$body = @{
    client_id             = $clientId
    tenant_id             = $tenantId
    client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
    client_assertion      = $jwtAssertion
    scope                 = "https://graph.microsoft.com/.default"
    grant_type            = "client_credentials"
}

try {
    # 直接传递哈希表,PowerShell自动处理URL编码
    $response = Invoke-RestMethod -Uri $tokenUrl -Method Post -Body $body -ContentType "application/x-www-form-urlencoded"
    $response
} catch {
    Write-Error "错误: $($_.Exception.Message)"
    # 输出详细错误响应(如果有)
    if ($_.Exception.Response) {
        $reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())
        $errorResponse = $reader.ReadToEnd()
        Write-Error "详细错误响应: $errorResponse"
    }
}

关键修正说明

  • 添加JWT Header:明确指定签名算法为RS256,类型为JWT,这是Azure AD要求的必填项。
  • 修正Payload字段:iss和sub应为客户端ID(而非证书CN),aud使用v2.0端点的正确格式,添加nbf(生效时间)字段增强合规性。
  • URL安全Base64编码:实现专用函数处理JWT所需的编码格式,避免普通Base64的特殊字符导致令牌解析失败。
  • 兼容私钥类型:处理RSACng类型的私钥,确保在PowerShell 5.1中正常签名。
  • 简化请求体构建:直接传递哈希表给Invoke-RestMethod,避免手动拼接的编码错误。

内容的提问来源于stack exchange,提问作者StackExchangeGuy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 12:17:06