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


254

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

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

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


서버 측 애플리케이션 튜토리얼 freakyjolly.com/
Code Spy

답변:


437

두 가지 선택 모두 아이덴티티 공급자가 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 샘플을 보면 공개 키를 어디서나 구성 할 필요가 없음을 알 수 있습니다. 프레임 워크에서 자동으로 검색합니다.


46
rs256을 사용할 때 NB- 많은 라이브러리에 보안 위험 이 있거나 토큰을 사용하여 사용할 알고리즘을 결정할 수 있습니다. 기본적으로 공격자는 공개 rs256 키를 hs256 인코딩과 함께 사용하여 비밀 키인 것처럼 가장 할 수 있습니다. 따라서 라이브러리에 이러한 동작이 없는지 확인하십시오!
AlexFoxGill

7
하나의 작은 수정, "HS256 (SHA-256이있는 HMAC)은 대칭 알고리즘입니다"– HMAC는 대칭 키 알고리즘을 사용하지 않습니다 (정의에 의해 서명을 암호화 및 해독 할 수 있음). HMAC 아래의 암호화 해시 기능과 비밀 암호화 키를 사용 합니다. 비밀 키가 추가 된 메시지에 대한 해시 (단방향 함수) 계산을 의미합니다.
Kikoz

1
Google의 예 : accounts.google.com/.well-known/openid-configuration으로 이동하여 jwks_uri를보십시오. 키를 찾을 수있는 googleapis.com/oauth2/v3/certs 로 전달합니다 . 그런 다음 아이가 좋은 열쇠를 가져와야합니다.
Denis TRUFFAUT

HS256이 당사자 간에 키를 공유하므로이 알고리즘은 하나의 access_token에서 여러 대상을 지원할 수 없지만 RS256은 여러 대상을 지원할 수 있습니다. 이는 구성이 RS256 인 경우 access_token을 사용하여 / userinfo 엔드 포인트에 대한 요청 만 허용하는 Auth0과 같은 Identity 클라이언트와 관련이 있습니다.이 토큰은 auth0 도메인뿐만 아니라 api 도메인을 aud로 지원하기 위해이 토큰이 필요하기 때문입니다.
jezpez

94

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

대칭 알고리즘

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

비대칭 알고리즘

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

HS256 및 RS256 시나리오

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

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

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


38

성능에 차이가 있습니다.

간단히 말해 검증 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.
        }
    }
}

이것들은 중요한 숫자입니다. 감사합니다. 암호화는 다소 투명한 wrt 처리량으로 생각하지만 R256을 사용하여 시스템 간 통신에 서명하면 홉당 1ms가 추가됩니다.
Matthew Mark Miller

1
@MatthewMarkMiller 동일하지는 않지만 명심하십시오. 그들은 다른 특성을 가지고 있습니다. RS256은 비대칭이므로 공개 키만 공유하는 클라이언트 / 서버 스타일 통신에서는 더 나은 옵션입니다. HS256은 서명 및 확인할 수있는 키를 공유해야합니다. 두 당사자를 신뢰하거나 당사자 중 한 명이 필요없는 경우에만 유용합니다.
Rob Evans

7
@RobEvans 예, 여기서 성능 수치에 매달리지 마십시오. 문제에 대한 올바른 해결책을 선택하십시오. 이것은 단지 관찰에 불과하며 RS256보다 HS256을 선호하는 권장 사항이 아니라 상황에 따라 결정해야합니다.
John Leidegren

1
프로토콜 선택이 추가 킬로미터 케이블과 지연 시간에 동일한 영향을 미칠 수있는 경우 특히 긴 콜 체인과 긴밀한 TTL에서 알아야 할 가치가 있습니다.
Matthew Mark Miller

0

OAuth2에 대한 짧은 답변

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