functions/New-AadClientAssertion.ps1
<#
.SYNOPSIS Generates a client assertion for Azure AD. .DESCRIPTION Generates a client assertion for Azure AD. Requires a PFX with a Private Key. You can create one using "New-AadApplicationCertificate" .PARAMETER ClientId Specify the Azure AD application this client assertion is meant for. .PARAMETER CertificatePath Path to certificate (PFX) .PARAMETER CertificatePassword Password for the PFX .PARAMETER Tenant Specify the tenant name or ID this client assertion is meant for. .EXAMPLE New-AadClientAssertion -ClientId ac4dff1b-b7d7-4453-a4e4-613210c686c9 -CertificatePath "C:\path\to\certificate.pfx" -CertificatePassword "SomePassword" -Tenant contoso.onmicrosoft.com .NOTES .RELATED https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Client-Assertions #> function New-AadClientAssertion { [CmdletBinding(DefaultParameterSetName='Default')] param( [Parameter(mandatory=$true)] $ClientId, [Parameter(mandatory=$true)] [string]$CertificatePath, [Parameter(mandatory=$true)] [string]$CertificatePassword, [Parameter(mandatory=$true)] [string]$Tenant ) if(![System.IO.Path]::IsPathRooted($CertificatePath)) { $LocalPath = Get-Location $CertificatePath = "$LocalPath\$CertificatePath" } Write-Verbose "Looking for $CertificatePath" $certificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($CertificatePath, $CertificatePassword) $ThumbprintBase64 = ConvertFrom-AadThumbprintToBase64String $certificate.Thumbprint $nbf = [int][double]::parse((Get-Date -Date $(Get-Date).ToUniversalTime() -UFormat %s)) $exp = [int][double]::parse((Get-Date -Date $((Get-Date).addseconds($ValidforSeconds).ToUniversalTime()) -UFormat %s)) # Grab Unix Epoch Timestamp and add desired expiration. $jti = New-Guid $aud = "https://login.microsoftonline.com/$Tenant/oauth2/token" $sub = $ClientId $iss = $ClientId [hashtable]$header = @{ alg = "RS256"; typ = "JWT"; x5t = $ThumbprintBase64; kid = $ThumbprintBase64 } [hashtable]$payload = @{ aud = $aud; iss = $iss; sub = $sub; jti = $jti; nbf = $nbf; exp = $exp; tid = "aa00d1fa-5269-4e1c-b06d-30868371d2c5"; } $headerjson = $header | ConvertTo-Json -Compress $payloadjson = $payload | ConvertTo-Json -Compress $headerjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($headerjson)).Split('=')[0].Replace('+', '-').Replace('/', '_') $payloadjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($payloadjson)).Split('=')[0].Replace('+', '-').Replace('/', '_') $ToBeSigned = [System.Text.Encoding]::UTF8.GetBytes($headerjsonbase64 + "." + $payloadjsonbase64) $rsa = $certificate.PrivateKey if ($null -eq $rsa) { # Requiring the private key to be present; else cannot sign! throw "There's no private key in the supplied certificate - cannot sign" } else { # Overloads tested with RSACryptoServiceProvider, RSACng, RSAOpenSsl try { $Signature = [Convert]::ToBase64String($rsa.SignData([byte[]]$ToBeSigned,[Security.Cryptography.HashAlgorithmName]::SHA256,[Security.Cryptography.RSASignaturePadding]::Pkcs1)) -replace '\+','-' -replace '/','_' -replace '=' } catch { throw "Signing with SHA256 and Pkcs1 padding failed using private key $rsa >> " + $_ } } $token = "$headerjsonbase64.$payloadjsonbase64.$Signature" Write-Host "Generated Client Assertion..." return $token } |