JSON Web Token, подпись не совпадает с использованием powershell и приложения Azure AD - PullRequest
0 голосов
/ 14 октября 2019

Я создаю решение для получения информации от нашего клиента Azure AD. Я использую PowerShell Invoke-Webrequest и клиентские полномочия OAuth 2.0 для получения информации. Когда я использую секрет клиента, мой скрипт работает нормально, но я хочу сделать его более безопасным и использовать самозаверяющий сертификат.

Я использую следующую команду для создания нового сертификата на моем локальном компьютере:

$cert = New-SelfSignedCertificate -HashAlgorithm "SHA256" -Subject "CN=******" -CertStoreLocation "Cert:\Currentuser\My" -KeyExportPolicy Exportable -KeySpec Signature -NotAfter (Get-Date).AddYears(5)

После создания я экспортирую этот сертификат в .cer с помощью mmc и загружаю его в приложение Azure AD. Изображение из загруженного сертификата

Я также создаю веб-токен JSON с помощью следующего сценария:

############################################
## 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 является утверждением клиента в моем 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"}

1 Ответ

0 голосов
/ 15 октября 2019

Вы почти правы!

Единственная проблема заключается в том, что вы забыли использовать свой собственный сертификат:

$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
...