Private/Invoke-MsecKeyVaultSign.ps1
|
function Invoke-MsecKeyVaultSign { <# .SYNOPSIS Signs a digest using a Key Vault key via the Key Vault data-plane REST API. .DESCRIPTION POSTs to https://{vault}.vault.azure.net/keys/{name}/sign?api-version=7.4 with the digest base64url-encoded. Returns the signature as a raw byte array. We call REST directly because some Az.KeyVault versions ship Invoke-AzKeyVaultKeyOperation with -Operation 'Sign' declared but unimplemented ("Not supported operation 'Sign' yet"). REST is stable across vault API versions. Required Azure RBAC for the *calling user*: 'Key Vault Crypto User' on the vault (data action Microsoft.KeyVault/vaults/keys/sign/action). .PARAMETER VaultName Short name of the Key Vault (the host becomes <name>.vault.azure.net). .PARAMETER KeyName Name of the key inside the vault. .PARAMETER Digest The pre-computed message digest (e.g. SHA-256 hash of the JWT signing input). .PARAMETER Algorithm Signing algorithm. Defaults to 'RS256' which is what Entra expects for JWT client assertions. #> [CmdletBinding()] [OutputType([byte[]])] param( [Parameter(Mandatory)][string] $VaultName, [Parameter(Mandatory)][string] $KeyName, [Parameter(Mandatory)][byte[]] $Digest, [Parameter()][string] $Algorithm = 'RS256' ) # Key Vault data-plane endpoint for the current cloud (vault.azure.cn in China). Derive # from the Az context; fall back to commercial if environment resolution is unavailable. $envInfo = try { Get-MsecEnvironment } catch { $null } $kvResource = if ($envInfo) { $envInfo.KeyVaultResource } else { 'https://vault.azure.net' } $kvSuffix = if ($envInfo) { $envInfo.KeyVaultDnsSuffix } else { 'vault.azure.net' } # User token for the vault (the user is the one with Crypto User on the vault). $tokenInfo = Get-AzAccessToken -ResourceUrl $kvResource -ErrorAction Stop $kvToken = if ($tokenInfo.Token -is [securestring]) { $tokenInfo.Token | ConvertFrom-SecureString -AsPlainText } else { [string]$tokenInfo.Token } $body = @{ alg = $Algorithm value = ConvertTo-MsecBase64Url -InputObject $Digest } | ConvertTo-Json -Compress Write-Verbose "POST https://$VaultName.$kvSuffix/keys/$KeyName/sign" $response = Invoke-RestMethod -Method Post -ErrorAction Stop ` -Uri "https://$VaultName.$kvSuffix/keys/$KeyName/sign?api-version=7.4" ` -Headers @{ Authorization = "Bearer $kvToken" } ` -ContentType 'application/json' ` -Body $body # Response.value is base64url. Decode to raw signature bytes. $padded = $response.value + ('=' * ((4 - ($response.value.Length % 4)) % 4)) [byte[]] $sig = [Convert]::FromBase64String($padded.Replace('-', '+').Replace('_', '/')) $sig } |