Skip to content

Commit ad25ea3

Browse files
committed
preliminary support for encrypting jwt
1 parent b4e84da commit ad25ea3

3 files changed

Lines changed: 111 additions & 8 deletions

File tree

src/main/java/org/oidc/msg/AbstractMessage.java

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818

1919
import com.auth0.jwt.JWT;
2020
import com.auth0.jwt.JWTCreator;
21+
import com.auth0.jwt.JWTEncryptor;
2122
import com.auth0.jwt.JWTVerifier;
2223
import com.auth0.jwt.algorithms.Algorithm;
24+
import com.auth0.jwt.algorithms.CipherParams;
2325
import com.auth0.jwt.exceptions.JWTDecodeException;
2426
import com.auth0.jwt.exceptions.JWTVerificationException;
2527
import com.auth0.jwt.exceptions.oicmsg_exceptions.JWKException;
@@ -341,23 +343,43 @@ public void fromJwt(String jwt, KeyJar keyJar, String keyOwner,
341343
/**
342344
* Serialize the content of this instance (the claims map) into a jwt string.
343345
*
344-
* @param key
346+
* @param signingKey
345347
* signing key
346348
* @param alg
347-
* signing algorithm
349+
* signing algorithm name
348350
* @return message as jwt string.
351+
* @throws SerializationException
349352
*/
350-
public String toJwt(Key key, String alg)
353+
public String toJwt(Key signingKey, String alg) throws SerializationException {
354+
return toJwt(signingKey, alg, null, null, null);
355+
}
356+
/**
357+
* Serialize the content of this instance (the claims map) into a jwt string.
358+
*
359+
* @param signingKey
360+
* signing key
361+
* @param alg
362+
* signing algorithm name
363+
* @param transportKey
364+
* key transport key, if null encryption is not done.
365+
* @param encAlg
366+
* key transport algorithm name. Must not be null if transportKey is set.
367+
* @param encEnc
368+
* content encryption algorithm name. Must not be null if transportKey is set.
369+
* @return message as jwt string.
370+
*/
371+
372+
public String toJwt(Key signingKey, String alg, Key transportKey, String encAlg, String encEnc)
351373
throws SerializationException {
352374
header = new HashMap<String, Object>();
353375
header.put("alg", alg);
354376
header.put("typ", "JWT");
355-
if (key != null && key.getKid() != null) {
356-
header.put("kid", key.getKid());
377+
if (signingKey != null && signingKey.getKid() != null) {
378+
header.put("kid", signingKey.getKid());
357379
}
358380
Algorithm algorithm = null;
359381
try {
360-
algorithm = AlgorithmResolver.resolveSigningAlgorithm(key, alg);
382+
algorithm = AlgorithmResolver.resolveSigningAlgorithm(signingKey, alg);
361383
} catch (IllegalArgumentException | ValueError | UnsupportedEncodingException
362384
| SerializationNotPossible e) {
363385
throw new SerializationException(String
@@ -383,8 +405,24 @@ public String toJwt(Key key, String alg)
383405
}
384406

385407
}
386-
return newBuilder.sign(algorithm);
387-
408+
409+
String signedJwt = newBuilder.sign(algorithm);
410+
if (transportKey == null) {
411+
return signedJwt;
412+
}
413+
if (encAlg == null || encEnc == null) {
414+
throw new SerializationException(
415+
"encAlg and encEnc are mandatory parameters if transport key is set");
416+
}
417+
try {
418+
return JWTEncryptor.init().withPayload(signedJwt.getBytes("UTF-8")).encrypt(
419+
AlgorithmResolver.resolveKeyTransportAlgorithm(transportKey, encAlg),
420+
Algorithm.getContentEncryptionAlg(encEnc, CipherParams.getInstance(encEnc)));
421+
} catch (UnsupportedEncodingException | ValueError | SerializationNotPossible e) {
422+
throw new SerializationException(
423+
String.format("Not able to initialize key transport algorithm '%s' to encrypt JWS, '%s'",
424+
encAlg, e.getMessage()));
425+
}
388426
}
389427

390428
/**

src/main/java/org/oidc/msg/oidc/util/AlgorithmResolver.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ private static boolean verifyKeyType(Key key, String alg) {
4747
case "RS256":
4848
case "RS384":
4949
case "RS512":
50+
case "RSA1_5":
51+
case "RSA-OAEP":
52+
case "RSA-OAEP-256":
5053
if (key instanceof RSAKey) {
5154
return true;
5255
}
@@ -61,6 +64,9 @@ private static boolean verifyKeyType(Key key, String alg) {
6164
case "HS256":
6265
case "HS384":
6366
case "HS512":
67+
case "A128KW":
68+
case "A192KW":
69+
case "A256KW":
6470
if (key instanceof SYMKey) {
6571
return true;
6672
}
@@ -160,4 +166,49 @@ public static Algorithm resolveVerificationAlgorithm(Key key, String alg)
160166
throw new ValueError(String.format("Algorithm '%s' not supported ", alg));
161167
}
162168

169+
/**
170+
* Resolves key transport algorithm for key and algorithm identifier string.
171+
* @param key for signing.
172+
* @param alg algorithm name.
173+
* @return Algorithm instance
174+
* @throws ValueError if key or algorithm is of unexpected type
175+
* @throws UnsupportedEncodingException if symmetric key encoding fails
176+
* @throws SerializationNotPossible if symmetric key fails to serialize
177+
*/
178+
public static Algorithm resolveKeyTransportAlgorithm(Key key, String alg)
179+
throws ValueError, UnsupportedEncodingException, SerializationNotPossible {
180+
if (!verifyKeyType(key, alg)) {
181+
throw new ValueError(String.format("key does not match algorithm '%s' ", alg));
182+
}
183+
if (key == null || !key.isPublicKey()) {
184+
throw new ValueError(String.format("Key for key transport algorithm must be public"));
185+
}
186+
switch (alg) {
187+
case "RSA1_5":
188+
return Algorithm.RSA1_5((RSAPublicKey) key.getKey(false), null);
189+
case "RSA-OAEP":
190+
return Algorithm.RSAOAEP((RSAPublicKey) key.getKey(false), null);
191+
case "RSA-OAEP-256":
192+
return Algorithm.RSAOAEP256((RSAPublicKey) key.getKey(false), null);
193+
case "A128KW":
194+
return Algorithm.AES128Keywrap(((SYMKey) key).getKey(false).getEncoded());
195+
case "A192KW":
196+
return Algorithm.AES192Keywrap(((SYMKey) key).getKey(false).getEncoded());
197+
case "A256KW":
198+
return Algorithm.AES256Keywrap(((SYMKey) key).getKey(false).getEncoded());
199+
/*
200+
* TODO: rest of the algorithms requiring more parameters
201+
* case "ECDH-ES":
202+
* case "ECDH-ES+A128KW":
203+
* case "ECDH-ES+A192KW":
204+
* case "ECDH-ES+A256KW":
205+
* ..return Algorithm.ECDH_ES((ECPublicKey) key.getKey(false)...); ... ...
206+
*
207+
*/
208+
default:
209+
break;
210+
}
211+
throw new ValueError(String.format("Algorithm '%s' not supported ", alg));
212+
}
213+
163214
}

src/test/java/org/oidc/msg/AbstractMessageTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,20 @@ public void testSuccessToAndFromJWTNoneAlgBasicTypes() throws IOException, Inval
139139
CoreMatchers.is(mockMessage2.getClaims().get("foo4")));
140140
}
141141

142+
@Test
143+
public void testSuccessToJWTEncryptInitialVersion()
144+
throws IOException, InvalidClaimException, SerializationException, DeserializationException,
145+
IllegalArgumentException, ImportException, UnknownKeyType, ValueError, JWKException {
146+
//Initial test for jwt encryption. Not at all complete case.
147+
HashMap<String, Object> claims = new HashMap<String, Object>();
148+
claims.put("foo", "bar");
149+
MockMessage mockMessage = new MockMessage(claims);
150+
List<Key> keysDec = getKeyJarPrv().getDecryptKey("RSA", keyOwner, null, null);
151+
List<Key> keysEnc = getKeyJarPub().getEncryptKey("RSA", keyOwner, null, null);
152+
mockMessage.toJwt(keysDec.get(0), "RS256", keysEnc.get(0), "RSA1_5",
153+
"A128CBC-HS256");
154+
}
155+
142156
@Test
143157
public void testSuccessToJWTSignRS()
144158
throws IllegalArgumentException, ImportException, UnknownKeyType, ValueError,

0 commit comments

Comments
 (0)