The key to information card backed systems is calculating a stable unique identifier for your users based on the card claims. Typically this is done by hashing the issuer public key (plus some other information like a PPID for managed cards).
This involves the following steps:
- Find the issuer claim set containing the issuer RSA key
- Optionally find some other claims (e.g. a PPID)
- Create a hash from that pieces of information
In LeastPrivilege.IdentityModel I added some helper extension methods to simplify this process. The first one extends the RSACryptoServiceProvider class and allows calculating the hash the from the key (plus optional extra information):
public static class RSAExtensions
{
public static byte[] GetKeyHash(this RSACryptoServiceProvider rsa)
{
return rsa.GetKeyHash(string.Empty);
}
public static byte[] GetKeyHash(this RSACryptoServiceProvider rsa, string entropy)
{
int entropyLength = Encoding.UTF8.GetByteCount(entropy);
RSAParameters rsaParams = rsa.ExportParameters(false);
byte[] shaInput;
byte[] shaOutput;
int i = 0;
shaInput = new byte[rsaParams.Modulus.Length +
rsaParams.Exponent.Length + entropyLength];
rsaParams.Modulus.CopyTo(shaInput, i);
i += rsaParams.Modulus.Length;
rsaParams.Exponent.CopyTo(shaInput, i);
i += rsaParams.Exponent.Length;
i += Encoding.UTF8.GetBytes(entropy, 0, entropy.Length, shaInput, i);
using (SHA256 sha = SHA256.Create())
{
shaOutput = sha.ComputeHash(shaInput);
}
return shaOutput;
}
}
I use the same algorithm here as in Microsoft’s Token class for ASP.NET. This makes it easy to share the IDs between ASP.NET and other application types.
Another helper extends IEnumerable<ClaimSet> and simplifies retrieving the RSA claim:
public static RSACryptoServiceProvider FindIssuerRsaClaim(
this IEnumerable<ClaimSet> claimSets)
{
return claimSets.FindClaim(
ClaimTypes.Rsa,
ClaimSearchMode.Issuer).Get<RSACryptoServiceProvider>();
}
With these helpers you can retrieve the unique ID by using this code:
AuthorizationContext context =
ServiceSecurityContext.Current.AuthorizationContext;
byte[] uniqueId = context.ClaimSets.FindIssuerRsaClaim().GetKeyHash();