C #에서 RS256 (비대칭)으로 JWT 확인

Aug 19 2020

비대칭 RS256을 사용하고 있지만 "SymmetricSecurityKey ()"가 있기 때문에 실패한다고 생각되는 이와 같은 코드가 있습니다. 토큰은https://jwt.io/

  1. 내 비대칭 공개 키를 사용하도록 어떻게 변환합니까?
  2. 또한 저는 C #을 처음 사용하고 dotnet 표준을 대상으로하고 싶습니다. 그래서 잘못된 lib를 사용하고 있는지도 궁금합니다. (미리보기 릴리스에 따라 다름)
λ cat Program.cs
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using System.Linq;
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;

namespace jwttest
{
    class Program
    {
        static void Main(string[] args)
        {
            string jwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA";
            var pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQAB";
            var rawKey = Encoding.ASCII.GetBytes(pubKey);

            var tokenHandler = new JwtSecurityTokenHandler();
            // var rsa = ?
            tokenHandler.ValidateToken(jwt, new TokenValidationParameters {
                IssuerSigningKey = new SymmetricSecurityKey(rawKey)
            },
            out SecurityToken validatedToken);
        }
    }
}

C:\src\jwttest (cgt-test-5 -> origin)
λ dotnet run
[2020-08-18T23:41:05.7108585-07:00 Info] raw=System.Byte[] [392]
Unhandled exception. Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10503: Signature validation failed. Keys tried: 'System.Text.StringBuilder'.
Exceptions caught:
 'System.Text.StringBuilder'.
token: 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken'.
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
   at jwttest.Program.Main(String[] args) in C:\src\jwttest\Program.cs:line 22

λ cat jwttest.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <!-- Using preview release because it only depends on dotnet standard.  Prior versions need framework. -->
    <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.7.2-preview-10803222715" />
  </ItemGroup>
</Project>

λ cat jwt.json
{
  "alg": "RS256",
  "typ": "JWT"
}
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "iat": 1516239022
}

답변

2 Topaco Aug 19 2020 at 13:26
  • 첫 번째 질문과 관련하여
    게시 된 스택 추적에 따르면 .NET Core 3.1을 사용하는 것 같습니다. 이렇게하면 다음과 같이 공개 X.509 / SPKI 키를 쉽게 가져올 수 있습니다.

    var pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQAB";
    
    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
    rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(pubKey), out _); // import the public X.509/SPKI DER encoded key
    

    ImportSubjectPublicKeyInfo() .NET Core 3.0부터 사용할 수 있습니다.

    편집 시작 : 이전 버전의 .NET Core (3.0 이전) 또는 .NET Framework ImportSubjectPublicKeyInfo()에서는 사용할 수 없으므로 .NET Standard 2.1 이상 이 필요합니다.

    예를 들어 .NET Standard 2.0과 같은 이전 버전의 경우 한 가지 가능성은 BouncyCastle 을 사용하는 것입니다 . 더 정확하게는 Org.BouncyCastle.OpenSsl.PemReader클래스를 사용하여 X509 / SPKI 형식으로 공개 키를 가져올 수 있습니다. 에서 이 답변 당신이 사용하는 방법의 예를 찾을 수 있습니다 PemReader. PemReader이름에서 알 수 있듯이 PEM 인코딩, 즉 DER 인코딩으로의 변환 (즉, 머리글, 바닥 글 및 줄 바꿈 제거, 나머지 부분의 Base64 디코딩 제거)은 ImportSubjectPublicKeyInfo() 수행되지 않아야합니다 . 또한 PemReader머리글 ( -----BEGIN PUBLIC KEY-----\n) 바로 뒤의 줄 바꿈 ( )과 바닥 글 ( \n-----END PUBLIC KEY-----) 바로 앞의 두 번째 줄 바꿈 이 예상 되는 경우 64 자마다 Base64로 인코딩 된 본문의 줄 바꿈은 PemReader.

    또 다른 가능성은 패키지 opensslkey 가 방법을 제공하는 것입니다.이 방법 은 .NETopensslkey.DecodeX509PublicKey() 과 유사한 DER 인코딩에서 X509 / SPKI 키를 처리 할 수 ​​있습니다 ImportSubjectPublicKeyInfo. 끝 수정

  • 두 번째 질문과 관련하여 .NET 표준 버전
    이 여러 개 있습니다. 예를 들어 .NET Core 3.0은 .NET Standard 2.1을 구현합니다. 사용중인 System.IdentityModel.Tokens.Jwt 6.7.2-preview-10803222715 패키지 에는 .NET Standard 2.0이 필요합니다.

    System.IdentityModel.Tokens.JwtJWT (JSON Web Tokens)의 생성 및 유효성 검사를 지원하는 패키지입니다. 게시 된 토큰의 경우 다음과 같이 유효성 검사를 구현할 수 있습니다.

    string jwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.POstGetfAytaZS82wHcjoTyoqhMyxXiWdR7Nn7A29DNSl0EiXLdwJ6xC6AfgZWF1bOsS_TuYI3OG85AmiExREkrS6tDfTQ2B3WXlrr-wp5AokiRbz3_oB4OxG-W9KcEEbDRcZc0nH3L7LzYptiy1PtAylQGxHTWZXtGz4ht0bAecBgmpdgXMguEIcoqPJ1n3pIWk_dUZegpqx0Lka21H6XxUTxiy8OcaarA8zdnPUnV6AmNP3ecFawIFYdvJB_cm-GvpCSbr8G8y_Mllj8f4x9nBH8pQux89_6gUY618iYv7tuPWBFfEbLxtF2pZS6YC1aSfLQxeNe8djT9YjpvRZA";
    
    var tokenHandler = new JwtSecurityTokenHandler();
    bool verified = false;
    try
    {
        tokenHandler.ValidateToken(jwt, new TokenValidationParameters
        {
            ValidateAudience = false,                       
            ValidateLifetime = false,
            ValidateIssuer = false,
            IssuerSigningKey = new RsaSecurityKey(rsa)
        },
        out _);
    
        verified = true;
    }
    catch 
    {
        verified = false;
    }
    
    Console.WriteLine("Verified: " + verified);
    

    유효성 검사는 유효성 검사 매개 변수, 즉의 두 번째 매개 변수를 통해 제어 할 수 있습니다 ValidateToken(). 게시 된 토큰에 iss , audexp 클레임이 포함되어 있지 않기 때문에 (예 :https://jwt.io/), 내 예제의 유효성 검사에서 제외됩니다.

    ASP.NET Core에서 JWT 토큰 생성 및 유효성 검사 자습서 에서 특히 토큰 유효성 검사 장에서 더 자세한 설명을 찾을 수 있습니다.

    ValidateToken()본질적으로 JWT 서명의 확인 프로세스를 캡슐화합니다. JWT는 Base64url 인 개별 부품 부호화 도트가 서로 분리 된 헤더, 페이로드 및 서명, 세 부분으로 구성하는 데이터 구조이다.
    서명은 다양한 알고리즘 (예 : RS256 )을 사용하여 생성됩니다 . 이는 데이터 (Base64url 인코딩 헤더 및 구분 기호 포함 페이로드)가 PKCS # 1 v1.5 패딩 및 다이제스트 SHA256이있는 알고리즘 RSA를 사용하여 서명됨을 의미합니다.
    또한 (즉, 참여하지 않고 단독으로 암호화 API를 함께 할 수있는 서명의 검증에 토큰 대응의 검증 System.IdentityModel.Tokens.Jwt 그것이 이루어집니다 같이) 허용 대답 에 링크 된 질문 @zaitsman의 의견.