单点登录接口

This commit is contained in:
zhangqinbin
2022-06-16 08:23:07 +08:00
parent 70a5ed5ed8
commit 5551af646f
11 changed files with 1425 additions and 0 deletions

Binary file not shown.

View File

@ -88,6 +88,13 @@
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>org</groupId>
<artifactId>bouncycastle</artifactId>
<version>1.65.8</version>
<scope>system</scope>
<systemPath>${basedir}/lib/bcprov-jdk15to18-1.65.jar</systemPath>
</dependency>
</dependencies>
<repositories>

View File

@ -0,0 +1,457 @@
package com.chinaunicom.mall.ebtp.extend.singlePoint.common;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X962Parameters;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9ECPoint;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.util.io.pem.PemWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
/**
* 这个工具类的方法也适用于其他基于BC库的ECC算法
*
* @author renxx20
*/
public final class BcecUtil {
private static final Logger logger = LoggerFactory.getLogger(BcecUtil.class);
private static final String ALGO_NAME_EC = "EC";
private static final String PEM_STRING_PUBLIC = "PUBLIC KEY";
private static final String PEM_STRING_ECPRIVATEKEY = "EC PRIVATE KEY";
private BcecUtil() {
throw new IllegalStateException("Utility class");
}
/**
* 生成ECC密钥对
*
* @return ECC密钥对
*/
public static AsymmetricCipherKeyPair generateKeyPairParameter(ECDomainParameters domainParameters,
SecureRandom random) {
ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParameters,
random);
ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
keyGen.init(keyGenerationParams);
return keyGen.generateKeyPair();
}
public static KeyPair generateKeyPair(ECDomainParameters domainParameters, SecureRandom random) {
try {
KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
ECParameterSpec parameterSpec = new ECParameterSpec(domainParameters.getCurve(), domainParameters.getG(),
domainParameters.getN(), domainParameters.getH());
kpg.initialize(parameterSpec, random);
return kpg.generateKeyPair();
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
logger.error(e.getLocalizedMessage(), e);
}
return null;
}
public static int getCurveLength(ECKeyParameters ecKey) {
return getCurveLength(ecKey.getParameters());
}
public static int getCurveLength(ECDomainParameters domainParams) {
return (domainParams.getCurve().getFieldSize() + 7) / 8;
}
public static byte[] fixToCurveLengthBytes(int curveLength, byte[] src) {
if (src.length == curveLength) {
return src;
}
byte[] result = new byte[curveLength];
if (src.length > curveLength) {
System.arraycopy(src, src.length - result.length, result, 0, result.length);
} else {
System.arraycopy(src, 0, result, result.length - src.length, src.length);
}
return result;
}
public static ECPrivateKeyParameters createEcPrivateKeyParameters(BigInteger d,
ECDomainParameters domainParameters) {
return new ECPrivateKeyParameters(d, domainParameters);
}
public static ECPublicKeyParameters createEcPublicKeyParameters(BigInteger x, BigInteger y,
ECCurve curve,
ECDomainParameters domainParameters) {
return createEcPublicKeyParameters(x.toByteArray(), y.toByteArray(), curve, domainParameters);
}
public static ECPublicKeyParameters createEcPublicKeyParameters(String xHex, String yHex,
ECCurve curve,
ECDomainParameters domainParameters) {
return createEcPublicKeyParameters(ByteUtils.fromHexString(xHex), ByteUtils.fromHexString(yHex),
curve, domainParameters);
}
public static ECPublicKeyParameters createEcPublicKeyParameters(byte[] xBytes, byte[] yBytes,
ECCurve curve,
ECDomainParameters domainParameters) {
final byte UNCOMPRESSEDFLAG = 0x04;
int curveLength = getCurveLength(domainParameters);
xBytes = fixToCurveLengthBytes(curveLength, xBytes);
yBytes = fixToCurveLengthBytes(curveLength, yBytes);
byte[] encodedPubKey = new byte[1 + xBytes.length + yBytes.length];
encodedPubKey[0] = UNCOMPRESSEDFLAG;
System.arraycopy(xBytes, 0, encodedPubKey, 1, xBytes.length);
System.arraycopy(yBytes, 0, encodedPubKey, 1 + xBytes.length, yBytes.length);
return new ECPublicKeyParameters(curve.decodePoint(encodedPubKey), domainParameters);
}
public static ECPrivateKeyParameters convertPrivateKeyToParameters(BCECPrivateKey ecPriKey) {
ECParameterSpec parameterSpec = ecPriKey.getParameters();
ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(),
parameterSpec.getN(), parameterSpec.getH());
return new ECPrivateKeyParameters(ecPriKey.getD(), domainParameters);
}
public static ECPublicKeyParameters convertPublicKeyToParameters(BCECPublicKey ecPubKey) {
ECParameterSpec parameterSpec = ecPubKey.getParameters();
ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(),
parameterSpec.getN(), parameterSpec.getH());
return new ECPublicKeyParameters(ecPubKey.getQ(), domainParameters);
}
public static BCECPublicKey createPublicKeyFromSubjectPublicKeyInfo(SubjectPublicKeyInfo subPubInfo) {
try {
return BcecUtil.convertX509ToEcPublicKey(subPubInfo.toASN1Primitive().getEncoded(ASN1Encoding.DER));
} catch (IOException e) {
logger.error(e.getLocalizedMessage(), e);
}
return null;
}
/**
* 将ECC私钥转换为PKCS8标准的字节流
*
* @param priKey
* @param pubKey 可以为空但是如果为空的话得到的结果OpenSSL可能解析不了
* @return
*/
public static byte[] convertEcPrivateKeyToPkcs8(ECPrivateKeyParameters priKey,
ECPublicKeyParameters pubKey) {
ECDomainParameters domainParams = priKey.getParameters();
ECParameterSpec spec = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(),
domainParams.getN(), domainParams.getH());
BCECPublicKey publicKey = null;
if (pubKey != null) {
publicKey = new BCECPublicKey(ALGO_NAME_EC, pubKey, spec,
BouncyCastleProvider.CONFIGURATION);
}
BCECPrivateKey privateKey = new BCECPrivateKey(ALGO_NAME_EC, priKey, publicKey,
spec, BouncyCastleProvider.CONFIGURATION);
return privateKey.getEncoded();
}
/**
* 将PKCS8标准的私钥字节流转换为私钥对象
*
* @param pkcs8Key
* @return
*/
public static BCECPrivateKey convertPkcs8ToEcPrivateKey(byte[] pkcs8Key) {
PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(pkcs8Key);
try {
KeyFactory kf = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
return (BCECPrivateKey) kf.generatePrivate(peks);
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException e) {
logger.error(e.getLocalizedMessage(), e);
}
return null;
}
/**
* 将PKCS8标准的私钥字节流转换为PEM
*
* @param encodedKey
* @return
* @throws IOException
*/
public static String convertEcPrivateKeyPkcs8ToPem(byte[] encodedKey) throws IOException {
return convertEncodedDataToPem(PEM_STRING_ECPRIVATEKEY, encodedKey);
}
/**
* 将PEM格式的私钥转换为PKCS8标准字节流
*
* @param pemString
* @return
* @throws IOException
*/
public static byte[] convertEcPrivateKeyPemToPkcs8(String pemString) throws IOException {
return convertPemToEncodedData(pemString);
}
/**
* 将ECC私钥转换为SEC1标准的字节流
* openssl d2i_ECPrivateKey函数要求的DER编码的私钥也是SEC1标准的
* 这个工具函数的主要目的就是为了能生成一个openssl可以直接“识别”的ECC私钥.
* 相对RSA私钥的PKCS1标准ECC私钥的标准为SEC1
*
* @param priKey
* @param pubKey
* @return
* @throws IOException
*/
public static byte[] convertEcPrivateKeyToSec1(ECPrivateKeyParameters priKey,
ECPublicKeyParameters pubKey) throws IOException {
byte[] pkcs8Bytes = convertEcPrivateKeyToPkcs8(priKey, pubKey);
PrivateKeyInfo pki = PrivateKeyInfo.getInstance(pkcs8Bytes);
ASN1Encodable encodable = pki.parsePrivateKey();
ASN1Primitive primitive = encodable.toASN1Primitive();
byte[] sec1Bytes = primitive.getEncoded();
logger.info(Arrays.toString(sec1Bytes));
return sec1Bytes;
}
/**
* 将SEC1标准的私钥字节流恢复为PKCS8标准的字节流
*
* @param sec1Key
* @return
* @throws IOException
*/
public static byte[] convertEcPrivateKeySec1ToPkcs8(byte[] sec1Key) throws IOException {
/**
* 参考org.bouncycastle.asn1.pkcs.PrivateKeyInfo和
* org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey逆向拼装
*/
X962Parameters params = getDomainParametersFromName(Sm2Util.JDK_EC_SPEC, false);
ASN1OctetString privKey = new DEROctetString(sec1Key);
ASN1EncodableVector v = new ASN1EncodableVector();
//版本号
v.add(new ASN1Integer(0));
//算法标识
v.add(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params));
v.add(privKey);
DERSequence ds = new DERSequence(v);
return ds.getEncoded(ASN1Encoding.DER);
}
/**
* 将SEC1标准的私钥字节流转为BCECPrivateKey对象
*
* @param sec1Key
* @return
*/
public static BCECPrivateKey convertSec1ToBcecPrivateKey(byte[] sec1Key) {
try {
PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(convertEcPrivateKeySec1ToPkcs8(sec1Key));
KeyFactory kf = KeyFactory.getInstance(ALGO_NAME_EC, BouncyCastleProvider.PROVIDER_NAME);
return (BCECPrivateKey) kf.generatePrivate(peks);
} catch (NoSuchAlgorithmException | IOException | NoSuchProviderException | InvalidKeySpecException e) {
logger.error(e.getLocalizedMessage(), e);
}
return null;
}
/**
* 将SEC1标准的私钥字节流转为ECPrivateKeyParameters对象
* openssl i2d_ECPrivateKey函数生成的DER编码的ecc私钥是SEC1标准的、带有EC_GROUP、带有公钥的
* 这个工具函数的主要目的就是为了使Java程序能够“识别”openssl生成的ECC私钥
*
* @param sec1Key
* @return
*/
public static ECPrivateKeyParameters convertSec1ToEcPrivateKey(byte[] sec1Key) {
BCECPrivateKey privateKey = convertSec1ToBcecPrivateKey(sec1Key);
if (privateKey == null) {
return null;
}
return convertPrivateKeyToParameters(privateKey);
}
/**
* 将ECC公钥对象转换为X509标准的字节流
*
* @param pubKey
* @return
*/
public static byte[] convertEcPublicKeyToX509(ECPublicKeyParameters pubKey) {
ECDomainParameters domainParams = pubKey.getParameters();
ECParameterSpec spec = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(),
domainParams.getN(), domainParams.getH());
BCECPublicKey publicKey = new BCECPublicKey(ALGO_NAME_EC, pubKey, spec,
BouncyCastleProvider.CONFIGURATION);
return publicKey.getEncoded();
}
/**
* 将X509标准的公钥字节流转为公钥对象
*
* @param x509Bytes
* @return
* @throws NoSuchProviderException
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
public static BCECPublicKey convertX509ToEcPublicKey(byte[] x509Bytes) {
try {
X509EncodedKeySpec eks = new X509EncodedKeySpec(x509Bytes);
KeyFactory kf = KeyFactory.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
return (BCECPublicKey) kf.generatePublic(eks);
} catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException e) {
logger.error(e.getLocalizedMessage(), e);
}
return null;
}
/**
* 将X509标准的公钥字节流转为PEM
*
* @param encodedKey
* @return
* @throws IOException
*/
public static String convertEcPublicKeyX509ToPem(byte[] encodedKey) throws IOException {
return convertEncodedDataToPem(PEM_STRING_PUBLIC, encodedKey);
}
/**
* 将PEM格式的公钥转为X509标准的字节流
*
* @param pemString
* @return
* @throws IOException
*/
public static byte[] convertEcPublicKeyPemToX509(String pemString) throws IOException {
return convertPemToEncodedData(pemString);
}
/**
* copy from BC
*
* @param genSpec
* @return
*/
public static X9ECParameters getDomainParametersFromGenSpec(ECGenParameterSpec genSpec) {
return getDomainParametersFromName(genSpec.getName());
}
/**
* copy from BC
*
* @param curveName
* @return
*/
public static X9ECParameters getDomainParametersFromName(String curveName) {
X9ECParameters domainParameters;
try {
char zero = '0';
char two = '2';
char nullStr = ' ';
if (curveName.charAt(0) >= zero && curveName.charAt(0) <= two) {
ASN1ObjectIdentifier oidId = new ASN1ObjectIdentifier(curveName);
domainParameters = ECUtil.getNamedCurveByOid(oidId);
} else {
if (curveName.indexOf(nullStr) > -1) {
curveName = curveName.substring(curveName.indexOf(' ') + 1);
domainParameters = ECUtil.getNamedCurveByName(curveName);
} else {
domainParameters = ECUtil.getNamedCurveByName(curveName);
}
}
} catch (IllegalArgumentException ex) {
domainParameters = ECUtil.getNamedCurveByName(curveName);
}
return domainParameters;
}
/**
* copy from BC
* 405行之后的注释内容
* // 如果是1.62或更低版本的bcprov-jdk15on应该使用以下这段代码
* 因为高版本的EC5Util.convertPoint没有向下兼容
* X9ECParameters ecP = new X9ECParameters(curve,EC5Util.convertPoint(curve, ecSpec.getGenerator(),
* withCompression),ecSpec.getOrder(),BigInteger.valueOf(ecSpec.getCofactor()),ecSpec.getCurve().getSeed())
*
* @param ecSpec
* @param withCompression
* @return
*/
public static X962Parameters getDomainParametersFromName(java.security.spec.ECParameterSpec ecSpec,
boolean withCompression) {
X962Parameters params;
if (ecSpec instanceof ECNamedCurveSpec) {
ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec) ecSpec).getName());
if (curveOid == null) {
curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec) ecSpec).getName());
}
params = new X962Parameters(curveOid);
} else if (ecSpec == null) {
params = new X962Parameters(DERNull.INSTANCE);
} else {
ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
X9ECParameters ecP = new X9ECParameters(
curve,
new X9ECPoint(EC5Util.convertPoint(curve, ecSpec.getGenerator()), withCompression),
ecSpec.getOrder(),
BigInteger.valueOf(ecSpec.getCofactor()),
ecSpec.getCurve().getSeed());
params = new X962Parameters(ecP);
}
return params;
}
private static String convertEncodedDataToPem(String type, byte[] encodedData) throws IOException {
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PemWriter pWrt = new PemWriter(new OutputStreamWriter(bOut));
try {
PemObject pemObj = new PemObject(type, encodedData);
pWrt.writeObject(pemObj);
} finally {
pWrt.close();
}
return new String(bOut.toByteArray());
}
private static byte[] convertPemToEncodedData(String pemString) throws IOException {
ByteArrayInputStream bIn = new ByteArrayInputStream(pemString.getBytes());
PemReader pRdr = new PemReader(new InputStreamReader(bIn));
try {
PemObject pemObject = pRdr.readPemObject();
return pemObject.getContent();
} finally {
pRdr.close();
}
}
}

View File

@ -0,0 +1,23 @@
package com.chinaunicom.mall.ebtp.extend.singlePoint.common;
import groovy.util.logging.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.Security;
/**
* @author renxx20
*/
@Slf4j
public class GmBaseUtil {
private static final Logger logger = LoggerFactory.getLogger(GmBaseUtil.class);
protected GmBaseUtil() {
logger.info("GmBaseUtil");
}
static {
Security.addProvider(new BouncyCastleProvider());
}
}

View File

@ -0,0 +1,58 @@
package com.chinaunicom.mall.ebtp.extend.singlePoint.common;
/**
* @author renxx20
*/
public class Sm2Cipher {
/**
* ECC密钥
*/
private byte[] c1;
/**
* 真正的密文
*/
private byte[] c2;
/**
* 对c1+c2的SM3-HASH值
*/
private byte[] c3;
/**
* SM2标准的密文c1+c2+c3
*/
private byte[] cipherText;
public byte[] getC1() {
return c1;
}
public void setC1(byte[] c1) {
this.c1 = c1;
}
public byte[] getC2() {
return c2;
}
public void setC2(byte[] c2) {
this.c2 = c2;
}
public byte[] getC3() {
return c3;
}
public void setC3(byte[] c3) {
this.c3 = c3;
}
public byte[] getCipherText() {
return cipherText;
}
public void setCipherText(byte[] cipherText) {
this.cipherText = cipherText;
}
}

View File

@ -0,0 +1,105 @@
package com.chinaunicom.mall.ebtp.extend.singlePoint.common;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
/**
* 加密/解密工具类
*
* @author renxx20
* @version V1.0
* @date 2020-05-10
*/
@Slf4j
public class Sm2Encryptor {
private static final Logger logger = LoggerFactory.getLogger(Sm2Encryptor.class);
/**
* 使用给定的私钥解密给定的字
* 前端密文解密 使用
*
* @param privateKeyStr sessionId
* @param encryptText 密文。
* @return 原文字符串。
*/
public String decryptString(String privateKeyStr, String encryptText) throws InvalidCipherTextException {
ECPrivateKeyParameters priKey =
new ECPrivateKeyParameters(new BigInteger(ByteUtils.fromHexString(privateKeyStr)),
Sm2Util.DOMAIN_PARAMS);
byte[] decryptedData = Sm2Util.decrypt(priKey, fromHexString(encryptText));
return new String(decryptedData, StandardCharsets.UTF_8);
}
/**
* 公钥加密
*
* @param data 待加密字符串
* @param publicKeyStr 公钥
* @return 加密后字符串
*/
public String encryptString(String data, String publicKeyStr) throws InvalidCipherTextException {
String publicXandY = publicKeyStr.substring(2);
int selfLen = publicXandY.length() / 2;
int len = publicXandY.length();
String xHex = publicXandY.substring(0, selfLen);
String yHex = publicXandY.substring(selfLen, len);
ECPublicKeyParameters pubKey
= BcecUtil.createEcPublicKeyParameters(xHex, yHex, Sm2Util.CURVE, Sm2Util.DOMAIN_PARAMS);
byte[] encryptedData = Sm2Util.encrypt(pubKey, data.getBytes());
return ByteUtils.toHexString(encryptedData);
}
/**
* 生成秘钥, 供工具类调用
*/
public void generateKey() {
//生成 SM2密钥对
AsymmetricCipherKeyPair keyPair = Sm2Util.generateKeyPairParameter();
ECPrivateKeyParameters priKey = (ECPrivateKeyParameters) keyPair.getPrivate();
ECPublicKeyParameters pubKey = (ECPublicKeyParameters) keyPair.getPublic();
String privateKey = ByteUtils.toHexString(priKey.getD().toByteArray()).toUpperCase();
String publicKey = ByteUtils.toHexString(pubKey.getQ().getEncoded(false)).toUpperCase();
logger.info("SM2公钥请配置在unifast.security.public-key: {}", publicKey);
logger.info("SM2私钥请配置在unifast.security.private-key: : {}", privateKey);
}
public static void main(String[] args) {
Sm2Encryptor encryptor = new Sm2Encryptor();
encryptor.generateKey();
}
/**
* 将16进制字符串转换为byte[]
* 16进制字符串不区分大小写返回的数组相同
*
* @param hexString 16进制字符串
* @return byte[]
*/
private static byte[] fromHexString(String hexString) {
if (null == hexString || "".equals(hexString.trim())) {
return new byte[0];
}
byte[] bytes = new byte[hexString.length() / 2];
// 16进制字符串
String hex;
int two = 2;
for (int i = 0; i < hexString.length() / two; i++) {
// 每次截取2位
hex = hexString.substring(i * 2, i * 2 + 2);
// 16进制-->十进制
bytes[i] = (byte) Integer.parseInt(hex, 16);
}
return bytes;
}
}

View File

@ -0,0 +1,545 @@
package com.chinaunicom.mall.ebtp.extend.singlePoint.common;
import org.bouncycastle.asn1.*;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.engines.SM2Engine.Mode;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.custom.gm.SM2P256V1Curve;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.SecureRandom;
import java.security.spec.ECFieldFp;
import java.security.spec.EllipticCurve;
/**
* @author renxx20
*/
public class Sm2Util extends GmBaseUtil {
//////////////////////////////////////////////////////////////////////////////////////
/**
* 以下为SM2推荐曲线参数
*/
public static final SM2P256V1Curve CURVE = new SM2P256V1Curve();
public static final BigInteger SM2_ECC_P = CURVE.getQ();
public static final BigInteger SM2_ECC_A = CURVE.getA().toBigInteger();
public static final BigInteger SM2_ECC_B = CURVE.getB().toBigInteger();
public static final BigInteger SM2_ECC_N = CURVE.getOrder();
public static final BigInteger SM2_ECC_H = CURVE.getCofactor();
public static final BigInteger SM2_ECC_GX = new BigInteger(
"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
public static final BigInteger SM2_ECC_GY = new BigInteger(
"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);
public static final ECPoint G_POINT = CURVE.createPoint(SM2_ECC_GX, SM2_ECC_GY);
public static final ECDomainParameters DOMAIN_PARAMS = new ECDomainParameters(CURVE, G_POINT,
SM2_ECC_N, SM2_ECC_H);
public static final int CURVE_LEN = BcecUtil.getCurveLength(DOMAIN_PARAMS);
//////////////////////////////////////////////////////////////////////////////////////
public static final EllipticCurve JDK_CURVE = new EllipticCurve(new ECFieldFp(SM2_ECC_P), SM2_ECC_A, SM2_ECC_B);
public static final java.security.spec.ECPoint JDK_G_POINT = new java.security.spec.ECPoint(
G_POINT.getAffineXCoord().toBigInteger(), G_POINT.getAffineYCoord().toBigInteger());
public static final java.security.spec.ECParameterSpec JDK_EC_SPEC = new java.security.spec.ECParameterSpec(
JDK_CURVE, JDK_G_POINT, SM2_ECC_N, SM2_ECC_H.intValue());
//////////////////////////////////////////////////////////////////////////////////////
public static final int SM3_DIGEST_LENGTH = 32;
private static final String UNSUPPORTEDMODE = "Unsupported mode:";
/**
* 生成ECC密钥对
*
* @return ECC密钥对
*/
public static AsymmetricCipherKeyPair generateKeyPairParameter() {
SecureRandom random = new SecureRandom();
return BcecUtil.generateKeyPairParameter(DOMAIN_PARAMS, random);
}
public static KeyPair generateKeyPair() {
SecureRandom random = new SecureRandom();
return BcecUtil.generateKeyPair(DOMAIN_PARAMS, random);
}
/**
* 只获取私钥里的d32字节
*
* @param privateKey
* @return
*/
public static byte[] getRawPrivateKey(BCECPrivateKey privateKey) {
return fixToCurveLengthBytes(privateKey.getD().toByteArray());
}
/**
* 只获取公钥里的XY分量64字节
*
* @param publicKey
* @return
*/
public static byte[] getRawPublicKey(BCECPublicKey publicKey) {
byte[] src65 = publicKey.getQ().getEncoded(false);
//SM2的话这里应该是64字节
byte[] rawXandY = new byte[CURVE_LEN * 2];
System.arraycopy(src65, 1, rawXandY, 0, rawXandY.length);
return rawXandY;
}
/**
* @param pubKey
* @param srcData
* @return 默认输出C1C3C2顺序的密文
* @throws InvalidCipherTextException
*/
public static byte[] encrypt(BCECPublicKey pubKey, byte[] srcData) throws InvalidCipherTextException {
ECPublicKeyParameters pubKeyParameters = BcecUtil.convertPublicKeyToParameters(pubKey);
return encrypt(Mode.C1C3C2, pubKeyParameters, srcData);
}
/**
* @param mode 指定密文结构旧标准的为C1C2C3新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
* @param pubKey
* @param srcData
* @return
* @throws InvalidCipherTextException
*/
public static byte[] encrypt(Mode mode, BCECPublicKey pubKey, byte[] srcData) throws InvalidCipherTextException {
ECPublicKeyParameters pubKeyParameters = BcecUtil.convertPublicKeyToParameters(pubKey);
return encrypt(mode, pubKeyParameters, srcData);
}
/**
* @param pubKeyParameters
* @param srcData
* @return 默认输出C1C3C2顺序的密文
* @throws InvalidCipherTextException
*/
public static byte[] encrypt(ECPublicKeyParameters pubKeyParameters, byte[] srcData)
throws InvalidCipherTextException {
return encrypt(Mode.C1C3C2, pubKeyParameters, srcData);
}
/**
* @param mode 指定密文结构旧标准的为C1C2C3新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
* @param pubKeyParameters
* @param srcData
* @return
* @throws InvalidCipherTextException
*/
public static byte[] encrypt(Mode mode, ECPublicKeyParameters pubKeyParameters, byte[] srcData)
throws InvalidCipherTextException {
SM2Engine engine = new SM2Engine(mode);
ParametersWithRandom pwr = new ParametersWithRandom(pubKeyParameters, new SecureRandom());
engine.init(true, pwr);
return engine.processBlock(srcData, 0, srcData.length);
}
/**
* @param priKey
* @param sm2Cipher 默认输入C1C3C2顺序的密文
* @return
* @throws InvalidCipherTextException
*/
public static byte[] decrypt(BCECPrivateKey priKey, byte[] sm2Cipher) throws InvalidCipherTextException {
ECPrivateKeyParameters priKeyParameters = BcecUtil.convertPrivateKeyToParameters(priKey);
return decrypt(Mode.C1C3C2, priKeyParameters, sm2Cipher);
}
/**
* @param mode 指定密文结构旧标准的为C1C2C3新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
* @param priKey
* @param sm2Cipher
* @return
* @throws InvalidCipherTextException
*/
public static byte[] decrypt(Mode mode, BCECPrivateKey priKey, byte[] sm2Cipher) throws InvalidCipherTextException {
ECPrivateKeyParameters priKeyParameters = BcecUtil.convertPrivateKeyToParameters(priKey);
return decrypt(mode, priKeyParameters, sm2Cipher);
}
/**
* @param priKeyParameters
* @param sm2Cipher 默认输入C1C3C2顺序的密文
* @return
* @throws InvalidCipherTextException
*/
public static byte[] decrypt(ECPrivateKeyParameters priKeyParameters, byte[] sm2Cipher)
throws InvalidCipherTextException {
return decrypt(Mode.C1C3C2, priKeyParameters, sm2Cipher);
}
/**
* 前端密文解密
*
* @param mode 指定密文结构旧标准的为C1C2C3新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
* @param priKeyParameters
* @param sm2Cipher
* @return
* @throws InvalidCipherTextException
*/
public static byte[] decrypt(Mode mode, ECPrivateKeyParameters priKeyParameters, byte[] sm2Cipher)
throws InvalidCipherTextException {
SM2Engine engine = new SM2Engine(mode);
engine.init(false, priKeyParameters);
return engine.processBlock(sm2Cipher, 0, sm2Cipher.length);
}
/**
* 分解SM2密文
*
* @param cipherText 默认输入C1C3C2顺序的密文
* @return
*/
public static Sm2Cipher parseSm2Cipher(byte[] cipherText) {
int curveLength = BcecUtil.getCurveLength(DOMAIN_PARAMS);
return parseSm2Cipher(Mode.C1C3C2, curveLength, SM3_DIGEST_LENGTH, cipherText);
}
/**
* 分解SM2密文
*
* @param mode 指定密文结构旧标准的为C1C2C3新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
* @param cipherText
* @return
*/
public static Sm2Cipher parseSm2Cipher(Mode mode, byte[] cipherText) {
int curveLength = BcecUtil.getCurveLength(DOMAIN_PARAMS);
return parseSm2Cipher(mode, curveLength, SM3_DIGEST_LENGTH, cipherText);
}
/**
* @param curveLength
* @param digestLength
* @param cipherText 默认输入C1C3C2顺序的密文
* @return
*/
public static Sm2Cipher parseSm2Cipher(int curveLength, int digestLength,
byte[] cipherText) {
return parseSm2Cipher(Mode.C1C3C2, curveLength, digestLength, cipherText);
}
/**
* 分解SM2密文
*
* @param mode 指定密文结构旧标准的为C1C2C3新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
* @param curveLength ECC曲线长度
* @param digestLength HASH长度
* @param cipherText SM2密文
* @return
*/
public static Sm2Cipher parseSm2Cipher(Mode mode, int curveLength, int digestLength,
byte[] cipherText) {
byte[] c1 = new byte[curveLength * 2 + 1];
byte[] c2 = new byte[cipherText.length - c1.length - digestLength];
byte[] c3 = new byte[digestLength];
System.arraycopy(cipherText, 0, c1, 0, c1.length);
if (mode == Mode.C1C2C3) {
System.arraycopy(cipherText, c1.length, c2, 0, c2.length);
System.arraycopy(cipherText, c1.length + c2.length, c3, 0, c3.length);
} else if (mode == Mode.C1C3C2) {
System.arraycopy(cipherText, c1.length, c3, 0, c3.length);
System.arraycopy(cipherText, c1.length + c3.length, c2, 0, c2.length);
} else {
throw new RuntimeException(UNSUPPORTEDMODE + mode);
}
Sm2Cipher result = new Sm2Cipher();
result.setC1(c1);
result.setC2(c2);
result.setC3(c3);
result.setCipherText(cipherText);
return result;
}
/**
* DER编码
*
* @param cipher 默认输入C1C3C2顺序的密文
* @return 默认输出按C1C3C2编码的结果
*/
public static byte[] encodeSm2CipherToDer(byte[] cipher) throws IOException {
int curveLength = BcecUtil.getCurveLength(DOMAIN_PARAMS);
return encodeSm2CipherToDer(Mode.C1C3C2, curveLength, SM3_DIGEST_LENGTH, cipher);
}
/**
* @param mode 指定密文结构旧标准的为C1C2C3新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
* @param cipher
* @return 按指定的mode输出相应的编码结果
* @throws IOException
*/
public static byte[] encodeSm2CipherToDer(Mode mode, byte[] cipher) throws IOException {
int curveLength = BcecUtil.getCurveLength(DOMAIN_PARAMS);
return encodeSm2CipherToDer(mode, curveLength, SM3_DIGEST_LENGTH, cipher);
}
/**
* @param curveLength
* @param digestLength
* @param cipher 默认输入C1C3C2顺序的密文
* @return 默认输出按C1C3C2编码的结果
* @throws IOException
*/
public static byte[] encodeSm2CipherToDer(int curveLength, int digestLength, byte[] cipher) throws IOException {
return encodeSm2CipherToDer(Mode.C1C3C2, curveLength, digestLength, cipher);
}
/**
* @param mode 指定密文结构旧标准的为C1C2C3新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
* @param curveLength
* @param digestLength
* @param cipher
* @return 按指定的mode输出相应的编码结果
* @throws Exception
*/
public static byte[] encodeSm2CipherToDer(Mode mode, int curveLength,
int digestLength, byte[] cipher) throws IOException {
byte[] c1x = new byte[curveLength];
byte[] c1y = new byte[curveLength];
byte[] c2 = new byte[cipher.length - c1x.length - c1y.length - 1 - digestLength];
byte[] c3 = new byte[digestLength];
int startPos = 1;
System.arraycopy(cipher, startPos, c1x, 0, c1x.length);
startPos += c1x.length;
System.arraycopy(cipher, startPos, c1y, 0, c1y.length);
startPos += c1y.length;
if (mode == Mode.C1C2C3) {
System.arraycopy(cipher, startPos, c2, 0, c2.length);
startPos += c2.length;
System.arraycopy(cipher, startPos, c3, 0, c3.length);
} else if (mode == Mode.C1C3C2) {
System.arraycopy(cipher, startPos, c3, 0, c3.length);
startPos += c3.length;
System.arraycopy(cipher, startPos, c2, 0, c2.length);
} else {
throw new RuntimeException(UNSUPPORTEDMODE + mode);
}
ASN1Encodable[] arr = new ASN1Encodable[4];
arr[0] = new ASN1Integer(c1x);
arr[1] = new ASN1Integer(c1y);
if (mode == Mode.C1C2C3) {
arr[2] = new DEROctetString(c2);
arr[3] = new DEROctetString(c3);
} else if (mode == Mode.C1C3C2) {
arr[2] = new DEROctetString(c3);
arr[3] = new DEROctetString(c2);
}
DERSequence ds = new DERSequence(arr);
return ds.getEncoded(ASN1Encoding.DER);
}
/**
* 解码DER密文
*
* @param derCipher 默认输入按C1C3C2顺序编码的密文
* @return 输出按C1C3C2排列的字节数组
*/
public static byte[] decodeDerSm2Cipher(byte[] derCipher) {
return decodeDerSm2Cipher(Mode.C1C3C2, derCipher);
}
/**
* @param mode 指定密文结构旧标准的为C1C2C3新的[《SM2密码算法使用规范》 GM/T 0009-2012]标准为C1C3C2
* @param derCipher
* @return 按指定的mode输出相应的解码结果
* @throws Exception
*/
public static byte[] decodeDerSm2Cipher(Mode mode, byte[] derCipher) {
ASN1Sequence as = ASN1Sequence.getInstance(derCipher);
byte[] c3;
byte[] c2;
if (mode == Mode.C1C2C3) {
c2 = ((DEROctetString) as.getObjectAt(2)).getOctets();
c3 = ((DEROctetString) as.getObjectAt(3)).getOctets();
} else if (mode == Mode.C1C3C2) {
c3 = ((DEROctetString) as.getObjectAt(2)).getOctets();
c2 = ((DEROctetString) as.getObjectAt(3)).getOctets();
} else {
throw new RuntimeException(UNSUPPORTEDMODE + mode);
}
int pos = 0;
byte[] c1x = ((ASN1Integer) as.getObjectAt(0)).getValue().toByteArray();
byte[] c1y = ((ASN1Integer) as.getObjectAt(1)).getValue().toByteArray();
byte[] cipherText = new byte[1 + c1x.length + c1y.length + c2.length + c3.length];
final byte UNCOMPRESSEDFLAG = 0x04;
cipherText[0] = UNCOMPRESSEDFLAG;
pos += 1;
System.arraycopy(c1x, 0, cipherText, pos, c1x.length);
pos += c1x.length;
System.arraycopy(c1y, 0, cipherText, pos, c1y.length);
pos += c1y.length;
if (mode == Mode.C1C2C3) {
System.arraycopy(c2, 0, cipherText, pos, c2.length);
pos += c2.length;
System.arraycopy(c3, 0, cipherText, pos, c3.length);
} else if (mode == Mode.C1C3C2) {
System.arraycopy(c3, 0, cipherText, pos, c3.length);
pos += c3.length;
System.arraycopy(c2, 0, cipherText, pos, c2.length);
}
return cipherText;
}
public static byte[] sign(BCECPrivateKey priKey, byte[] srcData) throws CryptoException {
ECPrivateKeyParameters priKeyParameters = BcecUtil.convertPrivateKeyToParameters(priKey);
return sign(priKeyParameters, null, srcData);
}
/**
* ECC私钥签名
* 不指定withId则默认withId为字节数组:"1234567812345678".getBytes()
*
* @param priKeyParameters ECC私钥
* @param srcData 源数据
* @return 签名
* @throws CryptoException
*/
public static byte[] sign(ECPrivateKeyParameters priKeyParameters, byte[] srcData) throws CryptoException {
return sign(priKeyParameters, null, srcData);
}
public static byte[] sign(BCECPrivateKey priKey, byte[] withId, byte[] srcData) throws CryptoException {
ECPrivateKeyParameters priKeyParameters = BcecUtil.convertPrivateKeyToParameters(priKey);
return sign(priKeyParameters, withId, srcData);
}
/**
* ECC私钥签名
*
* @param priKeyParameters ECC私钥
* @param withId 可以为null若为null则默认withId为字节数组:"1234567812345678".getBytes()
* @param srcData 源数据
* @return 签名
* @throws CryptoException
*/
public static byte[] sign(ECPrivateKeyParameters priKeyParameters, byte[] withId, byte[] srcData)
throws CryptoException {
SM2Signer signer = new SM2Signer();
CipherParameters param = null;
ParametersWithRandom pwr = new ParametersWithRandom(priKeyParameters, new SecureRandom());
if (withId != null) {
param = new ParametersWithID(pwr, withId);
} else {
param = pwr;
}
signer.init(true, param);
signer.update(srcData, 0, srcData.length);
return signer.generateSignature();
}
/**
* 将DER编码的SM2签名解析成64字节的纯R+S字节流
*
* @param derSign
* @return
*/
public static byte[] decodeDerSm2Sign(byte[] derSign) {
ASN1Sequence as = ASN1Sequence.getInstance(derSign);
byte[] rBytes = ((ASN1Integer) as.getObjectAt(0)).getValue().toByteArray();
byte[] sBytes = ((ASN1Integer) as.getObjectAt(1)).getValue().toByteArray();
//由于大数的补0规则所以可能会出现33个字节的情况要修正回32个字节
rBytes = fixToCurveLengthBytes(rBytes);
sBytes = fixToCurveLengthBytes(sBytes);
byte[] rawSign = new byte[rBytes.length + sBytes.length];
System.arraycopy(rBytes, 0, rawSign, 0, rBytes.length);
System.arraycopy(sBytes, 0, rawSign, rBytes.length, sBytes.length);
return rawSign;
}
/**
* 把64字节的纯R+S字节流转换成DER编码字节流
*
* @param rawSign
* @return
* @throws IOException
*/
public static byte[] encodeSm2SignToDer(byte[] rawSign) throws IOException {
//要保证大数是正数
BigInteger r = new BigInteger(1, extractBytes(rawSign, 0, 32));
BigInteger s = new BigInteger(1, extractBytes(rawSign, 32, 32));
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(r));
v.add(new ASN1Integer(s));
return new DERSequence(v).getEncoded(ASN1Encoding.DER);
}
public static boolean verify(BCECPublicKey pubKey, byte[] srcData, byte[] sign) {
ECPublicKeyParameters pubKeyParameters = BcecUtil.convertPublicKeyToParameters(pubKey);
return verify(pubKeyParameters, null, srcData, sign);
}
/**
* ECC公钥验签
* 不指定withId则默认withId为字节数组:"1234567812345678".getBytes()
*
* @param pubKeyParameters ECC公钥
* @param srcData 源数据
* @param sign 签名
* @return 验签成功返回true失败返回false
*/
public static boolean verify(ECPublicKeyParameters pubKeyParameters, byte[] srcData, byte[] sign) {
return verify(pubKeyParameters, null, srcData, sign);
}
public static boolean verify(BCECPublicKey pubKey, byte[] withId, byte[] srcData, byte[] sign) {
ECPublicKeyParameters pubKeyParameters = BcecUtil.convertPublicKeyToParameters(pubKey);
return verify(pubKeyParameters, withId, srcData, sign);
}
/**
* ECC公钥验签
*
* @param pubKeyParameters ECC公钥
* @param withId 可以为null若为null则默认withId为字节数组:"1234567812345678".getBytes()
* @param srcData 源数据
* @param sign 签名
* @return 验签成功返回true失败返回false
*/
public static boolean verify(ECPublicKeyParameters pubKeyParameters, byte[] withId, byte[] srcData, byte[] sign) {
SM2Signer signer = new SM2Signer();
CipherParameters param;
if (withId != null) {
param = new ParametersWithID(pubKeyParameters, withId);
} else {
param = pubKeyParameters;
}
signer.init(false, param);
signer.update(srcData, 0, srcData.length);
return signer.verifySignature(sign);
}
private static byte[] extractBytes(byte[] src, int offset, int length) {
byte[] result = new byte[length];
System.arraycopy(src, offset, result, 0, result.length);
return result;
}
private static byte[] fixToCurveLengthBytes(byte[] src) {
if (src.length == CURVE_LEN) {
return src;
}
byte[] result = new byte[CURVE_LEN];
if (src.length > CURVE_LEN) {
System.arraycopy(src, src.length - result.length, result, 0, result.length);
} else {
System.arraycopy(src, 0, result, result.length - src.length, src.length);
}
return result;
}
}

View File

@ -0,0 +1,49 @@
package com.chinaunicom.mall.ebtp.extend.singlePoint.common;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Map;
/**
* update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述]
*
* @author wangwenjing
* @version v1.0
* @date 2021/2/24
*/
@Service
public class UriGeneratorUtil{
/**
* 根据clientId、用户标识、重定向地址和自定义参数生成加密串
*
* @param uid
* @param params
* @return
*/
public static String generateEncodeUrl(String clientId, String uid, String redirectUrl, String publicKey, Map<String, String> params) {
Sm2Encryptor sm2Encryptor = new Sm2Encryptor();
LocalDateTime now = LocalDateTime.now();
String splitChar = "@@";
StringBuilder builder = new StringBuilder(100);
builder.append(clientId).append(splitChar);
builder.append(uid).append(splitChar);
builder.append(redirectUrl).append(splitChar);
builder.append(now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
if (params != null && !params.isEmpty()) {
builder.append(splitChar);
for (Map.Entry<String, String> entry : params.entrySet()) {
builder.append(entry.getKey()).append("=").append(entry.getValue());
}
}
try {
return sm2Encryptor.encryptString(builder.toString(),publicKey);
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}

View File

@ -0,0 +1,70 @@
package com.chinaunicom.mall.ebtp.extend.singlePoint.controller;
import com.chinaunicom.mall.ebtp.common.base.entity.BaseResponse;
import com.chinaunicom.mall.ebtp.common.base.service.impl.BaseCacheUserServiceImpl;
import com.chinaunicom.mall.ebtp.extend.signature.entity.ExpertSignature;
import com.chinaunicom.mall.ebtp.extend.signature.service.ExpertSignatureService;
import com.chinaunicom.mall.ebtp.extend.singlePoint.service.SinglePointService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@RestController
@Api(tags = "")
@RequestMapping("/v1/singlePoint")
public class SinglePointController {
@Autowired
private SinglePointService singlePointService;
@Autowired
private BaseCacheUserServiceImpl userService;
/**
* 获取单点登录地址
*
* @param userId
* @return
*/
@ApiOperation("获取单点登录地址")
@GetMapping("/getSsoUrl")
public BaseResponse<String> getSsoUrl(@ApiParam(value = "用户id", required = false) @RequestParam(name = "userId", required = false) String userId) {
String uid = userId;
if(userService.getCacheUser()!=null&&
userService.getCacheUser().getUserId()!=null){
uid = userService.getCacheUser().getUserId();
}
String encodeUrl = singlePointService.generateEncodeUrl(userId);
return BaseResponse.success(encodeUrl);
}
/**
* 获取单点登录地址 + 指定公告参数
*
* @param userId
* @return
*/
@ApiOperation("获取单点登录地址 + 指定参数")
@GetMapping("/getSsoUrlByPage")
public BaseResponse<String> getSsoUrlByPage(@RequestParam(name = "userId", required = false) String userId,
@RequestParam(name = "page", required = false) String page) {
String uid = userId;
if(userService.getCacheUser()!=null&&
userService.getCacheUser().getUserId()!=null){
uid = userService.getCacheUser().getUserId();
}
String encodeUrl = singlePointService.generateEncodeUrl(uid,page);
return BaseResponse.success(encodeUrl);
}
}

View File

@ -0,0 +1,22 @@
package com.chinaunicom.mall.ebtp.extend.singlePoint.service;
import com.chinaunicom.mall.ebtp.extend.signature.entity.ExpertSignature;
public interface SinglePointService {
/**
* 根据clientId、用户标识、重定向地址生成加密串
*
* @param uid
* @return
*/
String generateEncodeUrl(String uid);
/**
* 根据clientId、用户标识、重定向地址生成加密串
*
* @param uid
* @return
*/
String generateEncodeUrl(String uid, String page);
}

View File

@ -0,0 +1,89 @@
package com.chinaunicom.mall.ebtp.extend.singlePoint.service.impl;
import com.chinaunicom.mall.ebtp.extend.singlePoint.common.Sm2Encryptor;
import com.chinaunicom.mall.ebtp.extend.singlePoint.service.SinglePointService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Map;
@Slf4j
@Service
public class SinglePointServiceImpl implements SinglePointService {
@Value("${unifast.sso.public-key}")
private String publicKey;
//"04819CF427F9150FEEBD91E8D2346F203FC47312D212022A967D8372EA30B9581CCEEFCE2670BDDAF2E8DA1620EA73948126078ED9FF9773AA3A94EE6C80035A18"
@Value("${unifast.sso.clientId}")
private String clientId;
@Value("${unifast.sso.redirectUrl}")
private String redirectUrl;
@Value("${unifast.sso.getCode.url}")
private String codeUrl;
/**
* 根据clientId、用户标识、重定向地址生成加密串
*
* @param uid
* @return
*/
@Override
public String generateEncodeUrl(String uid) {
return generateEncodeUrl(uid, null);
}
/**
* 根据clientId、用户标识、重定向地址生成加密串
*
* @param uid
* @return
*/
@Override
public String generateEncodeUrl(String uid,String page) {
return generateEncodeUrl(clientId, uid, redirectUrl,publicKey,page,null);
}
/**
* 根据clientId、用户标识、重定向地址和自定义参数生成加密串
*
* @param uid
* @param params
* @return
*/
private String generateEncodeUrl(String clientId, String uid, String redirectUrl,String publicKey,String page, Map<String, String> params) {
Sm2Encryptor sm2Encryptor = new Sm2Encryptor();
LocalDateTime now = LocalDateTime.now();
String splitChar = "@@";
StringBuilder builder = new StringBuilder(100);
builder.append(clientId).append(splitChar);
builder.append(uid).append(splitChar);
String pageValue = "";
if(page!=null&&!"".equals(page)){
pageValue = "?page="+page;
}
builder.append(redirectUrl+pageValue).append(splitChar);
builder.append(now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
if (params != null && !params.isEmpty()) {
builder.append(splitChar);
for (Map.Entry<String, String> entry : params.entrySet()) {
builder.append(entry.getKey()).append("=").append(entry.getValue());
builder.append("&");
}
builder.append("1=1");
}
try {
return codeUrl+"?"+ sm2Encryptor.encryptString(builder.toString(),publicKey);
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}