[jwt] RS256 vs HS256 : 차이점은 무엇입니까?

웹 앱에서 인증을 처리하기 위해 Auth0을 사용하고 있습니다. ASP.NET Core v1.0.0 및 Angular 2 rc5를 사용하고 있으며 일반적으로 인증 / 보안에 대해 잘 모릅니다.

에서 ASP.NET 코어 웹 API에 대한 Auth0 워드 프로세서 , RS256 및 HS256을 인 JWT 알고리즘에 대한 두 가지 선택이있다. 이것은 멍청한 질문이지만 다음과 같습니다.

RS256과 HS256의 차이점은 무엇입니까? 사용 사례는 무엇입니까 (해당되는 경우)?



답변

두 가지 선택 모두 아이덴티티 공급자가 JWT 에 서명 하는 데 사용하는 알고리즘을 나타 냅니다. 서명은 토큰 수신자가 토큰이 변경되지 않았 음을 확인하기 위해 유효성을 검사 할 수있는 “서명”(JWT의 일부)을 생성하는 암호화 작업입니다.

  • RS256 ( SHA-256을 사용한 RSA 서명 )은 비대칭 알고리즘 이며 공개 / 개인 키 쌍을 사용합니다. 아이덴티티 공급자에는 서명을 생성하는 데 사용되는 개인 (비밀) 키가 있으며 JWT 소비자는 공개 키를 얻습니다. 서명의 유효성을 검사합니다. 공개 키는 개인 키와 달리 보안을 유지할 필요가 없기 때문에 대부분의 ID 공급자는 소비자가 일반적으로 메타 데이터 URL을 통해 쉽게 구하고 사용할 수 있도록합니다.

  • 반면 HS256 ( SHA-256이있는 HMAC )은 해싱 기능과 서명으로 사용될 해시를 생성하는 데 사용되는 두 당사자간에 공유되는 하나의 비밀 키 조합을 포함합니다. 동일한 키가 서명을 생성하고 유효성을 검증하는 데 사용되므로 키가 손상되지 않도록주의해야합니다.

JWT를 사용하는 애플리케이션을 개발할 경우 비밀 키를 사용하는 사람을 제어 할 수 있으므로 HS256을 안전하게 사용할 수 있습니다. 반면에 클라이언트를 제어 할 수 없거나 비밀 키를 보호 할 방법이 없다면 소비자는 공개 (공유) 키만 알면되기 때문에 RS256이 더 적합합니다.

공개 키는 일반적으로 메타 데이터 엔드 포인트에서 사용 가능하므로 클라이언트는 공개 키를 자동으로 검색하도록 프로그래밍 할 수 있습니다. 이 경우 (.Net Core 라이브러리에서와 같이) 구성에 대한 작업이 줄어 듭니다 (라이브러리는 서버에서 공개 키를 가져옵니다). 반면 대칭 키는 대역 외 (보안 통신 채널 보장)에서 교환해야하며 서명 키 롤오버가있는 경우 수동으로 업데이트해야합니다.

Auth0은 공개 키를 검색 할 수있는 OIDC, SAML 및 WS-Fed 프로토콜에 대한 메타 데이터 엔드 포인트를 제공합니다. 클라이언트의 “고급 설정”에서 해당 엔드 포인트를 볼 수 있습니다.

예를 들어 OIDC 메타 데이터 엔드 포인트는 형식입니다 https://{account domain}/.well-known/openid-configuration. 해당 URL을 탐색 https://{account domain}/.well-known/jwks.json하면 계정의 공개 키를 포함 하는에 대한 참조가있는 JSON 객체가 표시 됩니다.

RS256 샘플을 보면 공개 키를 어디서나 구성 할 필요가 없음을 알 수 있습니다. 프레임 워크에서 자동으로 검색합니다.


답변

암호화에는 두 가지 유형의 알고리즘이 사용됩니다.

대칭 알고리즘

단일 키는 데이터를 암호화하는 데 사용됩니다. 키로 암호화 된 경우 동일한 키를 사용하여 데이터를 해독 할 수 있습니다. 예를 들어 Mary가 “my-secret”키를 사용하여 메시지를 암호화하여 John에게 보내면 동일한 키 “my-secret”을 사용하여 메시지를 올바르게 해독 할 수 있습니다.

비대칭 알고리즘

메시지를 암호화하고 해독하는 데 두 개의 키가 사용됩니다. 한 키 (공용)는 메시지를 암호화하는 데 사용되지만 다른 키 (비공개)는 메시지를 해독하는 데만 사용할 수 있습니다. 따라서 John은 공개 키와 개인 키를 모두 생성 한 다음 공개 키만 Mary에게 보내 메시지를 암호화 할 수 있습니다. 메시지는 개인 키를 통해서만 해독 할 수 있습니다.

HS256 및 RS256 시나리오

이 알고리즘은 데이터를 암호화 / 삭제하는 데 사용되지 않습니다. 오히려 데이터의 출처 또는 진위 여부를 확인하는 데 사용됩니다. Mary가 공개 메시지를 Jhon에게 보내야 할 때 메시지가 Mary의 메시지인지 반드시 HS256 또는 RS256인지 확인해야합니다.

HS256은 단일 키를 사용하여 주어진 데이터 샘플에 대한 서명을 생성 할 수 있습니다. 메시지가 서명과 함께 전송되면 수신 측은 동일한 키를 사용하여 서명이 메시지와 일치하는지 확인할 수 있습니다.

RS256은 키 쌍을 사용하여 동일한 작업을 수행합니다. 개인 키를 사용해서 만 서명을 생성 할 수 있습니다. 서명을 확인하려면 공개 키를 사용해야합니다. 이 시나리오에서 Jack은 공개 키를 찾더라도 Mary를 가장하는 서명이있는 스푸핑 메시지를 만들 수 없습니다.


답변

성능에 차이가 있습니다.

간단히 말해 검증 HS256보다 약 1 RS256배 빠르지 만 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.
        }
    }
}


답변

OAuth2에 대한 짧은 답변

  • 토큰 서명을 생성하기위한 HS256 사용자 클라이언트 시크릿 및 백엔드에서 토큰의 유효성을 검증하려면 동일한 시크릿이 필요합니다. 따라서 서명을 확인하려면 백엔드 서버에 해당 비밀의 사본이 있어야합니다.
  • RS256 은 공개 키 암호화를 사용하여 토큰에 서명합니다 .Signature (hash)는 개인 키를 사용하여 생성하며 공개 키를 사용하여 확인할 수 있습니다. 따라서 백엔드 서버에 저장하기 위해 개인 키 또는 클라이언트 비밀번호가 필요하지 않지만 백엔드 서버는 테넌트의 openid 구성 URL에서 공개 키를 가져옵니다 ( https : // [tenant] /.well-known/openid -configuration )을 사용하여 토큰을 확인하십시오. access_toekn 내부의 KID 매개 변수는 openid 구성에서 올바른 키 (공용)를 감지하는 데 사용됩니다.

답변