I have a Json Web Key that contains public key information and I'd like to get the public key as byte array. My goal is to eventually derive ethereum address from it, using Keccak hash. I understand the process of getting the address from the public key byte array, but I don't know how to obtain this byte array. My scenario is, generate EC key using Azure Key Vault API, retrieve the key (JWK format) and then find the address. The JWK has the following format.
{
"kid": "https: //mykeyvault.vault.azure.net/keys/testeckey/8bad08aaae514efe981eaab4e590778d",
"kty": "EC",
"key_ops": [
"sign",
"verify"
],
"crv": "P-256",
"x": "YooqHyo7hlmcrBs5lDSSUsB0axzvorjxzNl6DBZLUf0",
"y": "NM-JrV6NTbUgILY_sBm5VgYxt1zYccCOCFtSDicSfWM"
}
I'm using the Azure .NET SDK and I have the option to use Bouncy Castle for .NET or any JS library, since project can also run in a Node.js environment. How can I obtain the byte array of the public key? Thank you
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