JSON Web令牌,使用powershell和Azure AD应用程序的签名不匹配
我正在创建一个解决方案,以从我们的Azure广告租户获取信息。我正在使用powershell Invoke Webrequest和OAuth 2.0客户端凭据授权来获取信息。当我使用客户机密码时,我的脚本工作正常,但我想让它更安全,并使用自签名证书 我使用以下命令在本地pc上创建新证书:JSON Web令牌,使用powershell和Azure AD应用程序的签名不匹配,powershell,oauth-2.0,jwt,azure-active-directory,Powershell,Oauth 2.0,Jwt,Azure Active Directory,我正在创建一个解决方案,以从我们的Azure广告租户获取信息。我正在使用powershell Invoke Webrequest和OAuth 2.0客户端凭据授权来获取信息。当我使用客户机密码时,我的脚本工作正常,但我想让它更安全,并使用自签名证书 我使用以下命令在本地pc上创建新证书: $cert = New-SelfSignedCertificate -HashAlgorithm "SHA256" -Subject "CN=******" -CertStoreLocation "Cert:\
$cert = New-SelfSignedCertificate -HashAlgorithm "SHA256" -Subject "CN=******" -CertStoreLocation "Cert:\Currentuser\My" -KeyExportPolicy Exportable -KeySpec Signature -NotAfter (Get-Date).AddYears(5)
创建后,我使用mmc将此证书导出到.cer,并将其上载到Azure AD应用程序。
我还使用以下脚本创建JSON Web令牌:
############################################
## Variable
############################################
$appEndPoint = "https://login.microsoftonline.com/************/oauth2/token"
$appClientID = "*******************************"
$exportPath = [Environment]::GetFolderPath("desktop")
$guid = [guid]::NewGuid()
############################################
## JWT Token starttime/endtime
############################################
$jwtStartTimeUnix = ([DateTimeOffset](Get-Date).ToUniversalTime()).ToUnixTimeSeconds()
$jwtEndTimeUnix = ([DateTimeOffset](Get-Date).AddHours(1).ToUniversalTime()).ToUnixTimeSeconds()
###########################################
##Decoded Json JWT Token
###########################################
$jwtID = $guid.Guid
$decJwtHeader = '{"alg":"RS256","typ":"JWT","x5t":"' + [System.Convert]::ToBase64String(($cert.GetCertHash())) + '"}'
#$decJwtPayLoad = '{"ver":"2.0","aud":"'+ $appEndPoint + '","exp":' + $jwtEndTimeUnix + ',"iss":"' + $appClientID + '","jti":"' + $jwtID + '","nbf":' + $jwtStartTimeUnix + ',"sub":"' + $appClientID +'"}'
$decJwtPayLoad = '{
"aud":"' + $appEndPoint + '"
, "exp":"' + $jwtEndTimeUnix + '"
, "iss":"' + $appClientID + '"
, "jti":"' + $jwtID + '"
, "nbf":"' + $jwtStartTimeUnix + '"
, "sub":"' + $appClientID + '"
}'
##########################################
##Encode Json JWT Token
##########################################
$encJwtHeaderBytes = [system.text.encoding]::UTF8.GetBytes($decJwtHeader)
$encHeader = [system.convert]::ToBase64String($encJwtHeaderBytes).Split('=')[0].Replace('+', '-').Replace('/', '_')
$encJwtPayLoadBytes = [system.text.encoding]::UTF8.GetBytes($decJwtPayLoad)
$encPayLoad = [system.convert]::ToBase64String($encJwtPayLoadBytes).Split('=')[0].Replace('+', '-').Replace('/', '_')
$jwtToken = $encHeader + '.' + $encPayLoad
$toSign = [system.text.encoding]::UTF8.GetBytes($jwtToken)
#########################################
##Sign JWT Token
#########################################
$RSACryptoSP = [System.Security.Cryptography.RSACryptoServiceProvider]::new()
$HashAlgo = [System.Security.Cryptography.SHA256CryptoServiceProvider]::new()
$sha256oid = "2.16.840.1.101.3.4.2.1"
$dataBytes = [System.Text.Encoding]::UTF8.GetBytes($toSign)
$hashBytes = $HashAlgo.ComputeHash($dataBytes)
$signedBytes = $RSACryptoSP.SignHash($hashBytes, $sha256oid)
$sig = [System.Convert]::ToBase64String($signedBytes) -replace '\+','-' -replace '/','_' -replace '='
#$sig = [system.convert]::ToBase64String($cert.PrivateKey.SignData($toSign,[Security.Cryptography.HashAlgorithmName]::SHA256,[Security.Cryptography.RSASignaturePadding]::Pkcs1)) -replace '\+','-' -replace '/','_' -replace '='
$jwtToken = $jwtToken + '.' + $sig
$exportPath = $exportPath + "\jwtToken.txt"
$jwtToken | Out-File $exportPath
此JSON Web令牌是我的Invoke Webrequest中的客户端断言
#################################################################
## ENKEL VOOR TESTING, NIET IN AMP GEBRUIKEN ####################
#################################################################
$tenantid = '**********'
$subscriptionid = '**********'
$clientid = '**********'
$client_assertion_type = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
$client_assertion = '********'
##################################################################
##################################################################
##################################################################
$return = Invoke-Command -ScriptBlock {
param($tenantid,$subscriptionid,$clientid,$client_assertion_type,$client_assertion)
Add-Type -AssemblyName System.Web
$enc_client_assertion_type = [System.Web.HttpUtility]::UrlEncode($client_assertion_type)
$encScope = [System.Web.HttpUtility]::UrlEncode('https://management.azure.com/.default')
$enc_client_assertion = [System.Web.HttpUtility]::UrlEncode($client_assertion)
$body = "scope=$encScope&client_id=$clientid&client_assertion_type=$enc_client_assertion_type&client_assertion=$enc_client_assertion&grant_type=client_credentials"
$auth = Invoke-WebRequest "https://login.microsoftonline.com/$tenantid/oauth2/token" -Method Post -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing
$token = ($auth | ConvertFrom-Json).access_token
$headers = @{
'Authorization'="Bearer $($token)"
}
$data = Invoke-WebRequest "https://management.azure.com/subscriptions/$subscriptionid/providers/Microsoft.Advisor/recommendations?api-version=2017-04-19" -Method GET -Headers $headers -UseBasicParsing
New-Object PSObject -Property @{
content=$data.content
}
} -ArgumentList $tenantid,$subscriptionid,$clientid,$client_assertion_type,$client_assertion
$content = $return.content
Write-Host $content
我收到以下错误,但无法找出原因。有人有主意吗
Invoke-WebRequest : {"error":"invalid_client","error_description":"AADSTS700027: Client assertion contains an invalid signature. [Reason - The provided signature value did not match the expected signature value.,
Thumbprint of key used by client: '******************', Found key 'Start=10/14/2019 09:02:58, End=10/14/2024 09:12:59', Please visit 'https://developer.microsoft.com/en-us/graph/graph-explorer'
and query for 'https://graph.microsoft.com/beta/applications/***********' to see configured keys]\r\nTrace ID: 085758a5-7470-4a3d-91ba-6f98518e7100\r\nCorrelation ID:
1ab6c7d2-4e46-4e7b-a56f-42720a24286a\r\nTimestamp: 2019-10-14 09:43:15Z","error_codes":[700027],"timestamp":"2019-10-14
09:43:15Z","trace_id":"085758a5-7470-4a3d-91ba-6f98518e7100","correlation_id":"1ab6c7d2-4e46-4e7b-a56f-42720a24286a","error_uri":"https://login.microsoftonline.com/error?code=700027"}
你几乎是对的 唯一的问题是您忘记使用自己的证书:
$RSACryptoSP = [System.Security.Cryptography.RSACryptoServiceProvider]::new()
$HashAlgo = [System.Security.Cryptography.SHA256CryptoServiceProvider]::new()
$sha256oid = "2.16.840.1.101.3.4.2.1"
$dataBytes = [System.Text.Encoding]::UTF8.GetBytes($toSign)
$hashBytes = $HashAlgo.ComputeHash($dataBytes)
$signedBytes = $RSACryptoSP.SignHash($hashBytes, $sha256oid)
正确的方法是:
#Get cert from your cert store. Please make sure that the private key is exportable
$Cert = Get-ChildItem -Path cert:\Currentuser\My | Where {$_.Subject -eq "CN=AADApplicationWithCert"}
$RSACryptoSP = [System.Security.Cryptography.RSACryptoServiceProvider]::new()
$HashAlgo = [System.Security.Cryptography.SHA256CryptoServiceProvider]::new()
$sha256oid = [System.Security.Cryptography.CryptoConfig]::MapNameToOID("SHA256");
#Use your private key
$RSACryptoSP.FromXmlString($Cert.PrivateKey.ToXmlString($true))
$hashBytes = $HashAlgo.ComputeHash($toSign)
$signedBytes = $RSACryptoSP.SignHash($hashBytes, $sha256oid)
$signedBytes = [Convert]::ToBase64String($signedBytes) -replace '\+','-' -replace '/','_' -replace '='
我的全部样本:
$appEndPoint = "https://login.microsoftonline.com/hanxia.onmicrosoft.com/oauth2/token"
$appClientID = "dc175b96-c196-43cf-aa0b-ea03e56da5e7"
$jwtStartTimeUnix = ([DateTimeOffset](Get-Date).ToUniversalTime()).ToUnixTimeSeconds()
$jwtEndTimeUnix = ([DateTimeOffset](Get-Date).AddHours(1).ToUniversalTime()).ToUnixTimeSeconds()
$jwtID = [guid]::NewGuid().Guid
$Cert = Get-ChildItem -Path cert:\Currentuser\My | Where {$_.Subject -eq "CN=AADApplicationWithCert"}
$decJwtHeader = @{
alg="RS256";
typ="JWT";
x5t=[System.Convert]::ToBase64String($Cert.GetCertHash())
} | ConvertTo-Json -Compress
$decJwtPayLoad = @{
aud = $appEndPoint;
exp = $jwtEndTimeUnix;
iss = $appClientID;
jti = $jwtID;
nbf = $jwtStartTimeUnix;
sub = $appClientID
} | ConvertTo-Json -Compress
$encJwtHeaderBytes = [system.text.encoding]::UTF8.GetBytes($decJwtHeader)
$encHeader = [system.convert]::ToBase64String($encJwtHeaderBytes) -replace '\+','-' -replace '/','_' -replace '='
$encJwtPayLoadBytes = [system.text.encoding]::UTF8.GetBytes($decJwtPayLoad)
$encPayLoad = [system.convert]::ToBase64String($encJwtPayLoadBytes) -replace '\+','-' -replace '/','_' -replace '='
$jwtToken = $encHeader + '.' + $encPayLoad
$toSign = [system.text.encoding]::UTF8.GetBytes($jwtToken)
$RSACryptoSP = [System.Security.Cryptography.RSACryptoServiceProvider]::new()
$HashAlgo = [System.Security.Cryptography.SHA256CryptoServiceProvider]::new()
$sha256oid = [System.Security.Cryptography.CryptoConfig]::MapNameToOID("SHA256");
$RSACryptoSP.FromXmlString($Cert.PrivateKey.ToXmlString($true))
$hashBytes = $HashAlgo.ComputeHash($toSign)
$signedBytes = $RSACryptoSP.SignHash($hashBytes, $sha256oid)
$sig = [Convert]::ToBase64String($signedBytes) -replace '\+','-' -replace '/','_' -replace '='
$jwtToken = $jwtToken + '.' + $sig
$jwtToken
如果您想使用自己的证书获取Azure访问令牌,请参考@Jim Xu我正在使用此方法,但问题是我的JWT令牌不正确。为什么不直接使用?只需使用证书指纹登录即可。