Backend & Spring (스프링)/Authorization & Authentication

JWT (JSON Web Tokens) - JWS와 JWE

jw92 2024. 12. 13. 17:27

JWT는 Token 기반 인증에서 RFC 7519 의 표준으로 지정된 Token
 
※ Claim이란 어떠한 주제에 관해 주장하는 정보이며, name/value pair로 표시한다.
 

1. Javascript Object Signing and Encryption (JOSE)

Claim을 안전하게 전송하기 위한 프레임워크
JWK, JWS, JWE, JWT 등을 포함한다.
 

2. JWK(Json Web Key): 암호화 키를 나타내는 Json Data 구조

jwk = {'k': <password>}

위와 같이 표현되며, k가 key 혹은 password를 나타낸다.
 

3. JWS(Json Web Signature): Set of claims에 signature(서명)을 하여 Base64Url로 Encoding

import jose

claims = {
    'iss': 'http://www.example.com',
    'exp': int(time()) + 3600,
    'sub': 42,
}
jwk = {'k': 'password'}

jws = jose.sign(claims, jwk, alg='HS256')

예를 들면 위와같이 jws를 구성할 수 있고, 아래와 같은 JWS가 만들어진다.

JWS(header='eyJhbGciOiAiSFMyNTYifQ',
payload='eyJpc3MiOiAiaHR0cDovL3d3dy5leGFtcGxlLmNvbSIsICJzdWIiOiA0MiwgImV4cCI6IDEzOTU2NzQ0Mjd9',
signature='WYApAiwiKd-eDClA1fg7XFrnfHzUTgrmdRQY4M19Vr8')

 
이 때, 서명에는 Digital Signature 혹은 MAC 이 사용된다.
header에는 다음과 같은 signature 알고리즘 정보가 있다.

{"alg": "HS256"}

 
 
Signature를 하기 때문에 Integrity가 있으나 payload를 누구나 볼 수 있어 Confidentiality가 없다.
 

4. JWE(Json Web Encryption): Set of claims를 암호화. 

import jose
from time import time
from Crypto.PublicKey import RSA

# key for demonstration purposes
key = RSA.generate(2048)
claims = {
    'iss': 'http://www.example.com',
    'exp': int(time()) + 3600,
    'sub': 42,
}
# encrypt claims using the public key
pub_jwk = {'k': key.publickey().exportKey('PEM')}

jwe = jose.encrypt(claims, pub_jwk)

위와 같이 JWE를 만들 수 있으며, 아래와 같은 JWE가 만들어진다.

JWE(header='eyJhbGciOiAiUlNBLU9BRVAiLCAiZW5jIjogIkExMjhDQkMtSFMyNTYifQ',
cek='SsLgP2bNKYDYGzHvLYY7rsVEBHSms6_jW-WfglHqD9giJhWwrOwqLZOaoOycsf_EBJCkHq9-vbxRb7WiNdy_C9J0_RnRRBGII6z_G4bVb18bkbJMeZMV6vpUut_iuRWoct_weg_VZ3iR2xMbl-yE8Hnc63pAGJcIwngfZ3sMX8rBeni_koxCc88LhioP8zRQxNkoNpvw-kTCz0xv6SU_zL8p79_-_2zilVyMt76Pc7WV46iI3EWIvP6SG04sguaTzrDXCLp6ykLGaXB7NRFJ5PJ9Lmh5yinAJzCdWQ-4XKKkNPorSiVmRiRSQ4z0S2eo2LtvqJhXCrghKpBNgbtnJQ',
iv='Awelp3ryBVpdFhRckQ-KKw',
ciphertext='1MyZ-3nky1EFO4UgTB-9C2EHpYh1Z-ij0RbiuuMez70nIH7uqL9hlhskutO0oPjqdpmNc9glSmO9pheMH2DVag',
tag='Xccck85XZMvG-fAJ6oDnAw')

 
이를 Decryption 해보면 아래와 같이 저장되어 있는 것을 확인할 수 있다.

JWT(header={u'alg': u'RSA-OAEP', u'enc': u'A128CBC-HS256'},
claims={u'iss': u'http://www.example.com', u'sub': 42, u'exp': 1395606273})

 
JWE에는 2가지의 Key가 사용된다.
- CEK(Content Encryption Key) Content를 암호화하기 위한 Key, AEAD 알고리즘을 위한 Symmetric Key가 사용된다.
- Encryption Key: CEK를 암호화하기 위한 Encryption Key. 암호화된 CEK는 JWE Encrypted Key로 부른다.
  Asymmetric Key(비대칭 키)를 사용한다.
 
송신자 측에서는 Public Key를 가지고 있으므로 CEK를 암호화할 수 있고,
이를 Header에 포함해서 보내면
수신자 측에서 Private Key를 이용해서 CEK를 복호화할 수 있고,
이를 이용해서 Content를 확인할 수 있다.
 
암호화 알고리즘을 파악하기 위해 Header에 아래와 같은 정보를 포함해서 보낸다.

{"alg":"RSA-OAEP","enc":"A256GCM"}

 
JWE만을 사용하면 Confidentiality는 지켜지나 Integrity가 없다.
이를 해결하기 위하여 AEAD를 사용하여 Integrity와 Confidentiality를 모두 지킨다.
 

5. JWT (Json Web Tokens)

권한에 관련한 Claim 집합을 JWS 혹은(or + and) JWE 구조로 Base64Url 으로 Encoding하여
JWS Compact Serialization 혹은 the JWE Compact Serialization 으로 표현한 것이다.
 
JWS to JWT

jwt = jose.serialize_compact(jws)
# 'eyJhbGciOiAiSFMyNTYifQ.eyJpc3MiOiAiaHR0cDovL3d3dy5leGFtcGxlLmNvbSIsICJzdWIiOiA0MiwgImV4cCI6IDEzOTU2NzQ0Mjd9.WYApAiwiKd-eDClA1fg7XFrnfHzUTgrmdRQY4M19Vr8'

jose.verify(jose.deserialize_compact(jwt), jwk, 'HS256')
# JWT(header={u'alg': u'HS256'}, claims={u'iss': u'http://www.example.com', u'sub': 42, u'exp': 1395674427})

 
JWE to JWT

jwt = jose.serialize_compact(jwe)
# 'eyJhbGciOiAiUlNBLU9BRVAiLCAiZW5jIjogIkExMjhDQkMtSFMyNTYifQ.SsLgP2bNKYDYGzHvLYY7rsVEBHSms6_jW-WfglHqD9giJhWwrOwqLZOaoOycsf_EBJCkHq9-vbxRb7WiNdy_C9J0_RnRRBGII6z_G4bVb18bkbJMeZMV6vpUut_iuRWoct_weg_VZ3iR2xMbl-yE8Hnc63pAGJcIwngfZ3sMX8rBeni_koxCc88LhioP8zRQxNkoNpvw-kTCz0xv6SU_zL8p79_-_2zilVyMt76Pc7WV46iI3EWIvP6SG04sguaTzrDXCLp6ykLGaXB7NRFJ5PJ9Lmh5yinAJzCdWQ-4XKKkNPorSiVmRiRSQ4z0S2eo2LtvqJhXCrghKpBNgbtnJQ.Awelp3ryBVpdFhRckQ-KKw.1MyZ-3nky1EFO4UgTB-9C2EHpYh1Z-ij0RbiuuMez70nIH7uqL9hlhskutO0oPjqdpmNc9glSmO9pheMH2DVag.Xccck85XZMvG-fAJ6oDnAw'

# decrypt on the other end using the private key
priv_jwk = {'k': key.exportKey('PEM')}

jwt = jose.decrypt(jose.deserialize_compact(jwt), priv_jwk)
# JWT(header={u'alg': u'RSA-OAEP', u'enc': u'A128CBC-HS256'},
# claims={u'iss': u'http://www.example.com', u'sub': 42, u'exp': 1395606273})

 
일반적인 토큰 인증 환경에서는 TLS 등을 통하여 Confidentiality가 지켜지기 때문에
일반적으로 JWS만을 사용하고, 필요 시 JWS + JWE 를 모두 사용한다.
 
즉, 일반적으로 JWT는 JWS와 JWS Compact Serialization을 의미한다.
 
따라서 JWT는 아래 그림과 같이 3 부분으로 구성된다.

1. Header를 Base64Url로 Encoding한 부분
2. Payload를 Encoding한 부분
3. Signature를 Encoding한 부분
 
JWE Compact Serialization은 아래와 같이 5 부분으로 구성된다.

BASE64URL(UTF8(JWE Protected Header)) || '.' ||
BASE64URL(JWE Encrypted Key) || '.' ||
BASE64URL(JWE Initialization Vector) || '.' ||
BASE64URL(JWE Ciphertext) || '.' ||
BASE64URL(JWE Authentication Tag)

 
https://jwt.io/ 에서 간단하게 실제 jwt token을 만들어 볼 수 있다.

 

 

 

2023.11.27 - [Backend & Spring (스프링)/Authorization & Authentication] - Authorization (허가) Authentication (인증) 총 정리 - MAC AEAD

 

Authorization (허가) Authentication (인증) 총 정리 - MAC AEAD

1. Authorization과 Authentication- Authentication 이란 자신이 누구라고 주장하는 사람을 확인하는 절차- Authorization 이란 가고 싶은 곳으로 가도록 혹은 원하는 정보를 얻도록 허용하는 과정2. Why Authorization

jw92.tistory.com

 

 

2023.11.29 - [Backend & Spring (스프링)/Authorization & Authentication] - OAuth 2.0 & OIDC with JWT

 

OAuth 2.0 & OIDC with JWT

Authorization (허가) Authentication (인증) 총 정리 - MAC AEAD 에 이어지는 글9. JWT (JSON Web Tokens)2024.12.13 - [Backend & Spring (스프링)/Authorization & Authentication] - JWT (JSON Web Tokens) - JWS와 JWE JWT (JSON Web Tokens) - JWS와

jw92.tistory.com