diff --git a/JSD-8034-配置使用文档.docx b/JSD-8034-配置使用文档.docx
new file mode 100644
index 0000000..de13e05
Binary files /dev/null and b/JSD-8034-配置使用文档.docx differ
diff --git a/JSD-8034-需求确认书V1.docx b/JSD-8034-需求确认书V1.docx
new file mode 100644
index 0000000..ed02ab0
Binary files /dev/null and b/JSD-8034-需求确认书V1.docx differ
diff --git a/README.md b/README.md
index 1c4f2d3..00347a6 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,6 @@
# open-JSD-8034
-JSD-8034 国密(SM2/SM3)签名和加解密单点登录 开源任务材料
\ No newline at end of file
+JSD-8034 国密(SM2/SM3)签名和加解密单点登录 开源任务材料
+免责说明:该源码为第三方爱好者提供,不保证源码和方案的可靠性,也不提供任何形式的源码教学指导和协助!\
+仅作为开发者学习参考使用!禁止用于任何商业用途!\
+为保护开发者隐私,开发者信息已隐去!若原开发者希望公开自己的信息,可联系hugh处理。
\ No newline at end of file
diff --git a/lib/bcprov-jdk15on-1.54.jar b/lib/bcprov-jdk15on-1.54.jar
new file mode 100644
index 0000000..bd95185
Binary files /dev/null and b/lib/bcprov-jdk15on-1.54.jar differ
diff --git a/lib/finekit-10.0.jar b/lib/finekit-10.0.jar
new file mode 100644
index 0000000..f4482fc
Binary files /dev/null and b/lib/finekit-10.0.jar differ
diff --git a/plugin.xml b/plugin.xml
new file mode 100644
index 0000000..f8d6677
--- /dev/null
+++ b/plugin.xml
@@ -0,0 +1,25 @@
+
+
+ com.fr.plugin.hjcd.sso
+
+ yes
+ 1.1.4
+ 10.0
+ 2018-07-31
+ mqh
+
+
+ com.fr.plugin.hjcd
+
+ com.fanruan.api
+ org.bouncycastle
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/hjcd/LocaleFinder.java b/src/main/java/com/fr/plugin/hjcd/LocaleFinder.java
new file mode 100644
index 0000000..eba9d00
--- /dev/null
+++ b/src/main/java/com/fr/plugin/hjcd/LocaleFinder.java
@@ -0,0 +1,37 @@
+ /*
+ * Copyright (C), 2018-2020
+ * Project: starter
+ * FileName: LocaleFinder
+ * Author: Louis
+ * Date: 2020/8/31 22:19
+ */
+ package com.fr.plugin.hjcd;
+
+ import com.fr.intelli.record.Focus;
+ import com.fr.intelli.record.Original;
+ import com.fr.record.analyzer.EnableMetrics;
+ import com.fr.stable.fun.impl.AbstractLocaleFinder;
+
+ import static com.fr.plugin.hjcd.config.SsoConfig.PLUGIN_ID;
+
+ /**
+ *
+ *
+ *
+ * @author fr.open
+ * @since 1.0.0
+ */
+ @EnableMetrics
+ public class LocaleFinder extends AbstractLocaleFinder {
+
+ @Override
+ @Focus(id = PLUGIN_ID, text = "Plugin-hjcd", source = Original.PLUGIN)
+ public String find() {
+ return "com/fr/plugin/hjcd/locale/lang";
+ }
+
+ @Override
+ public int currentAPILevel() {
+ return CURRENT_LEVEL;
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/hjcd/PluginMonitor.java b/src/main/java/com/fr/plugin/hjcd/PluginMonitor.java
new file mode 100644
index 0000000..01c776c
--- /dev/null
+++ b/src/main/java/com/fr/plugin/hjcd/PluginMonitor.java
@@ -0,0 +1,34 @@
+ /*
+ * Copyright (C), 2018-2021
+ * Project: starter
+ * FileName: PluginMonitor
+ * Author: Louis
+ * Date: 2021/3/30 15:10
+ */
+ package com.fr.plugin.hjcd;
+
+ import com.fr.plugin.context.PluginContext;
+ import com.fr.plugin.hjcd.config.SsoConfig;
+ import com.fr.plugin.observer.inner.AbstractPluginLifecycleMonitor;
+
+
+ /**
+ *
+ *
+ *
+ * @author fr.open
+ * @since 1.0.0
+ */
+ public class PluginMonitor extends AbstractPluginLifecycleMonitor {
+ public PluginMonitor() {
+ }
+
+ @Override
+ public void afterRun(PluginContext pluginContext) {
+ SsoConfig.getInstance();
+ }
+
+ @Override
+ public void beforeStop(PluginContext pluginContext) {
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/hjcd/config/SsoConfig.java b/src/main/java/com/fr/plugin/hjcd/config/SsoConfig.java
new file mode 100644
index 0000000..a5aa5d4
--- /dev/null
+++ b/src/main/java/com/fr/plugin/hjcd/config/SsoConfig.java
@@ -0,0 +1,64 @@
+ /*
+ * Copyright (C), 2018-2021
+ * Project: starter
+ * FileName: SsoConfig
+ * Author: Louis
+ * Date: 2021/3/30 9:38
+ */
+ package com.fr.plugin.hjcd.config;
+
+ import com.fanruan.api.util.StringKit;
+ import com.fr.config.*;
+ import com.fr.config.holder.Conf;
+ import com.fr.config.holder.factory.Holders;
+
+ /**
+ *
+ *
+ *
+ * @author fr.open
+ * @since 1.0.0
+ */
+ @Visualization(category = "Plugin-hjcd_Group")
+ public class SsoConfig extends DefaultConfiguration {
+ public static final String PLUGIN_ID = "com.fr.plugin.hjcd.sso";
+
+ private static volatile SsoConfig config = null;
+ @Identifier(value = "uriBase", name = "Plugin-hjcd_Config_UriBase", description = "Plugin-hjcd_Config_UriBase_Description", status = Status.SHOW)
+ private final Conf uriBase = Holders.simple(StringKit.EMPTY);
+ @Identifier(value = "channelId", name = "Plugin-hjcd_Config_ChannelId", description = "Plugin-hjcd_Config_ChannelId_Description", status = Status.SHOW)
+ private final Conf channelId = Holders.simple(StringKit.EMPTY);
+ @Identifier(value = "publicKey", name = "Plugin-hjcd_Config_PublicKey", description = "Plugin-hjcd_Config_PublicKey_Description", status = Status.SHOW)
+ private final Conf publicKey = Holders.simple(StringKit.EMPTY);
+
+ public static SsoConfig getInstance() {
+ if (config == null) {
+ config = ConfigContext.getConfigInstance(SsoConfig.class);
+ }
+ return config;
+ }
+
+ public String getUriBase() {
+ return uriBase.get();
+ }
+
+ public void setUriBase(String uriBase) {
+ this.uriBase.set(uriBase);
+ }
+
+ public String getChannelId() {
+ return channelId.get();
+ }
+
+ public void setChannelId(String channelId) {
+ this.channelId.set(channelId);
+ }
+
+ public String getPublicKey() {
+ return publicKey.get();
+ }
+
+ public void setPublicKey(String publicKey) {
+ this.publicKey.set(publicKey);
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/hjcd/request/OAuthLogin.java b/src/main/java/com/fr/plugin/hjcd/request/OAuthLogin.java
new file mode 100644
index 0000000..617d9c2
--- /dev/null
+++ b/src/main/java/com/fr/plugin/hjcd/request/OAuthLogin.java
@@ -0,0 +1,231 @@
+ /*
+ * Copyright (C), 2018-2021
+ * Project: starter
+ * FileName: OAuthLogin
+ * Author: Louis
+ * Date: 2021/3/30 22:09
+ */
+ package com.fr.plugin.hjcd.request;
+
+ import com.fanruan.api.decision.user.UserKit;
+ import com.fanruan.api.i18n.I18nKit;
+ import com.fanruan.api.log.LogKit;
+ import com.fanruan.api.net.NetworkKit;
+ import com.fanruan.api.net.http.HttpKit;
+ import com.fanruan.api.util.StringKit;
+ import com.fr.data.NetworkHelper;
+ import com.fr.decision.authority.data.User;
+ import com.fr.decision.config.FSConfig;
+ import com.fr.decision.fun.impl.AbstractGlobalRequestFilterProvider;
+ import com.fr.decision.mobile.terminal.TerminalHandler;
+ import com.fr.decision.webservice.bean.authentication.LoginClientBean;
+ import com.fr.decision.webservice.utils.ControllerFactory;
+ import com.fr.decision.webservice.utils.DecisionServiceConstants;
+ import com.fr.decision.webservice.utils.DecisionStatusService;
+ import com.fr.decision.webservice.utils.controller.AuthenticController;
+ import com.fr.decision.webservice.v10.login.LoginService;
+ import com.fr.decision.webservice.v10.register.RegisterService;
+ import com.fr.decision.webservice.v10.user.UserService;
+ import com.fr.json.JSONObject;
+ import com.fr.plugin.context.PluginContexts;
+ import com.fr.plugin.hjcd.config.SsoConfig;
+ import com.fr.plugin.hjcd.sm2utils.SM2Utils;
+ import com.fr.plugin.hjcd.sm2utils.Util;
+ import com.fr.stable.StringUtils;
+ import com.fr.stable.fun.Authorize;
+ import com.fr.stable.web.Device;
+ import com.fr.store.Converter;
+
+ import javax.servlet.FilterChain;
+ import javax.servlet.FilterConfig;
+ import javax.servlet.http.HttpServletRequest;
+ import javax.servlet.http.HttpServletResponse;
+ import java.nio.charset.StandardCharsets;
+ import java.util.HashMap;
+ import java.util.Map;
+
+ import static com.fr.plugin.hjcd.config.SsoConfig.PLUGIN_ID;
+
+ /**
+ *
+ *
+ *
+ * @author fr.open
+ * @since 1.0.0
+ */
+ @Authorize(callSignKey = PLUGIN_ID)
+ public class OAuthLogin extends AbstractGlobalRequestFilterProvider {
+
+ public static final String ACCESSTOKEN = "Accesstoken";
+ private SsoConfig config;
+
+ /**
+ * 过滤器名称
+ *
+ * @return
+ */
+ @Override
+ public String filterName() {
+ return "hjcdFilter";
+ }
+
+ /**
+ * 过滤规则
+ *
+ * @return
+ */
+ @Override
+ public String[] urlPatterns() {
+ return new String[]{"/decision", "/decision/view/form", "/decision/view/report", "/decision/v10/entry/access/*"};
+ }
+
+ /**
+ * 过滤器初始化
+ *
+ * @param filterConfig
+ */
+ @Override
+ public void init(FilterConfig filterConfig) {
+ this.config = SsoConfig.getInstance();
+ super.init(filterConfig);
+ }
+
+ /**
+ * 过滤器处理
+ *
+ * @param request
+ * @param response
+ * @param filterChain
+ */
+ @Override
+ public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
+ try {
+ if (operation(request, response)) {
+ filterChain.doFilter(request, response);
+ }
+ } catch (Exception e) {
+ LogKit.error(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 用户验证登陆操作
+ *
+ * @param req
+ * @param res
+ * @throws Exception
+ */
+ private boolean operation(HttpServletRequest req, HttpServletResponse res) throws Exception {
+ String token = NetworkKit.getHTTPRequestParameter(req, ACCESSTOKEN);
+ if (StringKit.isBlank(token)) {
+ return true;
+ }
+ String username = getUsername(token);
+ if (StringKit.isEmpty(username) || !UserKit.existUsername(username)) {
+ return true;
+ }
+ if (!PluginContexts.currentContext().isAvailable()) {
+ LogKit.error(I18nKit.getLocText("Plugin-hjcd_Licence_Expired"));
+ return true;
+ }
+ loginFR(req, res, username);
+ res.sendRedirect(removeToken(req));
+ return false;
+ }
+
+ /**
+ * 移除url的token参数
+ *
+ * @param request
+ * @return
+ */
+ private String removeToken(HttpServletRequest request) {
+ String url = getOriginalURL(request);
+ if (StringUtils.contains(url, ACCESSTOKEN)) {
+ return url.substring(0, url.indexOf(ACCESSTOKEN) - 1);
+ }
+ return url;
+ }
+
+ /**
+ * 得到请求url和参数
+ *
+ * @param request
+ * @return
+ */
+ private String getOriginalURL(HttpServletRequest request) {
+ StringBuffer url = request.getRequestURL();
+ if (StringKit.isNotBlank(request.getQueryString())) {
+ url.append("?").append(request.getQueryString());
+ }
+ return url.toString();
+ }
+
+ public Boolean loginFR(HttpServletRequest req, HttpServletResponse res, String userName) {
+ try {
+ User user = UserService.getInstance().getUserByUserName(userName);
+ if (user == null) {
+ return false;
+ }
+ String token = LoginService.getInstance().login(req, res, userName);
+ Device device = NetworkHelper.getDevice(req);
+ TerminalHandler terminal = TerminalHandler.getTerminal(req, device);
+ AuthenticController authenticController = ControllerFactory.getInstance().getAuthenticController(user.getId());
+ //已登录禁止登录
+ authenticController.verifySingleLoginStatus(user.getUserName(), terminal, token);
+ RegisterService.getInstance().checkLicExpireSoon(user);
+ LoginClientBean clientBean = new LoginClientBean(req, device, terminal);
+ clientBean.setUsername(user.getUserName());
+ clientBean.setToken(token);
+ clientBean.setValidity(-1);//默认就是-1了
+ clientBean.setUserId(user.getId());
+ //后登录踢出先登录
+ authenticController.logoutSingleLoginInvalidUser(user.getUserName(), terminal);
+ addLoginStatus(token, clientBean, FSConfig.getInstance().getLoginConfig().getLoginTimeout());
+ req.setAttribute(DecisionServiceConstants.FINE_AUTH_TOKEN_NAME, token);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ private void addLoginStatus(String token, LoginClientBean clientBean, long timeout) throws Exception {
+ DecisionStatusService.loginStatusService().put(token, clientBean, (Converter) var1 -> new String[]{var1.getUsername()}, timeout);
+ }
+
+ /**
+ * 通过凭证获得username
+ *
+ * @param token
+ * @return
+ */
+ private String getUsername(String token) throws Exception {
+ LogKit.info("hjcd-OAuthLogin-getUsername-token:{}", token);
+ Map params = new HashMap<>();
+ params.put("channelid", SsoConfig.getInstance().getChannelId());
+ params.put("Encrypted", encryptData(token));
+ String url = HttpKit.buildUrl(this.config.getUriBase(), params);
+ LogKit.info("hjcd-OAuthLogin-getUsername-url:{}", url);
+ String userRes = HttpKit.post(url, new HashMap<>());
+ LogKit.info("hjcd-OAuthLogin-getUsername-userRes:{}", userRes);
+ JSONObject result = new JSONObject(userRes);
+ if (StringKit.equals(result.getString("status"), "0")) {
+ return result.getString("workcode");
+ }
+ return StringKit.EMPTY;
+ }
+
+ /**
+ * 数据加密处理
+ *
+ * @param token
+ * @return
+ */
+ private String encryptData(String token) throws Exception {
+ String publicKey = SsoConfig.getInstance().getPublicKey();
+ String data = SsoConfig.getInstance().getChannelId() + "-" + token;
+ //SM2非对称加密
+ return SM2Utils.encrypt(Util.hexToByte(publicKey), data.getBytes(StandardCharsets.UTF_8));
+ }
+ }
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/hjcd/sm2utils/Cipher.java b/src/main/java/com/fr/plugin/hjcd/sm2utils/Cipher.java
new file mode 100644
index 0000000..eb3944a
--- /dev/null
+++ b/src/main/java/com/fr/plugin/hjcd/sm2utils/Cipher.java
@@ -0,0 +1,92 @@
+package com.fr.plugin.hjcd.sm2utils;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.math.BigInteger;
+
+public class Cipher {
+ private int ct;
+ private ECPoint p2;
+ private SM3Digest sm3keybase;
+ private SM3Digest sm3c3;
+ private byte key[];
+ private byte keyOff;
+
+ public Cipher() {
+ this.ct = 1;
+ this.key = new byte[32];
+ this.keyOff = 0;
+ }
+
+ private void Reset() {
+ this.sm3keybase = new SM3Digest();
+ this.sm3c3 = new SM3Digest();
+
+ byte p[] = Util.byteConvert32Bytes(p2.getX().toBigInteger());
+ this.sm3keybase.update(p, 0, p.length);
+ this.sm3c3.update(p, 0, p.length);
+
+ p = Util.byteConvert32Bytes(p2.getY().toBigInteger());
+ this.sm3keybase.update(p, 0, p.length);
+ this.ct = 1;
+ NextKey();
+ }
+
+ private void NextKey() {
+ SM3Digest sm3keycur = new SM3Digest(this.sm3keybase);
+ sm3keycur.update((byte) (ct >> 24 & 0xff));
+ sm3keycur.update((byte) (ct >> 16 & 0xff));
+ sm3keycur.update((byte) (ct >> 8 & 0xff));
+ sm3keycur.update((byte) (ct & 0xff));
+ sm3keycur.doFinal(key, 0);
+ this.keyOff = 0;
+ this.ct++;
+ }
+
+ public ECPoint Init_enc(SM2 sm2, ECPoint userKey) {
+ AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
+ ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
+ ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
+ BigInteger k = ecpriv.getD();
+ ECPoint c1 = ecpub.getQ();
+ this.p2 = userKey.multiply(k);
+ Reset();
+ return c1;
+ }
+
+ public void Encrypt(byte data[]) {
+ this.sm3c3.update(data, 0, data.length);
+ for (int i = 0; i < data.length; i++) {
+ if (keyOff == key.length) {
+ NextKey();
+ }
+ data[i] ^= key[keyOff++];
+ }
+ }
+
+ public void Init_dec(BigInteger userD, ECPoint c1) {
+ this.p2 = c1.multiply(userD);
+ Reset();
+ }
+
+ public void Decrypt(byte data[]) {
+ for (int i = 0; i < data.length; i++) {
+ if (keyOff == key.length) {
+ NextKey();
+ }
+ data[i] ^= key[keyOff++];
+ }
+
+ this.sm3c3.update(data, 0, data.length);
+ }
+
+ public void Dofinal(byte c3[]) {
+ byte p[] = Util.byteConvert32Bytes(p2.getY().toBigInteger());
+ this.sm3c3.update(p, 0, p.length);
+ this.sm3c3.doFinal(c3, 0);
+ Reset();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/hjcd/sm2utils/SM2.java b/src/main/java/com/fr/plugin/hjcd/sm2utils/SM2.java
new file mode 100644
index 0000000..55ecc38
--- /dev/null
+++ b/src/main/java/com/fr/plugin/hjcd/sm2utils/SM2.java
@@ -0,0 +1,62 @@
+package com.fr.plugin.hjcd.sm2utils;
+
+import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
+import org.bouncycastle.math.ec.ECFieldElement.Fp;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+public class SM2 {
+ //测试参数
+// public static final String[] ecc_param = {
+
+// };
+
+ //正式参数
+ public static String[] ecc_param = {
+ "xxxxx"
+ };
+ public final BigInteger ecc_p;
+ public final BigInteger ecc_a;
+ public final BigInteger ecc_b;
+ public final BigInteger ecc_n;
+ public final BigInteger ecc_gx;
+ public final BigInteger ecc_gy;
+ public final ECCurve ecc_curve;
+ public final ECPoint ecc_point_g;
+ public final ECDomainParameters ecc_bc_spec;
+ public final ECKeyPairGenerator ecc_key_pair_generator;
+ public final ECFieldElement ecc_gx_fieldelement;
+ public final ECFieldElement ecc_gy_fieldelement;
+ public SM2() {
+ this.ecc_p = new BigInteger(ecc_param[0], 16);
+ this.ecc_a = new BigInteger(ecc_param[1], 16);
+ this.ecc_b = new BigInteger(ecc_param[2], 16);
+ this.ecc_n = new BigInteger(ecc_param[3], 16);
+ this.ecc_gx = new BigInteger(ecc_param[4], 16);
+ this.ecc_gy = new BigInteger(ecc_param[5], 16);
+
+ this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx);
+ this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy);
+
+ this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);
+ this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement);
+
+ this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.ecc_n);
+
+ ECKeyGenerationParameters ecc_ecgenparam;
+ ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());
+
+ this.ecc_key_pair_generator = new ECKeyPairGenerator();
+ this.ecc_key_pair_generator.init(ecc_ecgenparam);
+ }
+
+ public static SM2 Instance() {
+ return new SM2();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/hjcd/sm2utils/SM2KeyPair.java b/src/main/java/com/fr/plugin/hjcd/sm2utils/SM2KeyPair.java
new file mode 100644
index 0000000..1aadd69
--- /dev/null
+++ b/src/main/java/com/fr/plugin/hjcd/sm2utils/SM2KeyPair.java
@@ -0,0 +1,29 @@
+package com.fr.plugin.hjcd.sm2utils;
+
+public class SM2KeyPair {
+
+ /**
+ * 公钥
+ */
+ private String publicKey;
+
+ /**
+ * 私钥
+ */
+ private String privateKey;
+
+ SM2KeyPair(String publicKey, String privateKey) {
+ this.publicKey = publicKey;
+ this.privateKey = privateKey;
+ }
+
+ public String getPublicKey() {
+ return publicKey;
+ }
+
+ public String getPrivateKey() {
+ return privateKey;
+ }
+
+
+}
diff --git a/src/main/java/com/fr/plugin/hjcd/sm2utils/SM2Utils.java b/src/main/java/com/fr/plugin/hjcd/sm2utils/SM2Utils.java
new file mode 100644
index 0000000..fdd17a6
--- /dev/null
+++ b/src/main/java/com/fr/plugin/hjcd/sm2utils/SM2Utils.java
@@ -0,0 +1,94 @@
+package com.fr.plugin.hjcd.sm2utils;
+
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECPoint;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+public class SM2Utils {
+ //生成随机秘钥对
+ public static SM2KeyPair generateKeyPair() {
+ SM2 sm2 = SM2.Instance();
+ AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
+ ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
+ ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
+ BigInteger privateKey = ecpriv.getD();
+ ECPoint publicKey = ecpub.getQ();
+ System.out.println("公钥: " + Util.byteToHex(publicKey.getEncoded()));
+ System.out.println("私钥: " + Util.byteToHex(privateKey.toByteArray()));
+ return new SM2KeyPair(Util.byteToHex(publicKey.getEncoded(false)), Util.byteToHex(privateKey.toByteArray()));
+ }
+
+ public static void main(String[] args) {
+ System.out.println(SM2Utils.generateKeyPair());
+ }
+
+ //数据加密
+ public static String encrypt(byte[] publicKey, byte[] data) throws IOException {
+ if (publicKey == null || publicKey.length == 0) {
+ return null;
+ }
+
+ if (data == null || data.length == 0) {
+ return null;
+ }
+
+ byte[] source = new byte[data.length];
+ System.arraycopy(data, 0, source, 0, data.length);
+
+ Cipher cipher = new Cipher();
+ SM2 sm2 = SM2.Instance();
+ ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);
+
+ ECPoint c1 = cipher.Init_enc(sm2, userKey);
+ cipher.Encrypt(source);
+ byte[] c3 = new byte[32];
+ cipher.Dofinal(c3);
+
+// System.out.println("C1 " + Util.byteToHex(c1.getEncoded()));
+// System.out.println("C2 " + Util.byteToHex(source));
+// System.out.println("C3 " + Util.byteToHex(c3));
+ //C1 C2 C3拼装成加密字串
+ return Util.byteToHex(c1.getEncoded(false)) + Util.byteToHex(source) + Util.byteToHex(c3);
+
+ }
+
+ //数据解密
+ public static byte[] decrypt(byte[] privateKey, byte[] encryptedData) throws IOException {
+ if (privateKey == null || privateKey.length == 0) {
+ return null;
+ }
+
+ if (encryptedData == null || encryptedData.length == 0) {
+ return null;
+ }
+ //加密字节数组转换为十六进制的字符串 长度变为encryptedData.length * 2
+ String data = Util.byteToHex(encryptedData);
+ /***分解加密字串
+ * (C1 = C1标志位2位 + C1实体部分128位 = 130)
+ * (C3 = C3实体部分64位 = 64)
+ * (C2 = encryptedData.length * 2 - C1长度 - C2长度)
+ */
+ byte[] c1Bytes = Util.hexToByte(data.substring(0, 130));
+ int c2Len = encryptedData.length - 97;
+ byte[] c2 = Util.hexToByte(data.substring(130, 130 + 2 * c2Len));
+ byte[] c3 = Util.hexToByte(data.substring(130 + 2 * c2Len, 194 + 2 * c2Len));
+
+ SM2 sm2 = SM2.Instance();
+ BigInteger userD = new BigInteger(1, privateKey);
+
+ //通过C1实体字节来生成ECPoint
+ ECPoint c1 = sm2.ecc_curve.decodePoint(c1Bytes);
+ Cipher cipher = new Cipher();
+ cipher.Init_dec(userD, c1);
+ cipher.Decrypt(c2);
+ cipher.Dofinal(c3);
+
+ //返回解密结果
+ return c2;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/hjcd/sm2utils/SM3.java b/src/main/java/com/fr/plugin/hjcd/sm2utils/SM3.java
new file mode 100644
index 0000000..2a3bef5
--- /dev/null
+++ b/src/main/java/com/fr/plugin/hjcd/sm2utils/SM3.java
@@ -0,0 +1,269 @@
+package com.fr.plugin.hjcd.sm2utils;
+
+
+public class SM3 {
+ public static final byte[] iv = {0x73, (byte) 0x80, 0x16, 0x6f, 0x49,
+ 0x14, (byte) 0xb2, (byte) 0xb9, 0x17, 0x24, 0x42, (byte) 0xd7,
+ (byte) 0xda, (byte) 0x8a, 0x06, 0x00, (byte) 0xa9, 0x6f, 0x30,
+ (byte) 0xbc, (byte) 0x16, 0x31, 0x38, (byte) 0xaa, (byte) 0xe3,
+ (byte) 0x8d, (byte) 0xee, 0x4d, (byte) 0xb0, (byte) 0xfb, 0x0e,
+ 0x4e};
+
+ public static int[] Tj = new int[64];
+
+ static {
+ for (int i = 0; i < 16; i++) {
+ Tj[i] = 0x79cc4519;
+ }
+
+ for (int i = 16; i < 64; i++) {
+ Tj[i] = 0x7a879d8a;
+ }
+ }
+
+ public static byte[] CF(byte[] V, byte[] B) {
+ int[] v, b;
+ v = convert(V);
+ b = convert(B);
+ return convert(CF(v, b));
+ }
+
+ private static int[] convert(byte[] arr) {
+ int[] out = new int[arr.length / 4];
+ byte[] tmp = new byte[4];
+ for (int i = 0; i < arr.length; i += 4) {
+ System.arraycopy(arr, i, tmp, 0, 4);
+ out[i / 4] = bigEndianByteToInt(tmp);
+ }
+ return out;
+ }
+
+ private static byte[] convert(int[] arr) {
+ byte[] out = new byte[arr.length * 4];
+ byte[] tmp = null;
+ for (int i = 0; i < arr.length; i++) {
+ tmp = bigEndianIntToByte(arr[i]);
+ System.arraycopy(tmp, 0, out, i * 4, 4);
+ }
+ return out;
+ }
+
+ public static int[] CF(int[] V, int[] B) {
+ int a, b, c, d, e, f, g, h;
+ int ss1, ss2, tt1, tt2;
+ a = V[0];
+ b = V[1];
+ c = V[2];
+ d = V[3];
+ e = V[4];
+ f = V[5];
+ g = V[6];
+ h = V[7];
+
+ int[][] arr = expand(B);
+ int[] w = arr[0];
+ int[] w1 = arr[1];
+
+ for (int j = 0; j < 64; j++) {
+ ss1 = (bitCycleLeft(a, 12) + e + bitCycleLeft(Tj[j], j));
+ ss1 = bitCycleLeft(ss1, 7);
+ ss2 = ss1 ^ bitCycleLeft(a, 12);
+ tt1 = FFj(a, b, c, j) + d + ss2 + w1[j];
+ tt2 = GGj(e, f, g, j) + h + ss1 + w[j];
+ d = c;
+ c = bitCycleLeft(b, 9);
+ b = a;
+ a = tt1;
+ h = g;
+ g = bitCycleLeft(f, 19);
+ f = e;
+ e = P0(tt2);
+
+ /*System.out.print(j+" ");
+ System.out.print(Integer.toHexString(a)+" ");
+ System.out.print(Integer.toHexString(b)+" ");
+ System.out.print(Integer.toHexString(c)+" ");
+ System.out.print(Integer.toHexString(d)+" ");
+ System.out.print(Integer.toHexString(e)+" ");
+ System.out.print(Integer.toHexString(f)+" ");
+ System.out.print(Integer.toHexString(g)+" ");
+ System.out.print(Integer.toHexString(h)+" ");
+ System.out.println("");*/
+ }
+// System.out.println("");
+
+ int[] out = new int[8];
+ out[0] = a ^ V[0];
+ out[1] = b ^ V[1];
+ out[2] = c ^ V[2];
+ out[3] = d ^ V[3];
+ out[4] = e ^ V[4];
+ out[5] = f ^ V[5];
+ out[6] = g ^ V[6];
+ out[7] = h ^ V[7];
+
+ return out;
+ }
+
+ private static int[][] expand(int[] B) {
+ int W[] = new int[68];
+ int W1[] = new int[64];
+ for (int i = 0; i < B.length; i++) {
+ W[i] = B[i];
+ }
+
+ for (int i = 16; i < 68; i++) {
+ W[i] = P1(W[i - 16] ^ W[i - 9] ^ bitCycleLeft(W[i - 3], 15))
+ ^ bitCycleLeft(W[i - 13], 7) ^ W[i - 6];
+ }
+
+ for (int i = 0; i < 64; i++) {
+ W1[i] = W[i] ^ W[i + 4];
+ }
+
+ int arr[][] = new int[][]{W, W1};
+ return arr;
+ }
+
+ private static byte[] bigEndianIntToByte(int num) {
+ return back(Util.intToBytes(num));
+ }
+
+ private static int bigEndianByteToInt(byte[] bytes) {
+ return Util.byteToInt(back(bytes));
+ }
+
+ private static int FFj(int X, int Y, int Z, int j) {
+ if (j >= 0 && j <= 15) {
+ return FF1j(X, Y, Z);
+ } else {
+ return FF2j(X, Y, Z);
+ }
+ }
+
+ private static int GGj(int X, int Y, int Z, int j) {
+ if (j >= 0 && j <= 15) {
+ return GG1j(X, Y, Z);
+ } else {
+ return GG2j(X, Y, Z);
+ }
+ }
+
+ // 逻辑位运算函数
+ private static int FF1j(int X, int Y, int Z) {
+ int tmp = X ^ Y ^ Z;
+ return tmp;
+ }
+
+ private static int FF2j(int X, int Y, int Z) {
+ int tmp = ((X & Y) | (X & Z) | (Y & Z));
+ return tmp;
+ }
+
+ private static int GG1j(int X, int Y, int Z) {
+ int tmp = X ^ Y ^ Z;
+ return tmp;
+ }
+
+ private static int GG2j(int X, int Y, int Z) {
+ int tmp = (X & Y) | (~X & Z);
+ return tmp;
+ }
+
+ private static int P0(int X) {
+ int y = rotateLeft(X, 9);
+ y = bitCycleLeft(X, 9);
+ int z = rotateLeft(X, 17);
+ z = bitCycleLeft(X, 17);
+ int t = X ^ y ^ z;
+ return t;
+ }
+
+ private static int P1(int X) {
+ int t = X ^ bitCycleLeft(X, 15) ^ bitCycleLeft(X, 23);
+ return t;
+ }
+
+ /**
+ * 对最后一个分组字节数据padding
+ *
+ * @param in
+ * @param bLen 分组个数
+ * @return
+ */
+ public static byte[] padding(byte[] in, int bLen) {
+ int k = 448 - (8 * in.length + 1) % 512;
+ if (k < 0) {
+ k = 960 - (8 * in.length + 1) % 512;
+ }
+ k += 1;
+ byte[] padd = new byte[k / 8];
+ padd[0] = (byte) 0x80;
+ long n = in.length * 8 + bLen * 512;
+ byte[] out = new byte[in.length + k / 8 + 64 / 8];
+ int pos = 0;
+ System.arraycopy(in, 0, out, 0, in.length);
+ pos += in.length;
+ System.arraycopy(padd, 0, out, pos, padd.length);
+ pos += padd.length;
+ byte[] tmp = back(Util.longToBytes(n));
+ System.arraycopy(tmp, 0, out, pos, tmp.length);
+ return out;
+ }
+
+ /**
+ * 字节数组逆序
+ *
+ * @param in
+ * @return
+ */
+ private static byte[] back(byte[] in) {
+ byte[] out = new byte[in.length];
+ for (int i = 0; i < out.length; i++) {
+ out[i] = in[out.length - i - 1];
+ }
+
+ return out;
+ }
+
+ public static int rotateLeft(int x, int n) {
+ return (x << n) | (x >> (32 - n));
+ }
+
+ private static int bitCycleLeft(int n, int bitLen) {
+ bitLen %= 32;
+ byte[] tmp = bigEndianIntToByte(n);
+ int byteLen = bitLen / 8;
+ int len = bitLen % 8;
+ if (byteLen > 0) {
+ tmp = byteCycleLeft(tmp, byteLen);
+ }
+
+ if (len > 0) {
+ tmp = bitSmall8CycleLeft(tmp, len);
+ }
+
+ return bigEndianByteToInt(tmp);
+ }
+
+ private static byte[] bitSmall8CycleLeft(byte[] in, int len) {
+ byte[] tmp = new byte[in.length];
+ int t1, t2, t3;
+ for (int i = 0; i < tmp.length; i++) {
+ t1 = (byte) ((in[i] & 0x000000ff) << len);
+ t2 = (byte) ((in[(i + 1) % tmp.length] & 0x000000ff) >> (8 - len));
+ t3 = (byte) (t1 | t2);
+ tmp[i] = (byte) t3;
+ }
+
+ return tmp;
+ }
+
+ private static byte[] byteCycleLeft(byte[] in, int byteLen) {
+ byte[] tmp = new byte[in.length];
+ System.arraycopy(in, byteLen, tmp, 0, in.length - byteLen);
+ System.arraycopy(in, 0, tmp, in.length - byteLen, byteLen);
+ return tmp;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/hjcd/sm2utils/SM3Digest.java b/src/main/java/com/fr/plugin/hjcd/sm2utils/SM3Digest.java
new file mode 100644
index 0000000..429749e
--- /dev/null
+++ b/src/main/java/com/fr/plugin/hjcd/sm2utils/SM3Digest.java
@@ -0,0 +1,139 @@
+package com.fr.plugin.hjcd.sm2utils;
+
+import org.bouncycastle.util.encoders.Hex;
+
+public class SM3Digest {
+ /**
+ * SM3值的长度
+ */
+ private static final int BYTE_LENGTH = 32;
+
+ /**
+ * SM3分组长度
+ */
+ private static final int BLOCK_LENGTH = 64;
+
+ /**
+ * 缓冲区长度
+ */
+ private static final int BUFFER_LENGTH = BLOCK_LENGTH * 1;
+
+ /**
+ * 缓冲区
+ */
+ private byte[] xBuf = new byte[BUFFER_LENGTH];
+
+ /**
+ * 缓冲区偏移量
+ */
+ private int xBufOff;
+
+ /**
+ * 初始向量
+ */
+ private byte[] V = SM3.iv.clone();
+
+ private int cntBlock = 0;
+
+ public SM3Digest() {
+ }
+
+ public SM3Digest(SM3Digest t) {
+ System.arraycopy(t.xBuf, 0, this.xBuf, 0, t.xBuf.length);
+ this.xBufOff = t.xBufOff;
+ System.arraycopy(t.V, 0, this.V, 0, t.V.length);
+ }
+
+ public static void main(String[] args) {
+ byte[] md = new byte[32];
+ byte[] msg1 = "ererfeiisgod".getBytes();
+ SM3Digest sm3 = new SM3Digest();
+ sm3.update(msg1, 0, msg1.length);
+ sm3.doFinal(md, 0);
+ String s = new String(Hex.encode(md));
+ System.out.println(s.toUpperCase());
+ }
+
+ /**
+ * SM3结果输出
+ *
+ * @param out 保存SM3结构的缓冲区
+ * @param outOff 缓冲区偏移量
+ * @return
+ */
+ public int doFinal(byte[] out, int outOff) {
+ byte[] tmp = doFinal();
+ System.arraycopy(tmp, 0, out, 0, tmp.length);
+ return BYTE_LENGTH;
+ }
+
+ public void reset() {
+ xBufOff = 0;
+ cntBlock = 0;
+ V = SM3.iv.clone();
+ }
+
+ /**
+ * 明文输入
+ *
+ * @param in 明文输入缓冲区
+ * @param inOff 缓冲区偏移量
+ * @param len 明文长度
+ */
+ public void update(byte[] in, int inOff, int len) {
+ int partLen = BUFFER_LENGTH - xBufOff;
+ int inputLen = len;
+ int dPos = inOff;
+ if (partLen < inputLen) {
+ System.arraycopy(in, dPos, xBuf, xBufOff, partLen);
+ inputLen -= partLen;
+ dPos += partLen;
+ doUpdate();
+ while (inputLen > BUFFER_LENGTH) {
+ System.arraycopy(in, dPos, xBuf, 0, BUFFER_LENGTH);
+ inputLen -= BUFFER_LENGTH;
+ dPos += BUFFER_LENGTH;
+ doUpdate();
+ }
+ }
+
+ System.arraycopy(in, dPos, xBuf, xBufOff, inputLen);
+ xBufOff += inputLen;
+ }
+
+ private void doUpdate() {
+ byte[] B = new byte[BLOCK_LENGTH];
+ for (int i = 0; i < BUFFER_LENGTH; i += BLOCK_LENGTH) {
+ System.arraycopy(xBuf, i, B, 0, B.length);
+ doHash(B);
+ }
+ xBufOff = 0;
+ }
+
+ private void doHash(byte[] B) {
+ byte[] tmp = SM3.CF(V, B);
+ System.arraycopy(tmp, 0, V, 0, V.length);
+ cntBlock++;
+ }
+
+ private byte[] doFinal() {
+ byte[] B = new byte[BLOCK_LENGTH];
+ byte[] buffer = new byte[xBufOff];
+ System.arraycopy(xBuf, 0, buffer, 0, buffer.length);
+ byte[] tmp = SM3.padding(buffer, cntBlock);
+ for (int i = 0; i < tmp.length; i += BLOCK_LENGTH) {
+ System.arraycopy(tmp, i, B, 0, B.length);
+ doHash(B);
+ }
+ return V;
+ }
+
+ public void update(byte in) {
+ byte[] buffer = new byte[]{in};
+ update(buffer, 0, 1);
+ }
+
+ public int getDigestSize() {
+ return BYTE_LENGTH;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/hjcd/sm2utils/Test.java b/src/main/java/com/fr/plugin/hjcd/sm2utils/Test.java
new file mode 100644
index 0000000..92f9598
--- /dev/null
+++ b/src/main/java/com/fr/plugin/hjcd/sm2utils/Test.java
@@ -0,0 +1,17 @@
+package com.fr.plugin.hjcd.sm2utils;
+
+public class Test {
+
+ public static void main(String[] args) throws Exception {
+
+ String info = "xxxx";// 渠道id-token参数
+
+ String publickey = "xxxx";
+ // 将信息加密,公钥的字节码需要使用util里的hexToByte方法
+ String encrypt = SM2Utils.encrypt(Util.hexToByte(publickey), info.getBytes("utf-8"));
+
+ System.out.println("加密后信息:" + encrypt);
+
+ }
+
+}
diff --git a/src/main/java/com/fr/plugin/hjcd/sm2utils/Util.java b/src/main/java/com/fr/plugin/hjcd/sm2utils/Util.java
new file mode 100644
index 0000000..29411a3
--- /dev/null
+++ b/src/main/java/com/fr/plugin/hjcd/sm2utils/Util.java
@@ -0,0 +1,616 @@
+package com.fr.plugin.hjcd.sm2utils;
+
+
+import java.math.BigInteger;
+
+public class Util {
+ /**
+ * 用于建立十六进制字符的输出的小写字符数组
+ */
+ private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ /**
+ * 用于建立十六进制字符的输出的大写字符数组
+ */
+ private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ /**
+ * 整形转换成网络传输的字节流(字节数组)型数据
+ *
+ * @param num 一个整型数据
+ * @return 4个字节的自己数组
+ */
+ public static byte[] intToBytes(int num) {
+ byte[] bytes = new byte[4];
+ bytes[0] = (byte) (0xff & (num >> 0));
+ bytes[1] = (byte) (0xff & (num >> 8));
+ bytes[2] = (byte) (0xff & (num >> 16));
+ bytes[3] = (byte) (0xff & (num >> 24));
+ return bytes;
+ }
+
+ /**
+ * 四个字节的字节数据转换成一个整形数据
+ *
+ * @param bytes 4个字节的字节数组
+ * @return 一个整型数据
+ */
+ public static int byteToInt(byte[] bytes) {
+ int num = 0;
+ int temp;
+ temp = (0x000000ff & (bytes[0])) << 0;
+ num = num | temp;
+ temp = (0x000000ff & (bytes[1])) << 8;
+ num = num | temp;
+ temp = (0x000000ff & (bytes[2])) << 16;
+ num = num | temp;
+ temp = (0x000000ff & (bytes[3])) << 24;
+ num = num | temp;
+ return num;
+ }
+
+ /**
+ * 长整形转换成网络传输的字节流(字节数组)型数据
+ *
+ * @param num 一个长整型数据
+ * @return 4个字节的自己数组
+ */
+ public static byte[] longToBytes(long num) {
+ byte[] bytes = new byte[8];
+ for (int i = 0; i < 8; i++) {
+ bytes[i] = (byte) (0xff & (num >> (i * 8)));
+ }
+
+ return bytes;
+ }
+
+ /**
+ * 大数字转换字节流(字节数组)型数据
+ *
+ * @param n
+ * @return
+ */
+ public static byte[] byteConvert32Bytes(BigInteger n) {
+ byte tmpd[] = (byte[]) null;
+ if (n == null) {
+ return null;
+ }
+
+ if (n.toByteArray().length == 33) {
+ tmpd = new byte[32];
+ System.arraycopy(n.toByteArray(), 1, tmpd, 0, 32);
+ } else if (n.toByteArray().length == 32) {
+ tmpd = n.toByteArray();
+ } else {
+ tmpd = new byte[32];
+ for (int i = 0; i < 32 - n.toByteArray().length; i++) {
+ tmpd[i] = 0;
+ }
+ System.arraycopy(n.toByteArray(), 0, tmpd, 32 - n.toByteArray().length, n.toByteArray().length);
+ }
+ return tmpd;
+ }
+
+ /**
+ * 换字节流(字节数组)型数据转大数字
+ *
+ * @param b
+ * @return
+ */
+ public static BigInteger byteConvertInteger(byte[] b) {
+ if (b[0] < 0) {
+ byte[] temp = new byte[b.length + 1];
+ temp[0] = 0;
+ System.arraycopy(b, 0, temp, 1, b.length);
+ return new BigInteger(temp);
+ }
+ return new BigInteger(b);
+ }
+
+ /**
+ * 根据字节数组获得值(十六进制数字)
+ *
+ * @param bytes
+ * @return
+ */
+ public static String getHexString(byte[] bytes) {
+ return getHexString(bytes, true);
+ }
+
+ /**
+ * 根据字节数组获得值(十六进制数字)
+ *
+ * @param bytes
+ * @param upperCase
+ * @return
+ */
+ public static String getHexString(byte[] bytes, boolean upperCase) {
+ String ret = "";
+ for (int i = 0; i < bytes.length; i++) {
+ ret += Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1);
+ }
+ return upperCase ? ret.toUpperCase() : ret;
+ }
+
+ /**
+ * 打印十六进制字符串
+ *
+ * @param bytes
+ */
+ public static void printHexString(byte[] bytes) {
+ for (int i = 0; i < bytes.length; i++) {
+ String hex = Integer.toHexString(bytes[i] & 0xFF);
+ if (hex.length() == 1) {
+ hex = '0' + hex;
+ }
+ System.out.print("0x" + hex.toUpperCase() + ",");
+ }
+ System.out.println("");
+ }
+
+ /**
+ * Convert hex string to byte[]
+ *
+ * @param hexString the hex string
+ * @return byte[]
+ */
+ public static byte[] hexStringToBytes(String hexString) {
+ if (hexString == null || hexString.equals("")) {
+ return null;
+ }
+
+ hexString = hexString.toUpperCase();
+ int length = hexString.length() / 2;
+ char[] hexChars = hexString.toCharArray();
+ byte[] d = new byte[length];
+ for (int i = 0; i < length; i++) {
+ int pos = i * 2;
+ d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
+ }
+ return d;
+ }
+
+ /**
+ * Convert char to byte
+ *
+ * @param c char
+ * @return byte
+ */
+ public static byte charToByte(char c) {
+ return (byte) "0123456789ABCDEF".indexOf(c);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符数组
+ *
+ * @param data byte[]
+ * @return 十六进制char[]
+ */
+ public static char[] encodeHex(byte[] data) {
+ return encodeHex(data, true);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符数组
+ *
+ * @param data byte[]
+ * @param toLowerCase true
传换成小写格式 , false
传换成大写格式
+ * @return 十六进制char[]
+ */
+ public static char[] encodeHex(byte[] data, boolean toLowerCase) {
+ return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符数组
+ *
+ * @param data byte[]
+ * @param toDigits 用于控制输出的char[]
+ * @return 十六进制char[]
+ */
+ protected static char[] encodeHex(byte[] data, char[] toDigits) {
+ int l = data.length;
+ char[] out = new char[l << 1];
+ // two characters form the hex value.
+ for (int i = 0, j = 0; i < l; i++) {
+ out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
+ out[j++] = toDigits[0x0F & data[i]];
+ }
+ return out;
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符串
+ *
+ * @param data byte[]
+ * @return 十六进制String
+ */
+ public static String encodeHexString(byte[] data) {
+ return encodeHexString(data, true);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符串
+ *
+ * @param data byte[]
+ * @param toLowerCase true
传换成小写格式 , false
传换成大写格式
+ * @return 十六进制String
+ */
+ public static String encodeHexString(byte[] data, boolean toLowerCase) {
+ return encodeHexString(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
+ }
+
+ /**
+ * 将字节数组转换为十六进制字符串
+ *
+ * @param data byte[]
+ * @param toDigits 用于控制输出的char[]
+ * @return 十六进制String
+ */
+ protected static String encodeHexString(byte[] data, char[] toDigits) {
+ return new String(encodeHex(data, toDigits));
+ }
+
+ /**
+ * 将十六进制字符数组转换为字节数组
+ *
+ * @param data 十六进制char[]
+ * @return byte[]
+ * @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常
+ */
+ public static byte[] decodeHex(char[] data) {
+ int len = data.length;
+
+ if ((len & 0x01) != 0) {
+ throw new RuntimeException("Odd number of characters.");
+ }
+
+ byte[] out = new byte[len >> 1];
+
+ // two characters form the hex value.
+ for (int i = 0, j = 0; j < len; i++) {
+ int f = toDigit(data[j], j) << 4;
+ j++;
+ f = f | toDigit(data[j], j);
+ j++;
+ out[i] = (byte) (f & 0xFF);
+ }
+
+ return out;
+ }
+
+ /**
+ * 将十六进制字符转换成一个整数
+ *
+ * @param ch 十六进制char
+ * @param index 十六进制字符在字符数组中的位置
+ * @return 一个整数
+ * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常
+ */
+ protected static int toDigit(char ch, int index) {
+ int digit = Character.digit(ch, 16);
+ if (digit == -1) {
+ throw new RuntimeException("Illegal hexadecimal character " + ch
+ + " at index " + index);
+ }
+ return digit;
+ }
+
+ /**
+ * 数字字符串转ASCII码字符串
+ *
+ * @param content 字符串
+ * @return ASCII字符串
+ */
+ public static String StringToAsciiString(String content) {
+ String result = "";
+ int max = content.length();
+ for (int i = 0; i < max; i++) {
+ char c = content.charAt(i);
+ String b = Integer.toHexString(c);
+ result = result + b;
+ }
+ return result;
+ }
+
+ /**
+ * 十六进制转字符串
+ *
+ * @param hexString 十六进制字符串
+ * @param encodeType 编码类型4:Unicode,2:普通编码
+ * @return 字符串
+ */
+ public static String hexStringToString(String hexString, int encodeType) {
+ String result = "";
+ int max = hexString.length() / encodeType;
+ for (int i = 0; i < max; i++) {
+ char c = (char) hexStringToAlgorism(hexString
+ .substring(i * encodeType, (i + 1) * encodeType));
+ result += c;
+ }
+ return result;
+ }
+
+ /**
+ * 十六进制字符串装十进制
+ *
+ * @param hex 十六进制字符串
+ * @return 十进制数值
+ */
+ public static int hexStringToAlgorism(String hex) {
+ hex = hex.toUpperCase();
+ int max = hex.length();
+ int result = 0;
+ for (int i = max; i > 0; i--) {
+ char c = hex.charAt(i - 1);
+ int algorism = 0;
+ if (c >= '0' && c <= '9') {
+ algorism = c - '0';
+ } else {
+ algorism = c - 55;
+ }
+ result += Math.pow(16, max - i) * algorism;
+ }
+ return result;
+ }
+
+ /**
+ * 十六转二进制
+ *
+ * @param hex 十六进制字符串
+ * @return 二进制字符串
+ */
+ public static String hexStringToBinary(String hex) {
+ hex = hex.toUpperCase();
+ String result = "";
+ int max = hex.length();
+ for (int i = 0; i < max; i++) {
+ char c = hex.charAt(i);
+ switch (c) {
+ case '0':
+ result += "0000";
+ break;
+ case '1':
+ result += "0001";
+ break;
+ case '2':
+ result += "0010";
+ break;
+ case '3':
+ result += "0011";
+ break;
+ case '4':
+ result += "0100";
+ break;
+ case '5':
+ result += "0101";
+ break;
+ case '6':
+ result += "0110";
+ break;
+ case '7':
+ result += "0111";
+ break;
+ case '8':
+ result += "1000";
+ break;
+ case '9':
+ result += "1001";
+ break;
+ case 'A':
+ result += "1010";
+ break;
+ case 'B':
+ result += "1011";
+ break;
+ case 'C':
+ result += "1100";
+ break;
+ case 'D':
+ result += "1101";
+ break;
+ case 'E':
+ result += "1110";
+ break;
+ case 'F':
+ result += "1111";
+ break;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * ASCII码字符串转数字字符串
+ *
+ * @param content ASCII字符串
+ * @return 字符串
+ */
+ public static String AsciiStringToString(String content) {
+ String result = "";
+ int length = content.length() / 2;
+ for (int i = 0; i < length; i++) {
+ String c = content.substring(i * 2, i * 2 + 2);
+ int a = hexStringToAlgorism(c);
+ char b = (char) a;
+ String d = String.valueOf(b);
+ result += d;
+ }
+ return result;
+ }
+
+ /**
+ * 将十进制转换为指定长度的十六进制字符串
+ *
+ * @param algorism int 十进制数字
+ * @param maxLength int 转换后的十六进制字符串长度
+ * @return String 转换后的十六进制字符串
+ */
+ public static String algorismToHexString(int algorism, int maxLength) {
+ String result = "";
+ result = Integer.toHexString(algorism);
+
+ if (result.length() % 2 == 1) {
+ result = "0" + result;
+ }
+ return patchHexString(result.toUpperCase(), maxLength);
+ }
+
+ /**
+ * 字节数组转为普通字符串(ASCII对应的字符)
+ *
+ * @param bytearray byte[]
+ * @return String
+ */
+ public static String byteToString(byte[] bytearray) {
+ String result = "";
+ char temp;
+
+ int length = bytearray.length;
+ for (int i = 0; i < length; i++) {
+ temp = (char) bytearray[i];
+ result += temp;
+ }
+ return result;
+ }
+
+ /**
+ * 二进制字符串转十进制
+ *
+ * @param binary 二进制字符串
+ * @return 十进制数值
+ */
+ public static int binaryToAlgorism(String binary) {
+ int max = binary.length();
+ int result = 0;
+ for (int i = max; i > 0; i--) {
+ char c = binary.charAt(i - 1);
+ int algorism = c - '0';
+ result += Math.pow(2, max - i) * algorism;
+ }
+ return result;
+ }
+
+ /**
+ * 十进制转换为十六进制字符串
+ *
+ * @param algorism int 十进制的数字
+ * @return String 对应的十六进制字符串
+ */
+ public static String algorismToHEXString(int algorism) {
+ String result = "";
+ result = Integer.toHexString(algorism);
+
+ if (result.length() % 2 == 1) {
+ result = "0" + result;
+
+ }
+ result = result.toUpperCase();
+
+ return result;
+ }
+
+ /**
+ * HEX字符串前补0,主要用于长度位数不足。
+ *
+ * @param str String 需要补充长度的十六进制字符串
+ * @param maxLength int 补充后十六进制字符串的长度
+ * @return 补充结果
+ */
+ static public String patchHexString(String str, int maxLength) {
+ String temp = "";
+ for (int i = 0; i < maxLength - str.length(); i++) {
+ temp = "0" + temp;
+ }
+ str = (temp + str).substring(0, maxLength);
+ return str;
+ }
+
+ /**
+ * 将一个字符串转换为int
+ *
+ * @param s String 要转换的字符串
+ * @param defaultInt int 如果出现异常,默认返回的数字
+ * @param radix int 要转换的字符串是什么进制的,如16 8 10.
+ * @return int 转换后的数字
+ */
+ public static int parseToInt(String s, int defaultInt, int radix) {
+ int i = 0;
+ try {
+ i = Integer.parseInt(s, radix);
+ } catch (NumberFormatException ex) {
+ i = defaultInt;
+ }
+ return i;
+ }
+
+ /**
+ * 将一个十进制形式的数字字符串转换为int
+ *
+ * @param s String 要转换的字符串
+ * @param defaultInt int 如果出现异常,默认返回的数字
+ * @return int 转换后的数字
+ */
+ public static int parseToInt(String s, int defaultInt) {
+ int i = 0;
+ try {
+ i = Integer.parseInt(s);
+ } catch (NumberFormatException ex) {
+ i = defaultInt;
+ }
+ return i;
+ }
+
+ /**
+ * 十六进制串转化为byte数组
+ *
+ * @return the array of byte
+ */
+ public static byte[] hexToByte(String hex)
+ throws IllegalArgumentException {
+ if (hex.length() % 2 != 0) {
+ throw new IllegalArgumentException();
+ }
+ char[] arr = hex.toCharArray();
+ byte[] b = new byte[hex.length() / 2];
+ for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) {
+ String swap = "" + arr[i++] + arr[i];
+ int byteint = Integer.parseInt(swap, 16) & 0xFF;
+ b[j] = new Integer(byteint).byteValue();
+ }
+ return b;
+ }
+
+ /**
+ * 字节数组转换为十六进制字符串
+ *
+ * @param b byte[] 需要转换的字节数组
+ * @return String 十六进制字符串
+ */
+ public static String byteToHex(byte b[]) {
+ if (b == null) {
+ throw new IllegalArgumentException(
+ "Argument b ( byte array ) is null! ");
+ }
+ String hs = "";
+ String stmp = "";
+ for (int n = 0; n < b.length; n++) {
+ stmp = Integer.toHexString(b[n] & 0xff);
+ if (stmp.length() == 1) {
+ hs = hs + "0" + stmp;
+ } else {
+ hs = hs + stmp;
+ }
+ }
+ return hs.toUpperCase();
+ }
+
+ public static byte[] subByte(byte[] input, int startIndex, int length) {
+ byte[] bt = new byte[length];
+ for (int i = 0; i < length; i++) {
+ bt[i] = input[i + startIndex];
+ }
+ return bt;
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/com/fr/plugin/hjcd/locale/lang.properties b/src/main/resources/com/fr/plugin/hjcd/locale/lang.properties
new file mode 100644
index 0000000..44778de
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/hjcd/locale/lang.properties
@@ -0,0 +1,9 @@
+Plugin-hjcd=Sso Plugin
+Plugin-hjcd_Group=Sso Plugin
+Plugin-hjcd_Config_UriBase=Uri Base
+Plugin-hjcd_Config_UriBase_Description=Uri Base
+Plugin-hjcd_Licence_Expired=Sso Plugin Licence Expired
+Plugin-hjcd_Config_ChannelId=Channel Id
+Plugin-hjcd_Config_ChannelId_Description=Channel Id
+Plugin-hjcd_Config_PublicKey=Public Key
+Plugin-hjcd_Config_PublicKey_Description=Public Key
\ No newline at end of file
diff --git a/src/main/resources/com/fr/plugin/hjcd/locale/lang_zh_CN.properties b/src/main/resources/com/fr/plugin/hjcd/locale/lang_zh_CN.properties
new file mode 100644
index 0000000..852c6f2
--- /dev/null
+++ b/src/main/resources/com/fr/plugin/hjcd/locale/lang_zh_CN.properties
@@ -0,0 +1,9 @@
+Plugin-hjcd=\u5355\u70B9\u767B\u9646\u63D2\u4EF6
+Plugin-hjcd_Group=\u5355\u70B9\u767B\u9646\u63D2\u4EF6
+Plugin-hjcd_Config_UriBase=token\u6821\u9A8C\u63A5\u53E3\u5730\u5740
+Plugin-hjcd_Config_UriBase_Description=token\u6821\u9A8C\u63A5\u53E3\u5730\u5740
+Plugin-hjcd_Licence_Expired=\u5355\u70B9\u767B\u9646\u63D2\u4EF6\u8BB8\u53EF\u8FC7\u671F
+Plugin-hjcd_Config_ChannelId=\u6E20\u9053ID
+Plugin-hjcd_Config_ChannelId_Description=\u6E20\u9053ID
+Plugin-hjcd_Config_PublicKey=\u52A0\u5BC6SM2\u516C\u94A5
+Plugin-hjcd_Config_PublicKey_Description=\u52A0\u5BC6SM2\u516C\u94A5
\ No newline at end of file