JSON Web Key¶
A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key (via RFC7517).
OctKey¶
An OctKey
is a symmetric key defined in
RFC7518 section 6.4.
Create an “oct” key¶
You can generate an OctKey
with the OctKey.generate_key()
method:
from joserfc.jwk import OctKey
key_size = 256 # in bit size, 256 equals 32 bytes
key = OctKey.generate_key(key_size)
Import an “oct” key¶
You can import an OctKey
from string, bytes and a JWK (in dict).
from joserfc.jwk import OctKey
OctKey.import_key("a-random-string-as-key")
OctKey.import_key(b"a-random-bytes-as-key")
OctKey.import_key({
"kty": "oct",
"k": "Zm9v",
})
When importing a key, you can add extra parameters into a key:
>>> from joserfc.jwk import OctKey
>>> key = OctKey.import_key("foo", {"use": "sig"})
>>> key.as_dict()
{'k': 'Zm9v', 'use': 'sig', 'kty': 'oct'}
RSAKey¶
An RSAKey
is an asymmetric key defined in
RFC7518 section 6.3.
It represents RSA keys.
Generate an “RSA” key¶
You can generate an “EC” key with a given key size (in bit):
from joserfc.jwk import RSAKey
key_size = 2048
key = RSAKey.generate_key(key_size)
Import an “RSA” key¶
You can import an RSAKey
from string, bytes and a JWK (in dict).
from joserfc.jwk import RSAKey
pem_file = """
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAm0tWm31IQ3zYU27bk/NZ
3wMJOJ+Moska3WqnptWyiVR+p/qCBlV18NUSwshoctTkETi8+HIhOjUPb0WRvQV0
YcpsqBVdSuPZ3m4Q+uX/rudAoDKHJ6B7vwjfeg4w9aT/YF+Zi61tEy1c15rHKyXA
HjSQGzIasOiXK1eSssim6Exx+caRL0/vWV8+0QICmEBVJiJyfDB4O3WXKac+QsI3
LM7ZjWqQFdvx3o1v7sDycz0zdpk4qEK7hEHUsYIsyYHb70iKSkiuo3nqq2HUHklW
y322djy/IqEq03KWuePRUZdPTDzlx5qyKpVLpMswYporngvXKpMTCal5HYfAGuYS
MuOAVa1oL1gX8W+N4+XNrVCHSCh1JHjnO2qUT6em/HJ2gERj3kZDDfE6UXVjAw2i
US2lP+GEim3AdUQ1jTO27Vjvuv+rNk7UjL8iDW1THlvYI9AeQnqtTTBib2b5+k6a
8AzSPhMX/F7WP9hf0NUbkYyrJ7zRfERKqLrwpZu83PRWclnB6afPIZcN58uc+4J5
516Ryk6PUawbBHj6zfSIDEuwKj71ki+t0GHaG4RO9QFk75ArsHWrRZNQhELBVep/
ohwl4vscRMQFgdwdzZN8ZaaJRPFih7B+YiwIhuxpAF9fPrETa6UGoBK6MlWKE6EZ
i5YRKx6rVWvFfMWAV3Tx9uECAwEAAQ==
-----END PUBLIC KEY-----
"""
RSAKey.import_key(pem_file)
RSAKey.import_key({
"kty": "RSA",
"kid": "[email protected]",
"use": "sig",
"n": "n4EPtAOCc9AlkeQHPzHSt...",
"e": "AQAB",
"d": "bWUC9B-...",
"q": "uKE2dh-...",
"dp": "B8PV...",
"dq": "CLDm...",
"qi": "3PiFU4..."
})
ECKey¶
An ECKey
is an asymmetric key defined in
RFC7518 section 6.2.
It represents Elliptic Curve [DSS] keys.
Generate an “EC” key¶
You can generate an “EC” key with the given curve:
from joserfc.jwk import ECKey
key = ECKey.generate_key("P-256")
The “crv” values that ECKey
supports:
P-256
via RFC7518P-384
via RFC7518P-521
via RFC7518secp256k1
via RFC8812
Hint
It is P-521
, not P-512
, it is not a typo.
Import an “EC” key¶
You can import an ECKey
from string, bytes and a JWK (in dict).
from joserfc.jwk import ECKey
pem_file = """
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBnRS4Tf1PY6Jb7QOwAM7OWUOMJTBenEWRvGBCGgctBfoAoGCCqGSM49
AwEHoUQDQgAE3r15c+Yd+0GXKysfWtwkqF7k12ylNE9LdfRP4TfkUcJSQXyGQjcx
U8E81rOHjo+9xv2e64n4X6pC3yuP+pX4eA==
-----END EC PRIVATE KEY-----
"""
ECKey.import_key(pem_file)
ECKey.import_key({
"kty": "EC",
"crv": "P-256",
"x": "WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis",
"y": "y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE",
"d": "Hndv7ZZjs_ke8o9zXYo3iq-Yr8SewI5vrqd0pAvEPqg"
})
OKPKey¶
An OKPKey
is an asymmetric key defined in RFC8037
CFRG Elliptic Curve Diffie-Hellman (ECDH) and Signatures in
JSON Object Signing and Encryption (JOSE).
Generate an “OKP” key¶
You can generate an “OKP” key with the given curve:
from joserfc.jwk import OKPKey
key = OKPKey.generate_key("Ed25519")
OKPKey
accepts “crv” values of Ed25519
, Ed448
,
X25519
, and X448
.
Import an “OKP” key¶
You can import an OKPKey
from string, bytes and a JWK (in dict).
from joserfc.jwk import OKPKey
pem_file = """
-----BEGIN PRIVATE KEY-----
MEcCAQAwBQYDK2VxBDsEOaVsPKMXOBfq9aHlDEaMlBY+FR63hwrINHa2X74uHXUr
3/VXE8eMhrr8stXn41CQKqVmFEeL5Uj5Gg==
-----END PRIVATE KEY-----
"""
OKPKey.import_key(pem_file)
OKPKey.import_key({
"kty": "OKP",
"crv": "Ed25519",
"x": "t-nFRaxyM5DZcpg5lxiEeJcZpMRB8JgcKaQC0HRefXU",
"d": "gUF17HCe-pbN7Ej2rDSXl-e7uSj7rQW5u2dNu0KINP0",
"kid": "5V_IcL-iX5IbaNz9vg0CjXtWLZiJ94-ESnHI-HN1L2Y"
})
Key Set¶
A JWK Set is a JSON object that represents a set of JWKs. An example of a JWK Set:
{"keys": [
{
"kty":"EC",
"crv":"P-256",
"x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
"y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
"use":"enc",
"kid":"1"
},
{
"kty":"RSA",
"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx...",
"e":"AQAB",
"alg":"RS256",
"kid":"2011-04-29"
}
]}
Create a key set¶
You can create a key set with a given set of keys:
from joserfc.jwk import KeySet
key_set = KeySet([rsa_key1, rsa_key2, ec_key1])
Or, you can generate a key set for a certain “kty”:
key_set = KeySet.generate_key_set("EC", "P-256", count=4)
Import a key set¶
An example about importing JWKS from a local file:
import json
with open("your-jwks.json") as f:
data = json.load(f)
key_set = KeySet.import_key_set(data)
An example about importing JWKS from a URL:
import requests
resp = requests.get("https://example.com/jwks.json")
key_set = KeySet.import_key_set(resp.json())
Key methods¶
thumbprint
¶
Call this method will generate the thumbprint with algorithm defined in RFC7638.
>>> from joserfc.jwk import OctKey
>>> key = OctKey.import_key("foo")
>>> key.thumbprint()
'8-e-qGDS2nDpfZzOPtD8Sb7NkifUbw70MeqOKIqyaRw'
ensure_kid
¶
Call this method to make sure the key contains a kid
. If the key has no
kid
, generate one with the above .thumbprint
method.
>>> from joserfc.jwk import OctKey
>>> key = OctKey.import_key("foo")
>>> key.kid
None
>>> key.ensure_kid()
>>> key.kid
'8-e-qGDS2nDpfZzOPtD8Sb7NkifUbw70MeqOKIqyaRw'
as_dict
¶
Dump a key or key set into dict format, which can be used to convert to JSON:
data = key.as_dict(private=False) # dump as a public key
# data = key.as_dict(private=True) # dump as a private key
with open("my-key.json", "w") as f:
json.dump(data, f)
as_pem
¶
Dump an asymmetric key into PEM format (in bytes):
# text = key.as_pem(public=True) # dump as a public key
text: bytes = key.as_pem(private=True) # dump as a private key
with open("my-key.pem", "w") as f:
f.write(text)
as_der
¶
Dump an asymmetric key into DER format (in bytes):
# text = key.as_der(public=True) # dump as a public key
text: bytes = key.as_der(private=True) # dump as a private key
with open("my-key.der", "w") as f:
f.write(text)
JWKRegistry
¶
The JWKRegistry
class serves as a registry for storing all
the supported key types in the joserfc
library. While developers
typically use specific key types such as RSAKey
or ECKey
,
this registry offers a means to dynamically import and generate keys.
Import keys¶
The JWKRegistry.import_key()
can choose the correct key type
automatically when importing a JWK in dict:
data = {"kty": "oct", "k": "..."}
key = JWKRegistry.import_key(data) # returns a OctKey
data = {"kty": "RSA", ...}
key = JWKRegistry.import_key(data) # returns a RSAKey
data = {"kty": "EC", ...}
key = JWKRegistry.import_key(data) # returns a ECKey
data = {"kty": "OKP", ...}
key = JWKRegistry.import_key(data) # returns a OKPKey
If the key is in bytes or string, not dict, developers SHOULD specify the key type manually:
data = b"---- BEGIN RSA PRIVATE KEY ----\n..."
key = JWKRegistry.import_key(data, "RSA")
Generate keys¶
The JWKRegistry.generate_key()
can generate a key with all
the supported key types. For oct
and RSA
the parameters
in this method:
# (key_type: str, size: int, parameters: Optional[dict], private: bool=True)
key = JWKRegistry.generate_key("oct", 256)
key = JWKRegistry.generate_key("RSA", 2048, {"use": "sig"})
For EC
and OKP
keys, the parameters are:
# (key_type: str, crv: str, parameters: Optional[dict], private: bool=True)
key = JWKRegistry.generate_key("EC", "P-256")
key = JWKRegistry.generate_key("OKP", "Ed25519")
Options¶
The import_key
and generate_key
methods available in OctKey
, RSAKey
,
ECKey
, OKPKey
, and JWKRegistry
classes have an optional parameters
parameter.
This parameters
allows you to provide a dict that includes additional key parameters
to be included in the JWK.
Some of the standard (registered) header fields are:
kty
: Key Type, it is automatically addeduse
: Public Key Use, “sig” or “enc”key_ops
: Key Operations, allowed operations of this keyalg
: Algorithm, allowed algorithm of this keykid
: Key ID, a string of the key ID
When using import_key
and generate_key
, developers can pass the extra key parameters
:
parameters = {"use": "sig", "alg": "RS256", "key_ops": ["verify"]}
RSAKey.import_key(data, parameters=parameters)
The above RSAKey
then can only be used for JWS
with alg
of RS256
, and it can
only be used for deserialization (verify
).