web-dev-qa-db-ja.com

RS256とHS256の違い:違いは何ですか?

私は自分のWebアプリで認証を処理するためにAuth0を使用しています。私はASP.NET Core v1.0.0とAngular 2 rc5を使用していますが、認証/セキュリティ全般についてはあまり知りません。

ASP.NET Core Web ApiのAuth0ドキュメント では、JWTアルゴリズムにはRS256とHS256の2つの選択肢があります。これは愚かな質問かもしれませんが:

RS256とHS256の違いは何ですか?いくつかのユースケースは何ですか(該当する場合)。

173
Rico Kahler

どちらの選択も、IDプロバイダーが sign JWTに使用するアルゴリズムを表します。署名は、トークンの改ざんが行われていないことを確認するためにトークンの受信者が検証できる「署名」(JWTの一部)を生成する暗号化操作です。

  • RS256( SHA-256 を持つRSA署名)は 非対称アルゴリズム で、公開鍵と秘密鍵のペアを使用します。アイデンティティプロバイダには署名の生成に使用される秘密(秘密)鍵があります。 JWTの利用者は、署名を検証するための公開鍵を入手します。秘密鍵ではなく公開鍵を保護する必要がないため、ほとんどのアイデンティティプロバイダは、(通常メタデータURLを通じて)消費者が簡単に入手して使用できるようにしています。

  • 一方、HS256( _ hmac _ with SHA-256)は、 対称アルゴリズム で、2者間で共有される唯一の(秘密)キーを持ちます。署名の生成と検証の両方に同じキーが使用されるため、キーが危険にさらされないように注意する必要があります。

JWTを消費するアプリケーションを開発する場合は、誰が秘密鍵を使用するかを制御できるため、安全にHS256を使用できます。一方、クライアントを制御できない場合、または秘密鍵を保護する方法がない場合は、消費者は公開(共有)鍵を知っているだけでよいため、RS256が適しています。

公開鍵は通常、メタデータエンドポイントから利用可能になるため、クライアントは公開鍵を自動的に取得するようにプログラムできます。このような場合(.NETコアライブラリの場合と同様)、設定作業は少なくなります(ライブラリはサーバーから公開キーを取得します)。一方、対称鍵は帯域外で交換する必要があり(安全な通信チャネルを確保するため)、署名鍵のロールオーバーが発生した場合は手動で更新する必要があります。

Auth0は、公開鍵を取得できるOIDC、SAML、およびWS-Fedプロトコル用のメタデータエンドポイントを提供します。これらのエンドポイントは、クライアントの[詳細設定]の下に表示されます。

たとえば、OIDCメタデータエンドポイントはhttps://{account domain}/.well-known/openid-configurationの形式を取ります。そのURLを参照すると、アカウントの公開鍵を含むhttps://{account domain}/.well-known/jwks.jsonへの参照を含むJSONオブジェクトが表示されます。

RS256のサンプルを見れば、公開鍵をどこにも設定する必要がないことがわかります。フレームワークによって自動的に取得されます。

298
Nico Sabena

暗号化では、2種類のアルゴリズムが使用されます。

対称アルゴリズム

データの暗号化には単一の鍵が使用されます。キーで暗号化すると、同じキーを使用してデータを復号化できます。たとえば、Maryがキー「my-secret」を使用してメッセージを暗号化してJohnに送信すると、同じキー「my-secret」を使用してメッセージを正しく復号化できます。

非対称アルゴリズム

メッセージの暗号化と復号化には2つの鍵が使用されます。一方の鍵(パブリック)はメッセージの暗号化に使用されますが、もう一方の鍵(プライベート)はメッセージの復号化にのみ使用できます。したがって、Johnは公開鍵と秘密鍵の両方を生成してから、公開鍵のみをMaryに送信してメッセージを暗号化できます。メッセージは秘密鍵を使用してのみ復号化できます。

HS256およびRS256シナリオ

これらのアルゴリズムはデータの暗号化/復号化には使用されません。それどころか、それらはオリジンまたはデータの信憑性を検証するために使用されます。 MaryがJhonにオープンメッセージを送信する必要があり、そのメッセージが確実にMaryからのものであることを確認する必要がある場合は、HS256またはRS256を使用できます。

HS256は、単一のキーを使用してデータの特定のサンプルに対する署名を作成できます。メッセージが署名と共に送信されると、受信側は同じ鍵を使用して署名がメッセージと一致することを確認できます。

RS256は同じことをするためにキーのペアを使います。署名は秘密鍵を使用してのみ生成できます。そして、公開鍵を使って署名を検証する必要があります。このシナリオでは、たとえJackが公開鍵を見つけたとしても、彼はMaryを偽装するための署名付きのなりすましメッセージを作成することはできません。

58
Charlie H

パフォーマンスに違いがあります。

簡単に言うと、HS256は検証のためのRS256よりも約1桁速いが、発行(署名)のためにはRS256よりも約2桁速い。

 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)

実際の数にこだわらずに、お互いを尊重しながら考えてください。

[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.
        }
    }
}
23
John Leidegren