드디어 대망의 JWK이다.
JWK는 RFC에 따르면 JSON 구조로 암호화에 필요한 키와 키들을 모은 셋, 이들이 어떻게 정의되는지에 대한 내용이라고 한다.
여기서 JWK는 Set
과 Key
로 나눌 수 있는데 대략적인 구조를 의사 코드로 짜면 대략 이렇다.
type Set struct{
[]*Key
}
type Key struct{
Kty string
...
}
별로 어려운 내용은 없는 것 같다. 바로 자세한 내용으로 들어가자.
일단 Set은 Key를 여러개 묶는 개념이라 먼저 Key부터 알아보자.
JWK Key 란 RFC에 따르면 암호화 키를 저장하는 방식이라고 한다.
여기서 Key는 RS256
일 수도 있고 ES256
일수도 있고 하여간 어떤 키든 올 수 있지만 키마다 공통되는 필드들이 존재한다.
예를 들면 해당 키가 어떤 종류의 키인지 나타내는 kty(Key TYpe)
, 해당 키가 어떤 용도로 쓰일 수 있는지를 나타내는 use
, 해당 키가 어떤 연산들이 가능한지 나타내는 key_ops(KEY OPerationS)
등 키들은 공통적으로 가지는 부분과 이전 포스트에서 적은 키의 종류에 따라 필요한 필드로 구성된다.
그럼 이제 공통적으로 사용되는 필드들과 각 필드가 어떤 의미와 어떤 값을 가질 수 있는지 알아보자.
'kty'
키의 종류kty
는 key type의 약자로 해당 키가 어떤 종류인지 나타내는 string 값으로 다음 값중 하나를 가질 수 있다.
oct
RSA
EC
만약 RS256
에 대한 키라면 kty
은 RSA
일거고, ES256
에 대한 키라면 kty
은 EC
일거다.
oct
는 HS256
에 대한 대칭키일때 사용된다.
'use'
키의 사용처해당 필드는 공개키의 경우 사용 가능한 용도를 기술하는 영역이다.
해당 필드는 다음 값중 하나이다.
enc
sig
enc
는 다른 키를 암호화한 것을 검증할 때 이 키가 쓰일 수 있다는 의미이고
sig
는 특정 메시지의 디지털 서명을 검증할 때 이 키가 쓰일 수 있다는 의미이다.
참고로 해당 값들 외의 값들이 들어올 수도 있다고 한다.
'key_ops'
해당 키로 어떤 작업들이 가능한가?key_ops
는 Key Operations의 약자로 해당 키가 어떤 작업이 가능한지를 기술하는 string 배열이다.
참고로 use
는 key_ops
에서 공개키일때(특정한 상황) 사용 가능한 일부 기능만을 정의할 때 사용하는 필드다.
즉 key_ops
가 있다면 use
필드는 안 써도 된다.
하지만 key_ops
는 대칭키나, 개인키에서도 쓸 수 있는 범용적인 필드이다.
해당 필드는 배열이지만 배열 안에 들어올 수 있는 문자열은 아래 내용들을 참조해야 한다.
sign
verify
encrypt
decrypt
wrapKey
unwrapKey
deriveKey
deriveBits
또한 sign
과 verify
기능은 동시에 가질 수 있지만(연관된 기능), sign
과 decrypt
기능을 동시에 가질 수는 없다고 한다.(연관되지 않은 기능)
연관은 아래와 같다.
sign
with verify
encrypt
with decrypt
wrapKey
with unwrapKey
이 외의 조합은 허용 안한다고 한다.
또한 key_ops
와 use
필드는 상호 배타적이다. 같이 존재하면 안된다고 한다.
하지만 불가피하게 같이 존재하는 경우에는 의미하는 바가 동일해야 한다고 한다.
그러니까
{"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 certificates
가 Base64 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
필드 하나밖에 없으니 끝난거나 다름없다.
Set 이란 Key들의 집합이다.
단 여기서 아까도 kid
에 대해 이야기 했지만 Set에서는 중복되는 id의 키가 있으면 안된다.
만약 있는 경우 오류를 내뿜거나 사전순으로 젤 마지막인 값을 키로 지정해야 한다고 한다.
그 외에도 keys는 Key 배열이고, key중에서 kty
필드에 이해 불가능한 값이 있으면 무시해야 하는 등. 뭐 이거저거 제약이 있었다.
사실 Set은 진짜로 정의할게 별로 없다;; 진짜 간단하다.
이제 RFC를 모두 읽었으니 난 JWK를 라이브러리를 작성할 최소한의 경험치를 얻었다.
근데 사실 이미 다 읽고 기본적인 구현도 하고 있기는 하다.
블로그글은 전에 공부한 내용을 정리하고 있을 뿐이다.
그럼 이제부터 실제로 API 구현을 공부하고 어째서, 혹은 어떻게 구현했는지에 대한 글들을 다음 편부터 쓰기로 하겠다.
아마 Context
편, Error
편, Decode
편, Encode
편, Fetch
편 해서 총 5편과 최종 정리글 1편으로 구성될 예정이다.
물론 예정은 예정일 뿐이다.
특히 x509 관련 부분들은 어떤식으로 사용되는건지를 본적이 없어 시간이 많이 걸릴 것 같다.
아마 다음 글은 구현을 한참 하고 쓸거라 좀 늦을 것 같다.
솔직히 많이 힘들긴 하다. 하지만 구현이 다되는 그날까지 화이팅!