Skip to content

Instantly share code, notes, and snippets.

@jpda
Created April 14, 2020 17:57
Show Gist options
  • Save jpda/e729ead6b66b040d7e2c859b70652fd7 to your computer and use it in GitHub Desktop.
Save jpda/e729ead6b66b040d7e2c859b70652fd7 to your computer and use it in GitHub Desktop.
get jwk thumbprint
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
namespace RsaModExpToPubKey
{
class Program
{
static void Main(string[] args)
{
var keys = new Ok(new HttpClient()).GetJwkThumbprint("jpdab2c", "B2C_1A_signup_signin").Result;
foreach (var x in keys)
{
Console.WriteLine($"=== keyid {x.Key} ===");
Console.WriteLine($"thumbprint base64: {Convert.ToBase64String(x.Value)}");
Console.WriteLine($"thumbprint base64url: {Base64UrlEncoder.Encode(x.Value)}");
}
Console.WriteLine("all finished");
Console.ReadLine();
}
}
public class Ok
{
private HttpClient _client;
public Ok(HttpClient c)
{
_client = c;
}
public async Task<Dictionary<string, byte[]>> GetJwkThumbprint(string tenantName, string policyName)
{
var uri = new Uri($"https://{tenantName}.b2clogin.com/{tenantName}.onmicrosoft.com/v2.0/.well-known/openid-configuration?p={policyName}");
return await GetJwkThumbprint(uri);
}
public async Task<Dictionary<string, byte[]>> GetJwkThumbprint(Uri discoveryDocument)
{
// get openid-configuration metadata - discovery URL is *per policy* - in the portal, open the policy, go to 'test' or in IEF just click one and the OpenID Connect discovery endpoint should be at the top
// although unless you've made changes to policies to add more signing keys, they should all use the main signing key in B2C_1A_TokenSigningKeyContainer
Console.WriteLine($"Getting discovery document at {discoveryDocument.ToString()}");
var metadataRequest = await _client.GetAsync(discoveryDocument);
if (!metadataRequest.IsSuccessStatusCode) { Console.WriteLine($"something went wrong getting metadata: {metadataRequest.StatusCode}"); }
metadataRequest.EnsureSuccessStatusCode();
// let's get the JWK url from the metadata:
var metadata = JsonDocument.Parse(await metadataRequest.Content.ReadAsStreamAsync());
var jwksUrl = metadata.RootElement.GetProperty("jwks_uri").GetString();
// and fetch the jwks document
Console.WriteLine($"Getting jwk document at {jwksUrl}");
var jwkRequest = await _client.GetAsync(jwksUrl);
if (!jwkRequest.IsSuccessStatusCode) { Console.WriteLine($"something went wrong getting jwks: {jwkRequest.StatusCode}"); }
jwkRequest.EnsureSuccessStatusCode();
var jwks = JsonDocument.Parse(await jwkRequest.Content.ReadAsStreamAsync());
// you likely only have one key
Console.Write($"Reading keys...");
var keys = jwks.RootElement.GetProperty("keys").EnumerateArray().ToList(); // prevent multiple enumeration
Console.WriteLine($"got {keys.Count} key{(keys.Count == 1 ? string.Empty : 's'.ToString()) }");
var keyList = new Dictionary<string, byte[]>();
foreach (var key in keys)
{
var kid = key.GetProperty("kid").GetString();
var e = key.GetProperty("e").GetString();
var n = key.GetProperty("n").GetString();
var exponent = Convert.FromBase64String(e);
var modulus = Base64UrlEncoder.DecodeBytes(n);
// create new RSA security key with exponent & modulus parameters
var ne = new RSAParameters()
{
Exponent = exponent,
Modulus = modulus
};
var k = new RsaSecurityKey(ne);
var thumbprint = k.ComputeJwkThumbprint();
keyList.Add(kid, thumbprint);
}
return keyList;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment