mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
add jwt
This commit is contained in:
@@ -6,7 +6,9 @@
|
|||||||
# 5.7.0 (2021-06-10)
|
# 5.7.0 (2021-06-10)
|
||||||
|
|
||||||
### 🐣新特性
|
### 🐣新特性
|
||||||
|
* 【jwt 】 添加JWT模块,实现了JWT的创建、解析和验证
|
||||||
* 【crypto 】 SymmetricCrypto增加update方法(pr#1642@Github)
|
* 【crypto 】 SymmetricCrypto增加update方法(pr#1642@Github)
|
||||||
|
* 【crypto 】 MacEngine增加接口update,doFinal,reset等接口
|
||||||
|
|
||||||
### 🐞Bug修复
|
### 🐞Bug修复
|
||||||
|
|
||||||
|
@@ -54,6 +54,15 @@ public class StrUtilTest {
|
|||||||
Assert.assertEquals(2, strings.length);
|
Assert.assertEquals(2, strings.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void splitTest2() {
|
||||||
|
String str = "a.b.";
|
||||||
|
List<String> split = StrUtil.split(str, '.');
|
||||||
|
Assert.assertEquals(3, split.size());
|
||||||
|
Assert.assertEquals("b", split.get(1));
|
||||||
|
Assert.assertEquals("", split.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void splitToLongTest() {
|
public void splitToLongTest() {
|
||||||
String str = "1,2,3,4, 5";
|
String str = "1,2,3,4, 5";
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
@@ -27,12 +27,12 @@
|
|||||||
<artifactId>hutool-core</artifactId>
|
<artifactId>hutool-core</artifactId>
|
||||||
<version>${project.parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcprov-jdk15to18</artifactId>
|
<artifactId>bcprov-jdk15to18</artifactId>
|
||||||
<version>${bouncycastle.version}</version>
|
<version>${bouncycastle.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@@ -367,6 +367,7 @@ public class KeyUtil {
|
|||||||
// ECIES算法对KEY的长度有要求,此处默认256
|
// ECIES算法对KEY的长度有要求,此处默认256
|
||||||
keySize = 256;
|
keySize = 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
return generateKeyPair(algorithm, keySize);
|
return generateKeyPair(algorithm, keySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,6 +623,7 @@ public class KeyUtil {
|
|||||||
* @since 4.5.2
|
* @since 4.5.2
|
||||||
*/
|
*/
|
||||||
public static String getMainAlgorithm(String algorithm) {
|
public static String getMainAlgorithm(String algorithm) {
|
||||||
|
Assert.notBlank(algorithm, "Algorithm must be not blank!");
|
||||||
final int slashIndex = algorithm.indexOf(CharUtil.SLASH);
|
final int slashIndex = algorithm.indexOf(CharUtil.SLASH);
|
||||||
if (slashIndex > 0) {
|
if (slashIndex > 0) {
|
||||||
return algorithm.substring(0, slashIndex);
|
return algorithm.substring(0, slashIndex);
|
||||||
|
@@ -1072,6 +1072,26 @@ public class SecureUtil {
|
|||||||
return mac;
|
return mac;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建{@link Signature}
|
||||||
|
*
|
||||||
|
* @param algorithm 算法
|
||||||
|
* @return {@link Signature}
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public static Signature createSignature(String algorithm) {
|
||||||
|
final Provider provider = GlobalBouncyCastleProvider.INSTANCE.getProvider();
|
||||||
|
|
||||||
|
Signature signature;
|
||||||
|
try {
|
||||||
|
signature = (null == provider) ? Signature.getInstance(algorithm) : Signature.getInstance(algorithm, provider);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new CryptoException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RC4算法
|
* RC4算法
|
||||||
*
|
*
|
||||||
|
@@ -1,8 +1,19 @@
|
|||||||
package cn.hutool.crypto.asymmetric;
|
package cn.hutool.crypto.asymmetric;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
|
import cn.hutool.core.util.HexUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.CryptoException;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.Signature;
|
import java.security.Signature;
|
||||||
@@ -11,11 +22,6 @@ import java.security.cert.X509Certificate;
|
|||||||
import java.security.spec.AlgorithmParameterSpec;
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import cn.hutool.core.codec.Base64;
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
|
||||||
import cn.hutool.crypto.CryptoException;
|
|
||||||
import cn.hutool.crypto.SecureUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 签名包装,{@link Signature} 包装类
|
* 签名包装,{@link Signature} 包装类
|
||||||
*
|
*
|
||||||
@@ -158,11 +164,7 @@ public class Sign extends BaseAsymmetric<Sign> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Sign init(String algorithm, PrivateKey privateKey, PublicKey publicKey) {
|
public Sign init(String algorithm, PrivateKey privateKey, PublicKey publicKey) {
|
||||||
try {
|
signature = SecureUtil.createSignature(algorithm);
|
||||||
signature = Signature.getInstance(algorithm);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new CryptoException(e);
|
|
||||||
}
|
|
||||||
super.init(algorithm, privateKey, publicKey);
|
super.init(algorithm, privateKey, publicKey);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -184,6 +186,52 @@ public class Sign extends BaseAsymmetric<Sign> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------- Sign and Verify
|
// --------------------------------------------------------------------------------- Sign and Verify
|
||||||
|
/**
|
||||||
|
* 生成文件签名
|
||||||
|
*
|
||||||
|
* @param data 被签名数据
|
||||||
|
* @param charset 编码
|
||||||
|
* @return 签名
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public byte[] sign(String data, Charset charset) {
|
||||||
|
return sign(StrUtil.bytes(data, charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成文件签名
|
||||||
|
*
|
||||||
|
* @param data 被签名数据
|
||||||
|
* @return 签名
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public byte[] sign(String data) {
|
||||||
|
return sign(data, CharsetUtil.CHARSET_UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成文件签名,并转为16进制字符串
|
||||||
|
*
|
||||||
|
* @param data 被签名数据
|
||||||
|
* @param charset 编码
|
||||||
|
* @return 签名
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public String signHex(String data, Charset charset) {
|
||||||
|
return HexUtil.encodeHexStr(sign(data, charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成文件签名
|
||||||
|
*
|
||||||
|
* @param data 被签名数据
|
||||||
|
* @return 签名
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public String signHex(String data) {
|
||||||
|
return signHex(data, CharsetUtil.CHARSET_UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用私钥对信息生成数字签名
|
* 用私钥对信息生成数字签名
|
||||||
*
|
*
|
||||||
@@ -191,11 +239,85 @@ public class Sign extends BaseAsymmetric<Sign> {
|
|||||||
* @return 签名
|
* @return 签名
|
||||||
*/
|
*/
|
||||||
public byte[] sign(byte[] data) {
|
public byte[] sign(byte[] data) {
|
||||||
|
return sign(new ByteArrayInputStream(data), -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成签名,并转为16进制字符串<br>
|
||||||
|
*
|
||||||
|
* @param data 被签名数据
|
||||||
|
* @return 签名
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public String signHex(byte[] data) {
|
||||||
|
return HexUtil.encodeHexStr(sign(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成签名,并转为16进制字符串<br>
|
||||||
|
* 使用默认缓存大小,见 {@link IoUtil#DEFAULT_BUFFER_SIZE}
|
||||||
|
*
|
||||||
|
* @param data 被签名数据
|
||||||
|
* @return 签名
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public String signHex(InputStream data) {
|
||||||
|
return HexUtil.encodeHexStr(sign(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成签名,使用默认缓存大小,见 {@link IoUtil#DEFAULT_BUFFER_SIZE}
|
||||||
|
*
|
||||||
|
* @param data {@link InputStream} 数据流
|
||||||
|
* @return 签名bytes
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public byte[] sign(InputStream data) {
|
||||||
|
return sign(data, IoUtil.DEFAULT_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成签名,并转为16进制字符串<br>
|
||||||
|
* 使用默认缓存大小,见 {@link IoUtil#DEFAULT_BUFFER_SIZE}
|
||||||
|
*
|
||||||
|
* @param data 被签名数据
|
||||||
|
* @param bufferLength 缓存长度,不足1使用 {@link IoUtil#DEFAULT_BUFFER_SIZE} 做为默认值
|
||||||
|
* @return 签名
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public String digestHex(InputStream data, int bufferLength) {
|
||||||
|
return HexUtil.encodeHexStr(sign(data, bufferLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成签名
|
||||||
|
*
|
||||||
|
* @param data {@link InputStream} 数据流
|
||||||
|
* @param bufferLength 缓存长度,不足1使用 {@link IoUtil#DEFAULT_BUFFER_SIZE} 做为默认值
|
||||||
|
* @return 签名bytes
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public byte[] sign(InputStream data, int bufferLength){
|
||||||
|
if (bufferLength < 1) {
|
||||||
|
bufferLength = IoUtil.DEFAULT_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] buffer = new byte[bufferLength];
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
signature.initSign(this.privateKey);
|
signature.initSign(this.privateKey);
|
||||||
signature.update(data);
|
byte[] result;
|
||||||
return signature.sign();
|
try {
|
||||||
|
int read = data.read(buffer, 0, bufferLength);
|
||||||
|
while (read > -1) {
|
||||||
|
signature.update(buffer, 0, read);
|
||||||
|
read = data.read(buffer, 0, bufferLength);
|
||||||
|
}
|
||||||
|
result = signature.sign();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CryptoException(e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new CryptoException(e);
|
throw new CryptoException(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@@ -5,33 +5,37 @@ package cn.hutool.crypto.asymmetric;
|
|||||||
* see: https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature
|
* see: https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature
|
||||||
*
|
*
|
||||||
* @author Looly
|
* @author Looly
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public enum SignAlgorithm {
|
public enum SignAlgorithm {
|
||||||
// The RSA signature algorithm
|
// The RSA signature algorithm
|
||||||
NONEwithRSA("NONEwithRSA"), //
|
NONEwithRSA("NONEwithRSA"),
|
||||||
|
|
||||||
// The MD2/MD5 with RSA Encryption signature algorithm
|
// The MD2/MD5 with RSA Encryption signature algorithm
|
||||||
MD2withRSA("MD2withRSA"), //
|
MD2withRSA("MD2withRSA"),
|
||||||
MD5withRSA("MD5withRSA"), //
|
MD5withRSA("MD5withRSA"),
|
||||||
|
|
||||||
// The signature algorithm with SHA-* and the RSA
|
// The signature algorithm with SHA-* and the RSA
|
||||||
SHA1withRSA("SHA1withRSA"), //
|
SHA1withRSA("SHA1withRSA"),
|
||||||
SHA256withRSA("SHA256withRSA"), //
|
SHA256withRSA("SHA256withRSA"),
|
||||||
SHA384withRSA("SHA384withRSA"), //
|
SHA384withRSA("SHA384withRSA"),
|
||||||
SHA512withRSA("SHA512withRSA"), //
|
SHA512withRSA("SHA512withRSA"),
|
||||||
|
|
||||||
// The Digital Signature Algorithm
|
// The Digital Signature Algorithm
|
||||||
NONEwithDSA("NONEwithDSA"), //
|
NONEwithDSA("NONEwithDSA"),
|
||||||
// The DSA with SHA-1 signature algorithm
|
// The DSA with SHA-1 signature algorithm
|
||||||
SHA1withDSA("SHA1withDSA"), //
|
SHA1withDSA("SHA1withDSA"),
|
||||||
|
|
||||||
// The ECDSA signature algorithms
|
// The ECDSA signature algorithms
|
||||||
NONEwithECDSA("NONEwithECDSA"), //
|
NONEwithECDSA("NONEwithECDSA"),
|
||||||
SHA1withECDSA("SHA1withECDSA"), //
|
SHA1withECDSA("SHA1withECDSA"),
|
||||||
SHA256withECDSA("SHA256withECDSA"), //
|
SHA256withECDSA("SHA256withECDSA"),
|
||||||
SHA384withECDSA("SHA384withECDSA"), //
|
SHA384withECDSA("SHA384withECDSA"),
|
||||||
SHA512withECDSA("SHA512withECDSA");//
|
SHA512withECDSA("SHA512withECDSA"),
|
||||||
|
|
||||||
|
// 需要BC库加入支持
|
||||||
|
SHA256withRSA_PSS("SHA256WithRSA/PSS"),
|
||||||
|
SHA384withRSA_PSS("SHA384WithRSA/PSS"),
|
||||||
|
SHA512withRSA_PSS("SHA512WithRSA/PSS");
|
||||||
|
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|
||||||
|
@@ -14,6 +14,7 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
|
||||||
@@ -89,6 +90,15 @@ public class HMac implements Serializable {
|
|||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------------------- Constructor end
|
// ------------------------------------------------------------------------------------------- Constructor end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得MAC算法引擎
|
||||||
|
*
|
||||||
|
* @return MAC算法引擎
|
||||||
|
*/
|
||||||
|
public MacEngine getEngine(){
|
||||||
|
return this.engine;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------- Digest
|
// ------------------------------------------------------------------------------------------- Digest
|
||||||
/**
|
/**
|
||||||
* 生成文件摘要
|
* 生成文件摘要
|
||||||
@@ -97,7 +107,7 @@ public class HMac implements Serializable {
|
|||||||
* @param charset 编码
|
* @param charset 编码
|
||||||
* @return 摘要
|
* @return 摘要
|
||||||
*/
|
*/
|
||||||
public byte[] digest(String data, String charset) {
|
public byte[] digest(String data, Charset charset) {
|
||||||
return digest(StrUtil.bytes(data, charset));
|
return digest(StrUtil.bytes(data, charset));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +118,7 @@ public class HMac implements Serializable {
|
|||||||
* @return 摘要
|
* @return 摘要
|
||||||
*/
|
*/
|
||||||
public byte[] digest(String data) {
|
public byte[] digest(String data) {
|
||||||
return digest(data, CharsetUtil.UTF_8);
|
return digest(data, CharsetUtil.CHARSET_UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,7 +128,7 @@ public class HMac implements Serializable {
|
|||||||
* @param charset 编码
|
* @param charset 编码
|
||||||
* @return 摘要
|
* @return 摘要
|
||||||
*/
|
*/
|
||||||
public String digestHex(String data, String charset) {
|
public String digestHex(String data, Charset charset) {
|
||||||
return HexUtil.encodeHexStr(digest(data, charset));
|
return HexUtil.encodeHexStr(digest(data, charset));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +139,7 @@ public class HMac implements Serializable {
|
|||||||
* @return 摘要
|
* @return 摘要
|
||||||
*/
|
*/
|
||||||
public String digestHex(String data) {
|
public String digestHex(String data) {
|
||||||
return digestHex(data, CharsetUtil.UTF_8);
|
return digestHex(data, CharsetUtil.CHARSET_UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,16 +1,11 @@
|
|||||||
package cn.hutool.crypto.digest.mac;
|
package cn.hutool.crypto.digest.mac;
|
||||||
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
|
||||||
import cn.hutool.crypto.CryptoException;
|
|
||||||
import org.bouncycastle.crypto.CipherParameters;
|
import org.bouncycastle.crypto.CipherParameters;
|
||||||
import org.bouncycastle.crypto.Digest;
|
import org.bouncycastle.crypto.Digest;
|
||||||
import org.bouncycastle.crypto.Mac;
|
import org.bouncycastle.crypto.Mac;
|
||||||
import org.bouncycastle.crypto.macs.HMac;
|
import org.bouncycastle.crypto.macs.HMac;
|
||||||
import org.bouncycastle.crypto.params.KeyParameter;
|
import org.bouncycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BouncyCastle的HMAC算法实现引擎,使用{@link Mac} 实现摘要<br>
|
* BouncyCastle的HMAC算法实现引擎,使用{@link Mac} 实现摘要<br>
|
||||||
* 当引入BouncyCastle库时自动使用其作为Provider
|
* 当引入BouncyCastle库时自动使用其作为Provider
|
||||||
@@ -59,31 +54,6 @@ public class BCHMacEngine implements MacEngine {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] digest(InputStream data, int bufferLength) {
|
|
||||||
if (bufferLength < 1) {
|
|
||||||
bufferLength = IoUtil.DEFAULT_BUFFER_SIZE;
|
|
||||||
}
|
|
||||||
final byte[] buffer = new byte[bufferLength];
|
|
||||||
|
|
||||||
byte[] result;
|
|
||||||
try {
|
|
||||||
int read = data.read(buffer, 0, bufferLength);
|
|
||||||
|
|
||||||
while (read > -1) {
|
|
||||||
mac.update(buffer, 0, read);
|
|
||||||
read = data.read(buffer, 0, bufferLength);
|
|
||||||
}
|
|
||||||
result = new byte[this.mac.getMacSize()];
|
|
||||||
mac.doFinal(result, 0);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new CryptoException(e);
|
|
||||||
} finally {
|
|
||||||
mac.reset();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得 {@link Mac}
|
* 获得 {@link Mac}
|
||||||
*
|
*
|
||||||
@@ -93,6 +63,23 @@ public class BCHMacEngine implements MacEngine {
|
|||||||
return mac;
|
return mac;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte[] in, int inOff, int len) {
|
||||||
|
this.mac.update(in, inOff, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] doFinal() {
|
||||||
|
final byte[] result = new byte[getMacLength()];
|
||||||
|
this.mac.doFinal(result, 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
this.mac.reset();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMacLength() {
|
public int getMacLength() {
|
||||||
return mac.getMacSize();
|
return mac.getMacSize();
|
||||||
|
@@ -1,14 +1,11 @@
|
|||||||
package cn.hutool.crypto.digest.mac;
|
package cn.hutool.crypto.digest.mac;
|
||||||
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
|
||||||
import cn.hutool.crypto.CryptoException;
|
import cn.hutool.crypto.CryptoException;
|
||||||
import cn.hutool.crypto.SecureUtil;
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,30 +71,6 @@ public class DefaultHMacEngine implements MacEngine {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] digest(InputStream data, int bufferLength) {
|
|
||||||
if (bufferLength < 1) {
|
|
||||||
bufferLength = IoUtil.DEFAULT_BUFFER_SIZE;
|
|
||||||
}
|
|
||||||
byte[] buffer = new byte[bufferLength];
|
|
||||||
|
|
||||||
byte[] result;
|
|
||||||
try {
|
|
||||||
int read = data.read(buffer, 0, bufferLength);
|
|
||||||
|
|
||||||
while (read > -1) {
|
|
||||||
mac.update(buffer, 0, read);
|
|
||||||
read = data.read(buffer, 0, bufferLength);
|
|
||||||
}
|
|
||||||
result = mac.doFinal();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new CryptoException(e);
|
|
||||||
} finally {
|
|
||||||
mac.reset();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得 {@link Mac}
|
* 获得 {@link Mac}
|
||||||
*
|
*
|
||||||
@@ -107,6 +80,26 @@ public class DefaultHMacEngine implements MacEngine {
|
|||||||
return mac;
|
return mac;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte[] in) {
|
||||||
|
this.mac.update(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(byte[] in, int inOff, int len) {
|
||||||
|
this.mac.update(in, inOff, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] doFinal() {
|
||||||
|
return this.mac.doFinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
this.mac.reset();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getMacLength() {
|
public int getMacLength() {
|
||||||
return mac.getMacLength();
|
return mac.getMacLength();
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
package cn.hutool.crypto.digest.mac;
|
package cn.hutool.crypto.digest.mac;
|
||||||
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
|
import cn.hutool.crypto.CryptoException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,6 +14,38 @@ import java.io.InputStream;
|
|||||||
*/
|
*/
|
||||||
public interface MacEngine {
|
public interface MacEngine {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加入需要被摘要的内容
|
||||||
|
* @param in 内容
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
default void update(byte[] in){
|
||||||
|
update(in, 0, in.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加入需要被摘要的内容
|
||||||
|
* @param in 内容
|
||||||
|
* @param inOff 内容起始位置
|
||||||
|
* @param len 内容长度
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
void update(byte[] in, int inOff, int len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束并生成摘要
|
||||||
|
*
|
||||||
|
* @return 摘要内容
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
byte[] doFinal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
void reset();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成摘要
|
* 生成摘要
|
||||||
*
|
*
|
||||||
@@ -19,7 +53,29 @@ public interface MacEngine {
|
|||||||
* @param bufferLength 缓存长度,不足1使用 {@link IoUtil#DEFAULT_BUFFER_SIZE} 做为默认值
|
* @param bufferLength 缓存长度,不足1使用 {@link IoUtil#DEFAULT_BUFFER_SIZE} 做为默认值
|
||||||
* @return 摘要bytes
|
* @return 摘要bytes
|
||||||
*/
|
*/
|
||||||
byte[] digest(InputStream data, int bufferLength);
|
default byte[] digest(InputStream data, int bufferLength){
|
||||||
|
if (bufferLength < 1) {
|
||||||
|
bufferLength = IoUtil.DEFAULT_BUFFER_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] buffer = new byte[bufferLength];
|
||||||
|
|
||||||
|
byte[] result;
|
||||||
|
try {
|
||||||
|
int read = data.read(buffer, 0, bufferLength);
|
||||||
|
|
||||||
|
while (read > -1) {
|
||||||
|
update(buffer, 0, read);
|
||||||
|
read = data.read(buffer, 0, bufferLength);
|
||||||
|
}
|
||||||
|
result = doFinal();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CryptoException(e);
|
||||||
|
} finally {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取MAC算法块大小
|
* 获取MAC算法块大小
|
||||||
|
@@ -79,7 +79,7 @@ public class SignTest {
|
|||||||
* 测试MD5withRSA算法的签名和验证签名
|
* 测试MD5withRSA算法的签名和验证签名
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void signAndVerify2() {
|
public void signAndVerifyTest2() {
|
||||||
String str = "wx2421b1c4370ec43b 支付测试 JSAPI支付测试 10000100 1add1a30ac87aa2db72f57a2375d8fec http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php oUpF8uMuAJO_M2pxb1Q9zNjWeS6o 1415659990 14.23.150.211 1 JSAPI 0CB01533B8C1EF103065174F50BCA001";
|
String str = "wx2421b1c4370ec43b 支付测试 JSAPI支付测试 10000100 1add1a30ac87aa2db72f57a2375d8fec http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php oUpF8uMuAJO_M2pxb1Q9zNjWeS6o 1415659990 14.23.150.211 1 JSAPI 0CB01533B8C1EF103065174F50BCA001";
|
||||||
byte[] data = StrUtil.utf8Bytes(str);
|
byte[] data = StrUtil.utf8Bytes(str);
|
||||||
Sign sign = SecureUtil.sign(SignAlgorithm.MD5withRSA);
|
Sign sign = SecureUtil.sign(SignAlgorithm.MD5withRSA);
|
||||||
@@ -105,4 +105,21 @@ public class SignTest {
|
|||||||
String sign3 = SecureUtil.signParamsSha1(build, "12345678", "abc");
|
String sign3 = SecureUtil.signParamsSha1(build, "12345678", "abc");
|
||||||
Assert.assertEquals("edee1b477af1b96ebd20fdf08d818f352928d25d", sign3);
|
Assert.assertEquals("edee1b477af1b96ebd20fdf08d818f352928d25d", sign3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试MD5withRSA算法的签名和验证签名
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void signAndVerifyPSSTest() {
|
||||||
|
String str = "wx2421b1c4370ec43b 支付测试 JSAPI支付测试 10000100 1add1a30ac87aa2db72f57a2375d8fec http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php oUpF8uMuAJO_M2pxb1Q9zNjWeS6o 1415659990 14.23.150.211 1 JSAPI 0CB01533B8C1EF103065174F50BCA001";
|
||||||
|
byte[] data = StrUtil.utf8Bytes(str);
|
||||||
|
Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA_PSS);
|
||||||
|
|
||||||
|
// 签名
|
||||||
|
byte[] signed = sign.sign(data);
|
||||||
|
|
||||||
|
// 验证签名
|
||||||
|
boolean verify = sign.verify(data, signed);
|
||||||
|
Assert.assertTrue(verify);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,11 @@
|
|||||||
<name>${project.artifactId}</name>
|
<name>${project.artifactId}</name>
|
||||||
<description>Hutool JWT生成、解析和验证实现</description>
|
<description>Hutool JWT生成、解析和验证实现</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<!-- versions -->
|
||||||
|
<bouncycastle.version>1.68</bouncycastle.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
@@ -25,5 +30,11 @@
|
|||||||
<artifactId>hutool-crypto</artifactId>
|
<artifactId>hutool-crypto</artifactId>
|
||||||
<version>${project.parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk15to18</artifactId>
|
||||||
|
<version>${bouncycastle.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@@ -1,19 +1,25 @@
|
|||||||
package cn.hutool.jwt;
|
package cn.hutool.jwt;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Claims 认证,简单的JSONObject包装
|
* Claims 认证,简单的JSONObject包装
|
||||||
*
|
*
|
||||||
* @author looly
|
* @author looly
|
||||||
|
* @since 5.7.0
|
||||||
*/
|
*/
|
||||||
public class Claims implements Serializable {
|
public class Claims implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final JSONObject claimJSON;
|
private JSONObject claimJSON;
|
||||||
|
|
||||||
public Claims() {
|
public Claims() {
|
||||||
this.claimJSON = new JSONObject();
|
this.claimJSON = new JSONObject();
|
||||||
@@ -34,6 +40,28 @@ public class Claims implements Serializable {
|
|||||||
claimJSON.set(name, value);
|
claimJSON.set(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加入多个Claims属性
|
||||||
|
* @param headerClaims 多个Claims属性
|
||||||
|
*/
|
||||||
|
protected void putAll(Map<String, ?> headerClaims){
|
||||||
|
if (MapUtil.isNotEmpty(headerClaims)) {
|
||||||
|
for (Map.Entry<String, ?> entry : headerClaims.entrySet()) {
|
||||||
|
setClaim(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定名称属性
|
||||||
|
*
|
||||||
|
* @param name 名称
|
||||||
|
* @return 属性
|
||||||
|
*/
|
||||||
|
public Object getClaim(String name) {
|
||||||
|
return this.claimJSON.getObj(name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取Claims的JSON字符串形式
|
* 获取Claims的JSON字符串形式
|
||||||
*
|
*
|
||||||
@@ -42,4 +70,14 @@ public class Claims implements Serializable {
|
|||||||
public String getClaimsJson() {
|
public String getClaimsJson() {
|
||||||
return this.claimJSON.toString();
|
return this.claimJSON.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析JWT JSON
|
||||||
|
*
|
||||||
|
* @param tokenPart JWT JSON
|
||||||
|
* @param charset 编码
|
||||||
|
*/
|
||||||
|
public void parse(String tokenPart, Charset charset) {
|
||||||
|
this.claimJSON = JSONUtil.parseObj(Base64.decodeStr(tokenPart, charset));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,18 @@
|
|||||||
package cn.hutool.jwt;
|
package cn.hutool.jwt;
|
||||||
|
|
||||||
import cn.hutool.core.codec.Base64;
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.util.CharUtil;
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.jwt.signers.AlgorithmUtil;
|
||||||
import cn.hutool.jwt.signers.JWTSigner;
|
import cn.hutool.jwt.signers.JWTSigner;
|
||||||
|
import cn.hutool.jwt.signers.JWTSignerUtil;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JSON Web Token (JWT),基于JSON的开放标准((RFC 7519)用于在网络应用环境间传递声明。<br>
|
* JSON Web Token (JWT),基于JSON的开放标准((RFC 7519)用于在网络应用环境间传递声明。<br>
|
||||||
@@ -31,6 +38,26 @@ public class JWT {
|
|||||||
private Charset charset;
|
private Charset charset;
|
||||||
private JWTSigner signer;
|
private JWTSigner signer;
|
||||||
|
|
||||||
|
private List<String> tokens;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建空的JWT对象
|
||||||
|
*
|
||||||
|
* @return {@link JWT}
|
||||||
|
*/
|
||||||
|
public static JWT create() {
|
||||||
|
return new JWT();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并解析JWT对象
|
||||||
|
*
|
||||||
|
* @return {@link JWT}
|
||||||
|
*/
|
||||||
|
public static JWT of(String token) {
|
||||||
|
return new JWT(token);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
*/
|
*/
|
||||||
@@ -40,6 +67,28 @@ public class JWT {
|
|||||||
this.charset = CharsetUtil.CHARSET_UTF_8;
|
this.charset = CharsetUtil.CHARSET_UTF_8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*/
|
||||||
|
public JWT(String token) {
|
||||||
|
this();
|
||||||
|
parse(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析JWT内容
|
||||||
|
*
|
||||||
|
* @param token JWT token
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public JWT parse(String token) {
|
||||||
|
final List<String> tokens = splitToken(token);
|
||||||
|
this.tokens = tokens;
|
||||||
|
this.header.parse(tokens.get(0), this.charset);
|
||||||
|
this.payload.parse(tokens.get(1), this.charset);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置编码
|
* 设置编码
|
||||||
*
|
*
|
||||||
@@ -51,6 +100,27 @@ public class JWT {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置密钥,默认算法是:HS256(HmacSHA256)
|
||||||
|
*
|
||||||
|
* @param key 密钥
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public JWT setKey(byte[] key) {
|
||||||
|
return setSigner(JWTSignerUtil.hs256(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置签名算法
|
||||||
|
*
|
||||||
|
* @param algorithmId 签名算法ID,如HS256
|
||||||
|
* @param key 密钥
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public JWT setSigner(String algorithmId, byte[] key) {
|
||||||
|
return setSigner(JWTSignerUtil.createSigner(algorithmId, key));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置签名算法
|
* 设置签名算法
|
||||||
*
|
*
|
||||||
@@ -62,16 +132,140 @@ public class JWT {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取头信息
|
||||||
|
*
|
||||||
|
* @param name 头信息名称
|
||||||
|
* @return 头信息
|
||||||
|
*/
|
||||||
|
public Object getHeader(String name) {
|
||||||
|
return this.header.getClaim(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置JWT头信息
|
||||||
|
*
|
||||||
|
* @param name 头名
|
||||||
|
* @param value 头
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public JWT setHeader(String name, Object value) {
|
||||||
|
this.header.setClaim(name, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加JWT头信息
|
||||||
|
*
|
||||||
|
* @param headers 头信息
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public JWT addHeaders(Map<String, ?> headers) {
|
||||||
|
this.header.addHeaders(headers);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取载荷信息
|
||||||
|
*
|
||||||
|
* @param name 载荷信息名称
|
||||||
|
* @return 载荷信息
|
||||||
|
*/
|
||||||
|
public Object getPayload(String name) {
|
||||||
|
return this.payload.getClaim(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置JWT载荷信息
|
||||||
|
*
|
||||||
|
* @param name 载荷名
|
||||||
|
* @param value 头
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public JWT setPayload(String name, Object value) {
|
||||||
|
this.payload.setClaim(name, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加JWT载荷信息
|
||||||
|
*
|
||||||
|
* @param payloads 载荷信息
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public JWT addPayloads(Map<String, ?> payloads) {
|
||||||
|
this.payload.addPayloads(payloads);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 签名生成JWT字符串
|
* 签名生成JWT字符串
|
||||||
*
|
*
|
||||||
* @return JWT字符串
|
* @return JWT字符串
|
||||||
*/
|
*/
|
||||||
public String sign() {
|
public String sign() {
|
||||||
|
return sign(this.signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 签名生成JWT字符串
|
||||||
|
*
|
||||||
|
* @param signer JWT签名器
|
||||||
|
* @return JWT字符串
|
||||||
|
*/
|
||||||
|
public String sign(JWTSigner signer) {
|
||||||
|
Assert.notNull(signer, () -> new JWTException("No Signer provided!"));
|
||||||
|
|
||||||
|
// 检查头信息中是否有算法信息
|
||||||
|
final String claim = (String) this.header.getClaim(JWTHeader.ALGORITHM);
|
||||||
|
if (StrUtil.isBlank(claim)) {
|
||||||
|
this.header.setClaim(JWTHeader.ALGORITHM,
|
||||||
|
AlgorithmUtil.getId(signer.getAlgorithm()));
|
||||||
|
}
|
||||||
|
|
||||||
final String headerBase64 = Base64.encodeUrlSafe(this.header.getClaimsJson(), charset);
|
final String headerBase64 = Base64.encodeUrlSafe(this.header.getClaimsJson(), charset);
|
||||||
final String payloadBase64 = Base64.encodeUrlSafe(this.payload.getClaimsJson(), charset);
|
final String payloadBase64 = Base64.encodeUrlSafe(this.payload.getClaimsJson(), charset);
|
||||||
final String sign = signer.sign(headerBase64, payloadBase64);
|
final String sign = signer.sign(headerBase64, payloadBase64);
|
||||||
|
|
||||||
return StrUtil.format("{}.{}.{}", headerBase64, payloadBase64, sign);
|
return StrUtil.format("{}.{}.{}", headerBase64, payloadBase64, sign);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证JWT Token是否有效
|
||||||
|
*
|
||||||
|
* @return 是否有效
|
||||||
|
*/
|
||||||
|
public boolean verify() {
|
||||||
|
return verify(this.signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证JWT Token是否有效
|
||||||
|
*
|
||||||
|
* @param signer 签名器(签名算法)
|
||||||
|
* @return 是否有效
|
||||||
|
*/
|
||||||
|
public boolean verify(JWTSigner signer) {
|
||||||
|
Assert.notNull(signer, () -> new JWTException("No Signer provided!"));
|
||||||
|
|
||||||
|
final List<String> tokens = this.tokens;
|
||||||
|
if(CollUtil.isEmpty(tokens)){
|
||||||
|
throw new JWTException("No token to verify!");
|
||||||
|
}
|
||||||
|
return signer.verify(tokens.get(0), tokens.get(1), tokens.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将JWT字符串拆分为3部分,无加密算法则最后一部分是""
|
||||||
|
*
|
||||||
|
* @param token JWT Token
|
||||||
|
* @return 三部分内容
|
||||||
|
*/
|
||||||
|
private static List<String> splitToken(String token) {
|
||||||
|
final List<String> tokens = StrUtil.split(token, CharUtil.DOT);
|
||||||
|
if (3 != tokens.size()) {
|
||||||
|
throw new JWTException("The token was expected 3 parts, but got {}.", tokens.size());
|
||||||
|
}
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
34
hutool-jwt/src/main/java/cn/hutool/jwt/JWTException.java
Normal file
34
hutool-jwt/src/main/java/cn/hutool/jwt/JWTException.java
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package cn.hutool.jwt;
|
||||||
|
|
||||||
|
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT异常
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public class JWTException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public JWTException(Throwable e) {
|
||||||
|
super(ExceptionUtil.getMessage(e), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JWTException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JWTException(String messageTemplate, Object... params) {
|
||||||
|
super(StrUtil.format(messageTemplate, params));
|
||||||
|
}
|
||||||
|
|
||||||
|
public JWTException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JWTException(Throwable throwable, String messageTemplate, Object... params) {
|
||||||
|
super(StrUtil.format(messageTemplate, params), throwable);
|
||||||
|
}
|
||||||
|
}
|
@@ -6,6 +6,7 @@ import java.util.Map;
|
|||||||
* JWT头部信息
|
* JWT头部信息
|
||||||
*
|
*
|
||||||
* @author looly
|
* @author looly
|
||||||
|
* @since 5.7.0
|
||||||
*/
|
*/
|
||||||
public class JWTHeader extends Claims {
|
public class JWTHeader extends Claims {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
@@ -28,6 +29,13 @@ public class JWTHeader extends Claims {
|
|||||||
*/
|
*/
|
||||||
public static String KEY_ID = "kid";
|
public static String KEY_ID = "kid";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造,初始化默认(typ=JWT)
|
||||||
|
*/
|
||||||
|
public JWTHeader() {
|
||||||
|
setClaim(TYPE, "JWT");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 增加“kid”头信息
|
* 增加“kid”头信息
|
||||||
*
|
*
|
||||||
@@ -46,14 +54,7 @@ public class JWTHeader extends Claims {
|
|||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public JWTHeader addHeaders(Map<String, ?> headerClaims) {
|
public JWTHeader addHeaders(Map<String, ?> headerClaims) {
|
||||||
if (headerClaims == null) {
|
putAll(headerClaims);
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<String, ?> entry : headerClaims.entrySet()) {
|
|
||||||
setClaim(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ import java.util.Map;
|
|||||||
* 详细介绍见:https://www.jianshu.com/p/576dbf44b2ae
|
* 详细介绍见:https://www.jianshu.com/p/576dbf44b2ae
|
||||||
*
|
*
|
||||||
* @author looly
|
* @author looly
|
||||||
|
* @since 5.7.0
|
||||||
*/
|
*/
|
||||||
public class JWTPayload extends Claims {
|
public class JWTPayload extends Claims {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
@@ -134,15 +135,8 @@ public class JWTPayload extends Claims {
|
|||||||
* @param payloadClaims 载荷信息
|
* @param payloadClaims 载荷信息
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public JWTPayload addPayload(Map<String, ?> payloadClaims) {
|
public JWTPayload addPayloads(Map<String, ?> payloadClaims) {
|
||||||
if (payloadClaims == null) {
|
putAll(payloadClaims);
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<String, ?> entry : payloadClaims.entrySet()) {
|
|
||||||
setClaim(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
97
hutool-jwt/src/main/java/cn/hutool/jwt/JWTUtil.java
Normal file
97
hutool-jwt/src/main/java/cn/hutool/jwt/JWTUtil.java
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package cn.hutool.jwt;
|
||||||
|
|
||||||
|
import cn.hutool.jwt.signers.JWTSigner;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON Web Token (JWT)工具类
|
||||||
|
*/
|
||||||
|
public class JWTUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建HS256(HmacSHA256) JWT Token
|
||||||
|
*
|
||||||
|
* @param payload 荷载信息
|
||||||
|
* @param key HS256(HmacSHA256)密钥
|
||||||
|
* @return JWT Token
|
||||||
|
*/
|
||||||
|
public static String createToken(Map<String, Object> payload, byte[] key) {
|
||||||
|
return createToken(null, payload, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建HS256(HmacSHA256) JWT Token
|
||||||
|
*
|
||||||
|
* @param headers 头信息
|
||||||
|
* @param payload 荷载信息
|
||||||
|
* @param key HS256(HmacSHA256)密钥
|
||||||
|
* @return JWT Token
|
||||||
|
*/
|
||||||
|
public static String createToken(Map<String, Object> headers, Map<String, Object> payload, byte[] key) {
|
||||||
|
return JWT.create()
|
||||||
|
.addHeaders(headers)
|
||||||
|
.addPayloads(payload)
|
||||||
|
.setKey(key)
|
||||||
|
.sign();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建JWT Token
|
||||||
|
*
|
||||||
|
* @param payload 荷载信息
|
||||||
|
* @param signer 签名算法
|
||||||
|
* @return JWT Token
|
||||||
|
*/
|
||||||
|
public static String createToken(Map<String, Object> payload, JWTSigner signer) {
|
||||||
|
return createToken(null, payload, signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建JWT Token
|
||||||
|
*
|
||||||
|
* @param headers 头信息
|
||||||
|
* @param payload 荷载信息
|
||||||
|
* @param signer 签名算法
|
||||||
|
* @return JWT Token
|
||||||
|
*/
|
||||||
|
public static String createToken(Map<String, Object> headers, Map<String, Object> payload, JWTSigner signer) {
|
||||||
|
return JWT.create()
|
||||||
|
.addHeaders(headers)
|
||||||
|
.addPayloads(payload)
|
||||||
|
.setSigner(signer)
|
||||||
|
.sign();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析JWT Token
|
||||||
|
*
|
||||||
|
* @param token token
|
||||||
|
* @return {@link JWT}
|
||||||
|
*/
|
||||||
|
public JWT parseToken(String token) {
|
||||||
|
return JWT.of(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证JWT Token有效性
|
||||||
|
*
|
||||||
|
* @param token JWT Token
|
||||||
|
* @param key HS256(HmacSHA256)密钥
|
||||||
|
* @return 是否有效
|
||||||
|
*/
|
||||||
|
public boolean verify(String token, byte[] key) {
|
||||||
|
return JWT.of(token).setKey(key).verify();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证JWT Token有效性
|
||||||
|
*
|
||||||
|
* @param token JWT Token
|
||||||
|
* @param signer 签名器
|
||||||
|
* @return 是否有效
|
||||||
|
*/
|
||||||
|
public boolean verify(String token, JWTSigner signer) {
|
||||||
|
return JWT.of(token).verify(signer);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,76 @@
|
|||||||
|
package cn.hutool.jwt.signers;
|
||||||
|
|
||||||
|
import cn.hutool.core.map.BiMap;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||||
|
import cn.hutool.crypto.digest.HmacAlgorithm;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 算法工具类,算法和JWT算法ID对应表
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public class AlgorithmUtil {
|
||||||
|
|
||||||
|
private static final BiMap<String, String> map;
|
||||||
|
|
||||||
|
static {
|
||||||
|
map = new BiMap<>(new HashMap<>());
|
||||||
|
map.put("HS256", HmacAlgorithm.HmacSHA256.getValue());
|
||||||
|
map.put("HS384", HmacAlgorithm.HmacSHA384.getValue());
|
||||||
|
map.put("HS512", HmacAlgorithm.HmacSHA512.getValue());
|
||||||
|
|
||||||
|
map.put("RS256", SignAlgorithm.SHA256withRSA.getValue());
|
||||||
|
map.put("RS384", SignAlgorithm.SHA384withRSA.getValue());
|
||||||
|
map.put("RS512", SignAlgorithm.SHA512withRSA.getValue());
|
||||||
|
|
||||||
|
map.put("ES256", SignAlgorithm.SHA256withECDSA.getValue());
|
||||||
|
map.put("ES384", SignAlgorithm.SHA384withECDSA.getValue());
|
||||||
|
map.put("ES512", SignAlgorithm.SHA512withECDSA.getValue());
|
||||||
|
|
||||||
|
map.put("PS256", SignAlgorithm.SHA256withRSA_PSS.getValue());
|
||||||
|
map.put("PS384", SignAlgorithm.SHA384withRSA_PSS.getValue());
|
||||||
|
map.put("PS512", SignAlgorithm.SHA512withRSA_PSS.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取算法,用户传入算法ID返回算法名,传入算法名返回本身
|
||||||
|
* @param idOrAlgorithm 算法ID或算法名
|
||||||
|
* @return 算法名
|
||||||
|
*/
|
||||||
|
public static String getAlgorithm(String idOrAlgorithm){
|
||||||
|
return ObjectUtil.defaultIfNull(getAlgorithmById(idOrAlgorithm), idOrAlgorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取算法ID,用户传入算法名返回ID,传入算法ID返回本身
|
||||||
|
* @param idOrAlgorithm 算法ID或算法名
|
||||||
|
* @return 算法ID
|
||||||
|
*/
|
||||||
|
public static String getId(String idOrAlgorithm){
|
||||||
|
return ObjectUtil.defaultIfNull(getIdByAlgorithm(idOrAlgorithm), idOrAlgorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据JWT算法ID获取算法
|
||||||
|
*
|
||||||
|
* @param id JWT算法ID
|
||||||
|
* @return 算法
|
||||||
|
*/
|
||||||
|
private static String getAlgorithmById(String id) {
|
||||||
|
return map.get(id.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据算法获取JWT算法ID
|
||||||
|
*
|
||||||
|
* @param algorithm 算法
|
||||||
|
* @return JWT算法ID
|
||||||
|
*/
|
||||||
|
private static String getIdByAlgorithm(String algorithm) {
|
||||||
|
return map.getInverse().get(algorithm);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,75 @@
|
|||||||
|
package cn.hutool.jwt.signers;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.asymmetric.Sign;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非对称加密JWT签名封装
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public class AsymmetricJWTSigner implements JWTSigner {
|
||||||
|
|
||||||
|
private Charset charset = CharsetUtil.CHARSET_UTF_8;
|
||||||
|
private final Sign sign;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param algorithm 算法字符串表示
|
||||||
|
* @param key 公钥{@link PublicKey}或私钥{@link PrivateKey},公钥用于验证签名,私钥用于产生签名
|
||||||
|
*/
|
||||||
|
public AsymmetricJWTSigner(String algorithm, Key key) {
|
||||||
|
final PublicKey publicKey = key instanceof PublicKey ? (PublicKey) key : null;
|
||||||
|
final PrivateKey privateKey = key instanceof PrivateKey ? (PrivateKey) key : null;
|
||||||
|
this.sign = new Sign(algorithm, privateKey, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param algorithm 算法字符串表示
|
||||||
|
* @param keyPair 密钥对
|
||||||
|
*/
|
||||||
|
public AsymmetricJWTSigner(String algorithm, KeyPair keyPair) {
|
||||||
|
this.sign = new Sign(algorithm, keyPair);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置编码
|
||||||
|
*
|
||||||
|
* @param charset 编码
|
||||||
|
* @return 编码
|
||||||
|
*/
|
||||||
|
public AsymmetricJWTSigner setCharset(Charset charset) {
|
||||||
|
this.charset = charset;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sign(String headerBase64, String payloadBase64) {
|
||||||
|
return Base64.encodeUrlSafe(sign.sign(StrUtil.format("{}.{}", headerBase64, payloadBase64)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(String headerBase64, String payloadBase64, String signBase64) {
|
||||||
|
return sign.verify(
|
||||||
|
StrUtil.bytes(StrUtil.format("{}.{}", headerBase64, payloadBase64), charset),
|
||||||
|
Base64.decode(signBase64));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAlgorithm() {
|
||||||
|
return this.sign.getSignature().getAlgorithm();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,69 @@
|
|||||||
|
package cn.hutool.jwt.signers;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.digest.HMac;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.Key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HMac算法签名实现
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public class HMacJWTSigner implements JWTSigner {
|
||||||
|
|
||||||
|
private Charset charset = CharsetUtil.CHARSET_UTF_8;
|
||||||
|
private final HMac hMac;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param algorithm HMAC签名算法
|
||||||
|
* @param key 密钥
|
||||||
|
*/
|
||||||
|
public HMacJWTSigner(String algorithm, byte[] key) {
|
||||||
|
this.hMac = new HMac(algorithm, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param algorithm HMAC签名算法
|
||||||
|
* @param key 密钥
|
||||||
|
*/
|
||||||
|
public HMacJWTSigner(String algorithm, Key key) {
|
||||||
|
this.hMac = new HMac(algorithm, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置编码
|
||||||
|
*
|
||||||
|
* @param charset 编码
|
||||||
|
* @return 编码
|
||||||
|
*/
|
||||||
|
public HMacJWTSigner setCharset(Charset charset) {
|
||||||
|
this.charset = charset;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sign(String headerBase64, String payloadBase64) {
|
||||||
|
return hMac.digestHex(StrUtil.format("{}.{}", headerBase64, payloadBase64), charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(String headerBase64, String payloadBase64, String signBase64) {
|
||||||
|
final String sign = sign(headerBase64, payloadBase64);
|
||||||
|
return hMac.verify(
|
||||||
|
StrUtil.bytes(sign, charset),
|
||||||
|
StrUtil.bytes(signBase64, charset));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAlgorithm() {
|
||||||
|
return this.hMac.getAlgorithm();
|
||||||
|
}
|
||||||
|
}
|
@@ -9,9 +9,27 @@ public interface JWTSigner {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 签名
|
* 签名
|
||||||
* @param header JWT头的JSON字符串
|
*
|
||||||
* @param payload JWT载荷的JSON字符串
|
* @param headerBase64 JWT头的JSON字符串的Base64表示
|
||||||
|
* @param payloadBase64 JWT载荷的JSON字符串Base64表示
|
||||||
* @return 签名结果,即JWT的第三部分
|
* @return 签名结果,即JWT的第三部分
|
||||||
*/
|
*/
|
||||||
String sign(String header, String payload);
|
String sign(String headerBase64, String payloadBase64);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验签
|
||||||
|
*
|
||||||
|
* @param headerBase64 JWT头的JSON字符串Base64表示
|
||||||
|
* @param payloadBase64 JWT载荷的JSON字符串Base64表示
|
||||||
|
* @param signBase64 被验证的签名Base64表示
|
||||||
|
* @return 签名是否一致
|
||||||
|
*/
|
||||||
|
boolean verify(String headerBase64, String payloadBase64, String signBase64);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取算法
|
||||||
|
*
|
||||||
|
* @return 算法
|
||||||
|
*/
|
||||||
|
String getAlgorithm();
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,164 @@
|
|||||||
|
package cn.hutool.jwt.signers;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT签名器工具类
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public class JWTSignerUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无签名
|
||||||
|
*
|
||||||
|
* @return 无签名的签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner none() {
|
||||||
|
return NoneJWTSigner.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------- HSxxx
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HS256(HmacSHA256)签名器
|
||||||
|
*
|
||||||
|
* @return 签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner hs256(byte[] key) {
|
||||||
|
return createSigner("HS256", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HS384(HmacSHA384)签名器
|
||||||
|
*
|
||||||
|
* @return 签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner hs384(byte[] key) {
|
||||||
|
return createSigner("HS384", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HS512(HmacSHA512)签名器
|
||||||
|
*
|
||||||
|
* @return 签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner hs512(byte[] key) {
|
||||||
|
return createSigner("HS512", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------- RSxxx
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RS256(SHA256withRSA)签名器
|
||||||
|
*
|
||||||
|
* @return 签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner rs256(Key key) {
|
||||||
|
return createSigner("RS256", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RS384(SHA384withRSA)签名器
|
||||||
|
*
|
||||||
|
* @return 签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner rs384(Key key) {
|
||||||
|
return createSigner("RS384", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RS512(SHA512withRSA)签名器
|
||||||
|
*
|
||||||
|
* @return 签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner rs512(Key key) {
|
||||||
|
return createSigner("RS512", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------- ESxxx
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ES256(SHA256withECDSA)签名器
|
||||||
|
*
|
||||||
|
* @return 签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner es256(Key key) {
|
||||||
|
return createSigner("ES256", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ES384(SHA383withECDSA)签名器
|
||||||
|
*
|
||||||
|
* @return 签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner es384(Key key) {
|
||||||
|
return createSigner("ES384", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ES512(SHA512withECDSA)签名器
|
||||||
|
*
|
||||||
|
* @return 签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner es512(Key key) {
|
||||||
|
return createSigner("ES512", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建签名器
|
||||||
|
*
|
||||||
|
* @param algorithmId 算法ID,见{@link AlgorithmUtil}
|
||||||
|
* @param key 密钥
|
||||||
|
* @return 签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner createSigner(String algorithmId, byte[] key) {
|
||||||
|
Assert.notNull(key, "Signer key must be not null!");
|
||||||
|
|
||||||
|
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
|
||||||
|
return none();
|
||||||
|
}
|
||||||
|
return new HMacJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建签名器
|
||||||
|
*
|
||||||
|
* @param algorithmId 算法ID,见{@link AlgorithmUtil}
|
||||||
|
* @param keyPair 密钥对
|
||||||
|
* @return 签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner createSigner(String algorithmId, KeyPair keyPair) {
|
||||||
|
Assert.notNull(keyPair, "Signer key pair must be not null!");
|
||||||
|
|
||||||
|
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
|
||||||
|
return none();
|
||||||
|
}
|
||||||
|
return new AsymmetricJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), keyPair);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建签名器
|
||||||
|
*
|
||||||
|
* @param algorithmId 算法ID,见{@link AlgorithmUtil}
|
||||||
|
* @param key 密钥
|
||||||
|
* @return 签名器
|
||||||
|
*/
|
||||||
|
public static JWTSigner createSigner(String algorithmId, Key key) {
|
||||||
|
Assert.notNull(key, "Signer key must be not null!");
|
||||||
|
|
||||||
|
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
|
||||||
|
return NoneJWTSigner.NONE;
|
||||||
|
}
|
||||||
|
if (key instanceof PrivateKey || key instanceof PublicKey) {
|
||||||
|
return new AsymmetricJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
|
||||||
|
}
|
||||||
|
return new HMacJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
package cn.hutool.jwt.signers;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无需签名的JWT签名器
|
||||||
|
*
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.0
|
||||||
|
*/
|
||||||
|
public class NoneJWTSigner implements JWTSigner {
|
||||||
|
|
||||||
|
public static final String ID_NONE = "none";
|
||||||
|
|
||||||
|
public static NoneJWTSigner NONE = new NoneJWTSigner();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String sign(String headerBase64, String payloadBase64) {
|
||||||
|
return StrUtil.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verify(String headerBase64, String payloadBase64, String signBase64) {
|
||||||
|
return StrUtil.isEmpty(signBase64);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAlgorithm() {
|
||||||
|
return ID_NONE;
|
||||||
|
}
|
||||||
|
}
|
121
hutool-jwt/src/test/java/cn/hutool/jwt/JWTSignerTest.java
Normal file
121
hutool-jwt/src/test/java/cn/hutool/jwt/JWTSignerTest.java
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package cn.hutool.jwt;
|
||||||
|
|
||||||
|
import cn.hutool.crypto.KeyUtil;
|
||||||
|
import cn.hutool.jwt.signers.AlgorithmUtil;
|
||||||
|
import cn.hutool.jwt.signers.JWTSigner;
|
||||||
|
import cn.hutool.jwt.signers.JWTSignerUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class JWTSignerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hs256Test(){
|
||||||
|
String id = "hs256";
|
||||||
|
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKey(AlgorithmUtil.getAlgorithm(id)));
|
||||||
|
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||||
|
|
||||||
|
signAndVerify(signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hs384Test(){
|
||||||
|
String id = "hs384";
|
||||||
|
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKey(AlgorithmUtil.getAlgorithm(id)));
|
||||||
|
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||||
|
|
||||||
|
signAndVerify(signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hs512Test(){
|
||||||
|
String id = "hs512";
|
||||||
|
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKey(AlgorithmUtil.getAlgorithm(id)));
|
||||||
|
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||||
|
|
||||||
|
signAndVerify(signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rs256Test(){
|
||||||
|
String id = "rs256";
|
||||||
|
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||||
|
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||||
|
|
||||||
|
signAndVerify(signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rs384Test(){
|
||||||
|
String id = "rs384";
|
||||||
|
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||||
|
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||||
|
|
||||||
|
signAndVerify(signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rs512Test(){
|
||||||
|
String id = "rs512";
|
||||||
|
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||||
|
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||||
|
|
||||||
|
signAndVerify(signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void es256Test(){
|
||||||
|
String id = "es256";
|
||||||
|
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||||
|
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||||
|
|
||||||
|
signAndVerify(signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void es384Test(){
|
||||||
|
String id = "es384";
|
||||||
|
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||||
|
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||||
|
|
||||||
|
signAndVerify(signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void es512Test(){
|
||||||
|
String id = "es512";
|
||||||
|
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||||
|
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||||
|
|
||||||
|
signAndVerify(signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ps256Test(){
|
||||||
|
String id = "ps256";
|
||||||
|
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||||
|
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||||
|
|
||||||
|
signAndVerify(signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ps384Test(){
|
||||||
|
String id = "ps384";
|
||||||
|
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||||
|
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||||
|
|
||||||
|
signAndVerify(signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void signAndVerify(JWTSigner signer){
|
||||||
|
JWT jwt = JWT.create()
|
||||||
|
.setPayload("sub", "1234567890")
|
||||||
|
.setPayload("name", "looly")
|
||||||
|
.setPayload("admin", true)
|
||||||
|
.setSigner(signer);
|
||||||
|
|
||||||
|
String token = jwt.sign();
|
||||||
|
Assert.assertTrue(JWT.of(token).setSigner(signer).verify());
|
||||||
|
}
|
||||||
|
}
|
76
hutool-jwt/src/test/java/cn/hutool/jwt/JWTTest.java
Normal file
76
hutool-jwt/src/test/java/cn/hutool/jwt/JWTTest.java
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package cn.hutool.jwt;
|
||||||
|
|
||||||
|
import cn.hutool.jwt.signers.JWTSignerUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class JWTTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createHs256Test(){
|
||||||
|
byte[] key = "1234567890".getBytes();
|
||||||
|
JWT jwt = JWT.create()
|
||||||
|
.setPayload("sub", "1234567890")
|
||||||
|
.setPayload("name", "looly")
|
||||||
|
.setPayload("admin", true)
|
||||||
|
.setKey(key);
|
||||||
|
|
||||||
|
String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
|
||||||
|
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." +
|
||||||
|
"536690902d931d857d2f47d337ec81048ee09a8e71866bcc8404edbbcbf4cc40";
|
||||||
|
|
||||||
|
String token = jwt.sign();
|
||||||
|
Assert.assertEquals(token, token);
|
||||||
|
|
||||||
|
Assert.assertTrue(JWT.of(rightToken).setKey(key).verify());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseTest(){
|
||||||
|
String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
|
||||||
|
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." +
|
||||||
|
"536690902d931d857d2f47d337ec81048ee09a8e71866bcc8404edbbcbf4cc40";
|
||||||
|
|
||||||
|
final JWT jwt = JWT.of(rightToken);
|
||||||
|
|
||||||
|
//header
|
||||||
|
Assert.assertEquals("JWT", jwt.getHeader(JWTHeader.TYPE));
|
||||||
|
Assert.assertEquals("HS256", jwt.getHeader(JWTHeader.ALGORITHM));
|
||||||
|
Assert.assertNull(jwt.getHeader(JWTHeader.CONTENT_TYPE));
|
||||||
|
|
||||||
|
//payload
|
||||||
|
Assert.assertEquals("1234567890", jwt.getPayload("sub"));
|
||||||
|
Assert.assertEquals("looly", jwt.getPayload("name"));
|
||||||
|
Assert.assertEquals(true, jwt.getPayload("admin"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createNoneTest(){
|
||||||
|
JWT jwt = JWT.create()
|
||||||
|
.setPayload("sub", "1234567890")
|
||||||
|
.setPayload("name", "looly")
|
||||||
|
.setPayload("admin", true)
|
||||||
|
.setSigner(JWTSignerUtil.none());
|
||||||
|
|
||||||
|
String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
|
||||||
|
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9.";
|
||||||
|
|
||||||
|
String token = jwt.sign();
|
||||||
|
Assert.assertEquals(token, token);
|
||||||
|
|
||||||
|
Assert.assertTrue(JWT.of(rightToken).setSigner(JWTSignerUtil.none()).verify());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 必须定义签名器
|
||||||
|
*/
|
||||||
|
@Test(expected = JWTException.class)
|
||||||
|
public void needSignerTest(){
|
||||||
|
JWT jwt = JWT.create()
|
||||||
|
.setPayload("sub", "1234567890")
|
||||||
|
.setPayload("name", "looly")
|
||||||
|
.setPayload("admin", true);
|
||||||
|
|
||||||
|
jwt.sign();
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user