mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
fix code
This commit is contained in:
parent
119d742e3f
commit
808d48a9fb
@ -59,6 +59,18 @@
|
||||
<version>2.0.25</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-gson</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -70,16 +70,38 @@ public class AsymmetricJWTSigner implements JWTSigner {
|
||||
|
||||
@Override
|
||||
public String sign(final String headerBase64, final String payloadBase64) {
|
||||
return Base64.encodeUrlSafe(sign.sign(StrUtil.format("{}.{}", headerBase64, payloadBase64)));
|
||||
final String dataStr = StrUtil.format("{}.{}", headerBase64, payloadBase64);
|
||||
return Base64.encodeUrlSafe(sign(ByteUtil.toBytes(dataStr, charset)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名字符串数据
|
||||
*
|
||||
* @param data 数据
|
||||
* @return 签名
|
||||
*/
|
||||
protected byte[] sign(byte[] data) {
|
||||
return sign.sign(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(final String headerBase64, final String payloadBase64, final String signBase64) {
|
||||
return sign.verify(
|
||||
return verify(
|
||||
ByteUtil.toBytes(StrUtil.format("{}.{}", headerBase64, payloadBase64), charset),
|
||||
Base64.decode(signBase64));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验签数据
|
||||
*
|
||||
* @param data 数据
|
||||
* @param signed 签名
|
||||
* @return 是否通过
|
||||
*/
|
||||
protected boolean verify(byte[] data, byte[] signed) {
|
||||
return sign.verify(data, signed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return this.sign.getSignature().getAlgorithm();
|
||||
|
@ -0,0 +1,197 @@
|
||||
package org.dromara.hutool.json.jwt.signers;
|
||||
|
||||
import org.dromara.hutool.json.jwt.JWTException;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
|
||||
/**
|
||||
* 椭圆曲线(Elliptic Curve)的JWT签名器。<br>
|
||||
* 按照https://datatracker.ietf.org/doc/html/rfc7518#section-3.4,<br>
|
||||
* Elliptic Curve Digital Signature Algorithm (ECDSA)算法签名需要转换DER格式为pair (R, S)
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.8.21
|
||||
*/
|
||||
public class EllipticCurveJWTSigner extends AsymmetricJWTSigner {
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param algorithm 算法
|
||||
* @param key 密钥
|
||||
*/
|
||||
public EllipticCurveJWTSigner(final String algorithm, final Key key) {
|
||||
super(algorithm, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param algorithm 算法
|
||||
* @param keyPair 密钥对
|
||||
*/
|
||||
public EllipticCurveJWTSigner(final String algorithm, final KeyPair keyPair) {
|
||||
super(algorithm, keyPair);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] sign(final byte[] data) {
|
||||
// https://datatracker.ietf.org/doc/html/rfc7518#section-3.4
|
||||
return derToConcat(super.sign(data), getSignatureByteArrayLength(getAlgorithm()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean verify(final byte[] data, final byte[] signed) {
|
||||
// https://datatracker.ietf.org/doc/html/rfc7518#section-3.4
|
||||
return super.verify(data, concatToDER(signed));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取签名长度
|
||||
*
|
||||
* @param alg 算法
|
||||
* @return 长度
|
||||
* @throws JWTException JWT异常
|
||||
*/
|
||||
private static int getSignatureByteArrayLength(final String alg) throws JWTException {
|
||||
switch (alg) {
|
||||
case "ES256":
|
||||
case "SHA256withECDSA":
|
||||
return 64;
|
||||
case "ES384":
|
||||
case "SHA384withECDSA":
|
||||
return 96;
|
||||
case "ES512":
|
||||
case "SHA512withECDSA":
|
||||
return 132;
|
||||
default:
|
||||
throw new JWTException("Unsupported Algorithm: {}", alg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DER格式转换为pair (R, S)
|
||||
*
|
||||
* @param derSignature DER格式签名
|
||||
* @param outputLength 算法签名长度
|
||||
* @return pair (R, S)
|
||||
* @throws JWTException JWT异常
|
||||
*/
|
||||
private static byte[] derToConcat(final byte[] derSignature, final int outputLength) throws JWTException {
|
||||
|
||||
if (derSignature.length < 8 || derSignature[0] != 48) {
|
||||
throw new JWTException("Invalid ECDSA signature format");
|
||||
}
|
||||
|
||||
final int offset;
|
||||
if (derSignature[1] > 0) {
|
||||
offset = 2;
|
||||
} else if (derSignature[1] == (byte) 0x81) {
|
||||
offset = 3;
|
||||
} else {
|
||||
throw new JWTException("Invalid ECDSA signature format");
|
||||
}
|
||||
|
||||
final byte rLength = derSignature[offset + 1];
|
||||
|
||||
int i = rLength;
|
||||
while ((i > 0) && (derSignature[(offset + 2 + rLength) - i] == 0)) {
|
||||
i--;
|
||||
}
|
||||
|
||||
final byte sLength = derSignature[offset + 2 + rLength + 1];
|
||||
|
||||
int j = sLength;
|
||||
while ((j > 0) && (derSignature[(offset + 2 + rLength + 2 + sLength) - j] == 0)) {
|
||||
j--;
|
||||
}
|
||||
|
||||
int rawLen = Math.max(i, j);
|
||||
rawLen = Math.max(rawLen, outputLength / 2);
|
||||
|
||||
if ((derSignature[offset - 1] & 0xff) != derSignature.length - offset
|
||||
|| (derSignature[offset - 1] & 0xff) != 2 + rLength + 2 + sLength
|
||||
|| derSignature[offset] != 2
|
||||
|| derSignature[offset + 2 + rLength] != 2) {
|
||||
throw new JWTException("Invalid ECDSA signature format");
|
||||
}
|
||||
|
||||
final byte[] concatSignature = new byte[2 * rawLen];
|
||||
|
||||
System.arraycopy(derSignature, (offset + 2 + rLength) - i, concatSignature, rawLen - i, i);
|
||||
System.arraycopy(derSignature, (offset + 2 + rLength + 2 + sLength) - j, concatSignature, 2 * rawLen - j, j);
|
||||
|
||||
return concatSignature;
|
||||
}
|
||||
|
||||
/**
|
||||
* pair (R, S)转换为DER格式
|
||||
*
|
||||
* @param jwsSignature JWT签名
|
||||
* @return DER格式签名
|
||||
*/
|
||||
private static byte[] concatToDER(final byte[] jwsSignature) {
|
||||
|
||||
final int rawLen = jwsSignature.length / 2;
|
||||
|
||||
int i = rawLen;
|
||||
|
||||
while ((i > 0) && (jwsSignature[rawLen - i] == 0)) {
|
||||
i--;
|
||||
}
|
||||
|
||||
int j = i;
|
||||
|
||||
if (jwsSignature[rawLen - i] < 0) {
|
||||
j += 1;
|
||||
}
|
||||
|
||||
int k = rawLen;
|
||||
|
||||
while ((k > 0) && (jwsSignature[2 * rawLen - k] == 0)) {
|
||||
k--;
|
||||
}
|
||||
|
||||
int l = k;
|
||||
|
||||
if (jwsSignature[2 * rawLen - k] < 0) {
|
||||
l += 1;
|
||||
}
|
||||
|
||||
final int len = 2 + j + 2 + l;
|
||||
|
||||
if (len > 255) {
|
||||
throw new JWTException("Invalid ECDSA signature format");
|
||||
}
|
||||
|
||||
int offset;
|
||||
|
||||
final byte[] derSignature;
|
||||
|
||||
if (len < 128) {
|
||||
derSignature = new byte[2 + 2 + j + 2 + l];
|
||||
offset = 1;
|
||||
} else {
|
||||
derSignature = new byte[3 + 2 + j + 2 + l];
|
||||
derSignature[1] = (byte) 0x81;
|
||||
offset = 2;
|
||||
}
|
||||
|
||||
derSignature[0] = 48;
|
||||
derSignature[offset++] = (byte) len;
|
||||
derSignature[offset++] = 2;
|
||||
derSignature[offset++] = (byte) j;
|
||||
|
||||
System.arraycopy(jwsSignature, rawLen - i, derSignature, (offset + j) - i, i);
|
||||
|
||||
offset += j;
|
||||
|
||||
derSignature[offset++] = 2;
|
||||
derSignature[offset++] = (byte) l;
|
||||
|
||||
System.arraycopy(jwsSignature, 2 * rawLen - k, derSignature, (offset + l) - k, k);
|
||||
|
||||
return derSignature;
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
package org.dromara.hutool.json.jwt.signers;
|
||||
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.core.regex.ReUtil;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
@ -261,6 +262,12 @@ public class JWTSignerUtil {
|
||||
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
|
||||
return none();
|
||||
}
|
||||
|
||||
// issue3205@Github
|
||||
if(ReUtil.isMatch("es\\d{3}", algorithmId.toLowerCase())){
|
||||
return new EllipticCurveJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), keyPair);
|
||||
}
|
||||
|
||||
return new AsymmetricJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), keyPair);
|
||||
}
|
||||
|
||||
@ -278,6 +285,11 @@ public class JWTSignerUtil {
|
||||
return NoneJWTSigner.NONE;
|
||||
}
|
||||
if (key instanceof PrivateKey || key instanceof PublicKey) {
|
||||
// issue3205@Github
|
||||
if(ReUtil.isMatch("ES\\d{3}", algorithmId)){
|
||||
return new EllipticCurveJWTSigner(algorithmId, key);
|
||||
}
|
||||
|
||||
return new AsymmetricJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
|
||||
}
|
||||
return new HMacJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
|
||||
|
@ -0,0 +1,37 @@
|
||||
package org.dromara.hutool.json.jwt;
|
||||
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import org.dromara.hutool.core.date.DateUtil;
|
||||
import org.dromara.hutool.crypto.KeyUtil;
|
||||
import org.dromara.hutool.json.jwt.signers.AlgorithmUtil;
|
||||
import org.dromara.hutool.json.jwt.signers.JWTSigner;
|
||||
import org.dromara.hutool.json.jwt.signers.JWTSignerUtil;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.security.KeyPair;
|
||||
|
||||
/**
|
||||
*https://github.com/dromara/hutool/issues/3205
|
||||
*/
|
||||
public class Issue3205Test {
|
||||
@Test
|
||||
public void es256Test() {
|
||||
final String id = "es256";
|
||||
final KeyPair keyPair = KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id));
|
||||
final JWTSigner signer = JWTSignerUtil.createSigner(id, keyPair);
|
||||
|
||||
final JWT jwt = JWT.of()
|
||||
.setPayload("sub", "1234567890")
|
||||
.setPayload("name", "looly")
|
||||
.setPayload("admin", true)
|
||||
.setExpiresAt(DateUtil.tomorrow())
|
||||
.setSigner(signer);
|
||||
|
||||
final String token = jwt.sign();
|
||||
|
||||
final boolean signed = Jwts.parserBuilder().setSigningKey(keyPair.getPublic()).build().isSigned(token);
|
||||
|
||||
Assertions.assertTrue(signed);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user