mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-12-07 01:28:34 +08:00
修复verify方法在定义alg为none时验证失效问题(issue#4105@Github)
This commit is contained in:
parent
614b4fd7e5
commit
d4790d29e0
@ -16,6 +16,7 @@
|
||||
|
||||
package cn.hutool.v7.crypto.digest.mac;
|
||||
|
||||
import cn.hutool.v7.core.lang.Assert;
|
||||
import cn.hutool.v7.crypto.bc.SmUtil;
|
||||
|
||||
import java.security.Key;
|
||||
@ -50,6 +51,7 @@ public class MacEngineFactory {
|
||||
* @since 5.7.12
|
||||
*/
|
||||
public static MacEngine createEngine(final String algorithm, final Key key, final AlgorithmParameterSpec spec) {
|
||||
Assert.notBlank(algorithm, "Algorithm must be not blank!");
|
||||
if (algorithm.equalsIgnoreCase(HmacAlgorithm.HmacSM3.getValue())) {
|
||||
// HmacSM3算法是BC库实现的,忽略加盐
|
||||
return SmUtil.createHmacSm3Engine(key.getEncoded());
|
||||
|
||||
@ -136,12 +136,7 @@ public class JWT implements RegisteredPayload<JWT> {
|
||||
* @return this
|
||||
*/
|
||||
public JWT setKey(final byte[] key) {
|
||||
// 检查头信息中是否有算法信息
|
||||
final String algorithmId = (String) this.header.getClaim(JWTHeader.ALGORITHM);
|
||||
if (StrUtil.isNotBlank(algorithmId)) {
|
||||
return setSigner(algorithmId, key);
|
||||
}
|
||||
return setSigner(JWTSignerUtil.hs256(key));
|
||||
return setSigner(getAlgorithm(), key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -420,6 +415,16 @@ public class JWT implements RegisteredPayload<JWT> {
|
||||
signer = NoneJWTSigner.NONE;
|
||||
}
|
||||
|
||||
// 用户定义alg为none但是签名器不是NoneJWTSigner
|
||||
if(NoneJWTSigner.isNone(getAlgorithm()) && !(signer instanceof NoneJWTSigner)){
|
||||
throw new JWTException("Alg is 'none' but use: {} !", signer.getClass());
|
||||
}
|
||||
|
||||
// alg非none,但签名器是NoneJWTSigner
|
||||
if(signer instanceof NoneJWTSigner && !NoneJWTSigner.isNone(getAlgorithm())){
|
||||
throw new JWTException("Alg is not 'none' but use NoneJWTSigner!");
|
||||
}
|
||||
|
||||
final List<String> tokens = this.tokens;
|
||||
if (CollUtil.isEmpty(tokens)) {
|
||||
throw new JWTException("No token to verify!");
|
||||
|
||||
@ -18,6 +18,7 @@ package cn.hutool.v7.json.jwt.signers;
|
||||
|
||||
import cn.hutool.v7.core.lang.Assert;
|
||||
import cn.hutool.v7.core.regex.ReUtil;
|
||||
import cn.hutool.v7.json.jwt.JWTException;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
@ -241,18 +242,22 @@ public class JWTSignerUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建签名器
|
||||
* 创建签名器<br>
|
||||
* 当key为{@code null}且签名算法为{@link NoneJWTSigner#NONE}时,返回无签名的签名器
|
||||
*
|
||||
* @param algorithmId 算法ID,见{@link AlgorithmUtil}
|
||||
* @param algorithmId 算法ID,见{@link AlgorithmUtil},{@code null} "" 空格使用无签名签名器
|
||||
* @param key 密钥
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner createSigner(final String algorithmId, final byte[] key) {
|
||||
Assert.notNull(key, "Signer key must be not null!");
|
||||
|
||||
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
|
||||
if (NoneJWTSigner.isNone(algorithmId)) {
|
||||
if(null == key){
|
||||
return none();
|
||||
}else{
|
||||
throw new JWTException("When key is not null, algorithmId must not be none.");
|
||||
}
|
||||
}
|
||||
|
||||
return new HMacJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
|
||||
}
|
||||
|
||||
@ -264,10 +269,12 @@ public class JWTSignerUtil {
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner createSigner(final String algorithmId, final KeyPair keyPair) {
|
||||
Assert.notNull(keyPair, "Signer key pair must be not null!");
|
||||
|
||||
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
|
||||
if (NoneJWTSigner.isNone(algorithmId)) {
|
||||
if(null == keyPair){
|
||||
return none();
|
||||
}else{
|
||||
throw new JWTException("When keyPair is not null, algorithmId must not be none.");
|
||||
}
|
||||
}
|
||||
|
||||
final String algorithm = AlgorithmUtil.getAlgorithm(algorithmId);
|
||||
@ -287,10 +294,12 @@ public class JWTSignerUtil {
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner createSigner(final String algorithmId, final Key key) {
|
||||
Assert.notNull(key, "Signer key must be not null!");
|
||||
|
||||
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
|
||||
return NoneJWTSigner.NONE;
|
||||
if (NoneJWTSigner.isNone(algorithmId)) {
|
||||
if(null == key){
|
||||
return none();
|
||||
}else{
|
||||
throw new JWTException("When key is not null, algorithmId must not be none.");
|
||||
}
|
||||
}
|
||||
|
||||
final String algorithm = AlgorithmUtil.getAlgorithm(algorithmId);
|
||||
|
||||
@ -36,6 +36,16 @@ public class NoneJWTSigner implements JWTSigner {
|
||||
*/
|
||||
public static NoneJWTSigner NONE = new NoneJWTSigner();
|
||||
|
||||
/**
|
||||
* 判断给定的算法是否为无签名的算法
|
||||
*
|
||||
* @param alg 算法
|
||||
* @return 如果是无签名的算法,则返回true;否则返回false
|
||||
*/
|
||||
public static boolean isNone(final String alg) {
|
||||
return StrUtil.isBlank( alg) || StrUtil.equalsIgnoreCase(alg, ID_NONE);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String sign(final String headerBase64, final String payloadBase64) {
|
||||
|
||||
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cn.hutool.v7.json.jwt;
|
||||
|
||||
import cn.hutool.v7.core.codec.binary.Base64;
|
||||
import cn.hutool.v7.core.text.StrUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class Issue4105Test {
|
||||
@Test
|
||||
void verifyNoneTest() {
|
||||
// {"alg": "none"}.{"exp": 1642196407}
|
||||
// 当定义alg为none时,校验总是成功
|
||||
String head = Base64.encode("{\"alg\": \"none\"}");
|
||||
String payload = Base64.encode("{\"exp\": 1642196407}");
|
||||
String token = StrUtil.format("{}.{}.", head, payload);
|
||||
|
||||
final JWT jwt = JWTUtil.parseToken(token);
|
||||
Assertions.assertNull(jwt.getSigner());
|
||||
// 对于签名为none的JWT,verify()方法总是返回true
|
||||
Assertions.assertTrue(jwt.verify());
|
||||
|
||||
// 对于签名为none的JWT,但是定义了key,不一致报错
|
||||
final JWT jwt2 = JWTUtil.parseToken(token);
|
||||
Assertions.assertThrows(JWTException.class, ()-> jwt2.setKey("123".getBytes(StandardCharsets.UTF_8)).verify());
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyEmptyTest() {
|
||||
// {"alg": "none"}.{"exp": 1642196407}
|
||||
// 当定义alg为none时,校验总是成功
|
||||
String head = Base64.encode("{\"alg\": \"\"}");
|
||||
String payload = Base64.encode("{\"exp\": 1642196407}");
|
||||
String token = StrUtil.format("{}.{}.", head, payload);
|
||||
|
||||
final JWT jwt = JWTUtil.parseToken(token);
|
||||
Assertions.assertNull(jwt.getSigner());
|
||||
// 对于签名为none的JWT,verify()方法总是返回true
|
||||
Assertions.assertTrue(jwt.verify());
|
||||
|
||||
// 对于签名为none的JWT,setKey使用的签名总是
|
||||
final JWT jwt2 = JWTUtil.parseToken(token);
|
||||
Assertions.assertThrows(JWTException.class, ()-> jwt2.setKey("123".getBytes(StandardCharsets.UTF_8)).verify());
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyHs256Test() {
|
||||
// {"alg": "none"}.{"exp": 1642196407}
|
||||
// 当定义alg为none时,校验总是成功
|
||||
String head = Base64.encode("{\"alg\": \"HS256\"}");
|
||||
String payload = Base64.encode("{\"exp\": 1642196407}");
|
||||
String token = StrUtil.format("{}.{}.", head, payload);
|
||||
|
||||
final JWT jwt = JWTUtil.parseToken(token);
|
||||
Assertions.assertNull(jwt.getSigner());
|
||||
|
||||
// 未定义签名器或key,但是JWT中要求了签名算法,异常
|
||||
Assertions.assertThrows(JWTException.class, jwt::verify);
|
||||
|
||||
// 手动定义签名器,但是签名部分为空或不一致,返回false
|
||||
final JWT jwt2 = JWTUtil.parseToken(token);
|
||||
Assertions.assertFalse(jwt2.setKey("123".getBytes(StandardCharsets.UTF_8)).verify());
|
||||
}
|
||||
}
|
||||
@ -19,6 +19,7 @@ package cn.hutool.v7.json.jwt;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -29,16 +30,20 @@ public class IssueI5QRUOTest {
|
||||
// https://jwt.io/
|
||||
|
||||
// 自定义header顺序
|
||||
final Map<String, Object> header = new LinkedHashMap<String, Object>(){
|
||||
final Map<String, Object> header = new LinkedHashMap<>() {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
{
|
||||
put(JWTHeader.ALGORITHM, "HS384");
|
||||
put(JWTHeader.TYPE, "JWT");
|
||||
}
|
||||
};
|
||||
|
||||
final Map<String, Object> payload = new LinkedHashMap<String, Object>(){
|
||||
final Map<String, Object> payload = new LinkedHashMap<>() {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
{
|
||||
put("sub", "1234567890");
|
||||
put("name", "John Doe");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user