Answering my own question in case anyone else needs this.
I also wrote an article about this, hope someone finds it useful.
https://tmarkovski.github.io/eth-azure/keyvault-part1/
Initially, I was generating the incorrect key type. I was able to generate the key by concatenating the X and Y arrays from JWK. Here's my sample code I was playing with in F#. It uses BouncyCastle for the Keccak hash function.
Important thing to note is the use of "EC-HSM" key type. This is part of the Premium SKU for Key Vault and only this type supports SECP256K1 curve, otherwise throws an exception if using "EC" key type.
open Microsoft.Azure.KeyVault
open System.Threading.Tasks
open System
open Microsoft.IdentityModel.Clients.ActiveDirectory
open Microsoft.Azure.KeyVault.Models
open Microsoft.Azure.KeyVault.WebKey
open Org.BouncyCastle.Crypto.Digests
open Org.BouncyCastle.Crypto
/// Implements an extension method that overloads the standard
/// 'Bind' of the 'async' builder. The new overload awaits on
/// a standard .NET task
type AsyncBuilder with
member __.Bind(t:Task<'T>, f:'T -> Async<'R>) : Async<'R> =
async.Bind(Async.AwaitTask t, f)
let vaultUri = "https://__mykeyvault__.vault.azure.net/"
let clientId = "..."
let clientSecret = "..."
type AuthenticationCallback = KeyVaultClient.AuthenticationCallback
let getAccessToken (authority:string) (resource:string) (scope:string) =
let clientCredential = new ClientCredential(clientId, clientSecret)
let context = new AuthenticationContext(authority, TokenCache.DefaultShared)
async {
let! result = context.AcquireTokenAsync(resource, clientCredential)
return result.AccessToken;
} |> Async.StartAsTask
let client = new KeyVaultClient(new KeyVaultCredential(new AuthenticationCallback(getAccessToken)))
let createKey name =
let keyParams = new NewKeyParameters()
keyParams.Kty <- "EC-HSM"
keyParams.CurveName <- EcKey.SECP256K1
async {
let! result = client.CreateKeyAsync(vaultUri, name, keyParams)
return result
} |> Async.RunSynchronously
let getKey name =
async {
let! result = client.GetKeyAsync(vaultBaseUrl = vaultUri, keyName = name)
return result
} |> Async.RunSynchronously
let getPubKey (jwk:KeyBundle) =
Array.concat [| jwk.Key.X; jwk.Key.Y |]
let hash (digest:IDigest) data =
digest.Reset()
digest.BlockUpdate(data, 0, data.Length)
let a = digest.GetDigestSize() |> Array.zeroCreate
digest.DoFinal(a, 0) |> ignore
a
let toHex (x:byte) = x.ToString("x2")
let getAddress pubKey =
pubKey
|> hash (new KeccakDigest(256))
|> Array.map toHex
|> Array.skip 12
|> String.Concat
|> (+) "0x"
createKey "testEcKey"
|> getPubKey
|> getAddress
|> Console.WriteLine
// 0x51e4370152c132d307c302d8146c63ca6bf41167