JWK 라이브러리를 만들어 보자! #4

김성현·2021년 12월 30일
0
post-thumbnail

4편 : RFC7517(JWK)를 읽고 구현을 위해 준비하자!

드디어 대망의 JWK이다.

JWK는 RFC에 따르면 JSON 구조로 암호화에 필요한 키와 키들을 모은 셋, 이들이 어떻게 정의되는지에 대한 내용이라고 한다.

여기서 JWK는 SetKey로 나눌 수 있는데 대략적인 구조를 의사 코드로 짜면 대략 이렇다.

type Set struct{
    []*Key
}
type Key struct{
    Kty string
    ...
}

별로 어려운 내용은 없는 것 같다. 바로 자세한 내용으로 들어가자.
일단 Set은 Key를 여러개 묶는 개념이라 먼저 Key부터 알아보자.

JWK KEY란?

JWK Key 란 RFC에 따르면 암호화 키를 저장하는 방식이라고 한다.
여기서 Key는 RS256일 수도 있고 ES256일수도 있고 하여간 어떤 키든 올 수 있지만 키마다 공통되는 필드들이 존재한다.
예를 들면 해당 키가 어떤 종류의 키인지 나타내는 kty(Key TYpe), 해당 키가 어떤 용도로 쓰일 수 있는지를 나타내는 use, 해당 키가 어떤 연산들이 가능한지 나타내는 key_ops(KEY OPerationS)등 키들은 공통적으로 가지는 부분과 이전 포스트에서 적은 키의 종류에 따라 필요한 필드로 구성된다.

그럼 이제 공통적으로 사용되는 필드들과 각 필드가 어떤 의미와 어떤 값을 가질 수 있는지 알아보자.

'kty' 키의 종류

kty는 key type의 약자로 해당 키가 어떤 종류인지 나타내는 string 값으로 다음 값중 하나를 가질 수 있다.

  • oct
  • RSA
  • EC

만약 RS256에 대한 키라면 ktyRSA일거고, ES256에 대한 키라면 ktyEC일거다.
octHS256에 대한 대칭키일때 사용된다.

'use' 키의 사용처

해당 필드는 공개키의 경우 사용 가능한 용도를 기술하는 영역이다.
해당 필드는 다음 값중 하나이다.

  • enc
  • sig

enc는 다른 키를 암호화한 것을 검증할 때 이 키가 쓰일 수 있다는 의미이고
sig는 특정 메시지의 디지털 서명을 검증할 때 이 키가 쓰일 수 있다는 의미이다.
참고로 해당 값들 외의 값들이 들어올 수도 있다고 한다.

'key_ops' 해당 키로 어떤 작업들이 가능한가?

key_ops는 Key Operations의 약자로 해당 키가 어떤 작업이 가능한지를 기술하는 string 배열이다.
참고로 usekey_ops에서 공개키일때(특정한 상황) 사용 가능한 일부 기능만을 정의할 때 사용하는 필드다.
key_ops가 있다면 use 필드는 안 써도 된다.
하지만 key_ops는 대칭키나, 개인키에서도 쓸 수 있는 범용적인 필드이다.

해당 필드는 배열이지만 배열 안에 들어올 수 있는 문자열은 아래 내용들을 참조해야 한다.

또한 signverify기능은 동시에 가질 수 있지만(연관된 기능), signdecrypt기능을 동시에 가질 수는 없다고 한다.(연관되지 않은 기능)

연관은 아래와 같다.

  • sign with verify
  • encrypt with decrypt
  • wrapKey with unwrapKey

이 외의 조합은 허용 안한다고 한다.

또한 key_opsuse필드는 상호 배타적이다. 같이 존재하면 안된다고 한다.
하지만 불가피하게 같이 존재하는 경우에는 의미하는 바가 동일해야 한다고 한다.

그러니까 {"use":"sig", "key_ops":["sign"]}은 되지만 {"use":"sig", "key_ops":["enc"]}는 안된다는 뜻인 것 같다.
물론 두 필드 모두 사용하는 것 자체가 나쁘지만.

'alg' 해당 키의 알고리즘이 뭔가?

해당 필드는 해당 키가 어떤 알고리즘을 위한 키인지를 설명한다고 한다.
그런데 해당 키는 선택적인 필드라서 없어도 된다고 한다.

아마 kty필드만 알면 키를 보고 어떤 알고리즘인지 추측이 가능하기 때문이라고 생각되는데, HS256/384/512는 추론이 불가능할텐데... 이 부분은 잘 모르겠다. 좀 더 확인이 필요할 것 같다.

'kid' 키의 고유 id

해당 필드는 선택적으로 JWE 나 JWS를 사용할 때 헤더에 특정 kid를 가진 키를 지정하는 경우 사용되는 값이라고 한다.
해당 필드는 사실 Key 자체만으로 쓸 때 사용되는 기능이 아니라 Set 에서 여러 키를 저장할 때 특정 키를 선택하기 위해 사용되는 값이라고 한다.

참고로 이 값은 반드시 Set에서 유일해야한다.(SHOULD) 즉 한 Set에서는 왠만하면 kid충돌이 없어야 한다고 한다.

하지만 만약 kid가 충돌하더라도 kty이 다르면 괜찮기는 하다고 한다.
애시당초 반드시 해야한다.(MUST) 가 아니기에.
하지만 권장되지는 않고 다른 kid를 가지게 하는 것이 좋다고 한다.

'x5u' x509 URL 주소

해당 값은 X.509 public key certificate 또는 X.509 public key certificate chain가 존재하는 URL 주소여야 한다고 한다.
그 외에도 해당 인증서를 받아올때는 반드시(MUST) HTTPS여야 하고, 해당 인증서는 PEM이여야 되고, 첫 키 인증서의 공개키는 JWK의 공개키와 일치해야 하는 등의 조건이 있었다.

해당 필드를 쓰는 JWK를 못봐서 이런게 있는 줄도 몰랐다...

'x5c' x509 Certificate Chain

해당 값은 X.509 certificate chain의 약자로 해당 필드는 string 배열이 들어가야 하고 하나 이상의 PKIX certificatesBase64 Std인코딩으로 들어가야 된다고 한다. (URL safe 인코딩이 아니더라.)

해당 필드도 처음 봤다...

'x5t', x5t#S256 x509 Thumbprint

해당 값은 X.509 certificate SHA-1 thumbprint값으로 Base64 URL safe하게 인코딩된 SHA-1 값이라고 한다.
x5t#S256는 다 같고 해시 함수만 SHA-256인 thumbprint이다.
당연히 해당 값은

이것도 처음 보는 필드니 공부좀 해야겠다...

이제 나머지 필드값은 JWA에서 이미 설명했으니 Key는 전부 알아냈다.

사실 Set은 keys필드 하나밖에 없으니 끝난거나 다름없다.

JWK Set이란?

Set 이란 Key들의 집합이다.
단 여기서 아까도 kid에 대해 이야기 했지만 Set에서는 중복되는 id의 키가 있으면 안된다.
만약 있는 경우 오류를 내뿜거나 사전순으로 젤 마지막인 값을 키로 지정해야 한다고 한다.

그 외에도 keys는 Key 배열이고, key중에서 kty필드에 이해 불가능한 값이 있으면 무시해야 하는 등. 뭐 이거저거 제약이 있었다.

사실 Set은 진짜로 정의할게 별로 없다;; 진짜 간단하다.


이제 RFC를 모두 읽었으니 난 JWK를 라이브러리를 작성할 최소한의 경험치를 얻었다.

근데 사실 이미 다 읽고 기본적인 구현도 하고 있기는 하다.
블로그글은 전에 공부한 내용을 정리하고 있을 뿐이다.

그럼 이제부터 실제로 API 구현을 공부하고 어째서, 혹은 어떻게 구현했는지에 대한 글들을 다음 편부터 쓰기로 하겠다.

아마 Context편, Error편, Decode편, Encode편, Fetch편 해서 총 5편과 최종 정리글 1편으로 구성될 예정이다.

물론 예정은 예정일 뿐이다.
특히 x509 관련 부분들은 어떤식으로 사용되는건지를 본적이 없어 시간이 많이 걸릴 것 같다.

아마 다음 글은 구현을 한참 하고 쓸거라 좀 늦을 것 같다.

솔직히 많이 힘들긴 하다. 하지만 구현이 다되는 그날까지 화이팅!

profile
수준 높은 기술 포스트를 위해서 노력중...

0개의 댓글