Skip to content

Commit ae1f344

Browse files
committed
X509 finalized
1 parent 3c25454 commit ae1f344

15 files changed

Lines changed: 178 additions & 118 deletions

File tree

lib/src/main/java/com/auth0/jwt/algorithms/Algorithm.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,8 @@ public String toString() {
385385
*/
386386
public abstract void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception;
387387

388+
public abstract void verifyWithX509(DecodedJWT jwt, EncodeType encodeType, String jwksFile, String pemFile) throws Exception;
389+
388390
/**
389391
* Sign the given content using this Algorithm instance.
390392
*

lib/src/main/java/com/auth0/jwt/algorithms/ECDSAAlgorithm.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ public void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception {
114114
}
115115
}
116116

117+
@Override
118+
public void verifyWithX509(DecodedJWT jwt, EncodeType encodeType, String jwksFile, String pemFile) throws Exception {
119+
throw new UnsupportedOperationException("X509 is not supported for ECDSA");
120+
}
121+
117122
@Override
118123
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
119124
try {

lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ public void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception {
9999
}
100100
}
101101

102+
@Override
103+
public void verifyWithX509(DecodedJWT jwt, EncodeType encodeType, String jwksFile, String pemFile) throws Exception {
104+
throw new UnsupportedOperationException("X509 is not supported for HMAC");
105+
}
106+
102107
@Override
103108
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
104109
try {

lib/src/main/java/com/auth0/jwt/algorithms/NoneAlgorithm.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ public void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception {
5959
}
6060
}
6161

62+
@Override
63+
public void verifyWithX509(DecodedJWT jwt, EncodeType encodeType, String jwksFile, String pemFile) throws Exception {
64+
throw new UnsupportedOperationException("X509 is not supported for None algorithm");
65+
}
66+
6267
@Override
6368
public byte[] sign(byte[] contentBytes) throws SignatureGenerationException {
6469
return new byte[0];

lib/src/main/java/com/auth0/jwt/algorithms/RSAAlgorithm.java

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@
4343
import java.security.interfaces.RSAPrivateKey;
4444
import java.security.interfaces.RSAPublicKey;
4545
import java.security.spec.X509EncodedKeySpec;
46+
import java.util.ArrayList;
4647
import java.util.Arrays;
48+
import java.util.List;
4749

4850
class RSAAlgorithm extends Algorithm {
4951

@@ -65,8 +67,65 @@ class RSAAlgorithm extends Algorithm {
6567
this(new CryptoHelper(), id, algorithm, keyProvider);
6668
}
6769

70+
@Override
71+
public void verifyWithX509(DecodedJWT jwt, EncodeType encodeType, String jwksFile, String pemFile) throws Exception {
72+
List<byte[]> byteArrayList = decode(jwt, encodeType);
73+
byte[] contentBytes = byteArrayList.get(0);
74+
byte[] signatureBytes = byteArrayList.get(1);
75+
try {
76+
PublicKey publicKey;
77+
if(jwksFile != null && !jwksFile.isEmpty() && jwksFile.endsWith(".json")) {
78+
String kid = jwt.getKeyId();
79+
JwkProvider provider = new UrlJwkProvider(new File(jwksFile).toURI().toURL());
80+
Jwk jwk = provider.get(kid);
81+
String cert = jwk.getCertificateChain().get(0);
82+
try (Writer writer = new BufferedWriter(new OutputStreamWriter(
83+
new FileOutputStream("./jwks.cert"), "utf-8"))) {
84+
writer.write("-----BEGIN CERTIFICATE-----");
85+
writer.append("\n" + cert + "\n");
86+
writer.append("-----END CERTIFICATE-----");
87+
}
88+
89+
FileReader file = new FileReader(pemFile);
90+
PemReader reader = new PemReader(file);
91+
X509EncodedKeySpec caKeySpec = new X509EncodedKeySpec(reader.readPemObject().getContent());
92+
KeyFactory kf = KeyFactory.getInstance("RSA");
93+
publicKey = kf.generatePublic(caKeySpec);
94+
} else {
95+
throw new IllegalArgumentException("Not a proper jwks file");
96+
}
97+
if (publicKey == null) {
98+
throw new IllegalStateException("The given Public Key is null.");
99+
}
100+
boolean valid = crypto.verifySignatureFor(getDescription(), publicKey, contentBytes, signatureBytes);
101+
if (!valid) {
102+
throw new SignatureVerificationException(this);
103+
}
104+
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) {
105+
throw new SignatureVerificationException(this, e);
106+
}
107+
}
108+
68109
@Override
69110
public void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception {
111+
List<byte[]> byteArrayList = decode(jwt, encodeType);
112+
byte[] contentBytes = byteArrayList.get(0);
113+
byte[] signatureBytes = byteArrayList.get(1);
114+
try {
115+
PublicKey publicKey = keyProvider.getPublicKeyById(jwt.getKeyId());
116+
if (publicKey == null) {
117+
throw new IllegalStateException("The given Public Key is null.");
118+
}
119+
boolean valid = crypto.verifySignatureFor(getDescription(), publicKey, contentBytes, signatureBytes);
120+
if (!valid) {
121+
throw new SignatureVerificationException(this);
122+
}
123+
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) {
124+
throw new SignatureVerificationException(this, e);
125+
}
126+
}
127+
128+
private List<byte[]> decode(DecodedJWT jwt, EncodeType encodeType) throws Exception{
70129
byte[] contentBytes = String.format("%s.%s", jwt.getHeader(), jwt.getPayload()).getBytes(StandardCharsets.UTF_8);
71130
byte[] signatureBytes = null;
72131
String signature = jwt.getSignature();
@@ -80,45 +139,13 @@ public void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception {
80139
Base32 base32 = new Base32();
81140
urlDecoded = URLDecoder.decode(signature, "UTF-8");
82141
signatureBytes = base32.decode(urlDecoded);
83-
System.out.println("signature bytes after being decoded: " + Arrays.toString(signatureBytes));
84-
bytesAfterBeingDecoded = signatureBytes;
85-
System.out.println("Are they equal? " + Arrays.equals(JWTCreator.bytesBeforeBeingDecoded, bytesAfterBeingDecoded));
86142
break;
87143
case Base64:
88144
signatureBytes = Base64.decodeBase64(signature);
89-
System.out.println("signature bytes after being decoded: " + Arrays.toString(signatureBytes));
90-
bytesAfterBeingDecoded = signatureBytes;
91-
System.out.println("Are they equal? " + Arrays.equals(JWTCreator.bytesBeforeBeingDecoded, bytesAfterBeingDecoded));
92145
break;
93146
}
94147

95-
try {
96-
String kid = jwt.getKeyId();
97-
JwkProvider provider = new UrlJwkProvider(new File("./jwksRSA.json").toURI().toURL());
98-
Jwk jwk = provider.get(kid);
99-
String cert = jwk.getCertificateChain().get(0);
100-
try (Writer writer = new BufferedWriter(new OutputStreamWriter(
101-
new FileOutputStream("./jwks.cert"), "utf-8"))) {
102-
writer.write("-----BEGIN CERTIFICATE-----");
103-
writer.append("\n"+ cert + "\n");
104-
writer.append("-----END CERTIFICATE-----");
105-
}
106-
107-
FileReader file = new FileReader("./src/main/java/com/auth0/jwt/algorithms/jwks.pem");
108-
PemReader reader = new PemReader(file);
109-
X509EncodedKeySpec caKeySpec = new X509EncodedKeySpec(reader.readPemObject().getContent());
110-
KeyFactory kf = KeyFactory.getInstance("RSA");
111-
PublicKey publicKey = kf.generatePublic(caKeySpec);
112-
if (publicKey == null) {
113-
throw new IllegalStateException("The given Public Key is null.");
114-
}
115-
boolean valid = crypto.verifySignatureFor(getDescription(), publicKey, contentBytes, signatureBytes);
116-
if (!valid) {
117-
throw new SignatureVerificationException(this);
118-
}
119-
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IllegalStateException e) {
120-
throw new SignatureVerificationException(this, e);
121-
}
148+
return new ArrayList<>(Arrays.asList(contentBytes, signatureBytes));
122149
}
123150

124151
@Override

lib/src/main/java/com/auth0/jwt/creators/JWTCreator.java

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -427,58 +427,30 @@ private String signBase16Encoding() throws UnsupportedEncodingException {
427427

428428
private String signBase32Encoding() throws UnsupportedEncodingException{
429429
Base32 base32 = new Base32();
430-
System.out.println("headerJson: " + headerJson);
431-
System.out.println("headerJson bytes: " + Arrays.toString(headerJson.getBytes()));
432430
String header = URLEncoder.encode(headerJson, "UTF-8");
433431
String payload = URLEncoder.encode(payloadJson, "UTF-8");
434432

435433
byte[] bHeader = header.getBytes("UTF-8");
436434
String encodedHeader = base32.encodeAsString(bHeader);
437435

438-
439-
System.out.println("header after base64 encoding: " + URLDecoder.decode(new String(base32.decode(encodedHeader))).getBytes());
440-
System.out.println("header after base64 encoding bytes: " + Arrays.toString(URLDecoder.decode(new String(base32.decode(encodedHeader))).getBytes()));
441-
System.out.println("header after base64 decoding: " + new String(URLDecoder.decode(new String(base32.decode(encodedHeader))).getBytes()));
442-
System.out.println("header after base64 decoding bytes: " + Arrays.toString(URLDecoder.decode(new String(base32.decode(encodedHeader))).getBytes()));
443-
444-
System.out.println("Are they equal I? " + Arrays.equals(headerJson.getBytes(), URLDecoder.decode(new String(base32.decode(encodedHeader))).getBytes()));
445-
446436
byte[] bPayload = payload.getBytes("UTF-8");
447437
String encodedPayload = base32.encodeAsString(bPayload);
448-
System.out.println("payload after base64 encoding: " + encodedPayload);
449-
System.out.println("payload after base64 encoding bytes: " + Arrays.toString(encodedPayload.getBytes()));
450438

451439
String content = String.format("%s.%s", encodedHeader, encodedPayload);
452440
byte[] signatureBytes = algorithm.sign(content.getBytes(StandardCharsets.UTF_8));
453-
System.out.println("signature bytes: " + Arrays.toString(signatureBytes));
454441
String signature = base32.encodeAsString(signatureBytes);
455442
String signatureFinal = URLEncoder.encode(signature, "UTF-8");
456-
System.out.println("signature after base64 encoding bytes: " + Arrays.toString(signature.getBytes()));
457-
bytesBeforeBeingDecoded = signatureBytes;
458443

459444
return String.format("%s.%s", content, signatureFinal);
460445
}
461446

462447
private String defaultSign() throws SignatureGenerationException {
463-
System.out.println("headerJson: " + headerJson);
464-
System.out.println("headerJson bytes: " + Arrays.toString(headerJson.getBytes()));
465448
String header = Base64.encodeBase64URLSafeString(headerJson.getBytes(StandardCharsets.UTF_8));
466-
System.out.println("header after base64 encoding: " + header);
467-
System.out.println("header after base64 encoding bytes: " + Arrays.toString(header.getBytes()));
468-
System.out.println("header after base64 decoding: " + new String(Base64.decodeBase64(header)));
469-
System.out.println("header after base64 decoding bytes: " + Arrays.toString(Base64.decodeBase64(header)));
470-
471-
System.out.println("Are they equal I? " + Arrays.equals(headerJson.getBytes(), Base64.decodeBase64(header)));
472449
String payload = Base64.encodeBase64URLSafeString(payloadJson.getBytes(StandardCharsets.UTF_8));
473-
System.out.println("payload after base64 encoding: " + payload);
474-
System.out.println("payload after base64 encoding bytes: " + Arrays.toString(payload.getBytes()));
475450
String content = String.format("%s.%s", header, payload);
476451

477452
byte[] signatureBytes = algorithm.sign(content.getBytes(StandardCharsets.UTF_8));
478-
System.out.println("signature bytes: " + Arrays.toString(signatureBytes));
479453
String signature = Base64.encodeBase64URLSafeString(signatureBytes);
480-
System.out.println("signature after base64 encoding bytes: " + Arrays.toString(signature.getBytes()));
481-
bytesBeforeBeingDecoded = signatureBytes;
482454
return String.format("%s.%s", content, signature);
483455
}
484456
}

lib/src/main/java/com/auth0/jwt/jwts/JWT.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,26 @@ public DecodedJWT decode(String token) throws Exception {
6666
return jwt;
6767
}
6868

69+
/**
70+
* Convert the given token to a DecodedJWT
71+
* <p>
72+
* Note that this method <b>doesn't verify the token's signature!</b> Use it only if you trust the token or you already verified it.
73+
*
74+
* @param token with jwt format as string.
75+
* @return a decoded JWT.
76+
* @throws AlgorithmMismatchException if the algorithm stated in the token's header it's not equal to the one defined in the {@link JWT}.
77+
* @throws SignatureVerificationException if the signature is invalid.
78+
* @throws TokenExpiredException if the token has expired.
79+
* @throws InvalidClaimException if a claim contained a different value than the expected one.
80+
*/
81+
public DecodedJWT decodeWithX509(String token, String jwksFile, String pemFile) throws Exception {
82+
DecodedJWT jwt = new JWTDecoder(token, EncodeType.Base64);
83+
VerificationAndAssertion.verifyAlgorithm(jwt, algorithm);
84+
algorithm.verifyWithX509(jwt, EncodeType.Base64, jwksFile, pemFile);
85+
VerificationAndAssertion.verifyClaims(clock, jwt, claims);
86+
return jwt;
87+
}
88+
6989
/**
7090
* Convert the given token to a DecodedJWT
7191
* <p>
@@ -86,6 +106,26 @@ public DecodedJWT decode16Bytes(String token) throws Exception {
86106
return jwt;
87107
}
88108

109+
/**
110+
* Convert the given token to a DecodedJWT
111+
* <p>
112+
* Note that this method <b>doesn't verify the token's signature!</b> Use it only if you trust the token or you already verified it.
113+
*
114+
* @param token with jwt format as string.
115+
* @return a decoded JWT.
116+
* @throws AlgorithmMismatchException if the algorithm stated in the token's header it's not equal to the one defined in the {@link JWT}.
117+
* @throws SignatureVerificationException if the signature is invalid.
118+
* @throws TokenExpiredException if the token has expired.
119+
* @throws InvalidClaimException if a claim contained a different value than the expected one.
120+
*/
121+
public DecodedJWT decode16BytesWithX509(String token, String jwksFile, String pemFile) throws Exception {
122+
DecodedJWT jwt = new JWTDecoder(token, EncodeType.Base16);
123+
VerificationAndAssertion.verifyAlgorithm(jwt, algorithm);
124+
algorithm.verifyWithX509(jwt, EncodeType.Base16, jwksFile, pemFile);
125+
VerificationAndAssertion.verifyClaims(clock, jwt, claims);
126+
return jwt;
127+
}
128+
89129
/**
90130
* Convert the given token to a DecodedJWT
91131
* <p>
@@ -106,6 +146,26 @@ public DecodedJWT decode32Bytes(String token) throws Exception {
106146
return jwt;
107147
}
108148

149+
/**
150+
* Convert the given token to a DecodedJWT
151+
* <p>
152+
* Note that this method <b>doesn't verify the token's signature!</b> Use it only if you trust the token or you already verified it.
153+
*
154+
* @param token with jwt format as string.
155+
* @return a decoded JWT.
156+
* @throws AlgorithmMismatchException if the algorithm stated in the token's header it's not equal to the one defined in the {@link JWT}.
157+
* @throws SignatureVerificationException if the signature is invalid.
158+
* @throws TokenExpiredException if the token has expired.
159+
* @throws InvalidClaimException if a claim contained a different value than the expected one.
160+
*/
161+
public DecodedJWT decode32BytesWithX509(String token, String jwksFile, String pemFile) throws Exception {
162+
DecodedJWT jwt = new JWTDecoder(token, EncodeType.Base32);
163+
VerificationAndAssertion.verifyAlgorithm(jwt, algorithm);
164+
algorithm.verifyWithX509(jwt, EncodeType.Base32, jwksFile, pemFile);
165+
VerificationAndAssertion.verifyClaims(clock, jwt, claims);
166+
return jwt;
167+
}
168+
109169
/**
110170
* Returns a {Verification} to be used to validate token signature.
111171
*

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/*
12
package com.auth0.jwt.oicmsg;
23
34
import com.auth0.jwt.exceptions.oicmsg_exceptions.HeaderError;
@@ -193,3 +194,4 @@ public static void setRequired(Set<String> required) {
193194
ECKey.required = required;
194195
}
195196
}
197+
*/

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/*
12
package com.auth0.jwt.oicmsg;
23
34
import com.auth0.jwt.exceptions.oicmsg_exceptions.HeaderError;
@@ -234,3 +235,4 @@ protected static void deser(Object item) {
234235
return base64ToLong(item);
235236
}
236237
}
238+
*/

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/*
12
package com.auth0.jwt.oicmsg;
23
34
import com.auth0.jwt.exceptions.oicmsg_exceptions.*;
@@ -517,3 +518,4 @@ public void dumpJwks(List<KeyBundle> kbl, String target, boolean isPrivate) {
517518
518519
519520
}
521+
*/

0 commit comments

Comments
 (0)