For the first issue, I suggest that you rise a feedback from here if you require this feature.
And for the second issue, it is same to verify the tokens from Azure AD B2C and normal Azure AD. We can generate the public key using the exponent(e
) and modulus(n
). But the keys endpoint is different, we need to using the link like below to retrieve the keys for the Azure AD B2C:
https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys?p={signInPolicy}
Here is the code to verify the token issued by Azure AD B2C for your reference:
static void Main(string[] args)
{
var idtoken = "";
var exponent = "AQAB";
var modulus = "";
var result= VerifyTokenDetails(idtoken, exponent, modulus);
}
private static bool VerifyTokenDetails(string idToken, string exponent, string modulus)
{
try
{
var parts = idToken.Split('.');
var header = parts[0];
var payload = parts[1];
string signedSignature = parts[2];
//Extract user info from payload
string userInfo = Encoding.UTF8.GetString(Base64UrlDecode(payload));
//Which will be Verified
string originalMessage = string.Concat(header, ".", payload);
byte[] keyBytes = Base64UrlDecode(modulus);
string keyBase = Convert.ToBase64String(keyBytes);
string key = @"<RSAKeyValue> <Modulus>" + keyBase + "</Modulus> <Exponent>" + exponent + "</Exponent> </RSAKeyValue>";
bool result = VerifyData(originalMessage, signedSignature, key);
if (result)
return true;
else
return false;
}
catch (Exception ex) { }
return false;
}
/// <summary>
/// Verifies encrypted signed message with public key encrypted original message.
/// </summary>
/// <param name="originalMessage">Original message as string. (Encrypted form)</param>
/// <param name="signedMessage">Signed message as string. (Encrypted form)</param>
/// <param name="publicKey">Public key as XML string.</param>
/// <returns>Boolean True if successful otherwise return false.</returns>
private static bool VerifyData(string originalMessage, string signedMessage, string publicKey)
{
bool success = false;
using (var rsa = new RSACryptoServiceProvider())
{
var encoder = new UTF8Encoding();
byte[] bytesToVerify = encoder.GetBytes(originalMessage);
byte[] signedBytes = Base64UrlDecode(signedMessage);
try
{
rsa.FromXmlString(publicKey);
SHA256Managed Hash = new SHA256Managed();
byte[] hashedData = Hash.ComputeHash(signedBytes);
// Summary:
// Verifies that a digital signature is valid by determining the hash value in the
// signature using the provided public key and comparing it to the hash value of
// the provided data.
success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA256"), signedBytes);
}
catch (CryptographicException e)
{
success = false;
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
return success;
}
private static byte[] Base64UrlDecode(string input)
{
var output = input;
output = output.Replace('-', '+'); // 62nd char of encoding
output = output.Replace('_', '/'); // 63rd char of encoding
switch (output.Length % 4) // Pad with trailing '='s
{
case 0: break; // No pad chars in this case
case 2: output += "=="; break; // Two pad chars
case 3: output += "="; break; // One pad char
default: throw new System.Exception("Illegal base64url string!");
}
var converted = Convert.FromBase64String(output); // Standard base64 decoder
return converted;
}