流程设计界面黑色背景图标色码问题;部分高危版本jar包更换;SM2,SM3,SM4工具类
|
@ -85,7 +85,7 @@ angular.module('flowableModeler')
|
|||
}
|
||||
|
||||
// Check all received items
|
||||
debugger
|
||||
//debugger
|
||||
for (var stencilIndex = 0; stencilIndex < data.stencils.length; stencilIndex++) {
|
||||
|
||||
// Check if the root group is the 'diagram' group. If so, this item should not be shown.
|
||||
|
|
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 182 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 424 B |
Before Width: | Height: | Size: 910 B After Width: | Height: | Size: 582 B |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 600 B |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 508 B |
|
@ -29,6 +29,12 @@
|
|||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--Shiro核心框架 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
|
@ -105,6 +111,16 @@
|
|||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--CAS-->
|
||||
<dependency>
|
||||
<groupId>org.jasig.cas.client</groupId>
|
||||
<artifactId>cas-client-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,282 @@
|
|||
package com.ruoyi.common.utils.sm;
|
||||
|
||||
import org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.Digest;
|
||||
import org.bouncycastle.crypto.InvalidCipherTextException;
|
||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||
import org.bouncycastle.crypto.params.*;
|
||||
import org.bouncycastle.math.ec.ECConstants;
|
||||
import org.bouncycastle.math.ec.ECFieldElement;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.bouncycastle.util.Arrays;
|
||||
import org.bouncycastle.util.BigIntegers;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public class SM2EngineExtend {
|
||||
private final Digest digest;
|
||||
|
||||
/**是否为加密模式*/
|
||||
private boolean forEncryption;
|
||||
private ECKeyParameters ecKey;
|
||||
private ECDomainParameters ecParams;
|
||||
private int curveLength;
|
||||
private SecureRandom random;
|
||||
/**密文排序方式*/
|
||||
private int cipherMode;
|
||||
|
||||
/**BC库默认排序方式-C1C2C3*/
|
||||
public static int CIPHERMODE_BC = 0;
|
||||
/**国密标准排序方式-C1C3C2*/
|
||||
public static int CIPHERMODE_NORM = 1;
|
||||
|
||||
public SM2EngineExtend() {
|
||||
this(new SM3Digest());
|
||||
}
|
||||
|
||||
public SM2EngineExtend(Digest digest) {
|
||||
this.digest = digest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置密文排序方式
|
||||
* @param cipherMode
|
||||
*/
|
||||
public void setCipherMode(int cipherMode){
|
||||
this.cipherMode = cipherMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认初始化方法,使用国密排序标准
|
||||
* @param forEncryption - 是否以加密模式初始化
|
||||
* @param param - 曲线参数
|
||||
*/
|
||||
public void init(boolean forEncryption, CipherParameters param) {
|
||||
init(forEncryption, CIPHERMODE_NORM, param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认初始化方法,使用国密排序标准
|
||||
* @param forEncryption 是否以加密模式初始化
|
||||
* @param cipherMode 加密数据排列模式:1-标准排序;0-BC默认排序
|
||||
* @param param 曲线参数
|
||||
*/
|
||||
public void init(boolean forEncryption, int cipherMode, CipherParameters param) {
|
||||
this.forEncryption = forEncryption;
|
||||
this.cipherMode = cipherMode;
|
||||
if (forEncryption) {
|
||||
ParametersWithRandom rParam = (ParametersWithRandom) param;
|
||||
|
||||
ecKey = (ECKeyParameters) rParam.getParameters();
|
||||
ecParams = ecKey.getParameters();
|
||||
|
||||
ECPoint s = ((ECPublicKeyParameters) ecKey).getQ().multiply(ecParams.getH());
|
||||
if (s.isInfinity()) {
|
||||
throw new IllegalArgumentException("invalid key: [h]Q at infinity");
|
||||
}
|
||||
|
||||
random = rParam.getRandom();
|
||||
} else {
|
||||
ecKey = (ECKeyParameters) param;
|
||||
ecParams = ecKey.getParameters();
|
||||
}
|
||||
|
||||
curveLength = (ecParams.getCurve().getFieldSize() + 7) / 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密或解密输入数据
|
||||
* @param in
|
||||
* @param inOff
|
||||
* @param inLen
|
||||
* @return
|
||||
* @throws InvalidCipherTextException
|
||||
*/
|
||||
public byte[] processBlock( byte[] in, int inOff, int inLen) throws InvalidCipherTextException {
|
||||
if (forEncryption) {
|
||||
// 加密
|
||||
return encrypt(in, inOff, inLen);
|
||||
} else {
|
||||
return decrypt(in, inOff, inLen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密实现,根据cipherMode输出指定排列的结果,默认按标准方式排列
|
||||
* @param in
|
||||
* @param inOff
|
||||
* @param inLen
|
||||
* @return
|
||||
* @throws InvalidCipherTextException
|
||||
*/
|
||||
private byte[] encrypt(byte[] in, int inOff, int inLen)
|
||||
throws InvalidCipherTextException {
|
||||
byte[] c2 = new byte[inLen];
|
||||
|
||||
System.arraycopy(in, inOff, c2, 0, c2.length);
|
||||
|
||||
byte[] c1;
|
||||
ECPoint kPB;
|
||||
do {
|
||||
BigInteger k = nextK();
|
||||
|
||||
ECPoint c1P = ecParams.getG().multiply(k).normalize();
|
||||
|
||||
c1 = c1P.getEncoded(false);
|
||||
|
||||
kPB = ((ECPublicKeyParameters) ecKey).getQ().multiply(k).normalize();
|
||||
|
||||
kdf(digest, kPB, c2);
|
||||
}
|
||||
while (notEncrypted(c2, in, inOff));
|
||||
|
||||
byte[] c3 = new byte[digest.getDigestSize()];
|
||||
|
||||
addFieldElement(digest, kPB.getAffineXCoord());
|
||||
digest.update(in, inOff, inLen);
|
||||
addFieldElement(digest, kPB.getAffineYCoord());
|
||||
|
||||
digest.doFinal(c3, 0);
|
||||
if (cipherMode == CIPHERMODE_NORM){
|
||||
return Arrays.concatenate(c1, c3, c2);
|
||||
}
|
||||
return Arrays.concatenate(c1, c2, c3);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密实现,默认按标准排列方式解密,解密时解出c2部分原文并校验c3部分
|
||||
* @param in
|
||||
* @param inOff
|
||||
* @param inLen
|
||||
* @return
|
||||
* @throws InvalidCipherTextException
|
||||
*/
|
||||
private byte[] decrypt(byte[] in, int inOff, int inLen)
|
||||
throws InvalidCipherTextException {
|
||||
byte[] c1 = new byte[curveLength * 2 + 1];
|
||||
|
||||
System.arraycopy(in, inOff, c1, 0, c1.length);
|
||||
|
||||
ECPoint c1P = ecParams.getCurve().decodePoint(c1);
|
||||
|
||||
ECPoint s = c1P.multiply(ecParams.getH());
|
||||
if (s.isInfinity()) {
|
||||
throw new InvalidCipherTextException("[h]C1 at infinity");
|
||||
}
|
||||
|
||||
c1P = c1P.multiply(((ECPrivateKeyParameters) ecKey).getD()).normalize();
|
||||
|
||||
byte[] c2 = new byte[inLen - c1.length - digest.getDigestSize()];
|
||||
if (cipherMode == CIPHERMODE_BC) {
|
||||
System.arraycopy(in, inOff + c1.length, c2, 0, c2.length);
|
||||
}else{
|
||||
// C1 C3 C2
|
||||
System.arraycopy(in, inOff + c1.length + digest.getDigestSize(), c2, 0, c2.length);
|
||||
}
|
||||
|
||||
kdf(digest, c1P, c2);
|
||||
|
||||
byte[] c3 = new byte[digest.getDigestSize()];
|
||||
|
||||
addFieldElement(digest, c1P.getAffineXCoord());
|
||||
digest.update(c2, 0, c2.length);
|
||||
addFieldElement(digest, c1P.getAffineYCoord());
|
||||
|
||||
digest.doFinal(c3, 0);
|
||||
|
||||
int check = 0;
|
||||
// 检查密文输入值C3部分和由摘要生成的C3是否一致
|
||||
if (cipherMode == CIPHERMODE_BC) {
|
||||
for (int i = 0; i != c3.length; i++) {
|
||||
check |= c3[i] ^ in[c1.length + c2.length + i];
|
||||
}
|
||||
}else{
|
||||
for (int i = 0; i != c3.length; i++) {
|
||||
check |= c3[i] ^ in[c1.length + i];
|
||||
}
|
||||
}
|
||||
|
||||
clearBlock(c1);
|
||||
clearBlock(c3);
|
||||
|
||||
if (check != 0) {
|
||||
clearBlock(c2);
|
||||
throw new InvalidCipherTextException("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]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void kdf(Digest digest, ECPoint c1, byte[] encData) {
|
||||
int ct = 1;
|
||||
int v = digest.getDigestSize();
|
||||
|
||||
byte[] buf = new byte[digest.getDigestSize()];
|
||||
int off = 0;
|
||||
|
||||
for (int i = 1; i <= ((encData.length + v - 1) / v); i++) {
|
||||
addFieldElement(digest, c1.getAffineXCoord());
|
||||
addFieldElement(digest, c1.getAffineYCoord());
|
||||
digest.update((byte) (ct >> 24));
|
||||
digest.update((byte) (ct >> 16));
|
||||
digest.update((byte) (ct >> 8));
|
||||
digest.update((byte) ct);
|
||||
|
||||
digest.doFinal(buf, 0);
|
||||
|
||||
if (off + buf.length < encData.length) {
|
||||
xor(encData, buf, off, buf.length);
|
||||
} else {
|
||||
xor(encData, buf, off, encData.length - off);
|
||||
}
|
||||
|
||||
off += buf.length;
|
||||
ct++;
|
||||
}
|
||||
}
|
||||
|
||||
private void xor(byte[] data, byte[] kdfOut, int dOff, int dRemaining) {
|
||||
for (int i = 0; i != dRemaining; i++) {
|
||||
data[dOff + i] ^= kdfOut[i];
|
||||
}
|
||||
}
|
||||
|
||||
private BigInteger nextK() {
|
||||
int qBitLength = ecParams.getN().bitLength();
|
||||
|
||||
BigInteger k;
|
||||
do {
|
||||
k = new BigInteger(qBitLength, random);
|
||||
}
|
||||
while (k.equals(ECConstants.ZERO) || k.compareTo(ecParams.getN()) >= 0);
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
private void addFieldElement(Digest digest, ECFieldElement v) {
|
||||
byte[] p = BigIntegers.asUnsignedByteArray(curveLength, v.toBigInteger());
|
||||
|
||||
digest.update(p, 0, p.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* clear possible sensitive data
|
||||
*/
|
||||
private void clearBlock(
|
||||
byte[] block) {
|
||||
for (int i = 0; i != block.length; i++) {
|
||||
block[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
package com.ruoyi.common.utils.sm;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.apache.tomcat.util.codec.binary.Base64;
|
||||
import org.bouncycastle.asn1.gm.GMNamedCurves;
|
||||
import org.bouncycastle.asn1.x9.X9ECParameters;
|
||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
|
||||
import org.bouncycastle.crypto.params.*;
|
||||
import org.bouncycastle.math.ec.ECPoint;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* SM2 加解密攻击类
|
||||
*
|
||||
* 注意:为了兼容前端js库(原文转base64后加密),后端使用加密方法请先转Base64字符, 否则解密会失败!
|
||||
* @author ZYL
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
public class SM2Utils {
|
||||
public static void main(String[] args) {
|
||||
// SM2KeyPair sM2KeyPair = getSm2Keys(false);
|
||||
// System.out.println(sM2KeyPair.getPubKey());
|
||||
// System.out.println(sM2KeyPair.getPriKey());
|
||||
|
||||
|
||||
String a = Base64.encodeBase64String("123".getBytes());
|
||||
System.out.println("文本="+a);
|
||||
String aa = SM2Utils.encrypt(SM2crypto.pubKey, a, SM2EngineExtend.CIPHERMODE_BC);
|
||||
System.out.println("密文="+aa);
|
||||
String aaa = SM2Utils.decrypt(SM2crypto.priKey, aa, SM2EngineExtend.CIPHERMODE_BC);
|
||||
System.out.println("解密后="+aaa);
|
||||
}
|
||||
|
||||
/**
|
||||
* SM2加密算法
|
||||
* @param publicKey 公钥
|
||||
* @param data 待加密的数据
|
||||
* @return 密文,BC库产生的密文带由04标识符,与非BC库对接时需要去掉开头的04
|
||||
*/
|
||||
public static String encrypt(String publicKey, String data){
|
||||
// 按国密排序标准加密
|
||||
return encrypt(publicKey, data, SM2EngineExtend.CIPHERMODE_NORM);
|
||||
}
|
||||
|
||||
/**
|
||||
* SM2加密算法
|
||||
* @param publicKey 公钥
|
||||
* @param data 待加密的数据
|
||||
* @param cipherMode 密文排列方式0-C1C2C3;1-C1C3C2;
|
||||
* @return 密文,BC库产生的密文带由04标识符,与非BC库对接时需要去掉开头的04
|
||||
*/
|
||||
public static String encrypt(String publicKey, String data, int cipherMode){
|
||||
// 获取一条SM2曲线参数
|
||||
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
|
||||
// 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
|
||||
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
|
||||
//提取公钥点
|
||||
ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));
|
||||
// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
|
||||
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
|
||||
|
||||
SM2EngineExtend sm2Engine = new SM2EngineExtend();
|
||||
// 设置sm2为加密模式
|
||||
sm2Engine.init(true, cipherMode, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
|
||||
|
||||
byte[] arrayOfBytes = null;
|
||||
try {
|
||||
byte[] in = data.getBytes();
|
||||
arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
|
||||
} catch (Exception e) {
|
||||
log.error("SM2加密时出现异常:{}", e.getMessage(), e);
|
||||
}
|
||||
return Hex.toHexString(arrayOfBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取sm2密钥对
|
||||
* BC库使用的公钥=64个字节+1个字节(04标志位),BC库使用的私钥=32个字节
|
||||
* SM2秘钥的组成部分有 私钥D 、公钥X 、 公钥Y , 他们都可以用长度为64的16进制的HEX串表示,
|
||||
* <br/>SM2公钥并不是直接由X+Y表示 , 而是额外添加了一个头,当启用压缩时:公钥=有头+公钥X ,即省略了公钥Y的部分
|
||||
* @param compressed 是否压缩公钥(加密解密都使用BC库才能使用压缩)
|
||||
* @return
|
||||
*/
|
||||
public static SM2KeyPair getSm2Keys(boolean compressed){
|
||||
//获取一条SM2曲线参数
|
||||
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
|
||||
//构造domain参数
|
||||
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
|
||||
//1.创建密钥生成器
|
||||
ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
|
||||
//2.初始化生成器,带上随机数
|
||||
try {
|
||||
keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
log.error("生成公私钥对时出现异常:", e);
|
||||
}
|
||||
//3.生成密钥对
|
||||
AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
|
||||
ECPublicKeyParameters publicKeyParameters = (ECPublicKeyParameters)asymmetricCipherKeyPair.getPublic();
|
||||
ECPoint ecPoint = publicKeyParameters.getQ();
|
||||
// 把公钥放入map中,默认压缩公钥
|
||||
// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥,04的时候,可以去掉前面的04
|
||||
String publicKey = Hex.toHexString(ecPoint.getEncoded(compressed));
|
||||
ECPrivateKeyParameters privateKeyParameters = (ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate();
|
||||
BigInteger intPrivateKey = privateKeyParameters.getD();
|
||||
// 把私钥放入map中
|
||||
String privateKey = intPrivateKey.toString(16);
|
||||
return new SM2KeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* SM2解密算法
|
||||
* @param privateKey 私钥
|
||||
* @param cipherData 密文数据
|
||||
* @return
|
||||
*/
|
||||
public static String decrypt(String privateKey, String cipherData) {
|
||||
// // 按国密排序标准解密
|
||||
return decrypt(privateKey, cipherData, SM2EngineExtend.CIPHERMODE_NORM);
|
||||
}
|
||||
|
||||
/**
|
||||
* SM2解密算法
|
||||
* @param privateKey 私钥
|
||||
* @param cipherData 密文数据
|
||||
* @param cipherMode 密文排列方式0-C1C2C3;1-C1C3C2;
|
||||
* @return
|
||||
*/
|
||||
public static String decrypt(String privateKey, String cipherData, int cipherMode) {
|
||||
// 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
|
||||
if (!cipherData.startsWith("04")){
|
||||
cipherData = "04" + cipherData;
|
||||
}
|
||||
byte[] cipherDataByte = Hex.decode(cipherData);
|
||||
|
||||
//获取一条SM2曲线参数
|
||||
X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
|
||||
//构造domain参数
|
||||
ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
|
||||
|
||||
BigInteger privateKeyD = new BigInteger(privateKey, 16);
|
||||
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
|
||||
|
||||
SM2EngineExtend sm2Engine = new SM2EngineExtend();
|
||||
// 设置sm2为解密模式
|
||||
sm2Engine.init(false, cipherMode, privateKeyParameters);
|
||||
|
||||
String result = "";
|
||||
try {
|
||||
//processBlock得到Base64格式,记得解码
|
||||
byte[] arrayOfBytes = Base64.decodeBase64(sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length));
|
||||
return new String(arrayOfBytes);
|
||||
} catch (Exception e) {
|
||||
log.error("SM2解密时出现异常:{}", e.getMessage(), e);
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
static class SM2KeyPair{
|
||||
private String pubKey;
|
||||
private String priKey;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.ruoyi.common.utils.sm;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SM2crypto {
|
||||
public static String pubKey = "04ebe49444321237d1d2415427a78b73ab1661e11a511221ac635546f647e44347332857b37f59705c6b95dbd9fcf0188f830946ac156b64c0e30623c3a0500342";
|
||||
public static String priKey = "7a70604a24c35fbdd099a84307bff1196eb9955280cc98cadcd18442ba3e3a83";
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
package com.ruoyi.common.utils.sm;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 国密SM3,消息摘要(MD5)
|
||||
*/
|
||||
@Slf4j
|
||||
public class SM3Utils {
|
||||
private static char[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
public static final byte[] IV = {0x73, (byte) 0x80, 0x16, 0x6f, 0x49, 0x14, (byte) 0xb2, (byte) 0xb9, 0x17, 0x24, 0x42,
|
||||
(byte) 0xd7, (byte) 0xda, (byte) 0x8a, 0x06, 0x00, (byte) 0xa9, 0x6f, 0x30, (byte) 0xbc, (byte) 0x16, 0x31,
|
||||
0x38, (byte) 0xaa, (byte) 0xe3, (byte) 0x8d, (byte) 0xee, 0x4d, (byte) 0xb0, (byte) 0xfb, 0x0e, 0x4e};
|
||||
private static final Integer TJ_15 = Integer.valueOf("79cc4519", 16);
|
||||
private static final Integer TJ_63 = Integer.valueOf("7a879d8a", 16);
|
||||
private static final byte[] FirstPadding = {(byte) 0x80};
|
||||
private static final byte[] ZeroPadding = {(byte) 0x00};
|
||||
|
||||
private static int T(int j) {
|
||||
if (j >= 0 && j <= 15) {
|
||||
return TJ_15.intValue();
|
||||
} else if (j >= 16 && j <= 63) {
|
||||
return TJ_63.intValue();
|
||||
} else {
|
||||
throw new RuntimeException("data invalid");
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer FF(Integer x, Integer y, Integer z, int j) {
|
||||
if (j >= 0 && j <= 15) {
|
||||
return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue());
|
||||
} else if (j >= 16 && j <= 63) {
|
||||
return Integer.valueOf(
|
||||
(x.intValue() & y.intValue()) | (x.intValue() & z.intValue()) | (y.intValue() & z.intValue()));
|
||||
} else {
|
||||
throw new RuntimeException("data invalid");
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer GG(Integer x, Integer y, Integer z, int j) {
|
||||
if (j >= 0 && j <= 15) {
|
||||
return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue());
|
||||
} else if (j >= 16 && j <= 63) {
|
||||
return Integer.valueOf((x.intValue() & y.intValue()) | (~x.intValue() & z.intValue()));
|
||||
} else {
|
||||
throw new RuntimeException("data invalid");
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer P0(Integer x) {
|
||||
return Integer
|
||||
.valueOf(x.intValue() ^ Integer.rotateLeft(x.intValue(), 9) ^ Integer.rotateLeft(x.intValue(), 17));
|
||||
}
|
||||
|
||||
private static Integer P1(Integer x) {
|
||||
return Integer.valueOf(x.intValue() ^ Integer.rotateLeft(x.intValue(), 15) ^ Integer.rotateLeft(x.intValue(), 23));
|
||||
}
|
||||
|
||||
private static byte[] padding(byte[] source) throws IOException {
|
||||
if (source.length >= 0x2000000000000000L) {
|
||||
throw new RuntimeException("src data invalid.");
|
||||
}
|
||||
long l = source.length * 8;
|
||||
long k = 448 - (l + 1) % 512;
|
||||
if (k < 0) {
|
||||
k = k + 512;
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("k = " + k);
|
||||
}
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();) {
|
||||
baos.write(source);
|
||||
baos.write(FirstPadding);
|
||||
long i = k - 7;
|
||||
while (i > 0) {
|
||||
baos.write(ZeroPadding);
|
||||
i -= 8;
|
||||
}
|
||||
baos.write(long2bytes(l));
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("paded size = " + baos.size());
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] long2bytes(long l) {
|
||||
byte[] bytes = new byte[8];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
bytes[i] = (byte) (l >>> ((7 - i) * 8));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static String encodeSM3(String source) throws IOException {
|
||||
byte[] b = encodeSM3(source.getBytes());
|
||||
return byteToHexString(b);
|
||||
}
|
||||
|
||||
public static byte[] encodeSM3(byte[] source) throws IOException {
|
||||
byte[] m1 = padding(source);
|
||||
int n = m1.length / (512 / 8);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("n = " + n);
|
||||
}
|
||||
byte[] b;
|
||||
byte[] vi = IV.clone();
|
||||
byte[] vi1 = null;
|
||||
for (int i = 0; i < n; i++) {
|
||||
b = Arrays.copyOfRange(m1, i * 64, (i + 1) * 64);
|
||||
vi1 = CF(vi, b);
|
||||
vi = vi1;
|
||||
}
|
||||
return vi1;
|
||||
}
|
||||
|
||||
private static byte[] CF(byte[] vi, byte[] bi) throws IOException {
|
||||
int a, b, c, d, e, f, g, h;
|
||||
a = toInteger(vi, 0);
|
||||
b = toInteger(vi, 1);
|
||||
c = toInteger(vi, 2);
|
||||
d = toInteger(vi, 3);
|
||||
e = toInteger(vi, 4);
|
||||
f = toInteger(vi, 5);
|
||||
g = toInteger(vi, 6);
|
||||
h = toInteger(vi, 7);
|
||||
|
||||
int[] w = new int[68];
|
||||
int[] w1 = new int[64];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
w[i] = toInteger(bi, i);
|
||||
}
|
||||
for (int j = 16; j < 68; j++) {
|
||||
w[j] = P1(w[j - 16] ^ w[j - 9] ^ Integer.rotateLeft(w[j - 3], 15)) ^ Integer.rotateLeft(w[j - 13], 7)
|
||||
^ w[j - 6];
|
||||
}
|
||||
for (int j = 0; j < 64; j++) {
|
||||
w1[j] = w[j] ^ w[j + 4];
|
||||
}
|
||||
int ss1, ss2, tt1, tt2;
|
||||
for (int j = 0; j < 64; j++) {
|
||||
ss1 = Integer.rotateLeft(Integer.rotateLeft(a, 12) + e + Integer.rotateLeft(T(j), j), 7);
|
||||
ss2 = ss1 ^ Integer.rotateLeft(a, 12);
|
||||
tt1 = FF(a, b, c, j) + d + ss2 + w1[j];
|
||||
tt2 = GG(e, f, g, j) + h + ss1 + w[j];
|
||||
d = c;
|
||||
c = Integer.rotateLeft(b, 9);
|
||||
b = a;
|
||||
a = tt1;
|
||||
h = g;
|
||||
g = Integer.rotateLeft(f, 19);
|
||||
f = e;
|
||||
e = P0(tt2);
|
||||
}
|
||||
byte[] v = toByteArray(a, b, c, d, e, f, g, h);
|
||||
for (int i = 0; i < v.length; i++) {
|
||||
v[i] = (byte) (v[i] ^ vi[i]);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
private static int toInteger(byte[] source, int index) {
|
||||
StringBuilder valueStr = new StringBuilder("");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
valueStr.append(chars[(byte) ((source[index * 4 + i] & 0xF0) >> 4)]);
|
||||
valueStr.append(chars[(byte) (source[index * 4 + i] & 0x0F)]);
|
||||
}
|
||||
return Long.valueOf(valueStr.toString(), 16).intValue();
|
||||
|
||||
}
|
||||
|
||||
private static byte[] toByteArray(int a, int b, int c, int d, int e, int f, int g, int h) throws IOException {
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(32);) {
|
||||
baos.write(toByteArray(a));
|
||||
baos.write(toByteArray(b));
|
||||
baos.write(toByteArray(c));
|
||||
baos.write(toByteArray(d));
|
||||
baos.write(toByteArray(e));
|
||||
baos.write(toByteArray(f));
|
||||
baos.write(toByteArray(g));
|
||||
baos.write(toByteArray(h));
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] toByteArray(int i) {
|
||||
byte[] byteArray = new byte[4];
|
||||
byteArray[0] = (byte) (i >>> 24);
|
||||
byteArray[1] = (byte) ((i & 0xFFFFFF) >>> 16);
|
||||
byteArray[2] = (byte) ((i & 0xFFFF) >>> 8);
|
||||
byteArray[3] = (byte) (i & 0xFF);
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
private static String byteToHexString(byte[] bytes)
|
||||
{
|
||||
StringBuilder resultHexString = new StringBuilder();
|
||||
String tempStr;
|
||||
for (byte b: bytes) {
|
||||
//这里需要对b与0xff做位与运算,
|
||||
//若b为负数,强制转换将高位位扩展,导致错误,
|
||||
//故需要高位清零
|
||||
tempStr = Integer.toHexString(b & 0xff);
|
||||
//若转换后的十六进制数字只有一位,
|
||||
//则在前补"0"
|
||||
if (tempStr.length() == 1) {
|
||||
resultHexString.append(0).append(tempStr);
|
||||
} else {
|
||||
resultHexString.append(tempStr);
|
||||
}
|
||||
}
|
||||
return resultHexString.toString();
|
||||
}
|
||||
|
||||
private SM3Utils() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package com.ruoyi.common.utils.sm;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import org.apache.tomcat.util.codec.binary.Base64;
|
||||
import org.bouncycastle.crypto.digests.SM3Digest;
|
||||
|
||||
/**
|
||||
* SM3加密
|
||||
*/
|
||||
public class SM3crypto {
|
||||
public static String getSaltStr() {
|
||||
return Base64.encodeBase64String(getSalt());
|
||||
}
|
||||
public static byte[] getSalt() {
|
||||
/*
|
||||
* 随机生成128位的随机数
|
||||
*/
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte bytes1[] = new byte[16];
|
||||
random.nextBytes(bytes1);
|
||||
return bytes1;
|
||||
}
|
||||
|
||||
public static String pwdSaltedHashValueStr(String salt, String passwdString) {
|
||||
return Base64.encodeBase64String(pwdSaltedHashValue(Base64.decodeBase64(salt), passwdString));
|
||||
}
|
||||
public static String pwdSaltedHashValueStr(byte[] salt, String passwdString) {
|
||||
return Base64.encodeBase64String(pwdSaltedHashValue(salt, passwdString));
|
||||
}
|
||||
public static byte[] pwdSaltedHashValue(byte[] bytes1, String passwdString) {
|
||||
// sm3加密密码
|
||||
try {
|
||||
passwdString = SM3Utils.encodeSM3(passwdString);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
/*
|
||||
* 加盐:即随机数和口令组合
|
||||
*/
|
||||
byte passwdbyte[] = arraycat(bytes1, passwdString.getBytes());
|
||||
// SM3计算
|
||||
SM3Digest mdDigest = new SM3Digest();
|
||||
mdDigest.update(passwdbyte, 0, passwdbyte.length);
|
||||
byte[] result = new byte[mdDigest.getDigestSize()];
|
||||
mdDigest.doFinal(result, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* 拼接buf1和buf2数组
|
||||
*/
|
||||
public static byte[] arraycat(byte[] buf1, byte[] buf2) {
|
||||
byte[] bufret = null;
|
||||
int len1 = 0;
|
||||
int len2 = 0;
|
||||
if (buf1 != null)
|
||||
len1 = buf1.length;
|
||||
if (buf2 != null)
|
||||
len2 = buf2.length;
|
||||
if (len1 + len2 > 0)
|
||||
bufret = new byte[len1 + len2];
|
||||
if (len1 > 0)
|
||||
System.arraycopy(buf1, 0, bufret, 0, len1);
|
||||
if (len2 > 0)
|
||||
System.arraycopy(buf2, 0, bufret, len1, len2);
|
||||
return bufret;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
package com.ruoyi.common.utils.sm;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.Security;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
|
||||
|
||||
public class SM4Utils {
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
public static final String[] POOL = new String[]{"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
|
||||
private static final String ENCODING = "UTF-8";
|
||||
public static final String ALGORITHM_NAME = "SM4";
|
||||
|
||||
// 加密算法/分组加密模式/分组填充方式
|
||||
// PKCS5Padding-以8个字节为一组进行分组加密
|
||||
// 定义分组加密模式使用:PKCS5Padding
|
||||
public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
System.out.println("开始测试SM4加密解密====================");
|
||||
String json = "18511921736";
|
||||
System.out.println("加密前:" + json);
|
||||
//自定义的32位16进制秘钥
|
||||
String key = SM4Utils.generateKeyStr();
|
||||
//sm4加密
|
||||
String cipher = SM4Utils.encryptEcb(key, json);
|
||||
System.out.println("加密后:" + cipher);
|
||||
//校验加密前后是否为同一数据
|
||||
System.out.println("校验:" + SM4Utils.verifyEcb(key, cipher, json));
|
||||
//解密
|
||||
json = SM4Utils.decryptEcb(key, cipher);
|
||||
System.out.println("解密后:" + json);
|
||||
System.out.println("结束===================");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成字符串
|
||||
* @return 生成的32位长度的16进制字符串
|
||||
*/
|
||||
public static String generateKeyStr(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 32; i++) {
|
||||
sb.append(POOL[random.nextInt(POOL.length)]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* sm4加密
|
||||
*
|
||||
* @param hexKey 16进制密钥(忽略大小写)
|
||||
* @param paramStr 待加密字符串
|
||||
* @return 返回16进制的加密字符串
|
||||
* @throws Exception
|
||||
* @explain 加密模式:ECB 密文长度不固定,会随着被加密字符串长度的变化而变化
|
||||
*/
|
||||
public static String encryptEcb(String hexKey, String paramStr) throws Exception {
|
||||
String cipherText = "";
|
||||
// 16进制字符串-->byte[]
|
||||
byte[] keyData = ByteUtils.fromHexString(hexKey);
|
||||
// String-->byte[]
|
||||
byte[] srcData = paramStr.getBytes(ENCODING);
|
||||
// 加密后的数组
|
||||
byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData);
|
||||
// byte[]-->hexString
|
||||
cipherText = ByteUtils.toHexString(cipherArray);
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密模式之Ecb
|
||||
*
|
||||
* @param key
|
||||
* @param data
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) throws Exception {
|
||||
Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);//声称Ecb暗号,通过第二个参数判断加密还是解密
|
||||
return cipher.doFinal(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成ECB暗号
|
||||
*
|
||||
* @param algorithmName 算法名称
|
||||
* @param mode 模式
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @explain ECB模式(电子密码本模式:Electronic codebook)
|
||||
*/
|
||||
private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
|
||||
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
|
||||
cipher.init(mode, sm4Key);
|
||||
return cipher;
|
||||
}
|
||||
|
||||
//解密****************************************
|
||||
|
||||
/**
|
||||
* sm4解密
|
||||
*
|
||||
* @param hexKey 16进制密钥
|
||||
* @param cipherText 16进制的加密字符串(忽略大小写)
|
||||
* @return 解密后的字符串
|
||||
* @throws Exception
|
||||
* @explain 解密模式:采用ECB
|
||||
*/
|
||||
public static String decryptEcb(String hexKey, String cipherText) throws Exception {
|
||||
// 用于接收解密后的字符串
|
||||
String decryptStr = "";
|
||||
// hexString-->byte[]
|
||||
byte[] keyData = ByteUtils.fromHexString(hexKey);
|
||||
// hexString-->byte[]
|
||||
byte[] cipherData = ByteUtils.fromHexString(cipherText);
|
||||
// 解密
|
||||
byte[] srcData = decrypt_Ecb_Padding(keyData, cipherData);
|
||||
// byte[]-->String
|
||||
decryptStr = new String(srcData, ENCODING);
|
||||
return decryptStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param key
|
||||
* @param cipherText
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @explain
|
||||
*/
|
||||
public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText) throws Exception {
|
||||
Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);//生成Ecb暗号,通过第二个参数判断加密还是解密
|
||||
return cipher.doFinal(cipherText);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验加密前后的字符串是否为同一数据
|
||||
*
|
||||
* @param hexKey 16进制密钥(忽略大小写)
|
||||
* @param cipherText 16进制加密后的字符串
|
||||
* @param paramStr 加密前的字符串
|
||||
* @return 是否为同一数据
|
||||
* @throws Exception
|
||||
* @explain
|
||||
*/
|
||||
public static boolean verifyEcb(String hexKey, String cipherText, String paramStr) throws Exception {
|
||||
// 用于接收校验结果
|
||||
boolean flag = false;
|
||||
// hexString-->byte[]
|
||||
byte[] keyData = ByteUtils.fromHexString(hexKey);
|
||||
// 将16进制字符串转换成数组
|
||||
byte[] cipherData = ByteUtils.fromHexString(cipherText);
|
||||
// 解密
|
||||
byte[] decryptData = decrypt_Ecb_Padding(keyData, cipherData);
|
||||
// 将原字符串转换成byte[]
|
||||
byte[] srcData = paramStr.getBytes(ENCODING);
|
||||
// 判断2个数组是否一致
|
||||
flag = Arrays.equals(decryptData, srcData);
|
||||
return flag;
|
||||
}
|
||||
}
|
17
pom.xml
|
@ -26,12 +26,13 @@
|
|||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
|
||||
<shiro.version>1.8.0</shiro.version>
|
||||
<shiro.version>1.12.0</shiro.version>
|
||||
<thymeleaf.extras.shiro.version>2.1.0</thymeleaf.extras.shiro.version>
|
||||
<druid.version>1.2.16</druid.version>
|
||||
<bitwalker.version>1.21</bitwalker.version>
|
||||
<kaptcha.version>2.3.2</kaptcha.version>
|
||||
<swagger.version>3.0.0</swagger.version>
|
||||
<springboot.version>2.7.12</springboot.version>
|
||||
<mybatis-spring-boot.version>2.2.2</mybatis-spring-boot.version>
|
||||
<pagehelper.boot.version>1.4.1</pagehelper.boot.version>
|
||||
<fastjson.version>1.2.79</fastjson.version>
|
||||
|
@ -81,10 +82,15 @@
|
|||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>2.7.12</version>
|
||||
<version>${springboot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${springboot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 阿里数据库连接池 -->
|
||||
<dependency>
|
||||
|
@ -236,6 +242,13 @@
|
|||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.17</version>
|
||||
</dependency>
|
||||
|
||||
<!--CAS-->
|
||||
<dependency>
|
||||
<groupId>org.jasig.cas.client</groupId>
|
||||
<artifactId>cas-client-core</artifactId>
|
||||
<version>3.6.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</dependencyManagement>
|
||||
|
|