记得上下班打卡 | git大法好,push需谨慎

Commit d199f9f1 authored by 姜秀龙's avatar 姜秀龙

获取到了pid

parent 2fb244b4
package com.liquidnet.service.adam.constant;
public final class AdamTpaConst {
private AdamTpaConst() {
}
/** 国家网络身份认证 */
public static final String PLATFORM_NIA = "NIA";
}
package com.liquidnet.service.adam.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.io.Serializable;
@ApiModel(value = "AdamNiaLoginParam", description = "国家网络身份认证登录入参(R01 凭证认证)")
@Data
public class AdamNiaLoginParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(position = 1, required = true, value = "业务序列号,须与前端 SDK bizSeq 一致[32]")
@NotBlank(message = "bizSeq不能为空")
@Size(min = 32, max = 32, message = "bizSeq长度须为32位")
private String bizSeq;
@ApiModelProperty(position = 2, required = true, value = "认证请求数据 idCardAuthData")
@NotBlank(message = "idCardAuthData不能为空")
private String idCardAuthData;
}
......@@ -22,7 +22,7 @@ public class AdamThirdPartParam implements Serializable {
@ApiModelProperty(position = 13, required = true, value = "头像[255]", example = "http://pic.zhengzai.tv/default/avatar.png")
@Size(max = 255, message = "已超出头像链接长度限制")
private String avatar;
@ApiModelProperty(position = 14, required = true, value = "平台类型[255]", allowableValues = "WEIBO,WECHAT,QQ")
@ApiModelProperty(position = 14, required = true, value = "平台类型[255]", allowableValues = "WEIBO,WECHAT,QQ,NIA")
@Pattern(regexp = LnsRegex.Valid.TRIPLE_PF_FOR_ULGOIN, message = "平台类型无效")
@NotBlank(message = "平台类型不能为空")
private String platform;
......
package com.liquidnet.service.adam.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Getter;
import java.io.Serializable;
/**
* 网证认证成功但未绑定本站账号时,随 10006 返回给前端,用于后续 bindTpa / login/tpa 注册绑定。
*/
@Getter
@Builder
@ApiModel(value = "AdamNiaUnboundVo", description = "网证未绑定账号时的绑定凭证")
public class AdamNiaUnboundVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "平台类型,固定 NIA")
private final String platform;
@ApiModelProperty(value = "网证用户唯一标识(40字节标识的标准Base64,56字符),绑定时原样作为 openId 回传")
private final String openId;
}
......@@ -119,7 +119,7 @@ public class LnsRegex {
/**
* 支持的第三方账号平台类型(用户中心:登录注册)
*/
public static final String TRIPLE_PF_FOR_ULGOIN = "\\b(WEIBO|WECHAT|QQ)\\b";
public static final String TRIPLE_PF_FOR_ULGOIN = "\\b(WEIBO|WECHAT|QQ|NIA)\\b";
/**
* 支持的支付终端
*/
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-third</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>liquidnet-common-third-secure-access</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-base</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.anicert.module</groupId>
<artifactId>anicert-sign-bouncycastle</artifactId>
<version>3.2.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/anicert-sign-bouncycastle-3.2.0.jar</systemPath>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.26</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-setting</artifactId>
<version>5.8.26</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-log</artifactId>
<version>5.8.26</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.68</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.68</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>
package cn.anicert.secure.access.bean;
import java.io.Serializable;
public class SARequestPackageVO implements Serializable {
private static final long serialVersionUID = 1L;
private Object bizPackage;
private String customerNo;
private String forwardUrl;
public SARequestPackageVO() {
}
public SARequestPackageVO(Object bizPackage, String customerNo, String forwardUrl) {
this.bizPackage = bizPackage;
this.customerNo = customerNo;
this.forwardUrl = forwardUrl;
}
public Object getBizPackage() {
return bizPackage;
}
public void setBizPackage(Object bizPackage) {
this.bizPackage = bizPackage;
}
public String getCustomerNo() {
return customerNo;
}
public void setCustomerNo(String customerNo) {
this.customerNo = customerNo;
}
public String getForwardUrl() {
return forwardUrl;
}
public void setForwardUrl(String forwardUrl) {
this.forwardUrl = forwardUrl;
}
}
package cn.anicert.secure.access.bean;
import java.io.Serializable;
public class SARequestVO implements Serializable {
private static final long serialVersionUID = 1L;
private Object requestPackage;
private String requestSign;
public SARequestVO() {
}
public SARequestVO(Object requestPackage, String requestSign) {
this.requestPackage = requestPackage;
this.requestSign = requestSign;
}
public Object getRequestPackage() {
return requestPackage;
}
public void setRequestPackage(Object requestPackage) {
this.requestPackage = requestPackage;
}
public String getRequestSign() {
return requestSign;
}
public void setRequestSign(String requestSign) {
this.requestSign = requestSign;
}
}
package cn.anicert.secure.access.bean;
import java.io.Serializable;
public class SAResponseVO implements Serializable {
private static final long serialVersionUID = 1L;
private Object responsePackage;
private String responseSign;
public SAResponseVO() {
}
public SAResponseVO(Object responsePackage, String responseSign) {
this.responsePackage = responsePackage;
this.responseSign = responseSign;
}
public Object getResponsePackage() {
return responsePackage;
}
public void setResponsePackage(Object responsePackage) {
this.responsePackage = responsePackage;
}
public String getResponseSign() {
return responseSign;
}
public void setResponseSign(String responseSign) {
this.responseSign = responseSign;
}
}
package cn.anicert.secure.access.bean;
import java.io.Serializable;
public class SASignRequestPackageVO implements Serializable {
private static final long serialVersionUID = 1L;
private String customerNo;
private String originalData;
public SASignRequestPackageVO() {
}
public SASignRequestPackageVO(String customerNo, String originalData) {
this.customerNo = customerNo;
this.originalData = originalData;
}
public String getCustomerNo() {
return customerNo;
}
public void setCustomerNo(String customerNo) {
this.customerNo = customerNo;
}
public String getOriginalData() {
return originalData;
}
public void setOriginalData(String originalData) {
this.originalData = originalData;
}
}
package cn.anicert.secure.access.config;
import cn.anicert.secure.access.config.bean.AccessConfig;
import cn.anicert.secure.access.enums.Configs;
import cn.anicert.secure.access.exceptions.SecureAccessConfigException;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.StaticLog;
import cn.hutool.setting.Setting;
import cn.hutool.setting.SettingUtil;
import java.util.Arrays;
public class ConfigReader {
private final static String CONFIG_FILE_NAME = "secure-access.setting";
private static Setting setting;
public static AccessConfig readConfig(String... filePath) {
if (filePath == null || filePath.length == 0) {
setting = SettingUtil.getFirstFound(CONFIG_FILE_NAME);
} else {
setting = SettingUtil.getFirstFound(filePath);
}
StaticLog.info("[安全接入平台]当前配置文件路径为:{}", setting.getSettingPath());
StaticLog.info("[安全接入平台]开始读取配置文件…………");
AccessConfig accessConfig = new AccessConfig();
Arrays.stream(Configs.values()).forEach(config -> {
if (config.isNecessary()) {
if (setting.containsKey(config.getKey())) {
String value = setting.getStr(config.getKey());
if (StrUtil.isBlank(value)) {
StaticLog.error("[安全接入平台]{}未配置", config.getName());
throwConfigException();
}
} else {
StaticLog.error("[安全接入平台]{}未配置", config.getName());
throwConfigException();
}
}
});
setting.autoLoad(true);
setting.toBean(accessConfig);
StaticLog.info("[安全接入平台]读取配置文件结束");
return accessConfig;
}
public static AccessConfig writeConfig(String key, String value) {
setting.set(key, value);
setting.store();
AccessConfig accessConfig = new AccessConfig();
return setting.toBean(accessConfig);
}
private static void throwConfigException() {
throw new SecureAccessConfigException("[安全接入平台]读取配置文件失败");
}
}
package cn.anicert.secure.access.config.bean;
public class AccessConfig {
//配置部分
private String orgId;
private String customerNo;
private String accessUrl;
private String signPrivateKey;
private String signVerifyPublicKey;
private String dataEncryptKey;
//请求部分
private int connectionRequestTimeout = 50000;
private int responseTimeout = 30000;
private int poolMaxConn = 100;
public String getCustomerNo() {
return customerNo;
}
public void setCustomerNo(String customerNo) {
this.customerNo = customerNo;
}
public String getAccessUrl() {
return accessUrl;
}
public void setAccessUrl(String accessUrl) {
this.accessUrl = accessUrl;
}
public String getSignPrivateKey() {
return signPrivateKey;
}
public void setSignPrivateKey(String signPrivateKey) {
this.signPrivateKey = signPrivateKey;
}
public String getSignVerifyPublicKey() {
return signVerifyPublicKey;
}
public void setSignVerifyPublicKey(String signVerifyPublicKey) {
this.signVerifyPublicKey = signVerifyPublicKey;
}
public String getDataEncryptKey() {
return dataEncryptKey;
}
public void setDataEncryptKey(String dataEncryptKey) {
this.dataEncryptKey = dataEncryptKey;
}
public int getConnectionRequestTimeout() {
return connectionRequestTimeout;
}
public void setConnectionRequestTimeout(int connectionRequestTimeout) {
this.connectionRequestTimeout = connectionRequestTimeout;
}
public int getResponseTimeout() {
return responseTimeout;
}
public void setResponseTimeout(int responseTimeout) {
this.responseTimeout = responseTimeout;
}
public int getPoolMaxConn() {
return poolMaxConn;
}
public void setPoolMaxConn(int poolMaxConn) {
this.poolMaxConn = poolMaxConn;
}
public String getOrgId() {
return orgId;
}
public void setOrgId(String orgId) {
this.orgId = orgId;
}
}
package cn.anicert.secure.access.constants;
public class InterfaceConstant {
private static final String BASE_URL = "/secureaccess/interf";
public static final String UNIFIED_AUTH_URL = BASE_URL + "/unified_auth/request";
public static final String P7_SIGN_URL = BASE_URL + "/pkcs7/signDataByP7";
}
package cn.anicert.secure.access.core;
import cn.anicert.secure.access.bean.SAResponseVO;
import cn.anicert.secure.access.constants.InterfaceConstant;
import cn.anicert.secure.access.utils.ConfigUtil;
import cn.anicert.secure.access.utils.HttpUtil;
import cn.anicert.secure.access.utils.JSONUtil;
import cn.anicert.secure.access.utils.SignUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.StaticLog;
public class SecureAccess {
public static void init(String... configPath) {
ConfigUtil.initConfig(configPath);
}
public static String unified_auth(String bizSeq, String reqStr) {
String url = ConfigUtil.getConfig().getAccessUrl() + InterfaceConstant.UNIFIED_AUTH_URL;
String responseStr = sendReq(bizSeq, url, reqStr);
if (StrUtil.isNotBlank(responseStr) && verifySign(bizSeq, responseStr)) {
return dealResult(bizSeq, responseStr);
}
return "";
}
public static String pkcs7Sign(String bizSeq, String reqStr) {
String url = ConfigUtil.getConfig().getAccessUrl() + InterfaceConstant.P7_SIGN_URL;
String responseStr = sendReq(bizSeq, url, reqStr);
if (StrUtil.isNotBlank(responseStr) && verifySign(bizSeq, responseStr)) {
return dealResult(bizSeq, responseStr);
}
return "";
}
private static String sendReq(String bizSeq, String url, String reqStr) {
try {
StaticLog.info("[安全接入平台],bizSeq--[{}]--请求开始", bizSeq);
StaticLog.info("[安全接入平台],bizSeq--[{}]--请求数据--[{}]", bizSeq, reqStr);
String responseStr = HttpUtil.getInstance().postRequest(url, reqStr);
if (StrUtil.isNotBlank(responseStr)) {
StaticLog.info("[安全接入平台],bizSeq--[{}]--请求结果--[{}]", bizSeq, responseStr);
return responseStr;
} else {
StaticLog.error("[安全接入平台],bizSeq--[{}]--请求返回结果为空", bizSeq);
}
} catch (Exception e) {
StaticLog.error(e, "[安全接入平台],bizSeq--[{}]--请求失败", bizSeq);
}
return "";
}
private static boolean verifySign(String bizSeq, String result) {
try {
SAResponseVO responseVO = JSONUtil.json2Object(result, SAResponseVO.class);
if (StrUtil.isNotBlank(responseVO.getResponseSign())) {
String signStr = JSONUtil.toJson(responseVO.getResponsePackage());
boolean verifyResult = SignUtil.verifySign(signStr, responseVO.getResponseSign());
if (!verifyResult) {
StaticLog.error("[安全接入平台],bizSeq--[{}]--验签不匹配", bizSeq);
}
return verifyResult;
} else {
StaticLog.error("[安全接入平台],bizSeq--[{}]--验签报文体为空", bizSeq);
return false;
}
} catch (Exception e) {
StaticLog.error(e, "[安全接入平台],bizSeq--[{}]--验签异常", bizSeq);
return false;
}
}
private static String dealResult(String bizSeq, String result) {
try {
SAResponseVO responseVO = JSONUtil.json2Object(result, SAResponseVO.class);
return JSONUtil.toJson(responseVO.getResponsePackage());
} catch (Exception e) {
StaticLog.error(e, "[安全接入平台],bizSeq--[{}]--处理返回报文异常", bizSeq);
return "";
}
}
}
package cn.anicert.secure.access.crypto.service;
import javax.security.auth.Subject;
/**
* @Description
* @Date 2024/5/29 15:30
* @Author yourongbin
**/
public interface ISoftCryptoService {
/**
* 数字信封加密
* @param cert 证书的base64编码
* @param data 加密数据
* @return
*/
public String encodeEnvelop(String cert, String data) throws Exception ;
/**
* 数字信封加密
* @param cert 证书的base64编码
* @param dataByte
* @return
*/
public String encodeEnvelop(String cert, byte[] dataByte) throws Exception;
/**
* 数字信封解密 返回明文
* @param priKey
* @param envelop
* @return
* @throws Exception
*/
public String decodeEnvelop(String priKey, String envelop) throws Exception;
/**
* 数字信封解密 返回明文
* @param priKey
* @param envelop
* @return
* @throws Exception
*/
public String decodeEnvelop(String priKey, byte[] envelop) throws Exception;
/**
* sm2加密
* @param publicKey
* @param data
* @return
* @throws Exception
*/
public String encrypt(String publicKey, String data) throws Exception;
/**
* sm2解密
* @param privateKey
* @param encryptedData
* @return
* @throws Exception
*/
public String decrypt(String privateKey, String encryptedData) throws Exception;
/**
* p7加签
* @param prikey 私钥
* @param cert 证书
* @param oriData 数据
* @return
* @throws Exception
*/
public String p7Sign(String prikey, String cert ,String oriData) throws Exception;
/**
* p7验签
* @param sign 签名值
* @param source 原文
* @return
* @throws Exception
*/
public Boolean p7Verify(String sign,String source) throws Exception;
/**
* p1加签
* @param prikey
* @param oriData
* @return
* @throws Exception
*/
public String p1Sign(String prikey,String oriData) throws Exception;
/**
* p1验签
* @param publicKey
* @param data
* @param signature
* @return
* @throws Exception
*/
public boolean verify(String publicKey, String data, String signature) throws Exception;
/**
* sm2加签
* @param prikey
* @param oriData
* @return
* @throws Exception
*/
public String sm2Sign(String prikey,String oriData) throws Exception;
/**
* sm2验签
* @param publicKey
* @param data
* @param signature
* @return
* @throws Exception
*/
public boolean sm2SignVerify(String publicKey, String data, String signature) throws Exception;
/**
* p1验签
* @param publicKey
* @param data
* @param charset
* @param signature
* @return
* @throws Exception
*/
public boolean verify(String publicKey, String data, String charset, String signature) throws Exception;
/**
* 获取业务站点号
* @param signature
* @return
* @throws Exception
*/
public String getCommonNameFromP7Sign(String signature) throws Exception;
/**
* sm3算法
* @param source
* @return
* @throws Exception
*/
public String sm3Hash(String source) throws Exception;
}
package cn.anicert.secure.access.crypto.service.impl;
import cn.anicert.module.sign.bc.ICryptoService;
import cn.anicert.module.sign.bc.mix.MixCryptoFactory;
import cn.anicert.module.sign.bc.std.Asymmetric;
import cn.anicert.module.sign.bc.std.Symmetric;
import cn.anicert.secure.access.crypto.util.CertUtils;
import cn.anicert.secure.access.crypto.util.SM2Utils;
import cn.anicert.secure.access.crypto.util.SM3Utils;
import cn.anicert.secure.access.crypto.service.ISoftCryptoService;
import cn.hutool.core.io.FileUtil;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* @Description
* @Date 2024/5/29 15:35
* @Author yourongbin
**/
public class SoftCryptoServiceImpl implements ISoftCryptoService {
private ICryptoService signService = MixCryptoFactory.getInstance(Asymmetric.SM2, Symmetric.SM4);
@Override
public String encodeEnvelop(String cert, String data) throws Exception {
return encodeEnvelop(cert, data.getBytes());
}
@Override
public String encodeEnvelop(String cert, byte[] dataByte) throws Exception {
return Base64.getEncoder().encodeToString(SM2Utils.encodeEnvelop(cert, dataByte));
}
@Override
public String decodeEnvelop(String priKey, String envelop) throws Exception {
return decodeEnvelop(priKey, Base64.getDecoder().decode(envelop));
}
@Override
public String decodeEnvelop(String priKey, byte[] envelop) throws Exception {
return new String(SM2Utils.decodeEnvelop(priKey, envelop), StandardCharsets.UTF_8);
}
@Override
public String encrypt(String publicKey, String data) throws Exception {
return SM2Utils.encrypt(publicKey, data);
}
@Override
public String decrypt(String privateKey, String encryptedData) throws Exception {
return SM2Utils.decrypt(privateKey, encryptedData);
}
@Override
public String p7Sign(String prikey, String cert, String oriData) throws Exception {
return SM2Utils.pkcs7SignDetach(prikey, cert, oriData);
}
@Override
public Boolean p7Verify(String sign, String source) throws Exception {
return SM2Utils.pkcs7Verify(Base64.getDecoder().decode(sign), source.getBytes());
}
@Override
public String p1Sign(String prikey, String oriData) throws Exception {
return SM2Utils.sign(prikey, oriData);
}
@Override
public boolean verify(String publicKey, String data, String signature) throws Exception {
return verify(publicKey, data, "utf-8", signature);
}
@Override
public String sm2Sign(String prikey, String oriData) throws Exception {
return Base64.getEncoder().encodeToString(
signService.sign(prikey, oriData.getBytes(StandardCharsets.UTF_8)));
}
@Override
public boolean sm2SignVerify(String publicKey, String data, String signature) throws Exception {
byte[] byteSign = Base64.getDecoder().decode(signature);
byte[] byteBody = data.getBytes(StandardCharsets.UTF_8);
return signService.verifySign(publicKey, byteSign, byteBody);
}
@Override
public boolean verify(String publicKey, String data, String charset, String signature) throws Exception {
return SM2Utils.verify(publicKey, data, charset, signature);
}
@Override
public String getCommonNameFromP7Sign(String signature) throws Exception {
return CertUtils.getSubjectDn(signature);
}
@Override
public String sm3Hash(String source) throws Exception {
return SM3Utils.sm3Hash(source.getBytes("UTF-16LE"));
}
}
package cn.anicert.secure.access.crypto.util;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Primitive;
import java.io.IOException;
/**
* @Description
* @Date 2024/5/30 17:18
* @Author yourongbin
**/
public class ASN1Util {
public static ASN1Object checkAndGetASN1Object(byte[] in)
throws IllegalArgumentException
{
ASN1InputStream din = null;
ASN1Primitive pkcs;
try
{
din = new ASN1InputStream(in);
pkcs = din.readObject();
} catch (IOException var11) {
throw new IllegalArgumentException("failed to construct sequence from byte[]:" + var11.getMessage());
} finally {
if (din != null) {
try {
din.close();
} catch (Exception var10) {
var10.printStackTrace();
}
}
}
return pkcs;
}
}
package cn.anicert.secure.access.crypto.util;
public class Base64 {
public Base64() {
}
public static String encode(byte[] data) {
return org.apache.commons.codec.binary.Base64.encodeBase64String(data);
}
public static byte[] decode(String data) {
return org.apache.commons.codec.binary.Base64.decodeBase64(data);
}
}
package cn.anicert.secure.access.crypto.util;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.Security;
/**
* @company cn.anicert
* @author: wangpengyuan
* @create: 2024-03-07
* @description 密码学基础类
**/
public class BaseCryptoUtil {
static {
Security.addProvider(new BouncyCastleProvider());
}
}
\ No newline at end of file
package cn.anicert.secure.access.crypto.util;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECPoint;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.CipherParameters;
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.math.ec.ECCurve;
public class SM2KeyUtil {
private static final X9ECParameters ecParameters;
private static final ECNamedCurveSpec sm2Spec;
private static final BouncyCastleProvider bcProvider;
private static final KeyPairGenerator keyGenerator;
private static final KeyFactory keyFactory;
private static final ECCurve curve;
public SM2KeyUtil() {
}
public static KeyPair genKeyPair() {
return keyGenerator.generateKeyPair();
}
public static byte[] encodeWithPoint(BCECPublicKey pub) {
return pub.getQ().getEncoded(false);
}
public static BCECPublicKey decodeWithPoint(byte[] data) throws InvalidKeySpecException {
ECPoint point = EC5Util.convertPoint(curve.decodePoint(data));
return (BCECPublicKey)keyFactory.generatePublic(new ECPublicKeySpec(point, sm2Spec));
}
public static String encodeStrWithPoint(BCECPublicKey pub) {
return Base64.encode(encodeWithPoint(pub));
}
public static BCECPublicKey decodeWithPoint(String base64) throws InvalidKeySpecException {
return decodeWithPoint(Base64.decode(base64));
}
public static String getX64(BCECPublicKey pub) {
return Base64.encode(pub.getQ().getXCoord().getEncoded());
}
public static String getY64(BCECPublicKey pub) {
return Base64.encode(pub.getQ().getYCoord().getEncoded());
}
public static BCECPublicKey fromXY(String x64, String y64) throws InvalidKeySpecException {
return fromXY(Base64.decode(x64), Base64.decode(y64));
}
public static byte[] getX(BCECPublicKey pub) {
return pub.getQ().getXCoord().getEncoded();
}
public static byte[] getY(BCECPublicKey pub) {
return pub.getQ().getYCoord().getEncoded();
}
public static BCECPublicKey fromXY(byte[] x, byte[] y) throws InvalidKeySpecException {
ECPoint point = EC5Util.convertPoint(curve.createPoint(new BigInteger(1, x), new BigInteger(1, y)));
return (BCECPublicKey)keyFactory.generatePublic(new ECPublicKeySpec(point, sm2Spec));
}
public static byte[] encodeWithBigInteger(BCECPrivateKey pri) {
return pri.getD().toByteArray();
}
public static BCECPrivateKey decodeWithBigInteger(byte[] data) throws InvalidKeySpecException {
return (BCECPrivateKey)keyFactory.generatePrivate(new ECPrivateKeySpec(new BigInteger(1, data), sm2Spec));
}
public static String encodeStrWithBigInteger(BCECPrivateKey pri) {
return Base64.encode(encodeWithBigInteger(pri));
}
public static BCECPrivateKey decodeWithBigInteger(String base64) throws InvalidKeySpecException {
return decodeWithBigInteger(Base64.decode(base64));
}
public static byte[] encodeWithKey(Key key) {
return key.getEncoded();
}
public static BCECPublicKey decodeWithX509(byte[] data) throws InvalidKeySpecException {
return (BCECPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(data));
}
public static BCECPrivateKey decodeWithPKCS8(byte[] data) throws InvalidKeySpecException {
return (BCECPrivateKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(data));
}
public static String encodeStrWithKey(Key key) {
return Base64.encode(encodeWithKey(key));
}
public static BCECPublicKey decodeWithX509(String base64) throws InvalidKeySpecException {
return decodeWithX509(Base64.decode(base64.replaceAll("-----.*-----", "")));
}
public static BCECPrivateKey decodeWithPKCS8(String base64) throws InvalidKeySpecException {
return decodeWithPKCS8(Base64.decode(base64.replaceAll("-----.*-----", "")));
}
public static CipherParameters forParameters(Key key) throws InvalidKeyException {
if (key instanceof PublicKey) {
return ECUtil.generatePublicKeyParameter((PublicKey)key);
} else if (key instanceof PrivateKey) {
return ECUtil.generatePrivateKeyParameter((PrivateKey)key);
} else {
throw new InvalidKeyException("not expect key type");
}
}
static {
try {
ecParameters = GMNamedCurves.getByName("sm2p256v1");
sm2Spec = new ECNamedCurveSpec(GMObjectIdentifiers.sm2p256v1.toString(), ecParameters.getCurve(), ecParameters.getG(), ecParameters.getN());
bcProvider = new BouncyCastleProvider();
keyGenerator = KeyPairGenerator.getInstance("EC", bcProvider);
keyGenerator.initialize(sm2Spec, new SecureRandom());
keyFactory = KeyFactory.getInstance("EC", bcProvider);
curve = ecParameters.getCurve();
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException var1) {
throw new RuntimeException("无法提供SM2算法", var1);
}
}
public static void main(String[] args) {
KeyPair smKeyPair = SM2KeyUtil.genKeyPair();
String publicKey = Base64.encode(smKeyPair.getPublic().getEncoded());
String privateKey = Base64.encode(smKeyPair.getPrivate().getEncoded());
System.out.println("public key:"+ publicKey);
System.out.println("private key:"+ privateKey);
}
}
package cn.anicert.secure.access.crypto.util;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Hex;
import java.util.Arrays;
public class SM3Utils extends BaseCryptoUtil {
/**
* 计算SM3摘要值
*
* @param srcData 原文
* @return 摘要值,对于SM3算法来说是32字节
*/
public static String sm3Hash(byte[] srcData) {
SM3Digest digest = new SM3Digest();
digest.update(srcData, 0, srcData.length);
byte[] hash = new byte[digest.getDigestSize()];
digest.doFinal(hash, 0);
String result = Hex.toHexString(hash);
return result.toUpperCase();
}
/**
* 计算两项身份信息hash
*
* @param idno 身份证号
* @param name 姓名
* @return 摘要值,对于SM3算法来说是32字节
*/
public static String sm3Hash(String idno, String name) {
String str = idno + name;
byte[] srcData = str.getBytes();
return sm3Hash(srcData);
}
}
package cn.anicert.secure.access.crypto.util;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.SM4Engine;
import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
import org.bouncycastle.crypto.macs.GMac;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.paddings.BlockCipherPadding;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
public class SM4Utils extends BaseCryptoUtil {
static {
Security.addProvider(new BouncyCastleProvider());
}
public static final String ALGORITHM_NAME = "SM4";
public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
public static final String ALGORITHM_NAME_ECB_NOPADDING = "SM4/ECB/NoPadding";
public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";
public static final String ALGORITHM_NAME_CBC_NOPADDING = "SM4/CBC/NoPadding";
/**
* SM4算法目前只支持128位(即密钥16字节)
*/
public static final int DEFAULT_KEY_SIZE = 16;
public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException {
return generateKey(DEFAULT_KEY_SIZE);
}
public static byte[] generateKey(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
// KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
// kg.init(keySize, new SecureRandom());
SecureRandom secureRandom = new SecureRandom();
byte[] random = new byte[keySize];
secureRandom.nextBytes(random);
return random;
// return kg.generateKey().getEncoded();
}
public static byte[] encrypt_ECB_Padding(byte[] key, byte[] data)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
}
public static byte[] decrypt_ECB_Padding(byte[] key, byte[] cipherText)
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherText);
}
public static byte[] encrypt_ECB_NoPadding(byte[] key, byte[] data)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
}
public static byte[] decrypt_ECB_NoPadding(byte[] key, byte[] cipherText)
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherText);
}
public static byte[] encrypt_CBC_Padding(byte[] key, byte[] iv, byte[] data)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException {
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key, iv);
return cipher.doFinal(data);
}
public static byte[] decrypt_CBC_Padding(byte[] key, byte[] iv, byte[] cipherText)
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key, iv);
return cipher.doFinal(cipherText);
}
public static byte[] encrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] data)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException {
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.ENCRYPT_MODE, key, iv);
return cipher.doFinal(data);
}
public static byte[] decrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] cipherText)
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.DECRYPT_MODE, key, iv);
return cipher.doFinal(cipherText);
}
public static byte[] doCMac(byte[] key, byte[] data) throws NoSuchProviderException, NoSuchAlgorithmException,
InvalidKeyException {
Key keyObj = new SecretKeySpec(key, ALGORITHM_NAME);
return doMac("SM4-CMAC", keyObj, data);
}
public static byte[] doGMac(byte[] key, byte[] iv, int tagLength, byte[] data) {
org.bouncycastle.crypto.Mac mac = new GMac(new GCMBlockCipher(new SM4Engine()), tagLength * 8);
return doMac(mac, key, iv, data);
}
/**
* 默认使用PKCS7Padding/PKCS5Padding填充的CBCMAC
*
* @param key
* @param iv
* @param data
* @return
*/
public static byte[] doCBCMac(byte[] key, byte[] iv, byte[] data) {
SM4Engine engine = new SM4Engine();
org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, new PKCS7Padding());
return doMac(mac, key, iv, data);
}
/**
* @param key
* @param iv
* @param padding 可以传null,传null表示NoPadding,由调用方保证数据必须是BlockSize的整数倍
* @param data
* @return
* @throws Exception
*/
public static byte[] doCBCMac(byte[] key, byte[] iv, BlockCipherPadding padding, byte[] data) throws Exception {
SM4Engine engine = new SM4Engine();
if (padding == null) {
if (data.length % engine.getBlockSize() != 0) {
throw new Exception("if no padding, data length must be multiple of SM4 BlockSize");
}
}
org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, padding);
return doMac(mac, key, iv, data);
}
private static byte[] doMac(org.bouncycastle.crypto.Mac mac, byte[] key, byte[] iv, byte[] data) {
CipherParameters cipherParameters = new KeyParameter(key);
mac.init(new ParametersWithIV(cipherParameters, iv));
mac.update(data, 0, data.length);
byte[] result = new byte[mac.getMacSize()];
mac.doFinal(result, 0);
return result;
}
private static byte[] doMac(String algorithmName, Key key, byte[] data) throws NoSuchProviderException,
NoSuchAlgorithmException, InvalidKeyException {
Mac mac = Mac.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
mac.init(key);
mac.update(data);
return mac.doFinal();
}
private static Cipher generateECBCipher(String algorithmName, int mode, byte[] key)
throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
InvalidKeyException {
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
cipher.init(mode, sm4Key);
return cipher;
}
private static Cipher generateCBCCipher(String algorithmName, int mode, byte[] key, byte[] iv)
throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
NoSuchProviderException, NoSuchPaddingException {
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(mode, sm4Key, ivParameterSpec);
return cipher;
}
}
package cn.anicert.secure.access.enums;
public enum Configs {
customerNo("customerNo", "业务站点号", true),
accessUrl("accessUrl", "安全接入平台地址", true),
signPrivateKey("signPrivateKey", "签名私钥", true),
signVerifyPublicKey("signVerifyPublicKey", "验签公钥", true),
dataEncryptKey("dataEncryptKey", "数据加密秘钥", false);
private String key;
private String name;
private boolean necessary;
Configs(String key, String name) {
this(key, name, false);
}
Configs(String key, String name, boolean necessary) {
this.key = key;
this.name = name;
this.necessary = necessary;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isNecessary() {
return necessary;
}
public void setNecessary(boolean necessary) {
this.necessary = necessary;
}
}
package cn.anicert.secure.access.exceptions;
public class SecureAccessConfigException extends RuntimeException {
public SecureAccessConfigException() {
super();
}
public SecureAccessConfigException(String message) {
super(message);
}
}
package cn.anicert.secure.access.utils;
import cn.anicert.secure.access.config.ConfigReader;
import cn.anicert.secure.access.config.bean.AccessConfig;
import cn.anicert.secure.access.enums.Configs;
public class ConfigUtil {
private static AccessConfig accessConfig;
public static synchronized void initConfig(String... filePath) {
accessConfig = ConfigReader.readConfig(filePath);
}
public static synchronized void initConfig(AccessConfig config) {
accessConfig = config;
}
public static AccessConfig getConfig() {
return accessConfig;
}
public static void updateConfigDataEncryptKey(String newKey) {
accessConfig=ConfigReader.writeConfig(Configs.dataEncryptKey.getKey(), newKey);
}
}
package cn.anicert.secure.access.utils;
import cn.hutool.log.StaticLog;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.TrustAllStrategy;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.config.Registry;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.core5.util.Timeout;
import javax.net.ssl.SSLContext;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
public class HttpUtil {
private final PoolingHttpClientConnectionManager connPoolMng;
private final RequestConfig requestConfig;
private final SSLContext sslContext;
private volatile static HttpUtil httpUtilInstance;
/**
* 私有构造方法
* 单例中连接池初始化一次
*/
private HttpUtil() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
//初始化http连接池
sslContext = SSLContextBuilder.create()
.loadTrustMaterial(new TrustAllStrategy())
.build();
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https",new SSLConnectionSocketFactory(sslContext))
.build();
connPoolMng = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connPoolMng.setMaxTotal(ConfigUtil.getConfig().getPoolMaxConn());
connPoolMng.setDefaultMaxPerRoute(ConfigUtil.getConfig().getPoolMaxConn());
//初始化请求超时控制参数
requestConfig = RequestConfig.custom()
//从线程池中获取线程超时时间
.setConnectionRequestTimeout(Timeout.ofMilliseconds(ConfigUtil.getConfig().getConnectionRequestTimeout()))
//设置数据读取超时时间
.setResponseTimeout(Timeout.ofMilliseconds(ConfigUtil.getConfig().getResponseTimeout()))
.build();
}
/**
* 单例模式
* 使用双检锁机制,线程安全且在多线程情况下能保持高性能
*
* @return HttpUtil
*/
public static HttpUtil getInstance() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
if (httpUtilInstance == null) {
synchronized (HttpUtil.class) {
if (httpUtilInstance == null) {
httpUtilInstance = new HttpUtil();
}
}
}
return httpUtilInstance;
}
/**
* 获取client客户端
*
* @return
*/
public CloseableHttpClient getClient() {
return HttpClients.custom()
.setConnectionManager(connPoolMng)
.setDefaultRequestConfig(requestConfig)
.build();
}
/**
* post请求
*
* @param postUrl
* @param jsonStr
* @return String
* @throws Exception
*/
public String postRequest(String postUrl, String jsonStr) throws Exception {
String result;
HttpPost post = new HttpPost(postUrl);
StringEntity reqEntity = new StringEntity(jsonStr, ContentType.APPLICATION_JSON);
post.setEntity(reqEntity);
result = getClient().execute(post, response -> {
StaticLog.debug("[安全接入平台]--请求地址--{}--响应状态--{}", postUrl, response.getCode());
return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
});
return result;
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package cn.anicert.secure.access.utils;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
public class JSONUtil {
private static final ObjectMapper mapper = new ObjectMapper();
public static final JavaType[] EMPTY_TYPES = new JavaType[0];
public static final String YYYYMMDDHHMMSS = "yyyy-MM-dd HH:mm:ss";
public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public JSONUtil() {
}
public static <T> T json2Object(String jsonStr, Class<T> clazz) {
try {
return mapper.readValue(jsonStr, clazz);
} catch (Exception var3) {
throw new RuntimeException(var3);
}
}
public static String object2Json(Object obj) {
try {
return mapper.writeValueAsString(obj);
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static String toJson(Object obj) {
try {
return mapper.writeValueAsString(obj);
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static String toPrettyJason(Object obj) {
try {
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static String object2DateFormatJson(Object obj) {
return object2DateFormatJson(obj, "yyyy-MM-dd HH:mm:ss");
}
public static String object2DateFormatJson(Object obj, String dateFmt) {
try {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(Feature.ALLOW_COMMENTS, false);
mapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
mapper.configure(Feature.ALLOW_SINGLE_QUOTES, true);
mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
mapper.setDateFormat(new SimpleDateFormat(dateFmt));
mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
mapper.registerModule(new JavaTimeModule());
return mapper.writeValueAsString(obj);
} catch (Exception var3) {
throw new RuntimeException(var3);
}
}
public static <T> List<T> json2ObjectList(String jsonString, Class<T> clazz) {
try {
if (jsonString == null) {
return null;
} else {
List<T> list = (List)mapper.readValue(jsonString, new TypeReference<List<T>>() {
});
return list;
}
} catch (Exception var3) {
throw new RuntimeException(var3);
}
}
public static List<String> json2StringList(String jsonString) {
try {
if (!"".equals(jsonString) && null != jsonString) {
JsonNode jsonNode = mapper.readTree(jsonString);
List<String> list = new ArrayList();
jsonNode.forEach((node) -> {
list.add(node.toString());
});
return list;
} else {
return null;
}
} catch (Exception var3) {
throw new RuntimeException(var3);
}
}
public static Map<String, Object> json2Map(String jsonString) {
try {
return (Map)mapper.readValue(jsonString, new TypeReference<Map<String, Object>>() {
});
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static List<Map<String, Object>> json2MapList(String jsonString) {
try {
return (List)mapper.readValue(jsonString, new TypeReference<List<Map<String, Object>>>() {
});
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static Object[] json2Array(String jsonString) {
try {
List<Object> objs = (List)mapper.readValue(jsonString, new TypeReference<List>() {
});
return objs.toArray();
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
}
public static JsonNode json2JSONArray(String jsonString) {
try {
JsonNode jsonNode = mapper.readTree(jsonString);
JsonNodeType nodeType = jsonNode.getNodeType();
if (nodeType.equals(JsonNodeType.ARRAY)) {
return jsonNode;
} else {
throw new IllegalArgumentException("json字符串不是数组");
}
} catch (Exception var3) {
throw new RuntimeException(var3);
}
}
public static String array2Json(String[] strs) {
try {
ArrayNode arrayNode = new ArrayNode(new JsonNodeFactory(false));
String[] var2 = strs;
int var3 = strs.length;
for(int var4 = 0; var4 < var3; ++var4) {
String str = var2[var4];
JsonNode jsonNode = mapper.readTree(str);
arrayNode.add(jsonNode);
}
return arrayNode.toString();
} catch (Exception var7) {
throw new RuntimeException(var7);
}
}
public static <T> T[] jsonArrayToObjectArray(String jsonStr, Class<T[]> t) {
try {
T[] ts = (T[]) mapper.readValue(jsonStr, t);
return ts;
} catch (Exception var3) {
throw new RuntimeException(var3);
}
}
static {
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(Feature.ALLOW_COMMENTS, false);
mapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
mapper.configure(Feature.ALLOW_SINGLE_QUOTES, true);
mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
mapper.setDateFormat(sdf);
mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
mapper.registerModule(new JavaTimeModule());
}
}
package cn.anicert.secure.access.utils;
import cn.anicert.module.sign.bc.ICryptoService;
import cn.anicert.module.sign.bc.exception.BadKeyException;
import cn.anicert.module.sign.bc.exception.BadSignatureException;
import cn.anicert.module.sign.bc.mix.MixCryptoFactory;
import cn.anicert.module.sign.bc.std.Asymmetric;
import cn.anicert.module.sign.bc.std.Symmetric;
import org.apache.commons.codec.binary.Base64;
import java.nio.charset.StandardCharsets;
public class SignUtil {
private final static ICryptoService cryptoService = MixCryptoFactory.getInstance(Asymmetric.SM2, Symmetric.SM4);
public static String sign(String signBody) throws BadSignatureException, BadKeyException {
return sign(signBody, ConfigUtil.getConfig().getSignPrivateKey());
}
public static String sign(String signBody, String signPriKey) throws BadSignatureException, BadKeyException {
return Base64.encodeBase64String(cryptoService.sign(signPriKey, signBody.getBytes(StandardCharsets.UTF_8)));
}
public static boolean verifySign(String body, String sign) throws BadSignatureException, BadKeyException {
return verifySign(body, sign, ConfigUtil.getConfig().getSignVerifyPublicKey());
}
public static boolean verifySign(String body, String sign, String key) throws BadSignatureException, BadKeyException {
byte[] byteSign = Base64.decodeBase64(sign);
byte[] byteBody = body.getBytes(StandardCharsets.UTF_8);
return cryptoService.verifySign(key, byteSign, byteBody);
}
}
package com.liquidnet.common.third.secureaccess.config;
import cn.anicert.secure.access.config.bean.AccessConfig;
import cn.anicert.secure.access.crypto.service.ISoftCryptoService;
import cn.anicert.secure.access.crypto.service.impl.SoftCryptoServiceImpl;
import cn.anicert.secure.access.utils.ConfigUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Slf4j
@Configuration
public class SecureAccessConfiguration {
private final SecureAccessProperties properties;
public SecureAccessConfiguration(SecureAccessProperties properties) {
this.properties = properties;
}
@Bean
public ISoftCryptoService softCryptoService() {
return new SoftCryptoServiceImpl();
}
@PostConstruct
public void initSecureAccessSdk() {
if (StringUtils.isAnyBlank(
properties.getCustomerNo(),
properties.getAccessUrl(),
properties.getSignPrivateKey(),
properties.getSignVerifyPublicKey())) {
log.warn("[secure-access] 配置不完整,网证认证功能不可用");
return;
}
AccessConfig accessConfig = new AccessConfig();
accessConfig.setOrgId(properties.getOrgId());
accessConfig.setCustomerNo(properties.getCustomerNo());
accessConfig.setAccessUrl(properties.getAccessUrl());
accessConfig.setSignPrivateKey(properties.getSignPrivateKey());
accessConfig.setSignVerifyPublicKey(properties.getSignVerifyPublicKey());
accessConfig.setDataEncryptKey(properties.getDataEncryptKey());
ConfigUtil.initConfig(accessConfig);
log.info("[secure-access] SDK 初始化完成, customerNo={}", properties.getCustomerNo());
}
}
package com.liquidnet.common.third.secureaccess.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "liquidnet.secure-access")
public class SecureAccessProperties {
/** 机构 ID */
private String orgId;
/** 业务站点号 */
private String customerNo;
/** 安全接入平台根地址,如 https://sap.ctdid.cn:29002 */
private String accessUrl;
/** 机构侧 P1 签名私钥(Base64) */
private String signPrivateKey;
/** 安全接入平台验签公钥(Base64) */
private String signVerifyPublicKey;
/** 数据加密证书(Base64) */
private String dataEncryptKey;
/** 应用名称,对应接口文档 appName */
private String appName;
/** 认证模式:R01 网络身份认证凭证 */
private String mode = "R01";
/** 公共服务平台 auth/request 地址(测试/生产由配置切换) */
private String forwardUrl = "http://authtest.ctdid.cn:10002/uentrance/interf/auth/request";
}
package com.liquidnet.common.third.secureaccess.model;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class NiaAuthRequest {
/** 与前端 SDK 一致的 32 位业务序列号 */
private final String bizSeq;
/** 前端拉起网证 App 后 getAuthResult 获取 */
private final String idCardAuthData;
}
package com.liquidnet.common.third.secureaccess.model;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class NiaAuthResult {
private final boolean success;
private final String resultCode;
private final String resultDesc;
private final String bizSeq;
private final String bizSerialNo;
/** 网络身份应用标识标准 Base64(40 字节 → 56 字符) */
private final String openId;
private final String photoCompareScore;
private final String encryptedIdInfo;
/** 失败阶段:CONFIG / PARAM / SAP / PARSE / PLATFORM / UNKNOWN */
private final String stage;
/** SAP/平台原始响应摘要,便于联调 */
private final String rawResponse;
@Override
public String toString() {
return "NiaAuthResult{success=" + success
+ ", resultCode=" + resultCode
+ ", resultDesc=" + resultDesc
+ ", bizSeq=" + bizSeq
+ ", bizSerialNo=" + bizSerialNo
+ ", openId=" + openId
+ ", stage=" + stage
+ '}';
}
}
package com.liquidnet.common.third.secureaccess.model;
import lombok.Builder;
import lombok.Getter;
import org.apache.commons.codec.binary.Hex;
/**
* PID(认证服务凭据)按表 6 解码后的结构。
*/
@Getter
@Builder
public class NiaPidParsed {
private static final int TIMESTAMP_LEN = 14;
private final int totalLen;
private final int version;
/** 第 1–40 字节:网络身份应用标识(二进制,非文本) */
private final byte[] identifier;
/** 第 41–54 字节:yyyymmddhhmmss */
private final String timestamp;
/** 第 55+ 字节:签名 */
private final byte[] signature;
public String identifierHex() {
return identifier == null ? null : Hex.encodeHexString(identifier).toUpperCase();
}
/** 标识段可视化:可打印字符原样,不可打印用 '.' */
public String identifierVisual() {
if (identifier == null) {
return null;
}
StringBuilder sb = new StringBuilder(identifier.length);
for (byte b : identifier) {
sb.append(b >= 32 && b < 127 ? (char) b : '.');
}
return sb.toString();
}
public String signatureHexPrefix(int maxBytes) {
if (signature == null || signature.length == 0) {
return null;
}
int len = Math.min(signature.length, maxBytes);
byte[] head = new byte[len];
System.arraycopy(signature, 0, head, 0, len);
String hex = Hex.encodeHexString(head);
return signature.length > maxBytes ? hex + "..." : hex;
}
}
package com.liquidnet.common.third.secureaccess.util;
import com.liquidnet.common.third.secureaccess.model.NiaPidParsed;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
/**
* PID 解析与 openId 编码,与网证官方说明一致:
* <pre>
* byte[] raw = Base64.getDecoder().decode(pid);
* System.arraycopy(raw, 1, identifier, 0, 40);
* </pre>
* 存库 openId 取 40 字节标识的<strong>标准 Base64</strong>(56 字符,可放进 varchar(64))。
*/
public final class NiaPidUtils {
public static final int IDENTIFIER_BYTE_LEN = 40;
/** 标准 Base64(含填充)固定 56 字符 */
public static final int OPEN_ID_LEN = 56;
private static final Decoder JDK_BASE64_DECODER = java.util.Base64.getDecoder();
private static final Encoder JDK_BASE64_ENCODER = java.util.Base64.getEncoder();
private static final int TIMESTAMP_LEN = 14;
private static final int MIN_PID_LEN = 1 + IDENTIFIER_BYTE_LEN + TIMESTAMP_LEN;
private NiaPidUtils() {
}
public static NiaPidParsed parse(String pidBase64) {
if (StringUtils.isBlank(pidBase64)) {
return null;
}
byte[] raw;
try {
raw = JDK_BASE64_DECODER.decode(pidBase64.trim());
} catch (IllegalArgumentException ex) {
return null;
}
if (raw.length < MIN_PID_LEN) {
return null;
}
int version = raw[0] & 0xFF;
byte[] identifier = new byte[IDENTIFIER_BYTE_LEN];
System.arraycopy(raw, 1, identifier, 0, IDENTIFIER_BYTE_LEN);
byte[] tsBytes = new byte[TIMESTAMP_LEN];
System.arraycopy(raw, 1 + IDENTIFIER_BYTE_LEN, tsBytes, 0, TIMESTAMP_LEN);
String timestamp = new String(tsBytes, StandardCharsets.US_ASCII);
if (!timestamp.matches("\\d{14}")) {
return null;
}
byte[] signature = new byte[raw.length - MIN_PID_LEN];
if (signature.length > 0) {
System.arraycopy(raw, MIN_PID_LEN, signature, 0, signature.length);
}
return NiaPidParsed.builder()
.totalLen(raw.length)
.version(version)
.identifier(identifier)
.timestamp(timestamp)
.signature(signature)
.build();
}
/** 40 字节标识 → 标准 Base64(56 字符) */
public static String extractOpenId(String pid) {
NiaPidParsed parsed = parse(pid);
if (parsed == null || parsed.getIdentifier() == null) {
return null;
}
return encodeOpenId(parsed.getIdentifier());
}
public static String encodeOpenId(byte[] identifier) {
if (identifier == null || identifier.length != IDENTIFIER_BYTE_LEN) {
return null;
}
return JDK_BASE64_ENCODER.encodeToString(identifier);
}
/** 日志排查用:40 字节标识 → 大写 hex */
public static String toHex(byte[] identifier) {
if (identifier == null || identifier.length != IDENTIFIER_BYTE_LEN) {
return null;
}
return Hex.encodeHexString(identifier).toUpperCase();
}
public static byte[] toIdentifierBytes(String openId) {
if (StringUtils.isBlank(openId)) {
return null;
}
try {
byte[] decoded = JDK_BASE64_DECODER.decode(openId.trim());
return decoded.length == IDENTIFIER_BYTE_LEN ? decoded : null;
} catch (IllegalArgumentException ex) {
return null;
}
}
public static boolean isValidOpenId(String openId) {
if (StringUtils.isBlank(openId) || openId.length() != OPEN_ID_LEN) {
return false;
}
return toIdentifierBytes(openId) != null;
}
public static String maskPid(String pid) {
if (StringUtils.isBlank(pid)) {
return null;
}
if (pid.length() <= 16) {
return "***";
}
return pid.substring(0, 8) + "..." + pid.substring(pid.length() - 8);
}
}
package com.liquidnet.common.third.secureaccess.util;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class NiaPidUtilsTest {
private static final String PID_1 =
"Al7Y07Qo4P5eHosb4ADGQC4ou83HI+U6tgsUXC3uQYlZMDAwMDA3MTIyMDI2MDYyMzE5MTg1MjBGAiEAt5CKWHwrjso5x8i4Vl5cNiSCZAFwQefMXHyPoNIV4IMCIQCV1/pVvwyOlF6I6qLywzXHCGDmT6OVnBPX3+ZyMBWYjg==";
private static final String PID_2 =
"Al7Y07Qo4P5eHosb4ADGQC4ou83HI+U6tgsUXC3uQYlZMDAwMDA3MTIyMDI2MDYyNDExNTMxNDBFAiBfpnywTM/7efM4tAK5GglCWG39yMmFdYpfZDFRIpg30gIhAKkCuggZx2KoW0Uf1xy2BmF7OdBlCEqjlzujwUBq1ZDU";
private static final String OPEN_ID_B64 =
"XtjTtCjg/l4eixvgAMZALii7zccj5Tq2CxRcLe5BiVkwMDAwMDcxMg==";
@Test
void openId_shouldBeStandardBase64AndStableAcrossAuths() {
String openId1 = NiaPidUtils.extractOpenId(PID_1);
String openId2 = NiaPidUtils.extractOpenId(PID_2);
assertEquals(openId1, openId2);
assertEquals(56, openId1.length());
assertEquals(OPEN_ID_B64, openId1);
}
@Test
void openId_roundTrip() {
String openId = NiaPidUtils.extractOpenId(PID_1);
byte[] bytes = NiaPidUtils.toIdentifierBytes(openId);
assertNotNull(bytes);
assertEquals(openId, NiaPidUtils.encodeOpenId(bytes));
}
}
......@@ -17,5 +17,6 @@
<module>liquidnet-common-third-antchain</module>
<module>liquidnet-common-third-xuper</module>
<module>liquidnet-common-third-sqb</module>
<module>liquidnet-common-third-secure-access</module>
</modules>
</project>
......@@ -272,4 +272,15 @@ liquidnet:
public-key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuf1oOZm3u5NraTs4F8AABXbtU2jSiWYp+IWmQ36vokuq6s2s3eKQR6l4RkrPPxjC86bIvjT4pApJZJrFMA4YcjY4G49wFZySfom4IPaZlKsOrNGJH0Kag0BSO9U5el1z7dMz7oP9cChbdl4mjKuqYtgnNtaPT+SqhXRQdFcc9kiVybAGs8WEGqsdwxsmD9aZTd4rQMvLEGWIj/MLdo7w1avc0WVSPQSM5jRHjjQmUzEuusv+QGcDt3ttNaip2uo1xoQdcwILYmS6fnWL8xKw4V8lX0CWypUKIZcIc1Y/1N8VeUN+8MirdrS5JSghq62Yifu9A3W/mANB+S6yYwD+WQIDAQAB'
merchant-id: 'b2d63146-934f-401f-a864-14926d952c16'
merchant-user-id: '6d0d632e-50e9-464e-bbb3-be76047ec835'
role: 'super_admin'
\ No newline at end of file
role: 'super_admin'
secure-access:
org-id: '00000712'
customer-no: '00000712AA'
access-url: 'https://sap.ctdid.cn:29002'
sign-private-key: 'MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgY1WpjKNKWWDbqDRaBTzjCwbQxUo/ZIWWES0Xrp9YzY6hRANCAATfC188MtTPD/ziXvqI2VHLOspvZ9bBh2n7LNWgaP2QIXBJ/0vIRoSMT7ZwklfgZu58gI5ig8N2XvGHxWBdyLfF' # 机构 P1 签名私钥,联调时填入
sign-verify-public-key: 'MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEUNi/1x2Bh3mGHdpTjs+jIdv62G7oAU/dKzczYtuz1yol0ivUwt3xpA7nPEacWK+IIj2ZfOev4G5qrndMEs7t6Q==' # SAP 验签公钥
data-encrypt-key: 'MIICcjCCAhagAwIBAgISEAIFAyYEAhMYU+LoH1l4EIShMAwGCCqBHM9VAYN1BQAwRDELMAkGA1UEBhMCQ04xETAPBgNVBAoMCENITkNURElEMREwDwYDVQQLDAhDSE5DVERJRDEPMA0GA1UEAwwGWVdDQTAxMB4XDTI2MDQwMjA1MjUxM1oXDTQyMDMyOTA1MjUxM1owMTEPMA0GA1UEAwwGUlpGVzAzMREwDwYDVQQLDAhDSE5DVERJRDELMAkGA1UEBhMCQ04wWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAAQunUvCkEN4ISqw4sp0+Tvr14uKQt0VpKkgpSwi0HhGOJlUrC9bmitCk0xa0+5zd6lw1Cx5o4M4xCmZQQfzxb9uo4H4MIH1MAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgM4MIGXBgNVHR8EgY8wgYwwTKBKoEikRjBEMQ8wDQYDVQQDDAZZV0NBMDExETAPBgNVBAsMCENITkNURElEMREwDwYDVQQKDAhDSE5DVERJRDELMAkGA1UEBhMCQ04wPKA6oDiGNmh0dHA6Ly8xNzIuMjguMzAuODoxODA2MC9jYXdlYi9jcmwvWVdDQTAxL1lXQ0EwMV8wLmNybDAdBgNVHQ4EFgQU264a6MnK3/Ks3dVrI0BoEg2CLokwHwYDVR0jBBgwFoAUm1gG9DaAOEzQny6vTi6LlnTNgagwDAYIKoEcz1UBg3UFAANIADBFAiAPqpe2P76Eu0TBHKPqtflsCXTMUolq+FBXKK8nPhW9SwIhAKHGhPvqIAWcDHpu8xlHYJ9bkAwhJDDHNwspU7x6V0au' # 数据加密证书
app-name: '正在现场'
mode: 'R01'
forward-url: 'http://authtest.ctdid.cn:10002/uentrance/interf/auth/request'
\ No newline at end of file
......@@ -270,4 +270,15 @@ liquidnet:
public-key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuf1oOZm3u5NraTs4F8AABXbtU2jSiWYp+IWmQ36vokuq6s2s3eKQR6l4RkrPPxjC86bIvjT4pApJZJrFMA4YcjY4G49wFZySfom4IPaZlKsOrNGJH0Kag0BSO9U5el1z7dMz7oP9cChbdl4mjKuqYtgnNtaPT+SqhXRQdFcc9kiVybAGs8WEGqsdwxsmD9aZTd4rQMvLEGWIj/MLdo7w1avc0WVSPQSM5jRHjjQmUzEuusv+QGcDt3ttNaip2uo1xoQdcwILYmS6fnWL8xKw4V8lX0CWypUKIZcIc1Y/1N8VeUN+8MirdrS5JSghq62Yifu9A3W/mANB+S6yYwD+WQIDAQAB'
merchant-id: 'b2d63146-934f-401f-a864-14926d952c16'
merchant-user-id: '6d0d632e-50e9-464e-bbb3-be76047ec835'
role: 'super_admin'
\ No newline at end of file
role: 'super_admin'
secure-access:
org-id: '00000712'
customer-no: '00000712AA'
access-url: 'https://sap.ctdid.cn:29002'
sign-private-key: 'MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgY1WpjKNKWWDbqDRaBTzjCwbQxUo/ZIWWES0Xrp9YzY6hRANCAATfC188MtTPD/ziXvqI2VHLOspvZ9bBh2n7LNWgaP2QIXBJ/0vIRoSMT7ZwklfgZu58gI5ig8N2XvGHxWBdyLfF' # 机构 P1 签名私钥,联调时填入
sign-verify-public-key: 'MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEUNi/1x2Bh3mGHdpTjs+jIdv62G7oAU/dKzczYtuz1yol0ivUwt3xpA7nPEacWK+IIj2ZfOev4G5qrndMEs7t6Q==' # SAP 验签公钥
data-encrypt-key: 'MIICcjCCAhagAwIBAgISEAIFAyYEAhMYU+LoH1l4EIShMAwGCCqBHM9VAYN1BQAwRDELMAkGA1UEBhMCQ04xETAPBgNVBAoMCENITkNURElEMREwDwYDVQQLDAhDSE5DVERJRDEPMA0GA1UEAwwGWVdDQTAxMB4XDTI2MDQwMjA1MjUxM1oXDTQyMDMyOTA1MjUxM1owMTEPMA0GA1UEAwwGUlpGVzAzMREwDwYDVQQLDAhDSE5DVERJRDELMAkGA1UEBhMCQ04wWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAAQunUvCkEN4ISqw4sp0+Tvr14uKQt0VpKkgpSwi0HhGOJlUrC9bmitCk0xa0+5zd6lw1Cx5o4M4xCmZQQfzxb9uo4H4MIH1MAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgM4MIGXBgNVHR8EgY8wgYwwTKBKoEikRjBEMQ8wDQYDVQQDDAZZV0NBMDExETAPBgNVBAsMCENITkNURElEMREwDwYDVQQKDAhDSE5DVERJRDELMAkGA1UEBhMCQ04wPKA6oDiGNmh0dHA6Ly8xNzIuMjguMzAuODoxODA2MC9jYXdlYi9jcmwvWVdDQTAxL1lXQ0EwMV8wLmNybDAdBgNVHQ4EFgQU264a6MnK3/Ks3dVrI0BoEg2CLokwHwYDVR0jBBgwFoAUm1gG9DaAOEzQny6vTi6LlnTNgagwDAYIKoEcz1UBg3UFAANIADBFAiAPqpe2P76Eu0TBHKPqtflsCXTMUolq+FBXKK8nPhW9SwIhAKHGhPvqIAWcDHpu8xlHYJ9bkAwhJDDHNwspU7x6V0au' # 数据加密证书
app-name: '正在现场'
mode: 'R01'
forward-url: 'http://authtest.ctdid.cn:10002/uentrance/interf/auth/request'
\ No newline at end of file
......@@ -6,7 +6,7 @@ create table adam_third_party
(
mid bigint unsigned auto_increment primary key,
uid varchar(64) default '',
open_id varchar(64) comment '第三方ID',
open_id varchar(64) comment '第三方ID(NIA为56位标准Base64)',
avatar varchar(255) comment '第三方头像',
nickname varchar(100) comment '第三方昵称',
platform varchar(20) comment '平台名称',
......
......@@ -77,6 +77,11 @@
<artifactId>liquidnet-common-third-shumei</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-third-secure-access</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
......
......@@ -14,9 +14,11 @@ import com.liquidnet.commons.lang.constant.LnsEnum;
import com.liquidnet.commons.lang.core.JwtValidator;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.adam.constant.AdamConst;
import com.liquidnet.service.adam.dto.AdamNiaLoginParam;
import com.liquidnet.service.adam.dto.AdamThirdPartParam;
import com.liquidnet.service.adam.dto.vo.AdamLoginInfoVo;
import com.liquidnet.service.adam.dto.vo.AdamUserInfoVo;
import com.liquidnet.service.adam.service.IAdamNiaLoginService;
import com.liquidnet.service.adam.service.AdamRdmService;
import com.liquidnet.service.adam.service.AdamWechatService;
import com.liquidnet.service.adam.service.IAdamUserService;
......@@ -79,6 +81,8 @@ public class AdamLoginController {
AdamUserSessionSupport adamUserSessionSupport;
@Autowired
SilentMobileOtpV2Support silentMobileOtpV2Support;
@Autowired
IAdamNiaLoginService adamNiaLoginService;
@Value("${liquidnet.reviewer.app-login.mobile}")
private String reviewMobile;
......@@ -328,6 +332,18 @@ public class AdamLoginController {
return this.loginVoResponseProcessing(loginInfoVo);
}
@ApiOperationSupport(order = 8)
@ApiOperation(value = "国家网络身份认证登录")
@PostMapping(value = {"login/nia"})
public ResponseDto<?> loginByNia(@Valid @RequestBody AdamNiaLoginParam param) {
log.info("login by nia, bizSeq={}", param.getBizSeq());
ResponseDto<?> loginResp = adamNiaLoginService.login(param);
if (!loginResp.isSuccess()) {
return loginResp;
}
return this.loginVoResponseProcessing((AdamLoginInfoVo) loginResp.getData());
}
@ApiOperationSupport(order = 7)
@ApiOperation(value = "手机号静默登录")
@ApiImplicitParams({
......@@ -521,19 +537,7 @@ public class AdamLoginController {
}
private String ssoProcess(AdamUserInfoVo userInfoVo) {
Map<String, Object> claimsMap = CollectionUtil.mapStringObject();
claimsMap.put(CurrentUtil.TOKEN_SUB, userInfoVo.getUid());
claimsMap.put(CurrentUtil.TOKEN_MOBILE, userInfoVo.getMobile());
claimsMap.put(CurrentUtil.TOKEN_NICKNAME, userInfoVo.getNickname());
claimsMap.put(CurrentUtil.TOKEN_TYPE, CurrentUtil.TOKEN_TYPE_VAL_USER);
claimsMap.put(CurrentUtil.TOKEN_UCREATED, DateUtil.Formatter.yyyyMMddHHmmssTrim.format(userInfoVo.getCreateAt()));
log.debug("Gentoken:{}", claimsMap);
String jti = jwtValidator.generateJti();
String token = jwtValidator.create(claimsMap, jti);
adamUserSessionSupport.bindSession(jti, userInfoVo.getUid());
return token;
return adamUserSessionSupport.issueToken(userInfoVo);
}
private ResponseDto<AdamLoginInfoVo> loginVoResponseProcessing(AdamLoginInfoVo loginInfoVo) {
......
package com.liquidnet.service.adam.service;
import com.liquidnet.service.adam.dto.AdamNiaLoginParam;
import com.liquidnet.service.base.ResponseDto;
public interface IAdamNiaLoginService {
ResponseDto<?> login(AdamNiaLoginParam param);
}
package com.liquidnet.service.adam.service.impl;
import com.liquidnet.common.third.secureaccess.biz.NiaAuthBiz;
import com.liquidnet.common.third.secureaccess.model.NiaAuthRequest;
import com.liquidnet.common.third.secureaccess.model.NiaAuthResult;
import com.liquidnet.common.third.secureaccess.util.NiaPidUtils;
import com.liquidnet.service.adam.constant.AdamTpaConst;
import com.liquidnet.service.adam.dto.AdamNiaLoginParam;
import com.liquidnet.service.adam.dto.vo.AdamLoginInfoVo;
import com.liquidnet.service.adam.dto.vo.AdamNiaUnboundVo;
import com.liquidnet.service.adam.dto.vo.AdamUserInfoVo;
import com.liquidnet.service.adam.service.AdamRdmService;
import com.liquidnet.service.adam.service.IAdamNiaLoginService;
import com.liquidnet.service.adam.support.AdamUserSessionSupport;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.ResponseDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class AdamNiaLoginServiceImpl implements IAdamNiaLoginService {
private final NiaAuthBiz niaAuthBiz;
private final AdamRdmService adamRdmService;
private final AdamUserSessionSupport adamUserSessionSupport;
@Override
public ResponseDto<?> login(AdamNiaLoginParam param) {
if (!niaAuthBiz.isReady()) {
return ResponseDto.failure("10005", "网证认证服务暂未开通,请稍后再试");
}
log.info("[nia-login] 请求 bizSeq={}, idCardAuthDataLen={}",
param.getBizSeq(), param.getIdCardAuthData() == null ? 0 : param.getIdCardAuthData().length());
NiaAuthResult authResult = niaAuthBiz.authenticate(NiaAuthRequest.builder()
.bizSeq(param.getBizSeq())
.idCardAuthData(param.getIdCardAuthData())
.build());
if (!authResult.isSuccess()) {
log.warn("[nia-login] 认证失败 bizSeq={}, stage={}, code={}, desc={}",
param.getBizSeq(),
authResult.getStage(),
authResult.getResultCode(),
authResult.getResultDesc());
String errMsg = authResult.getResultDesc() != null && !authResult.getResultDesc().isEmpty()
? authResult.getResultDesc() : "网证认证失败,请重试";
return ResponseDto.failure("10005", errMsg);
}
if (!NiaPidUtils.isValidOpenId(authResult.getOpenId())) {
log.warn("[nia-login] PID标识解析失败 bizSeq={}", param.getBizSeq());
return ResponseDto.failure("10005", "网证认证失败,请重试");
}
String openId = authResult.getOpenId();
String uid = adamRdmService.getUidByPlatformOpenId(AdamTpaConst.PLATFORM_NIA, openId);
if (uid == null || uid.isEmpty()) {
AdamNiaUnboundVo unboundVo = AdamNiaUnboundVo.builder()
.platform(AdamTpaConst.PLATFORM_NIA)
.openId(openId)
.build();
log.info("[nia-login] 未绑定 bizSeq={}, bizSerialNo={}, openId={}",
param.getBizSeq(), authResult.getBizSerialNo(), openId);
return ResponseDto.failure(ErrorMapping.get("10006"), unboundVo);
}
AdamUserInfoVo userInfoVo = adamRdmService.getUserInfoVoByUid(uid);
if (userInfoVo == null || userInfoVo.getState() == 2) {
log.warn("[nia-login] cancelled openId={}", openId);
return ResponseDto.failure(ErrorMapping.get("10024"));
}
AdamLoginInfoVo loginInfoVo = AdamLoginInfoVo.getNew();
loginInfoVo.setUserInfo(userInfoVo);
loginInfoVo.setUserMemberVo(adamRdmService.getUserMemberVoByUid(uid));
loginInfoVo.setToken(adamUserSessionSupport.issueToken(userInfoVo));
log.info("[nia-login] LOGIN uid={}, openId={}", uid, openId);
return ResponseDto.success(loginInfoVo);
}
}
......@@ -2,10 +2,16 @@ package com.liquidnet.service.adam.support;
import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.core.JwtValidator;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.adam.dto.vo.AdamUserInfoVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 用户 JTI 会话:adam:session:{jti}=uid,adam:session:user:{uid} 记录活跃 jti。
*/
......@@ -32,4 +38,17 @@ public class AdamUserSessionSupport {
redisUtil.setRemove(jwtValidator.userSessionsKey(uid), jti);
}
}
public String issueToken(AdamUserInfoVo userInfoVo) {
Map<String, Object> claimsMap = CollectionUtil.mapStringObject();
claimsMap.put(CurrentUtil.TOKEN_SUB, userInfoVo.getUid());
claimsMap.put(CurrentUtil.TOKEN_MOBILE, userInfoVo.getMobile());
claimsMap.put(CurrentUtil.TOKEN_NICKNAME, userInfoVo.getNickname());
claimsMap.put(CurrentUtil.TOKEN_TYPE, CurrentUtil.TOKEN_TYPE_VAL_USER);
claimsMap.put(CurrentUtil.TOKEN_UCREATED, DateUtil.Formatter.yyyyMMddHHmmssTrim.format(userInfoVo.getCreateAt()));
String jti = jwtValidator.generateJti();
String token = jwtValidator.create(claimsMap, jti);
bindSession(jti, userInfoVo.getUid());
return token;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment