mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
5.2.0
This commit is contained in:
parent
1ae9d35d83
commit
82f982f809
@ -3,7 +3,7 @@
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
## 5.1.6
|
||||
## 5.2.0
|
||||
|
||||
### 新特性
|
||||
* 【core 】 NumberUtil.decimalFormat增加Object对象参数支持
|
||||
@ -18,6 +18,8 @@
|
||||
* 【all 】 log、template、tokenizer使用SPI机制代替硬编码
|
||||
* 【poi 】 Word07Writer增加addPicture
|
||||
* 【crypto】 RSA算法中,BlockSize长度策略调整(issue#721@Github)
|
||||
* 【crypto】 删除SM2Engine,使用BC库中的对象替代
|
||||
* 【crypto】 增加PemUtil工具类
|
||||
|
||||
### Bug修复
|
||||
|
||||
|
10
README.md
10
README.md
@ -40,7 +40,7 @@
|
||||
-- 主页:<a href="https://hutool.cn">https://hutool.cn/</a> | <a href="https://www.hutool.club/">https://www.hutool.club/</a> --
|
||||
</p>
|
||||
<p align="center">
|
||||
-- QQ群③:<a href="https://shang.qq.com/wpa/qunwpa?idkey=35764b2247c46ffebe28e45.1.6b2af8f5dee5efcf47ceec69d21e4521aa8c75">555368316</a> --
|
||||
-- QQ群③:<a href="https://shang.qq.com/wpa/qunwpa?idkey=35764b2247c46ffebe28e45.2.0b2af8f5dee5efcf47ceec69d21e4521aa8c75">555368316</a> --
|
||||
-- QQ群④:<a href="https://shang.qq.com/wpa/qunwpa?idkey=309056e409a304a454c7ba250a10d38dd82b9b49cd0e1f180fedbde78b02ae0d">718802356</a> --
|
||||
</p>
|
||||
|
||||
@ -116,21 +116,21 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.1.6</version>
|
||||
<version>5.2.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### Gradle
|
||||
```
|
||||
compile 'cn.hutool:hutool-all:5.1.6'
|
||||
compile 'cn.hutool:hutool-all:5.2.0'
|
||||
```
|
||||
|
||||
### 非Maven项目
|
||||
|
||||
点击以下任一链接,下载`hutool-all-X.X.X.jar`即可:
|
||||
|
||||
- [Maven中央库1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.1.6/)
|
||||
- [Maven中央库2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.1.6/)
|
||||
- [Maven中央库1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.2.0/)
|
||||
- [Maven中央库2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.2.0/)
|
||||
|
||||
> 注意
|
||||
> Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类获工具方法可用。
|
||||
|
@ -1 +1 @@
|
||||
5.1.6
|
||||
5.2.0
|
||||
|
@ -1 +1 @@
|
||||
var version = '5.1.6'
|
||||
var version = '5.2.0'
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-all</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-aop</artifactId>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-bloomFilter</artifactId>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-bom</artifactId>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-cache</artifactId>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-captcha</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-core</artifactId>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-cron</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
|
@ -1,30 +1,25 @@
|
||||
package cn.hutool.crypto;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||
import org.bouncycastle.jce.ECNamedCurveTable;
|
||||
import org.bouncycastle.jce.ECPointUtil;
|
||||
import org.bouncycastle.jce.interfaces.ECKey;
|
||||
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
|
||||
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
|
||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||
import org.bouncycastle.math.ec.ECCurve;
|
||||
import org.bouncycastle.util.io.pem.PemObject;
|
||||
import org.bouncycastle.util.io.pem.PemObjectGenerator;
|
||||
import org.bouncycastle.util.io.pem.PemReader;
|
||||
import org.bouncycastle.util.io.pem.PemWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.ECFieldFp;
|
||||
import java.security.spec.ECParameterSpec;
|
||||
import java.security.spec.ECPoint;
|
||||
import java.security.spec.ECPublicKeySpec;
|
||||
import java.security.spec.EllipticCurve;
|
||||
@ -81,7 +76,7 @@ public class BCUtil {
|
||||
final ECPoint point = ECPointUtil.decodePoint(ecCurve, encodeByte);
|
||||
|
||||
// 根据曲线恢复公钥格式
|
||||
ECParameterSpec ecSpec = new ECNamedCurveSpec(curveName, curve, namedSpec.getG(), namedSpec.getN());
|
||||
ECNamedCurveSpec ecSpec = new ECNamedCurveSpec(curveName, curve, namedSpec.getG(), namedSpec.getN());
|
||||
|
||||
final KeyFactory PubKeyGen = KeyUtil.getKeyFactory("EC");
|
||||
try {
|
||||
@ -92,152 +87,37 @@ public class BCUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取PEM格式的私钥
|
||||
* ECKey转换为ECKeyParameters
|
||||
*
|
||||
* @param pemStream pem流
|
||||
* @return {@link PrivateKey}
|
||||
* @since 4.5.2
|
||||
*/
|
||||
public static PrivateKey readPrivateKey(InputStream pemStream) {
|
||||
return (PrivateKey) readPemKey(pemStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取PEM格式的公钥
|
||||
*
|
||||
* @param pemStream pem流
|
||||
* @return {@link PublicKey}
|
||||
* @since 4.5.2
|
||||
*/
|
||||
public static PublicKey readPublicKey(InputStream pemStream) {
|
||||
return (PublicKey) readPemKey(pemStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从pem文件中读取公钥或私钥<br>
|
||||
* 根据类型返回{@link PublicKey} 或者 {@link PrivateKey}
|
||||
*
|
||||
* @param pemKeyStream pem流
|
||||
* @return {@link Key}
|
||||
* @since 4.5.2
|
||||
* @deprecated 请使用{@link #readPemKey(InputStream)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static Key readKey(InputStream pemKeyStream) {
|
||||
return readPemKey(pemKeyStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从pem文件中读取公钥或私钥<br>
|
||||
* 根据类型返回{@link PublicKey} 或者 {@link PrivateKey}
|
||||
*
|
||||
* @param keyStream pem流
|
||||
* @return {@link Key},null表示无法识别的密钥类型
|
||||
* @param ecKey BCECPrivateKey或者BCECPublicKey
|
||||
* @return ECPrivateKeyParameters或者ECPublicKeyParameters
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public static Key readPemKey(InputStream keyStream) {
|
||||
final PemObject object = readPemObject(keyStream);
|
||||
final String type = object.getType();
|
||||
if (StrUtil.isNotBlank(type)) {
|
||||
if (type.endsWith("PRIVATE KEY")) {
|
||||
return KeyUtil.generateRSAPrivateKey(object.getContent());
|
||||
} else if (type.endsWith("PUBLIC KEY")) {
|
||||
return KeyUtil.generateRSAPublicKey(object.getContent());
|
||||
} else if (type.endsWith("CERTIFICATE")) {
|
||||
return KeyUtil.readPublicKeyFromCert(IoUtil.toStream(object.getContent()));
|
||||
}
|
||||
public static ECKeyParameters toParams(ECKey ecKey) {
|
||||
final ECParameterSpec parameterSpec = ecKey.getParameters();
|
||||
final ECDomainParameters ecDomainParameters = buildECDomainParameters(parameterSpec);
|
||||
|
||||
if (ecKey instanceof BCECPrivateKey) {
|
||||
return new ECPrivateKeyParameters(((BCECPrivateKey) ecKey).getD(), ecDomainParameters);
|
||||
} else if (ecKey instanceof BCECPublicKey) {
|
||||
return new ECPublicKeyParameters(((BCECPublicKey) ecKey).getQ(), ecDomainParameters);
|
||||
}
|
||||
|
||||
//表示无法识别的密钥类型
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从pem文件中读取公钥或私钥
|
||||
* 构建ECDomainParameters对象
|
||||
*
|
||||
* @param keyStream pem流
|
||||
* @return 密钥bytes
|
||||
* @since 4.5.2
|
||||
* @deprecated 使用{@link #readPem(InputStream)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static byte[] readKeyBytes(InputStream keyStream) {
|
||||
return readPem(keyStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从pem流中读取公钥或私钥
|
||||
*
|
||||
* @param keyStream pem流
|
||||
* @return 密钥bytes
|
||||
* @param parameterSpec ECParameterSpec
|
||||
* @return ECDomainParameters
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public static byte[] readPem(InputStream keyStream) {
|
||||
PemObject pemObject = readPemObject(keyStream);
|
||||
if (null != pemObject) {
|
||||
return pemObject.getContent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取pem文件中的信息,包括类型、头信息和密钥内容
|
||||
*
|
||||
* @param keyStream pem流
|
||||
* @return {@link PemObject}
|
||||
* @since 4.5.2
|
||||
*/
|
||||
public static PemObject readPemObject(InputStream keyStream) {
|
||||
return readPemObject(IoUtil.getUtf8Reader(keyStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取pem文件中的信息,包括类型、头信息和密钥内容
|
||||
*
|
||||
* @param reader pem Reader
|
||||
* @return {@link PemObject}
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public static PemObject readPemObject(Reader reader) {
|
||||
PemReader pemReader = null;
|
||||
try {
|
||||
pemReader = new PemReader(reader);
|
||||
return pemReader.readPemObject();
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
} finally {
|
||||
IoUtil.close(pemReader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出pem密钥(私钥、公钥、证书)
|
||||
*
|
||||
* @param type 密钥类型(私钥、公钥、证书)
|
||||
* @param content 密钥内容
|
||||
* @param keyStream pem流
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public static void writePemObject(String type, byte[] content, OutputStream keyStream) {
|
||||
writePemObject(new PemObject(type, content), keyStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出pem密钥(私钥、公钥、证书)
|
||||
*
|
||||
* @param pemObject pem对象,包括密钥和密钥类型等信息
|
||||
* @param keyStream pem流
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public static void writePemObject(PemObjectGenerator pemObject, OutputStream keyStream) {
|
||||
PemWriter writer = null;
|
||||
try {
|
||||
writer = new PemWriter(IoUtil.getUtf8Writer(keyStream));
|
||||
writer.writeObject(pemObject);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
} finally {
|
||||
IoUtil.close(writer);
|
||||
}
|
||||
public static ECDomainParameters buildECDomainParameters(ECParameterSpec parameterSpec) {
|
||||
return new ECDomainParameters(
|
||||
parameterSpec.getCurve(),
|
||||
parameterSpec.getG(),
|
||||
parameterSpec.getN(),
|
||||
parameterSpec.getH());
|
||||
}
|
||||
}
|
||||
|
154
hutool-crypto/src/main/java/cn/hutool/crypto/PemUtil.java
Normal file
154
hutool-crypto/src/main/java/cn/hutool/crypto/PemUtil.java
Normal file
@ -0,0 +1,154 @@
|
||||
package cn.hutool.crypto;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.bouncycastle.util.io.pem.PemObject;
|
||||
import org.bouncycastle.util.io.pem.PemObjectGenerator;
|
||||
import org.bouncycastle.util.io.pem.PemReader;
|
||||
import org.bouncycastle.util.io.pem.PemWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.security.Key;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* PEM(Privacy Enhanced Mail)格式相关工具类。
|
||||
*
|
||||
* <p>
|
||||
* PEM一般为文本格式,以 -----BEGIN... 开头,以 -----END... 结尾,中间的内容是 BASE64 编码。
|
||||
* <p>
|
||||
* 这种格式可以保存证书和私钥,有时我们也把PEM格式的私钥的后缀改为 .key 以区别证书与私钥。
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public class PemUtil {
|
||||
|
||||
/**
|
||||
* 读取PEM格式的私钥
|
||||
*
|
||||
* @param pemStream pem流
|
||||
* @return {@link PrivateKey}
|
||||
* @since 4.5.2
|
||||
*/
|
||||
public static PrivateKey readPemPrivateKey(InputStream pemStream) {
|
||||
return (PrivateKey) PemUtil.readPemKey(pemStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取PEM格式的公钥
|
||||
*
|
||||
* @param pemStream pem流
|
||||
* @return {@link PublicKey}
|
||||
* @since 4.5.2
|
||||
*/
|
||||
public static PublicKey readPemPublicKey(InputStream pemStream) {
|
||||
return (PublicKey) PemUtil.readPemKey(pemStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从pem文件中读取公钥或私钥<br>
|
||||
* 根据类型返回 {@link PublicKey} 或者 {@link PrivateKey}
|
||||
*
|
||||
* @param keyStream pem流
|
||||
* @return {@link Key},null表示无法识别的密钥类型
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public static Key readPemKey(InputStream keyStream) {
|
||||
final PemObject object = readPemObject(keyStream);
|
||||
final String type = object.getType();
|
||||
if (StrUtil.isNotBlank(type)) {
|
||||
if (type.endsWith("PRIVATE KEY")) {
|
||||
return KeyUtil.generateRSAPrivateKey(object.getContent());
|
||||
} else if (type.endsWith("PUBLIC KEY")) {
|
||||
return KeyUtil.generateRSAPublicKey(object.getContent());
|
||||
} else if (type.endsWith("CERTIFICATE")) {
|
||||
return KeyUtil.readPublicKeyFromCert(IoUtil.toStream(object.getContent()));
|
||||
}
|
||||
}
|
||||
|
||||
//表示无法识别的密钥类型
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从pem流中读取公钥或私钥
|
||||
*
|
||||
* @param keyStream pem流
|
||||
* @return 密钥bytes
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public static byte[] readPem(InputStream keyStream) {
|
||||
PemObject pemObject = readPemObject(keyStream);
|
||||
if (null != pemObject) {
|
||||
return pemObject.getContent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取pem文件中的信息,包括类型、头信息和密钥内容
|
||||
*
|
||||
* @param keyStream pem流
|
||||
* @return {@link PemObject}
|
||||
* @since 4.5.2
|
||||
*/
|
||||
public static PemObject readPemObject(InputStream keyStream) {
|
||||
return readPemObject(IoUtil.getUtf8Reader(keyStream));
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取pem文件中的信息,包括类型、头信息和密钥内容
|
||||
*
|
||||
* @param reader pem Reader
|
||||
* @return {@link PemObject}
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public static PemObject readPemObject(Reader reader) {
|
||||
PemReader pemReader = null;
|
||||
try {
|
||||
pemReader = new PemReader(reader);
|
||||
return pemReader.readPemObject();
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
} finally {
|
||||
IoUtil.close(pemReader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出pem密钥(私钥、公钥、证书)
|
||||
*
|
||||
* @param type 密钥类型(私钥、公钥、证书)
|
||||
* @param content 密钥内容
|
||||
* @param keyStream pem流
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public static void writePemObject(String type, byte[] content, OutputStream keyStream) {
|
||||
writePemObject(new PemObject(type, content), keyStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出pem密钥(私钥、公钥、证书)
|
||||
*
|
||||
* @param pemObject pem对象,包括密钥和密钥类型等信息
|
||||
* @param keyStream pem流
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public static void writePemObject(PemObjectGenerator pemObject, OutputStream keyStream) {
|
||||
PemWriter writer = null;
|
||||
try {
|
||||
writer = new PemWriter(IoUtil.getUtf8Writer(keyStream));
|
||||
writer.writeObject(pemObject);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
} finally {
|
||||
IoUtil.close(writer);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,8 +2,9 @@ package cn.hutool.crypto.asymmetric;
|
||||
|
||||
import cn.hutool.crypto.CryptoException;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.SM2Engine.SM2Mode;
|
||||
import org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.InvalidCipherTextException;
|
||||
import org.bouncycastle.crypto.engines.SM2Engine;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ParametersWithID;
|
||||
@ -19,23 +20,26 @@ import java.security.PublicKey;
|
||||
* 国密SM2算法实现,基于BC库<br>
|
||||
* SM2算法只支持公钥加密,私钥解密<br>
|
||||
* 参考:https://blog.csdn.net/pridas/article/details/86118774
|
||||
*
|
||||
*
|
||||
* @author looly
|
||||
* @since 4.3.2
|
||||
*/
|
||||
public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
|
||||
/** 算法EC */
|
||||
/**
|
||||
* 算法EC
|
||||
*/
|
||||
private static final String ALGORITHM_SM2 = "SM2";
|
||||
|
||||
protected SM2Engine engine;
|
||||
protected SM2Signer signer;
|
||||
|
||||
private SM2Mode mode;
|
||||
private SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
|
||||
private ECPublicKeyParameters publicKeyParams;
|
||||
private ECPrivateKeyParameters privateKeyParams;
|
||||
|
||||
// ------------------------------------------------------------------ Constructor start
|
||||
|
||||
/**
|
||||
* 构造,生成新的私钥公钥对
|
||||
*/
|
||||
@ -47,9 +51,9 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
* 构造<br>
|
||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||
*
|
||||
*
|
||||
* @param privateKeyStr 私钥Hex或Base64表示
|
||||
* @param publicKeyStr 公钥Hex或Base64表示
|
||||
* @param publicKeyStr 公钥Hex或Base64表示
|
||||
*/
|
||||
public SM2(String privateKeyStr, String publicKeyStr) {
|
||||
this(SecureUtil.decode(privateKeyStr), SecureUtil.decode(publicKeyStr));
|
||||
@ -59,9 +63,9 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
* 构造 <br>
|
||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||
*
|
||||
*
|
||||
* @param privateKey 私钥
|
||||
* @param publicKey 公钥
|
||||
* @param publicKey 公钥
|
||||
*/
|
||||
public SM2(byte[] privateKey, byte[] publicKey) {
|
||||
this(//
|
||||
@ -74,9 +78,9 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
* 构造 <br>
|
||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密或者解密
|
||||
*
|
||||
*
|
||||
* @param privateKey 私钥
|
||||
* @param publicKey 公钥
|
||||
* @param publicKey 公钥
|
||||
*/
|
||||
public SM2(PrivateKey privateKey, PublicKey publicKey) {
|
||||
super(ALGORITHM_SM2, privateKey, publicKey);
|
||||
@ -87,9 +91,9 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
* 初始化<br>
|
||||
* 私钥和公钥同时为空时生成一对新的私钥和公钥<br>
|
||||
* 私钥和公钥可以单独传入一个,如此则只能使用此钥匙来做加密(签名)或者解密(校验)
|
||||
*
|
||||
*
|
||||
* @param privateKey 私钥
|
||||
* @param publicKey 公钥
|
||||
* @param publicKey 公钥
|
||||
* @return this
|
||||
*/
|
||||
public SM2 init(PrivateKey privateKey, PublicKey publicKey) {
|
||||
@ -103,16 +107,17 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------- Encrypt
|
||||
|
||||
/**
|
||||
* 加密,SM2非对称加密的结果由C1,C2,C3三部分组成,其中:
|
||||
*
|
||||
*
|
||||
* <pre>
|
||||
* C1 生成随机数的计算出的椭圆曲线点
|
||||
* C2 密文数据
|
||||
* C3 SM3的摘要值
|
||||
* </pre>
|
||||
*
|
||||
* @param data 被加密的bytes
|
||||
*
|
||||
* @param data 被加密的bytes
|
||||
* @param keyType 私钥或公钥 {@link KeyType}
|
||||
* @return 加密后的bytes
|
||||
* @throws CryptoException 包括InvalidKeyException和InvalidCipherTextException的包装异常
|
||||
@ -123,22 +128,43 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
throw new IllegalArgumentException("Encrypt is only support by public key");
|
||||
}
|
||||
checkKey(keyType);
|
||||
return encrypt(data, new ParametersWithRandom(getCipherParameters(keyType)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密,SM2非对称加密的结果由C1,C2,C3三部分组成,其中:
|
||||
*
|
||||
* <pre>
|
||||
* C1 生成随机数的计算出的椭圆曲线点
|
||||
* C2 密文数据
|
||||
* C3 SM3的摘要值
|
||||
* </pre>
|
||||
*
|
||||
* @param data 被加密的bytes
|
||||
* @param pubKeyParameters 公钥参数
|
||||
* @return 加密后的bytes
|
||||
* @throws CryptoException 包括InvalidKeyException和InvalidCipherTextException的包装异常
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public byte[] encrypt(byte[] data, CipherParameters pubKeyParameters) throws CryptoException {
|
||||
lock.lock();
|
||||
final SM2Engine engine = getEngine();
|
||||
try {
|
||||
engine.init(true, new ParametersWithRandom(getCipherParameters(keyType)));
|
||||
engine.init(true, pubKeyParameters);
|
||||
return engine.processBlock(data, 0, data.length);
|
||||
} catch (InvalidCipherTextException e) {
|
||||
throw new CryptoException(e);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------- Decrypt
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param data SM2密文,实际包含三部分:ECC公钥、真正的密文、公钥和原文的SM3-HASH值
|
||||
*
|
||||
* @param data SM2密文,实际包含三部分:ECC公钥、真正的密文、公钥和原文的SM3-HASH值
|
||||
* @param keyType 私钥或公钥 {@link KeyType}
|
||||
* @return 加密后的bytes
|
||||
* @throws CryptoException 包括InvalidKeyException和InvalidCipherTextException的包装异常
|
||||
@ -149,21 +175,35 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
throw new IllegalArgumentException("Decrypt is only support by private key");
|
||||
}
|
||||
checkKey(keyType);
|
||||
return decrypt(data, getCipherParameters(keyType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param data SM2密文,实际包含三部分:ECC公钥、真正的密文、公钥和原文的SM3-HASH值
|
||||
* @param privateKeyParameters 私钥参数
|
||||
* @return 加密后的bytes
|
||||
* @throws CryptoException 包括InvalidKeyException和InvalidCipherTextException的包装异常
|
||||
* @since 5.1.6
|
||||
*/
|
||||
public byte[] decrypt(byte[] data, CipherParameters privateKeyParameters) throws CryptoException {
|
||||
lock.lock();
|
||||
final SM2Engine engine = getEngine();
|
||||
try {
|
||||
engine.init(false, getCipherParameters(keyType));
|
||||
engine.init(false, privateKeyParameters);
|
||||
return engine.processBlock(data, 0, data.length);
|
||||
} catch (InvalidCipherTextException e) {
|
||||
throw new CryptoException(e);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------- Sign and Verify
|
||||
|
||||
/**
|
||||
* 用私钥对信息生成数字签名
|
||||
*
|
||||
*
|
||||
* @param data 加密数据
|
||||
* @return 签名
|
||||
*/
|
||||
@ -173,9 +213,9 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
|
||||
/**
|
||||
* 用私钥对信息生成数字签名
|
||||
*
|
||||
*
|
||||
* @param data 加密数据
|
||||
* @param id 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
|
||||
* @param id 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
|
||||
* @return 签名
|
||||
*/
|
||||
public byte[] sign(byte[] data, byte[] id) {
|
||||
@ -198,7 +238,7 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
|
||||
/**
|
||||
* 用公钥检验数字签名的合法性
|
||||
*
|
||||
*
|
||||
* @param data 数据
|
||||
* @param sign 签名
|
||||
* @return 是否验证通过
|
||||
@ -209,10 +249,10 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
|
||||
/**
|
||||
* 用公钥检验数字签名的合法性
|
||||
*
|
||||
*
|
||||
* @param data 数据
|
||||
* @param sign 签名
|
||||
* @param id 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
|
||||
* @param id 可以为null,若为null,则默认withId为字节数组:"1234567812345678".getBytes()
|
||||
* @return 是否验证通过
|
||||
*/
|
||||
public boolean verify(byte[] data, byte[] sign, byte[] id) {
|
||||
@ -257,22 +297,23 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
|
||||
/**
|
||||
* 设置加密类型
|
||||
*
|
||||
* @param mode {@link SM2Mode}
|
||||
*
|
||||
* @param mode {@link SM2Engine.Mode}
|
||||
* @return this
|
||||
*/
|
||||
public SM2 setMode(SM2Mode mode) {
|
||||
public SM2 setMode(SM2Engine.Mode mode) {
|
||||
this.mode = mode;
|
||||
if (null != this.engine) {
|
||||
this.engine.setMode(mode);
|
||||
this.engine = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------- Private method start
|
||||
|
||||
/**
|
||||
* 初始化加密解密参数(包括私钥和公钥参数)
|
||||
*
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
private SM2 initCipherParams() {
|
||||
@ -292,16 +333,16 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
|
||||
/**
|
||||
* 获取密钥类型对应的加密参数对象{@link CipherParameters}
|
||||
*
|
||||
*
|
||||
* @param keyType Key类型枚举,包括私钥或公钥
|
||||
* @return {@link CipherParameters}
|
||||
*/
|
||||
private CipherParameters getCipherParameters(KeyType keyType) {
|
||||
switch (keyType) {
|
||||
case PublicKey:
|
||||
return this.publicKeyParams;
|
||||
case PrivateKey:
|
||||
return this.privateKeyParams;
|
||||
case PublicKey:
|
||||
return this.publicKeyParams;
|
||||
case PrivateKey:
|
||||
return this.privateKeyParams;
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -309,27 +350,27 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
|
||||
/**
|
||||
* 检查对应类型的Key是否存在
|
||||
*
|
||||
*
|
||||
* @param keyType key类型
|
||||
*/
|
||||
private void checkKey(KeyType keyType) {
|
||||
switch (keyType) {
|
||||
case PublicKey:
|
||||
if (null == this.publicKey) {
|
||||
throw new NullPointerException("No public key provided");
|
||||
}
|
||||
break;
|
||||
case PrivateKey:
|
||||
if (null == this.privateKey) {
|
||||
throw new NullPointerException("No private key provided");
|
||||
}
|
||||
break;
|
||||
case PublicKey:
|
||||
if (null == this.publicKey) {
|
||||
throw new NullPointerException("No public key provided");
|
||||
}
|
||||
break;
|
||||
case PrivateKey:
|
||||
if (null == this.privateKey) {
|
||||
throw new NullPointerException("No private key provided");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link SM2Engine}
|
||||
*
|
||||
* 获取{@link SM2Engine},此对象为懒加载模式
|
||||
*
|
||||
* @return {@link SM2Engine}
|
||||
*/
|
||||
private SM2Engine getEngine() {
|
||||
@ -338,10 +379,10 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
|
||||
}
|
||||
return this.engine;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取{@link SM2Signer}
|
||||
*
|
||||
* 获取{@link SM2Signer},此对象为懒加载模式
|
||||
*
|
||||
* @return {@link SM2Signer}
|
||||
*/
|
||||
private SM2Signer getSigner() {
|
||||
|
@ -1,356 +0,0 @@
|
||||
package cn.hutool.crypto.asymmetric;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Random;
|
||||
|
||||
import org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.CryptoServicesRegistrar;
|
||||
import org.bouncycastle.crypto.Digest;
|
||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||
import org.bouncycastle.crypto.params.ECDomainParameters;
|
||||
import org.bouncycastle.crypto.params.ECKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ParametersWithRandom;
|
||||
import org.bouncycastle.math.ec.ECConstants;
|
||||
import org.bouncycastle.math.ec.ECFieldElement;
|
||||
import org.bouncycastle.math.ec.ECMultiplier;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
|
||||
import org.bouncycastle.util.Arrays;
|
||||
import org.bouncycastle.util.BigIntegers;
|
||||
import org.bouncycastle.util.Memoable;
|
||||
import org.bouncycastle.util.Pack;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.crypto.CryptoException;
|
||||
|
||||
/**
|
||||
* SM2加密解密引擎,来自Bouncy Castle库的SM2Engine类改造<br>
|
||||
* SM2加密后的数据格式为(两种模式):
|
||||
*
|
||||
* <pre>
|
||||
* curve(C1) | data(C2) | digest(C3)
|
||||
* curve(C1) | digest(C3) | data(C2)
|
||||
* </pre>
|
||||
*
|
||||
* @author looly, bouncycastle
|
||||
* @since 4.5.0
|
||||
*/
|
||||
public class SM2Engine {
|
||||
|
||||
private final Digest digest;
|
||||
|
||||
private boolean forEncryption;
|
||||
private ECKeyParameters ecKey;
|
||||
private ECDomainParameters ecParams;
|
||||
private int curveLength;
|
||||
private Random random;
|
||||
/** 加密解密模式 */
|
||||
private SM2Mode mode;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public SM2Engine() {
|
||||
this(new SM3Digest());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param mode SM2密钥生成模式,可选C1C2C3和C1C3C2
|
||||
*/
|
||||
public SM2Engine(SM2Mode mode) {
|
||||
this(new SM3Digest(), mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param digest 摘要算法啊
|
||||
*/
|
||||
public SM2Engine(Digest digest) {
|
||||
this(digest, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param digest 摘要算法啊
|
||||
* @param mode SM2密钥生成模式,可选C1C2C3和C1C3C2
|
||||
*/
|
||||
public SM2Engine(Digest digest, SM2Mode mode) {
|
||||
this.digest = digest;
|
||||
this.mode = ObjectUtil.defaultIfNull(mode, SM2Mode.C1C3C2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化引擎
|
||||
*
|
||||
* @param forEncryption 是否为加密模式
|
||||
* @param param {@link CipherParameters},此处应为{@link ParametersWithRandom}(加密时)或{@link ECKeyParameters}(解密时)
|
||||
*/
|
||||
public void init(boolean forEncryption, CipherParameters param) {
|
||||
this.forEncryption = forEncryption;
|
||||
|
||||
if (param instanceof ParametersWithRandom) {
|
||||
final ParametersWithRandom rParam = (ParametersWithRandom) param;
|
||||
this.ecKey = (ECKeyParameters) rParam.getParameters();
|
||||
this.random = rParam.getRandom();
|
||||
} else {
|
||||
this.ecKey = (ECKeyParameters) param;
|
||||
}
|
||||
this.ecParams = this.ecKey.getParameters();
|
||||
|
||||
if (forEncryption) {
|
||||
// 检查曲线点
|
||||
final ECPoint ecPoint = ((ECPublicKeyParameters) ecKey).getQ().multiply(ecParams.getH());
|
||||
if (ecPoint.isInfinity()) {
|
||||
throw new IllegalArgumentException("invalid key: [h]Q at infinity");
|
||||
}
|
||||
|
||||
// 检查随机参数
|
||||
if (null == this.random) {
|
||||
this.random = CryptoServicesRegistrar.getSecureRandom();
|
||||
}
|
||||
}
|
||||
|
||||
// 曲线位长度
|
||||
this.curveLength = (this.ecParams.getCurve().getFieldSize() + 7) / 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理块,包括加密和解密
|
||||
*
|
||||
* @param in 数据
|
||||
* @param inOff 数据开始位置
|
||||
* @param inLen 数据长度
|
||||
* @return 结果
|
||||
*/
|
||||
public byte[] processBlock(byte[] in, int inOff, int inLen) {
|
||||
if (forEncryption) {
|
||||
return encrypt(in, inOff, inLen);
|
||||
} else {
|
||||
return decrypt(in, inOff, inLen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置加密类型
|
||||
*
|
||||
* @param mode {@link SM2Mode}
|
||||
* @return this
|
||||
*/
|
||||
public SM2Engine setMode(SM2Mode mode) {
|
||||
this.mode = mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* SM2算法模式<br>
|
||||
* 在SM2算法中,C1C2C3为旧标准模式,C1C3C2为新标准模式
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
public enum SM2Mode {
|
||||
C1C2C3, C1C3C2
|
||||
}
|
||||
|
||||
protected ECMultiplier createBasePointMultiplier() {
|
||||
return new FixedPointCombMultiplier();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------------- Private method start
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param in 数据
|
||||
* @param inOff 位置
|
||||
* @param inLen 长度
|
||||
* @return 密文
|
||||
*/
|
||||
private byte[] encrypt(byte[] in, int inOff, int inLen) {
|
||||
// 加密数据
|
||||
byte[] c2 = new byte[inLen];
|
||||
System.arraycopy(in, inOff, c2, 0, c2.length);
|
||||
|
||||
final ECMultiplier multiplier = createBasePointMultiplier();
|
||||
|
||||
byte[] c1;
|
||||
ECPoint kPB;
|
||||
BigInteger k;
|
||||
do {
|
||||
k = nextK();
|
||||
// 产生随机数计算出曲线点C1
|
||||
c1 = multiplier.multiply(ecParams.getG(), k).normalize().getEncoded(false);
|
||||
kPB = ((ECPublicKeyParameters) ecKey).getQ().multiply(k).normalize();
|
||||
kdf(kPB, c2);
|
||||
} while (notEncrypted(c2, in, inOff));
|
||||
|
||||
// 杂凑值,效验数据
|
||||
byte[] c3 = new byte[digest.getDigestSize()];
|
||||
|
||||
addFieldElement(kPB.getAffineXCoord());
|
||||
this.digest.update(in, inOff, inLen);
|
||||
addFieldElement(kPB.getAffineYCoord());
|
||||
|
||||
this.digest.doFinal(c3, 0);
|
||||
|
||||
// 按照对应模式输出结果
|
||||
if (mode == SM2Mode.C1C3C2) {
|
||||
return Arrays.concatenate(c1, c3, c2);
|
||||
}
|
||||
return Arrays.concatenate(c1, c2, c3);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密,只支持私钥解密
|
||||
*
|
||||
* @param in 密文
|
||||
* @param inOff 位置
|
||||
* @param inLen 长度
|
||||
* @return 解密后的内容
|
||||
*/
|
||||
private byte[] decrypt(byte[] in, int inOff, int inLen) {
|
||||
// 获取曲线点
|
||||
final byte[] c1 = new byte[this.curveLength * 2 + 1];
|
||||
System.arraycopy(in, inOff, c1, 0, c1.length);
|
||||
|
||||
ECPoint c1P = this.ecParams.getCurve().decodePoint(c1);
|
||||
if (c1P.multiply(this.ecParams.getH()).isInfinity()) {
|
||||
throw new CryptoException("[h]C1 at infinity");
|
||||
}
|
||||
c1P = c1P.multiply(((ECPrivateKeyParameters) ecKey).getD()).normalize();
|
||||
|
||||
final int digestSize = this.digest.getDigestSize();
|
||||
|
||||
// 解密C2数据
|
||||
final byte[] c2 = new byte[inLen - c1.length - digestSize];
|
||||
|
||||
if (SM2Mode.C1C3C2 == this.mode) {
|
||||
// C2位于第三部分
|
||||
System.arraycopy(in, inOff + c1.length + digestSize, c2, 0, c2.length);
|
||||
} else {
|
||||
// C2位于第二部分
|
||||
System.arraycopy(in, inOff + c1.length, c2, 0, c2.length);
|
||||
}
|
||||
kdf(c1P, c2);
|
||||
|
||||
// 使用摘要验证C2数据
|
||||
final byte[] c3 = new byte[digestSize];
|
||||
|
||||
addFieldElement(c1P.getAffineXCoord());
|
||||
this.digest.update(c2, 0, c2.length);
|
||||
addFieldElement(c1P.getAffineYCoord());
|
||||
this.digest.doFinal(c3, 0);
|
||||
|
||||
int check = 0;
|
||||
for (int i = 0; i != c3.length; i++) {
|
||||
check |= c3[i] ^ in[inOff + c1.length + ((SM2Mode.C1C3C2 == this.mode) ? 0 : c2.length) + i];
|
||||
}
|
||||
|
||||
Arrays.fill(c1, (byte) 0);
|
||||
Arrays.fill(c3, (byte) 0);
|
||||
|
||||
if (check != 0) {
|
||||
Arrays.fill(c2, (byte) 0);
|
||||
throw new CryptoException("invalid cipher text");
|
||||
}
|
||||
|
||||
return c2;
|
||||
}
|
||||
|
||||
private boolean notEncrypted(byte[] encData, byte[] in, int inOff) {
|
||||
for (int i = 0; i != encData.length; i++) {
|
||||
if (encData[i] != in[inOff + i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密数据
|
||||
*
|
||||
* @param c1 c1点
|
||||
* @param encData 密文
|
||||
*/
|
||||
private void kdf(ECPoint c1, byte[] encData) {
|
||||
final Digest digest = this.digest;
|
||||
int digestSize = digest.getDigestSize();
|
||||
byte[] buf = new byte[Math.max(4, digestSize)];
|
||||
int off = 0;
|
||||
|
||||
Memoable memo = null;
|
||||
Memoable copy = null;
|
||||
|
||||
if (digest instanceof Memoable) {
|
||||
addFieldElement(c1.getAffineXCoord());
|
||||
addFieldElement(c1.getAffineYCoord());
|
||||
memo = (Memoable) digest;
|
||||
copy = memo.copy();
|
||||
}
|
||||
|
||||
int ct = 0;
|
||||
|
||||
while (off < encData.length) {
|
||||
if (memo != null) {
|
||||
memo.reset(copy);
|
||||
} else {
|
||||
addFieldElement(c1.getAffineXCoord());
|
||||
addFieldElement(c1.getAffineYCoord());
|
||||
}
|
||||
|
||||
Pack.intToBigEndian(++ct, buf, 0);
|
||||
digest.update(buf, 0, 4);
|
||||
digest.doFinal(buf, 0);
|
||||
|
||||
int xorLen = Math.min(digestSize, encData.length - off);
|
||||
xor(encData, buf, off, xorLen);
|
||||
off += xorLen;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 异或
|
||||
*
|
||||
* @param data 数据
|
||||
* @param kdfOut kdf输出值
|
||||
* @param dOff d偏移
|
||||
* @param dRemaining d剩余
|
||||
*/
|
||||
private void xor(byte[] data, byte[] kdfOut, int dOff, int dRemaining) {
|
||||
for (int i = 0; i != dRemaining; i++) {
|
||||
data[dOff + i] ^= kdfOut[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一个K值
|
||||
*
|
||||
* @return K值
|
||||
*/
|
||||
private BigInteger nextK() {
|
||||
final int qBitLength = this.ecParams.getN().bitLength();
|
||||
|
||||
BigInteger k;
|
||||
do {
|
||||
k = new BigInteger(qBitLength, this.random);
|
||||
} while (k.equals(ECConstants.ZERO) || k.compareTo(this.ecParams.getN()) >= 0);
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加字段节点
|
||||
*
|
||||
* @param v 节点
|
||||
*/
|
||||
private void addFieldElement(ECFieldElement v) {
|
||||
final byte[] p = BigIntegers.asUnsignedByteArray(this.curveLength, v.toBigInteger());
|
||||
this.digest.update(p, 0, p.length);
|
||||
}
|
||||
// --------------------------------------------------------------------------------------------------- Private method start
|
||||
}
|
@ -1,40 +1,39 @@
|
||||
package cn.hutool.crypto.test;
|
||||
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.crypto.PemUtil;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
public class PemUtilTest {
|
||||
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.crypto.BCUtil;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
|
||||
public class BCUtilTest {
|
||||
|
||||
@Test
|
||||
public void readPrivateKeyTest() {
|
||||
PrivateKey privateKey = BCUtil.readPrivateKey(ResourceUtil.getStream("test_private_key.pem"));
|
||||
PrivateKey privateKey = PemUtil.readPemPrivateKey(ResourceUtil.getStream("test_private_key.pem"));
|
||||
Assert.assertNotNull(privateKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readPublicKeyTest() {
|
||||
PublicKey publicKey = BCUtil.readPublicKey(ResourceUtil.getStream("test_public_key.csr"));
|
||||
PublicKey publicKey = PemUtil.readPemPublicKey(ResourceUtil.getStream("test_public_key.csr"));
|
||||
Assert.assertNotNull(publicKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readPemKeyTest() {
|
||||
PublicKey publicKey = (PublicKey) BCUtil.readPemKey(ResourceUtil.getStream("test_public_key.csr"));
|
||||
PublicKey publicKey = (PublicKey) PemUtil.readPemKey(ResourceUtil.getStream("test_public_key.csr"));
|
||||
Assert.assertNotNull(publicKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateKey() {
|
||||
PrivateKey privateKey = BCUtil.readPrivateKey(ResourceUtil.getStream("test_private_key.pem"));
|
||||
PublicKey publicKey = BCUtil.readPublicKey(ResourceUtil.getStream("test_public_key.csr"));
|
||||
PrivateKey privateKey = PemUtil.readPemPrivateKey(ResourceUtil.getStream("test_private_key.pem"));
|
||||
PublicKey publicKey = PemUtil.readPemPublicKey(ResourceUtil.getStream("test_public_key.csr"));
|
||||
|
||||
RSA rsa = new RSA(privateKey, publicKey);
|
||||
String str = "你好,Hutool";//测试字符串
|
@ -1,13 +1,5 @@
|
||||
package cn.hutool.crypto.test;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import cn.hutool.core.lang.Console;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.HexUtil;
|
||||
@ -17,7 +9,13 @@ import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.SmUtil;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.asymmetric.SM2;
|
||||
import cn.hutool.crypto.asymmetric.SM2Engine.SM2Mode;
|
||||
import org.bouncycastle.crypto.engines.SM2Engine;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* SM2算法单元测试
|
||||
@ -48,10 +46,9 @@ public class SM2Test {
|
||||
KeyPair pair = SecureUtil.generateKeyPair("SM2");
|
||||
byte[] privateKey = pair.getPrivate().getEncoded();
|
||||
byte[] publicKey = pair.getPublic().getEncoded();
|
||||
Console.log(HexUtil.encodeHexStr(publicKey));
|
||||
|
||||
SM2 sm2 = SmUtil.sm2(privateKey, publicKey);
|
||||
sm2.setMode(SM2Mode.C1C3C2);
|
||||
sm2.setMode(SM2Engine.Mode.C1C2C3);
|
||||
|
||||
// 公钥加密,私钥解密
|
||||
byte[] encrypt = sm2.encrypt(StrUtil.bytes("我是一段测试aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-db</artifactId>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-dfa</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-extra</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-http</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-json</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-log</artifactId>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-poi</artifactId>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-script</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-setting</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-socket</artifactId>
|
||||
|
@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.1.6-SNAPSHOT</version>
|
||||
<version>5.2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-system</artifactId>
|
||||
|
Loading…
x
Reference in New Issue
Block a user