본문 바로가기
Backend/C# .NET

[C#] JWS 생성 (JSON Web Signature) / 검증 / RS256

by SOLYI 2023. 8. 8.

용어 설명

  • JWT (JSON Web Token)

    • JSON 형식 토큰의 표준 [RFC-7519]
    • 장점: 권한 부여, 정보 교환시 유용
    • 형식: header, payload, signatrue의 3가지로 구성되어 있다. 사이에 점을 추가해서 header.payload.signature로 표현된다.
    const token = base64urlEncoding(header) + '.' + base64urlEncoding(payload) + '.' + base64urlEncoding(signature);
  • JWS (JSON Web Signature)

  • JSON Web Signature 를 의미한다.
    [RFC-7515]

  • JWTJWS의 차이

    • JWT는 클레임 기반의 웹 토큰으로 사용자의 인증, 권한 부여와 같은 정보를 담을 수 있다.
    • JWS는 JWT 유형 중 하나로 JWT를 생성하거나 사용되는 서명 매커니즘을 나타낸다. 즉, JWS는 JWT의 일부분으로 토큰의 신뢰성을 높이는 역할을 한다.

코드

  1. JWT 생성

     public void GenJWT()
     {
       // 1. HEADER
       RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
       RSAParameters privateKey = rsa.ExportParameters(true);
    
       var n = Base64UrlEncoder.Encode(privateKey.Modulus);
       var e = Base64UrlEncoder.Encode(privateKey.Exponent);
    
       var jsonWebKey = new JsonWebKey()
       {
         Kty = JsonWebAlgorithmsKeyTypes.RSA,
         N = n,
         E = e,
       };
    
       var securityKey = new RsaSecurityKey(privateKey);
       var header = new JwtHeader(new SigningCredentials(securityKey, SecurityAlgorithms.RsaSha256))
       {
         { "jwk", jsonWebKey }
       };
    
       // 2. PAYLOAD
       // 2-1. Data 가져오기
       UserInfo user = this.GetUserInfo();
    
       // 2-2. 토큰 만료 시간 설정
       DateTimeOffset currentDateTime = DateTimeOffset.Now;
       long nbf = currentDateTime.ToUnixTimeSeconds();
    
       DateTimeOffset expirationDateTime = currentDateTime.AddHours(24);
       long exp = expirationDateTime.ToUnixTimeSeconds();
    
       var payload = new JwtPayload()
       {
         { "iss", user.Company.Url },
         { "sub", user.Name },
         { "nbf", nbf },
         { "exp", exp },
         { "jti", user.Email },
         { "user", user },
       };
    
       // 3. JWT 생성
       var token = new JwtSecurityToken(header, payload);
       var handler = new JwtSecurityTokenHandler();
       var jwtToken = handler.WriteToken(token);
       Console.WriteLine(jwtToken);
     }
    • header
    {
      "alg": "RS256",
      "typ": "JWT",
      "jwk": {
        "e": "AQAB",
        "kty": "RSA",
        "n": "길어서 생략"
      }
    }
    • payload
    {
      "iss": "https://solyi.kr",
      "sub": "솔이",
      "nbf": 1691566698,
      "exp": 1691653098,
      "jti": "solyi@naver.com",
      "user": {
        "Id": "solyi",
        "Password": "password",
        "Name": "솔이",
        "Birthday": "1991-05-15T00:00:00",
        "Mobile": "010-6666-7777",
        "Email": "solyi@solyi.com",
        "Company": {
          "Name": "trivue",
          "Address": "서울 양천구",
          "Url": "https://solyi.kr"
        }
      }
    }
    • JWT
     eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImp3ayI6eyJlIjoiQVFBQiIsImt0eSI6IlJTQSIsIm4iOiJyMzZGU1hvOTFjQTNwcGpjTHo5c0VIeDZ2UnV0bDRNZ2lLVHZERWNldGlWZ1FjanFCd2EwSFRrbzJ4amJVaWlLSVFYaEY0OGktS2VTbHluZVpUOTV2RkpEM2ZzZ1FvZU5kaEYxWVBZMmxtZ21FOEFJdFV0emktdWRLa3lBanB6X2RlZnBCaGhnOHN4WFdQVW9iU2ZEUFIzOElFZXJIckJLVXZGZGNqRG42aHhhLV9ERWd0QmxXdzR6NktkVXZqRGhibWoyUHNaRHNrME9ZVFBTdFdxcU5KSm5BSXR4NktHNXJTbDZtZFlETVg4NTdkYVZqVEt6UEJOUHhldDRvVk5SZWJxdG5YbXNfV0tYM1pHQlBHMEJTVkVKMEdCck56c3dsekV4TGJiVFdmV0RlV0c5U0RVQm5DeHRkZWlhcUVTRU5XU0ExVkF1QWt5WGtZSG14dHIwalEifX0.eyJpc3MiOiJodHRwczovL3NvbHlpLmtyIiwic3ViIjoi7IaU7J20IiwibmJmIjoxNjkxNTY3MDIyLCJleHAiOjE2OTE2NTM0MjIsImp0aSI6InNvbHlpQG5hdmVyLmNvbSIsInVzZXIiOnsiSWQiOiJzb2x5aSIsIlBhc3N3b3JkIjoicGFzc3dvcmQiLCJOYW1lIjoi7IaU7J20IiwiQmlydGhkYXkiOiIxOTkxLTA1LTE1VDAwOjAwOjAwIiwiTW9iaWxlIjoiMDEwLTY2NjYtNzc3NyIsIkVtYWlsIjoic29seWlAbmF2ZXIuY29tIiwiQ29tcGFueSI6eyJOYW1lIjoidHJpdnVlIiwiQWRkcmVzcyI6IuyEnOyauCDslpHsspzqtawiLCJVcmwiOiJodHRwczovL3NvbHlpLmtyIn19fQ.gI6QwifCtzTzRILZl04yqr7ONZb1qMOROnIArYjBHvlDHtoPbQqNhiQpDzub7UBzW92gJR3tq1FPonuTRy5QD7B-nbVvnsgIRvBpUzoc2nIk174VotZbgvM3QmgTE2FQ9yrnJNq6CHFiGdGRlSOWSZtgeat9ehYMKwHemxqg5d54pqmSpr1Y6lHo2tMeFJBDH4RZGYzNzGN8Px35U0S0vtQUmcgFRcMO5dLch2f7Q8FDejg9RoDUkMM83UifGNvXdNONwCKd2nSX1UCvocJfmPGaRDq7U9hYFu6C8c052IdVwgvFrownp_xnWwzeyaDXxSe8VIbF8GWMZfBBeHcJrA
    • ↑ 이걸 복사해서 https://jwt.io 에 붙여넣기 하면 headerpayload 값이 짜잔
  2. JWT 검증

     public bool VerifyJWT_RS256_Signature(string jwt, string publicKey, string exponent)
     {
       var jwtArray = jwt.Split('.');
    
       string publicKeyFixed = (publicKey.Length % 4 == 0 ? publicKey : publicKey + "====".Substring(publicKey.Length % 4)).Replace("_", "/").Replace("-", "+");
       var publicKeyBytes = Convert.FromBase64String(publicKeyFixed);
    
       var jwtSignatureFixed = (jwtArray[2].Length % 4 == 0 ? jwtArray[2] : jwtArray[2] + "====".Substring(jwtArray[2].Length % 4)).Replace("_", "/").Replace("-", "+");
       var jwtSignatureBytes = Convert.FromBase64String(jwtSignatureFixed);
    
       RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
       rsa.ImportParameters(
         new RSAParameters()
         {
             Modulus = publicKeyBytes,
             Exponent = Convert.FromBase64String(exponent)
         }
       );
    
       SHA256 sha256 = SHA256.Create();
       byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(jwtArray[0] + '.' + jwtArray[1]));
    
       RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
       rsaDeformatter.SetHashAlgorithm("SHA256");
       if (rsaDeformatter.VerifySignature(hash, jwtSignatureBytes))
       {
         return true;
       }
       else
       {
         return false;
       }
     }
반응형