我正在學(xué)習(xí)用于身份驗(yàn)證的JWS,并嘗試使用其中一個(gè)java庫(kù)jjwt來實(shí)現(xiàn)。我創(chuàng)建了一個(gè)JWT令牌字符串,并在其末尾添加了一個(gè)字符。令我驚訝的是,jjwt庫(kù)解析它時(shí)沒有拋出任何異常。我不知道庫(kù)或使用的算法是否有任何問題。我用jwt調(diào)試器進(jìn)行了同樣的測(cè)試,它按預(yù)期工作(顯示無效令牌)。
CODE :
public class TestJwt {
// private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv5236v56d56w1xweec3wdn3i432oi"; // WORKING !!!
// private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv52f36v56d56w1xweec3wdn3i432oi"; // WORKING !!!
// private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv52f36345345weec3wdn3i432oi"; // WORKING !!!
// private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv52f36345432oi"; // NOT WORKING
// private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv52f3632222222245y6454524524tef45432oi"; // NOT WORKING
private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv5236v56d56w1xweec3wdn3i432oi"; // WORKING
private static final Key key = Keys.hmacShaKeyFor(JWT_SECRET_KEY.getBytes());
public static void main(String args[]) {
String token =
Jwts.builder()
.claim("name", "RANDOM")
.claim("surname", "ANOTHER_RANDOM")
.signWith(key)
.compact();
System.out.println("TOKEN BEFORE");
System.out.println(token);
// Adding single character to jwt and still working!!
token = token+"7";
System.out.println("TOKEN AFTER");
System.out.println(token);
Claims claims = Jwts.parserBuilder()
.setSigningKey(key).build()
.parseClaimsJws(token).getBody();
claims.forEach((k,v)->{
System.out.println("________________________");
System.out.println(k + " : "+v);
});
System.out.println("________________________");
}
}
OUTPUT :
TOKEN BEFORE
eyJhbGciOiJIUzM4NCJ9.eyJuYW1lIjoiUkFORE9NIiwic3VybmFtZSI6IkFOT1RIRVJfUkFORE9NIn0.evntuAcZ0Urnv-5QniShmENKNBSzrjoxeNWN0uW-sy-qXzC-G2PJyi316m9LqQH9
TOKEN AFTER
eyJhbGciOiJIUzM4NCJ9.eyJuYW1lIjoiUkFORE9NIiwic3VybmFtZSI6IkFOT1RIRVJfUkFORE9NIn0.evntuAcZ0Urnv-5QniShmENKNBSzrjoxeNWN0uW-sy-qXzC-G2PJyi316m9LqQH97
________________________
name : RANDOM
________________________
surname : ANOTHER_RANDOM
________________________
我正在等待SignatureException。我用一些密鑰和隨機(jī)聲明進(jìn)行了測(cè)試,其中一些有效,但一些解析沒有問題(注釋掉了密鑰)。我應(yīng)該使用更復(fù)雜的密鑰嗎?
這不是秘密的問題,而是Base64Url解碼器是如何實(shí)現(xiàn)的。
原始簽名長(zhǎng)度為64個(gè)字符,在Base64Url編碼(每個(gè)字符6位)中,這意味著有64*6位=384位編碼。這與使用HS384算法時(shí)的預(yù)期完全相同。384位/6正好是64位,因此簽名可以編碼為64個(gè)字符的Base64Url字符串,而沒有任何未使用的位。現(xiàn)在,當(dāng)您添加另一個(gè)字符時(shí),這意味著您添加了6位。Java代碼中的解碼器似乎只是忽略了這6位,因?yàn)樗鼪]有足夠的信息來容納一個(gè)完整的字節(jié)。但從技術(shù)上講,它是一個(gè)無效的Base64Url字符串,并且至少還需要一個(gè)字符。jwt.io上使用的解碼器在這方面似乎更嚴(yán)格。
另請(qǐng)參閱JWT令牌解碼,即使簽名的最后一個(gè)字符已更改,這解釋了為什么有時(shí)可以更改簽名的最后字符而不使其無效。