Created
April 14, 2020 17:57
-
-
Save jpda/e729ead6b66b040d7e2c859b70652fd7 to your computer and use it in GitHub Desktop.
get jwk thumbprint
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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