Skip to content

Commit d147b72

Browse files
committed
Fixed a couple of small bugs.
Added 2 new methods: dum&load to KeyBundle and KeyJar.
1 parent 65bbf8d commit d147b72

File tree

7 files changed

+133
-6
lines changed

7 files changed

+133
-6
lines changed

src/cryptojwt/jwk/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def __init__(self, kty="", alg="", use="", kid="", x5c=None,
9393
self.x5c = x5c or []
9494
self.x5t = x5t
9595
self.x5u = x5u
96-
self.inactive_since = 0
96+
self.inactive_since = kwargs.get("inactive_since", 0)
9797

9898
def to_dict(self):
9999
"""

src/cryptojwt/jwk/hmac.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ def __eq__(self, other):
140140

141141
for key in self.public_members:
142142
if getattr(other, key) != getattr(self, key):
143+
if key == 'kid':
144+
# if one has a value and the other not then assume they are the same
145+
if getattr(self, key) == '' or getattr(other, key) == '':
146+
return True
143147
return False
144148

145149
return True

src/cryptojwt/jwk/rsa.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from ..utils import b64e
1515
from ..utils import deser
1616
from ..utils import long_to_base64
17+
from ..utils import as_unicode
1718
from . import JWK
1819
from .asym import AsymmetricKey
1920

@@ -424,7 +425,7 @@ def serialize(self, private=False):
424425
if item:
425426
res[param] = item
426427
if self.x5c:
427-
res['x5c'] = [x.decode('utf-8') for x in self.x5c]
428+
res['x5c'] = [as_unicode(x) for x in self.x5c]
428429

429430
return res
430431

src/cryptojwt/key_bundle.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,16 @@
66
from functools import cmp_to_key
77

88
import requests
9+
910
from cryptojwt.jwk.ec import NIST2SEC
1011
from cryptojwt.jwk.hmac import new_sym_key
11-
1212
from .exception import DeSerializationNotPossible
1313
from .exception import JWKException
1414
from .exception import UnknownKeyType
1515
from .exception import UnsupportedAlgorithm
1616
from .exception import UnsupportedECurve
1717
from .exception import UpdateFailed
1818
from .jwk.ec import ECKey
19-
from .jwk.ec import import_private_key_from_file
2019
from .jwk.ec import new_ec_key
2120
from .jwk.hmac import SYMKey
2221
from .jwk.jwk import dump_jwk
@@ -649,6 +648,44 @@ def difference(self, bundle):
649648

650649
return [k for k in self._keys if k not in bundle]
651650

651+
def dump(self):
652+
_keys = []
653+
for _k in self._keys:
654+
_ser = _k.to_dict()
655+
if _k.inactive_since:
656+
_ser['inactive_since'] = _k.inactive_since
657+
_keys.append(_ser)
658+
659+
res = {
660+
"keys": _keys,
661+
"fileformat": self.fileformat,
662+
"last_updated": self.last_updated,
663+
"httpc_params": self.httpc_params,
664+
"remote": self.remote,
665+
"imp_jwks": self.imp_jwks,
666+
"time_out": self.time_out,
667+
"cache_time": self.cache_time
668+
}
669+
670+
if self.source:
671+
res['source'] = self.source
672+
673+
return res
674+
675+
def load(self, spec):
676+
_keys = spec.get("keys", [])
677+
if _keys:
678+
self.do_keys(_keys)
679+
self.source = spec.get("source", None)
680+
self.fileformat = spec.get("fileformat", "jwks")
681+
self.last_updated = spec.get("last_updated", 0)
682+
self.remote = spec.get("remote", False)
683+
self.imp_jwks = spec.get('imp_jwks', None)
684+
self.time_out = spec.get('time_out', 0)
685+
self.cache_time = spec.get('cache_time', 0)
686+
self.httpc_params = spec.get('httpc_params', {})
687+
return self
688+
652689

653690
def keybundle_from_local_file(filename, typ, usage, keytype="RSA"):
654691
"""

src/cryptojwt/key_jar.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,30 @@ def __len__(self):
638638
keys += len(kb)
639639
return keys
640640

641+
def dump(self):
642+
"""
643+
Returns the key jar content as dictionary
644+
645+
:return: A dictionary
646+
"""
647+
648+
info = {}
649+
for iss in list(self.owners()):
650+
info[iss] = []
651+
for kb in self.issuer_keys[iss]:
652+
info[iss].append(kb.dump())
653+
return info
654+
655+
def load(self, info):
656+
"""
657+
658+
:param info: A dictionary with the information
659+
:return:
660+
"""
661+
for iss, kbs in info.items():
662+
self.issuer_keys[iss] = [KeyBundle().load(val) for val in kbs]
663+
return self
664+
641665

642666
# =============================================================================
643667

tests/test_03_key_bundle.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
import requests
99
import responses
1010
from cryptography.hazmat.primitives.asymmetric import rsa
11-
from cryptojwt.jwk.ec import new_ec_key
11+
1212
from cryptojwt.jwk.ec import ECKey
13+
from cryptojwt.jwk.ec import new_ec_key
1314
from cryptojwt.jwk.hmac import SYMKey
1415
from cryptojwt.jwk.rsa import RSAKey
1516
from cryptojwt.jwk.rsa import import_rsa_key_from_cert_file
@@ -448,7 +449,7 @@ def test_dump_jwks():
448449
assert len(nkb.get('rsa')) == 2
449450

450451
# Will dump symmetric keys
451-
dump_jwks([kb1, kb2], 'jwks_combo',symmetric_too=True)
452+
dump_jwks([kb1, kb2], 'jwks_combo', symmetric_too=True)
452453

453454
# Now read it
454455
nkb = KeyBundle(source='file://jwks_combo', fileformat='jwks')
@@ -920,3 +921,48 @@ def test_init_key():
920921
# Now _jwk3 is stored in the file
921922
_jwk4 = init_key(filename, "RSA")
922923
assert _jwk4 == _jwk3
924+
925+
926+
def test_export_inactive():
927+
desc = {"kty": "oct", "key": "highestsupersecret", "use": "sig"}
928+
kb = KeyBundle([desc])
929+
assert len(kb.keys()) == 1
930+
for k in kb.keys():
931+
kb.mark_as_inactive(k.kid)
932+
desc = {"kty": "oct", "key": "highestsupersecret", "use": "enc"}
933+
kb.do_keys([desc])
934+
res = kb.dump()
935+
assert set(res.keys()) == {'cache_time',
936+
'fileformat',
937+
'httpc_params',
938+
'imp_jwks',
939+
'keys',
940+
'last_updated',
941+
'remote',
942+
'time_out'}
943+
944+
kb2 = KeyBundle().load(res)
945+
assert len(kb2.keys()) == 2
946+
assert len(kb2.active_keys()) == 1
947+
948+
949+
def test_remote():
950+
source = 'https://example.com/keys.json'
951+
# Mock response
952+
with responses.RequestsMock() as rsps:
953+
rsps.add(method="GET", url=source, json=JWKS_DICT, status=200)
954+
httpc_params = {'timeout': (2, 2)} # connect, read timeouts in seconds
955+
kb = KeyBundle(source=source, httpc=requests.request,
956+
httpc_params=httpc_params)
957+
kb.do_remote()
958+
959+
exp = kb.dump()
960+
kb2 = KeyBundle().load(exp)
961+
assert kb2.source == source
962+
assert len(kb2.keys()) == 3
963+
assert len(kb2.get("rsa")) == 1
964+
assert len(kb2.get("oct")) == 1
965+
assert len(kb2.get("ec")) == 1
966+
assert kb2.httpc_params == {'timeout': (2, 2)}
967+
assert kb2.imp_jwks
968+
assert kb2.last_updated

tests/test_04_key_jar.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import time
55

66
import pytest
7+
78
from cryptojwt.exception import JWKESTException
89
from cryptojwt.jwe.jwenc import JWEnc
910
from cryptojwt.jws.jws import JWS
@@ -960,3 +961,17 @@ def test_init_key_jar_create_directories():
960961
_keyjar = init_key_jar(**OIDC_KEYS)
961962
assert len(_keyjar.get_signing_key('RSA')) == 1
962963
assert len(_keyjar.get_signing_key('EC')) == 1
964+
965+
966+
def test_dump():
967+
kj = KeyJar()
968+
kj['Alice'] = [KeyBundle(JWK0['keys'])]
969+
kj['Bob'] = [KeyBundle(JWK1['keys'])]
970+
kj['C'] = [KeyBundle(JWK2['keys'])]
971+
972+
res = kj.dump()
973+
974+
nkj = KeyJar().load(res)
975+
assert set(nkj.owners()) == {'Alice', 'Bob', 'C'}
976+
assert nkj.get_signing_key('rsa', 'Alice', kid="abc")
977+
assert nkj.get_signing_key('rsa', 'C', kid='MnC_VZcATfM5pOYiJHMba9goEKY')

0 commit comments

Comments
 (0)