J'utilise Auth0 pour gérer l'authentification dans mon application Web. J'utilise ASP.NET Core v1.0.0 et Angular 2 rc5 et je ne connais pas grand-chose de l'authentification/de la sécurité en général.
Dans Auth0 docs pour ASP.NET Core Web Api , il existe deux choix pour l’algorithme JWT: RS256 et HS256. C'est peut-être une question idiote mais:
Quelle est la différence entre RS256 et HS256? Quels sont certains cas d'utilisation (le cas échéant)?
Les deux choix font référence à l'algorithme utilisé par le fournisseur d'identité pour signer le JWT. La signature est une opération cryptographique qui génère une "signature" (partie du JWT) que le destinataire du jeton peut valider pour s'assurer que le jeton n'a pas été falsifié.
RS256 (Signature RSA avec SHA-256 ) est un algorithme asymétrique , et il utilise une paire de clés publique/privée: le fournisseur d'identité dispose d'une clé privée (secrète) utilisée pour générer la signature et le consommateur du JWT obtient une clé publique pour valider la signature. Etant donné que la clé publique, contrairement à la clé privée, n'a pas besoin d'être sécurisée, la plupart des fournisseurs d'identité le rendent facile à obtenir et à utiliser par les consommateurs (généralement via une URL de métadonnées).
HS256 ( HMAC avec SHA-256), quant à lui, associe une fonction de hachage et une clé (secrète) partagée entre les deux parties et servant à générer le hachage qui servir de signature. Étant donné que la même clé est utilisée à la fois pour générer la signature et pour la valider, il faut veiller à ne pas compromettre la clé.
Si vous développez l'application qui utilise les JWT, vous pouvez utiliser le HS256 en toute sécurité, car vous aurez le contrôle sur les utilisateurs des clés secrètes. Si, par contre, vous ne contrôlez pas le client ou n’avez aucun moyen de sécuriser une clé secrète, le RS256 conviendra mieux car le consommateur n’a besoin que de connaître la clé publique (partagée).
Étant donné que la clé publique est généralement rendue disponible à partir de points de terminaison de métadonnées, les clients peuvent être programmés pour récupérer automatiquement la clé publique. Si tel est le cas (comme c'est le cas avec les bibliothèques .Net Core), vous aurez moins de travail de configuration (les bibliothèques récupéreront la clé publique du serveur). Les clés symétriques, en revanche, doivent être échangées hors bande (garantissant un canal de communication sécurisé) et mises à jour manuellement en cas de substitution de clé de signature.
Auth0 fournit des points de terminaison de métadonnées pour les protocoles OIDC, SAML et WS-Fed, dans lesquels les clés publiques peuvent être récupérées. Vous pouvez voir ces points de terminaison sous les "Paramètres avancés" d'un client.
Le point de terminaison de métadonnées OIDC, par exemple, prend la forme de https://{account domain}/.well-known/openid-configuration
. Si vous accédez à cette URL, vous verrez un objet JSON avec une référence à https://{account domain}/.well-known/jwks.json
, qui contient la clé publique (ou les clés) du compte.
Si vous examinez les exemples RS256, vous constaterez qu'il n'est pas nécessaire de configurer la clé publique où que ce soit: elle est automatiquement récupérée par la structure.
En cryptographie, deux types d’algorithmes sont utilisés:
algorithmes symétriques
Une seule clé est utilisée pour chiffrer les données. Une fois chiffrées avec la clé, les données peuvent être déchiffrées à l'aide de la même clé. Si, par exemple, Mary chiffre un message à l'aide de la clé "my-secret" et l'envoie à John, il sera en mesure de déchiffrer correctement le message avec la même clé "my-secret".
algorithmes asymétriques
Deux clés sont utilisées pour chiffrer et déchiffrer les messages. Alors qu'une clé (publique) est utilisée pour chiffrer le message, l'autre clé (privée) ne peut être utilisée que pour le déchiffrer. Ainsi, John peut générer des clés publiques et privées, puis envoyer uniquement la clé publique à Mary pour chiffrer son message. Le message ne peut être déchiffré qu'à l'aide de la clé privée.
Scénario HS256 et RS256
Ces algorithmes ne sont PAS utilisés pour chiffrer/déchiffrer des données. Ils sont plutôt utilisés pour vérifier l'origine ou l'authenticité des données. Lorsque Mary doit envoyer un message ouvert à Jhon et qu'il doit vérifier que le message provient bien de Mary, vous pouvez utiliser le HS256 ou le RS256.
Le HS256 peut créer une signature pour un échantillon de données donné en utilisant une clé unique. Lorsque le message est transmis avec la signature, la partie destinataire peut utiliser la même clé pour vérifier que la signature correspond au message.
RS256 utilise une paire de clés pour faire la même chose. Une signature ne peut être générée qu'avec la clé privée. Et la clé publique doit être utilisée pour vérifier la signature. Dans ce scénario, même si Jack trouve la clé publique, il ne peut pas créer de message d'usurpation avec une signature pour imiter Mary.
Il y a une différence de performance.
Autrement dit, HS256
est environ 1 ordre de grandeur plus rapide que RS256
pour la vérification, mais environ 2 ordres de grandeur plus rapide que RS256
pour l'émission (la signature).
640,251 91,464.3 ops/s
86,123 12,303.3 ops/s (RS256 verify)
7,046 1,006.5 ops/s (RS256 sign)
Ne vous attardez pas sur les chiffres réels, pensez simplement à les respecter les uns les autres.
[Program.cs]
class Program
{
static void Main(string[] args)
{
foreach (var duration in new[] { 1, 3, 5, 7 })
{
var t = TimeSpan.FromSeconds(duration);
byte[] publicKey, privateKey;
using (var rsa = new RSACryptoServiceProvider())
{
publicKey = rsa.ExportCspBlob(false);
privateKey = rsa.ExportCspBlob(true);
}
byte[] key = new byte[64];
using (var rng = new RNGCryptoServiceProvider())
{
rng.GetBytes(key);
}
var s1 = new Stopwatch();
var n1 = 0;
using (var hs256 = new HMACSHA256(key))
{
while (s1.Elapsed < t)
{
s1.Start();
var hash = hs256.ComputeHash(privateKey);
s1.Stop();
n1++;
}
}
byte[] sign;
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportCspBlob(privateKey);
sign = rsa.SignData(privateKey, "SHA256");
}
var s2 = new Stopwatch();
var n2 = 0;
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportCspBlob(publicKey);
while (s2.Elapsed < t)
{
s2.Start();
var success = rsa.VerifyData(privateKey, "SHA256", sign);
s2.Stop();
n2++;
}
}
var s3 = new Stopwatch();
var n3 = 0;
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportCspBlob(privateKey);
while (s3.Elapsed < t)
{
s3.Start();
rsa.SignData(privateKey, "SHA256");
s3.Stop();
n3++;
}
}
Console.WriteLine($"{s1.Elapsed.TotalSeconds:0} {n1,7:N0} {n1 / s1.Elapsed.TotalSeconds,9:N1} ops/s");
Console.WriteLine($"{s2.Elapsed.TotalSeconds:0} {n2,7:N0} {n2 / s2.Elapsed.TotalSeconds,9:N1} ops/s");
Console.WriteLine($"{s3.Elapsed.TotalSeconds:0} {n3,7:N0} {n3 / s3.Elapsed.TotalSeconds,9:N1} ops/s");
Console.WriteLine($"RS256 is {(n1 / s1.Elapsed.TotalSeconds) / (n2 / s2.Elapsed.TotalSeconds),9:N1}x slower (verify)");
Console.WriteLine($"RS256 is {(n1 / s1.Elapsed.TotalSeconds) / (n3 / s3.Elapsed.TotalSeconds),9:N1}x slower (issue)");
// RS256 is about 7.5x slower, but it can still do over 10K ops per sec.
}
}
}