forked from auth0/java-jwt
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJWTVerifier.java
More file actions
163 lines (130 loc) · 5.42 KB
/
Copy pathJWTVerifier.java
File metadata and controls
163 lines (130 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package com.auth0.jwt;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* JWT Java Implementation
* <p/>
* Adapted from https://bitbucket.org/lluisfaja/javajwt/wiki/Home
* See <a href="https://bitbucket.org/lluisfaja/javajwt/src/3941d23e8e70f681d8a9a2584760e58e79e498f1/JavaJWT/src/com/unblau/javajwt/JWTVerifier.java">JWTVerifier.java</a>
*/
public class JWTVerifier {
private final String secret;
private final String audience;
private final String issuer;
private final Base64 decoder;
private final ObjectMapper mapper;
private Map<String, String> algorithms;
public JWTVerifier(String secret, String audience, String issuer) {
if (secret == null || "".equals(secret)) {
throw new IllegalArgumentException("Secret cannot be null or empty");
}
decoder = new Base64(true);
mapper = new ObjectMapper();
algorithms = new HashMap<String, String>();
algorithms.put("HS256", "HmacSHA256");
algorithms.put("HS384", "HmacSHA384");
algorithms.put("HS512", "HmacSHA512");
this.secret = secret;
this.audience = audience;
this.issuer = issuer;
}
public JWTVerifier(String secret, String audience) {
this(secret, audience, null);
}
public JWTVerifier(String secret) {
this(secret, null, null);
}
/**
* Performs JWT validation
*
* @param token token to verify
* @throws SignatureException when signature is invalid
* @throws IllegalStateException when token's structure, expiration, issuer or audience are invalid
*/
public Map<String, Object> verify(String token)
throws NoSuchAlgorithmException, InvalidKeyException, IllegalStateException,
IOException, SignatureException {
if (token == null || "".equals(token)) {
throw new IllegalStateException("token not set");
}
String[] pieces = token.split("\\.");
// check number of segments
if (pieces.length != 3) {
throw new IllegalStateException("Wrong number of segments: " + pieces.length);
}
// get JWTHeader JSON object. Extract algorithm
JsonNode jwtHeader = decodeAndParse(pieces[0]);
String algorithm = getAlgorithm(jwtHeader);
// get JWTClaims JSON object
JsonNode jwtPayload = decodeAndParse(pieces[1]);
// check signature
verifySignature(pieces, algorithm);
// additional JWTClaims checks
verifyExpiration(jwtPayload);
verifyIssuer(jwtPayload);
verifyAudience(jwtPayload);
return mapper.treeToValue(jwtPayload, Map.class);
}
void verifySignature(String[] pieces, String algorithm) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
Mac hmac = Mac.getInstance(algorithm);
hmac.init(new SecretKeySpec(decoder.decodeBase64(secret), algorithm));
byte[] sig = hmac.doFinal(new StringBuilder(pieces[0]).append(".").append(pieces[1]).toString().getBytes());
if (!Arrays.equals(sig, decoder.decodeBase64(pieces[2]))) {
throw new SignatureException("signature verification failed");
}
}
void verifyExpiration(JsonNode jwtClaims) {
final long expiration = jwtClaims.has("exp") ? jwtClaims.get("exp").asLong(0) : 0;
if (expiration != 0 && System.currentTimeMillis() / 1000L >= expiration) {
throw new IllegalStateException("jwt expired");
}
}
void verifyIssuer(JsonNode jwtClaims) {
final String issuerFromToken = jwtClaims.has("iss") ? jwtClaims.get("iss").asText() : null;
if (issuerFromToken != null && issuer != null && !issuer.equals(issuerFromToken)) {
throw new IllegalStateException("jwt issuer invalid");
}
}
void verifyAudience(JsonNode jwtClaims) {
if (audience == null)
return;
JsonNode audNode = jwtClaims.get("aud");
if (audNode == null)
return;
if (audNode.isArray()) {
for (JsonNode jsonNode : audNode) {
if (audience.equals(jsonNode.textValue()))
return;
}
} else if (audNode.isTextual()) {
if (audience.equals(audNode.textValue()))
return;
}
throw new IllegalStateException("jwt audience invalid");
}
String getAlgorithm(JsonNode jwtHeader) {
final String algorithmName = jwtHeader.has("alg") ? jwtHeader.get("alg").asText() : null;
if (jwtHeader.get("alg") == null) {
throw new IllegalStateException("algorithm not set");
}
if (algorithms.get(algorithmName) == null) {
throw new IllegalStateException("unsupported algorithm");
}
return algorithms.get(algorithmName);
}
JsonNode decodeAndParse(String b64String) throws IOException {
String jsonString = new String(decoder.decodeBase64(b64String), "UTF-8");
JsonNode jwtHeader = mapper.readValue(jsonString, JsonNode.class);
return jwtHeader;
}
}