Skip to content

Commit 3c25454

Browse files
committed
Added KeyJar
1 parent 012d1e5 commit 3c25454

7 files changed

Lines changed: 247 additions & 16 deletions

File tree

lib/src/main/java/com/auth0/jwt/oicmsg/ECKey.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/*
21
package com.auth0.jwt.oicmsg;
32

43
import com.auth0.jwt.exceptions.oicmsg_exceptions.HeaderError;
@@ -194,4 +193,3 @@ public static void setRequired(Set<String> required) {
194193
ECKey.required = required;
195194
}
196195
}
197-
*/

lib/src/main/java/com/auth0/jwt/oicmsg/Key.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/*
21
package com.auth0.jwt.oicmsg;
32

43
import com.auth0.jwt.exceptions.oicmsg_exceptions.HeaderError;
@@ -122,7 +121,7 @@ public Map<String, String> toDict() {
122121
return hmap;
123122
}
124123

125-
public Map<String, String> serialize() {
124+
public List<Key> serialize() {
126125
Map<String, String> hmap = common();
127126
this.key.
128127
//TODO
@@ -235,4 +234,3 @@ protected static void deser(Object item) {
235234
return base64ToLong(item);
236235
}
237236
}
238-
*/

lib/src/main/java/com/auth0/jwt/oicmsg/KeyBundle.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/*
21
package com.auth0.jwt.oicmsg;
32

43
import com.auth0.jwt.exceptions.oicmsg_exceptions.*;
@@ -104,6 +103,10 @@ public KeyBundle(List<Key> keys, String source, long cacheTime, boolean verifySS
104103
}
105104
}
106105

106+
public String getSource() {
107+
return source;
108+
}
109+
107110
public KeyBundle() throws ImportException {
108111
this(null, "", 300, true, "jwk", "RSA", null);
109112
}
@@ -514,4 +517,3 @@ public void dumpJwks(List<KeyBundle> kbl, String target, boolean isPrivate) {
514517

515518

516519
}
517-
*/

lib/src/main/java/com/auth0/jwt/oicmsg/KeyJar.java

Lines changed: 235 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,37 @@
1-
/*
21
package com.auth0.jwt.oicmsg;
32

43
import com.auth0.jwt.exceptions.oicmsg_exceptions.ImportException;
4+
import com.auth0.jwt.exceptions.oicmsg_exceptions.TypeError;
5+
import com.auth0.jwt.impl.JWTParser;
6+
import com.auth0.jwt.jwts.JWT;
7+
import com.google.common.base.Strings;
58
import org.junit.Assert;
9+
import org.slf4j.LoggerFactory;
610

711
import java.security.KeyException;
812
import java.util.*;
13+
import java.util.logging.Logger;
914

1015
public class KeyJar {
1116

1217
private boolean verifySSL;
1318
private KeyBundle keyBundle;
14-
private int removeAfter;
19+
private float removeAfter;
1520
private Map<String,List<KeyBundle>> issuerKeys;
21+
final private static org.slf4j.Logger logger = LoggerFactory.getLogger(KeyJar.class);
1622

1723
public KeyJar(boolean verifySSL, KeyBundle keyBundle, int removeAfter) {
1824
this.verifySSL = verifySSL;
1925
this.keyBundle = keyBundle;
2026
this.removeAfter = removeAfter;
2127
}
2228

29+
public KeyJar() throws ImportException {
30+
this.verifySSL = true;
31+
this.keyBundle = new KeyBundle();
32+
this.removeAfter = 3600;
33+
}
34+
2335
public KeyBundle addUrl(String owner, String url, Map<String,String> args) throws KeyException, ImportException {
2436
if(url == null || url.isEmpty()) {
2537
throw new KeyException("No jwksUri");
@@ -139,9 +151,228 @@ public List<Key> getKeys(String keyUse, String keyType, String owner, String kid
139151
name = "P-{}" + args.get("alg").substring(2);
140152
List<Key> tempKeyList = new ArrayList<>();
141153
for(Key key : keyList) {
142-
Assert.assertTrue(name.equals(((ECKey) key).get);
154+
try {
155+
Assert.assertTrue(name.equals(((ECKey) key).getCrv()));
156+
} catch (AssertionError error) {
157+
continue;
158+
} finally {
159+
tempKeyList.add(key);
160+
}
161+
}
162+
keyList = tempKeyList;
163+
}
164+
165+
if(use.equals("enc") && keyType.equals("oct") && owner != null && !owner.isEmpty()) {
166+
for(KeyBundle keyBundle : this.issuerKeys.get("")) {
167+
for(Key key : keyBundle.get(keyType)) {
168+
if(key.getUse() == null || key.getUse() == use) {
169+
keyList.add(key);
170+
}
171+
}
172+
}
173+
}
174+
175+
return keyList;
176+
}
177+
178+
public List<Key> getSigningKey(String keyType, String owner, String kid, Map<String,String> args) {
179+
return getKeys("sig", keyType, owner, kid, args);
180+
}
181+
182+
public List<Key> getVerifyKey(String keyType, String owner, String kid, Map<String,String> args) {
183+
return getKeys("ver", keyType, owner, kid, args);
184+
}
185+
186+
public List<Key> getEncryptKey(String keyType, String owner, String kid, Map<String,String> args) {
187+
return getKeys("enc", keyType, owner, kid, args);
188+
}
189+
190+
public List<Key> getDecryptKey(String keyType, String owner, String kid, Map<String,String> args) {
191+
return getKeys("dec", keyType, owner, kid, args);
192+
}
193+
194+
public List<Key> keysByAlgAndUsage(String issuer, String algorithm, String usage) {
195+
String keyType;
196+
if(usage.equals("sig") || usage.equals("ver")) {
197+
keyType = algorithmToKeytypeForJWS(algorithm);
198+
} else {
199+
keyType = algorithmToKeytypeForJWE(algorithm);
200+
}
201+
202+
return getKeys(usage, keyType, issuer, null, null);
203+
}
204+
205+
public List<Key> getIssuerKeys(String issuer) {
206+
List<Key> keyList = new ArrayList<>();
207+
for(KeyBundle keyBundle : this.issuerKeys.get(issuer)) {
208+
keyList.addAll(keyBundle.getKeys());
209+
}
210+
return keyList;
211+
}
212+
213+
private String algorithmToKeytypeForJWS(String algorithm) {
214+
if(algorithm == null || algorithm.toLowerCase().equals("none")) {
215+
return "none";
216+
} else if(algorithm.startsWith("RS") || algorithm.startsWith("PS")) {
217+
return "RSA";
218+
} else if(algorithm.startsWith("HS") || algorithm.startsWith("A")) {
219+
return "oct";
220+
} else if(algorithm.startsWith("ES") || algorithm.startsWith("ECDH-ES")) {
221+
return "EC";
222+
} else {
223+
return null;
224+
}
225+
}
226+
227+
private String algorithmToKeytypeForJWE(String algorithm) {
228+
if(algorithm.startsWith("RSA")) {
229+
return "RSA";
230+
} else if(algorithm.startsWith("A")) {
231+
return "oct";
232+
} else if(algorithm.startsWith("ECDH")) {
233+
return "EC";
234+
} else {
235+
return null;
236+
}
237+
}
238+
239+
public String matchOwner(String url) throws KeyException {
240+
for(String key : this.issuerKeys.keySet()) {
241+
if(url.startsWith(key)) {
242+
return key;
143243
}
144244
}
245+
246+
throw new KeyException(String.format("No keys for %s", url));
247+
}
248+
249+
public void loadKeys(Map<String,String> pcr, String issuer, boolean shouldReplace) {
250+
logger.debug("loading keys for issuer: " + issuer);
251+
252+
if(shouldReplace || !this.issuerKeys.keySet().contains(issuer)) {
253+
this.issuerKeys.put(issuer, new ArrayList<KeyBundle>());
254+
}
255+
256+
//this.addUrl(null, issuer, pcr.get("jwks_uri")); ??
257+
}
258+
259+
public KeyBundle find(String source, String issuer) {
260+
for(KeyBundle keyBundle : this.issuerKeys.get(issuer)) {
261+
if(keyBundle.getSource().equals(source)) {
262+
return keyBundle;
263+
}
264+
}
265+
266+
return null;
267+
}
268+
269+
public Map<String,List<Key>> exportsJwks(boolean isPrivate, String issuer) {
270+
List<Key> keys = new ArrayList<>();
271+
for(KeyBundle keyBundle : this.issuerKeys.get(issuer)) {
272+
for(Key key : keyBundle.getKeys()) {
273+
if(key.getInactiveSince() == 0) {
274+
keys.addAll(key.serialize());
275+
}
276+
}
277+
}
278+
279+
Map<String,List<Key>> keysMap = new HashMap<>();
280+
keysMap.put("keys", keys);
281+
return keysMap;
282+
}
283+
284+
public Map<String,List<Key>> exportJwksAsJson(boolean isPrivate, String issuer) {
285+
return this.exportsJwks(isPrivate, issuer);
286+
}
287+
288+
public void importJwks(Map<String,String> jwks, String issuer) throws ImportException {
289+
String keys = jwks.get("keys");
290+
List<KeyBundle> keyBundleList = this.issuerKeys.get("issuer");
291+
if(keyBundleList == null) {
292+
keyBundleList = new ArrayList<>();
293+
}
294+
295+
keyBundleList.add(new KeyBundle(keys, this.verifySSL));
296+
this.issuerKeys.put(issuer, keyBundleList);
297+
}
298+
299+
public void importJwksAsJson(String js, String issuer) {
300+
importJwks();
301+
}
302+
303+
public void removeOutdated(int when) throws TypeError {
304+
List<KeyBundle> keyBundleList;
305+
for(String owner : this.issuerKeys.keySet()) {
306+
keyBundleList = new ArrayList<>();
307+
for(KeyBundle keyBundle : this.issuerKeys.get(owner)) {
308+
keyBundle.removeOutdated(this.removeAfter, when);
309+
if(keyBundle.getLength() > 0) {
310+
keyBundleList.add(keyBundle);
311+
}
312+
}
313+
314+
if(keyBundleList.size() > 0) {
315+
this.issuerKeys.put(owner, keyBundleList);
316+
} else {
317+
this.issuerKeys.remove(owner);
318+
}
319+
}
320+
}
321+
322+
public List<Key> addKey(List<Key> keys, String owner, String use, String keyType, String kid,
323+
Map<String,List<String>> noKidIssuer) {
324+
if(!this.issuerKeys.keySet().contains(owner)) {
325+
logger.error("Issuer " + owner + " not in keyjar");
326+
return keys;
327+
}
328+
329+
logger.debug("Key set summary for " + owner + " : " + keySummary(this, owner));
330+
331+
if(kid != null) {
332+
for(Key key : this.getKeys(use, owner, kid, keyType, null)) {
333+
if(key != null && !keys.contains(key)) {
334+
keys.add(key);
335+
}
336+
}
337+
return keys;
338+
} else {
339+
List<Key> keyList = this.getKeys(use, "", owner, keyType, null);
340+
if(keyList.size() == 0) {
341+
return keys;
342+
} else if(keyList.size() == 1) {
343+
if(!keys.contains(keyList.get(0))) {
344+
keys.add(keyList.get(0));
345+
}
346+
} else if(noKidIssuer != null) {
347+
List<String> allowedKids = noKidIssuer.get(owner);
348+
if(allowedKids != null) {
349+
for(Key key : keyList) {
350+
if(allowedKids.contains(key.getKid())) {
351+
keys.add(key);
352+
}
353+
}
354+
} else {
355+
keys.addAll(keyList);
356+
}
357+
}
358+
}
359+
return keys;
360+
}
361+
362+
public void getJwtVerifyKeys(JWT jwt, Map<String,String> args) {
363+
List<Key> keyList = new ArrayList<>();
364+
JWTParser converter = new JWTParser();
365+
String keyType = algorithmToKeytypeForJWS(converter.parseHeader(jwttoString().getHeader().getAlgorithm().getName());
366+
String kid = jwt.getHeader().;
367+
String nki = args.get("no_kid_issuer");
368+
369+
}
370+
371+
public KeyJar copy() throws ImportException {
372+
KeyJar keyJar = new KeyJar();
373+
for(String owner : this.issuerKeys.keySet()) {
374+
//kj[owner] = [kb.copy() for kb in self[owner]]; how is kj[owner] being caled??
375+
}
376+
return keyJar;
145377
}
146378
}
147-
*/

lib/src/main/java/com/auth0/jwt/oicmsg/RSAKey.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/*
21
package com.auth0.jwt.oicmsg;
32

43
import com.auth0.jwt.exceptions.oicmsg_exceptions.DeserializationNotPossible;
@@ -198,4 +197,3 @@ private void split() {
198197
}
199198

200199
}
201-
*/

lib/src/main/java/com/auth0/jwt/oicmsg/SYMKey.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/*
21
package com.auth0.jwt.oicmsg;
32

43
import com.auth0.jwt.exceptions.oicmsg_exceptions.JWKException;
@@ -66,4 +65,3 @@ public String encryptionKey(String alg) throws JWKException {
6665
return encryptedKey;
6766
}
6867
}
69-
*/

lib/src/test/java/com/auth0/jwt/creators/JWTCreatorTest.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,12 @@ public void shouldAddKeyIdIfAvailableFromRSAAlgorithmsForBase64() throws Excepti
150150
Algorithm algorithm = Algorithm.RSA256(provider);
151151

152152
String signed =
153-
"eyJhbGciOiJSUzI1NiIsImtpZCI6IjhSR29WZFZqRDhmSXR5UjNGRm8waFZOYVpZdFBHd29QNnhLaTllX1Y3YkkifQ.eyJpc3MiOiAiaHR0cHM6Ly9hZ2F0b24tc2F4LmNvbS8iLCAiaWF0IjogMTUxMzYyODc3OSwgImV4cCI6IDE1MTM3MTUxNzksICJraWQiOiAiOFJHb1ZkVmpEOGZJdHlSM0ZGbzBoVk5hWll0UEd3b1A2eEtpOWVfVjdiSSIsICJmb28iOiAiYmFyIiwgImtpdCI6ICJrYXQifQ.OMSHRJRW3J2MHFvPZhRaxxJhHJ5WFBaRzdPb3KpxWsF1Y3Or4BH-2pL8HE1CAoUTTqGYvNSShi2O-NFupGmaY5SRehOma_6XHcL2OrKKwFkG21M57T13_qagG7VUF7n7yhaLXMKWNli9JZ9iwHqLfA__j2X4XqxqPRxr5LxLXz-eynRp1jax2-eqiAMVYdSnH02e_bmnO89nIys6VUPoOAQFJjoPNtUo0urG8vTsiFPHFCgWUljDUFIu-TiRVTu5gJea-cigUJeG7i_4cp0qkWHo7POrS4Dq-gyzUSbkqBNdg-4LAAU40staTA236MPekQkzeTQlHa9418davEvfboYnCYeAhhX9Pnn7YotZSsD6S9HnTh5OjJ5E3O_Y-5MSK-eIYHV79FLQbaG1Xmcuv7WsRyhPadAwmNYLuWBtgaQDVV58ZmZPB9EaBDczqJDymjLkz2NpfLVI1kKFFDdLDC6ZBnKSsksDKvyrCS3JT3nHLR9LTIBN6mPii5xKy9Ysa1AQ1lK9ytcHCbG2iJJMZ57zpcjEevncvfGkB8RALe6GAthrNclA3mosB9b_z1TBPAzUZVh7VZLCsST7RIO1olDKEtZDvG-qGBEdjNS885nxoYe6ASCcEiUZ16Rixo8xw6lBb9l6qUkw3KST29W7_tcSGC3GnWQCN6q_SS-SD90";
153+
JWTCreator.init()
154+
.withKeyId("8RGoVdVjD8fItyR3FFo0hVNaZYtPGwoP6xKi9e_V7bI")
155+
.withIssuer("https://agaton-sax.com/")
156+
.withNonStandardClaim("foo","bar")
157+
.withNonStandardClaim("kit", "kat")
158+
.sign(algorithm, EncodeType.Base64);
154159

155160
JWT jwt = JWT.require(Algorithm.RSA256(provider)).withIssuer("https://agaton-sax.com/")
156161
.withNonStandardClaim("foo","bar")
@@ -159,6 +164,7 @@ public void shouldAddKeyIdIfAvailableFromRSAAlgorithmsForBase64() throws Excepti
159164
algorithm.verify(decoded, EncodeType.Base64);
160165
}
161166

167+
162168
@Test
163169
public void shouldNotOverwriteKeyIdIfAddedFromRSAAlgorithms() throws Exception {
164170
RSAPrivateKey privateKey = (RSAPrivateKey) PemUtils.readPrivateKeyFromFile(PRIVATE_KEY_FILE_RSA, "RSA");

0 commit comments

Comments
 (0)