Ver Fonte

银联API

caiyaru há 2 anos atrás
pai
commit
e51f29ea42

+ 1 - 1
src/main/java/com/ydtech/config/properties/UnionPayApiConfigurationProperties.java

@@ -21,7 +21,7 @@ public class UnionPayApiConfigurationProperties {
 
     private String accessTokenUrl;
 
-    private String factorVerifyUrl;
+    private String threeFactorVerifyUrl;
 
     private String bankCardVerifyUrl;
 

+ 39 - 34
src/main/java/com/ydtech/modules/admin/components/UnionPayRequestApiComponent.java

@@ -6,8 +6,9 @@ import com.ydtech.exception.SystemException;
 import com.ydtech.modules.admin.components.request.UnionPayRequest;
 import com.ydtech.modules.admin.components.response.UnionPayResponse;
 import com.ydtech.modules.admin.utils.UnionPayUtils;
-import com.ydtech.utils.SM2Utils;
+import com.ydtech.utils.SM2;
 import com.ydtech.utils.StringUtils;
+import com.ydtech.utils.idgen.IdGenerate;
 import lombok.RequiredArgsConstructor;
 import org.redisson.api.RLock;
 import org.redisson.api.RedissonClient;
@@ -58,35 +59,24 @@ public class UnionPayRequestApiComponent {
 
     private final static int UNION_PAY_SLEEP_TIME = 5000;
 
-    private final static int RANDOM_MAX = 999999999;
-
-    private final static int RANDOM_MIN = 100000000;
 
     /**
      * 运营商二要素验证接口
      *
      * @param request
      */
-    public UnionPayResponse twoFactorVerifyRequest(UnionPayRequest request) {
-        String url = unionPayApiConfigurationProperties.getFactorVerifyUrl();
+    public UnionPayResponse threeFactorVerifyRequest(UnionPayRequest request) {
+        String url = unionPayApiConfigurationProperties.getThreeFactorVerifyUrl();
 
         JSONObject params = new JSONObject();
-        if (StringUtils.isBlank(request.getPhoneNo())) {
-            throw new InvalidParameterException("手机号不允许为空!");
+        if (StringUtils.isBlank(request.getPhoneNo()) || StringUtils.isBlank(request.getCertNo()) ||
+                StringUtils.isBlank(request.getName())) {
+            throw new InvalidParameterException("手机号、身份证、用户姓名不允许为空!");
         }
         params.put("phoneNo", request.getPhoneNo());
-
-        //01:验证姓名+手机号,此时用户姓名必输 02:身份证+手机号,此时证件类型、证件
-        if (StringUtils.isNotBlank(request.getCertNo())) {
-            params.put("verifyType", "02");
-            params.put("certType", "01");//证件类型 01:身份证
-            params.put("certNo", request.getCertNo());
-        } else if (StringUtils.isNotBlank(request.getName())) {
-            params.put("verifyType", "01");
-            params.put("name", request.getName());
-        } else {
-            throw new InvalidParameterException("身份证、用户姓名至少传值一个!");
-        }
+        params.put("certType", "01");//证件类型 01:身份证
+        params.put("certNo", request.getCertNo());
+        params.put("name", request.getName());
         return commonRequest(url, params);
     }
 
@@ -96,7 +86,7 @@ public class UnionPayRequestApiComponent {
      * @param request
      */
     public UnionPayResponse bankCardVerifyRequest(UnionPayRequest request) {
-        String url = unionPayApiConfigurationProperties.getFactorVerifyUrl();
+        String url = unionPayApiConfigurationProperties.getBankCardVerifyUrl();
 
         JSONObject params = new JSONObject();
         if (StringUtils.isBlank(request.getCardNo())) {
@@ -108,7 +98,7 @@ public class UnionPayRequestApiComponent {
         }
         params.put("cardNo", request.getCardNo());
         params.put("personalMandate", "1");//是否取得个人授权,字段取值0或1。1:是 0:否
-        params.put("sceneId", "13"); //TODO 业务场景 13:车险
+        params.put("sceneId", "13"); // 业务场景 13:车险
         if (StringUtils.isNotBlank(request.getCertNo())) {
             params.put("certType", "01");//证件类型 01:身份证
             params.put("certNo", request.getCertNo());
@@ -119,20 +109,32 @@ public class UnionPayRequestApiComponent {
         if (StringUtils.isNotBlank(request.getPhoneNo())) {
             params.put("phoneNo", request.getPhoneNo());
         }
-        params.put("appName", "01晋掌柜");//TODO 交易发起应用名称
+        params.put("appName", "01晋掌柜");// 交易发起应用名称
         params.put("ipType", "04");//IP版本号 04:IPV4 06:IPV6
         try {
-            params.put("sourceIp", InetAddress.getLocalHost().getHostAddress()); // ip
+            params.put("sourceIp", ipFormat(InetAddress.getLocalHost().getHostAddress())); // ip
         } catch (UnknownHostException e) {
             LOGGER.error("获取ipv4地址异常", e);
             throw new RuntimeException("获取ipv4地址异常:", e);
         }
-        params.put("protocolVersion", ""); // 用户授权协议版本号 //TODO
-        params.put("protocolNo", ""); // 用户授权协议流水号
+        params.put("protocolVersion", "V1.0"); // 用户授权协议版本号
+        params.put("protocolNo", IdGenerate.nextId());
         return commonRequest(url, params);
     }
 
 
+    private String ipFormat(String ip) {
+        String[] ips = ip.split("\\.");
+        StringBuffer br = new StringBuffer();
+        for (int i = 0; i < ips.length; i++) {
+            br.append(StringUtils.leftPad(ips[i], 3, "0"));
+            if (i < ips.length - 1) {
+                br.append(".");
+            }
+        }
+        return br.toString();
+    }
+
     /**
      * 调用银联接口
      *
@@ -145,8 +147,7 @@ public class UnionPayRequestApiComponent {
             String publicKey = unionPayApiConfigurationProperties.getPublicKey();
 
             JSONObject data = new JSONObject();
-            String dataValue = Base64.getEncoder().encodeToString(SM2Utils.encrypt(params.toString(),
-                    publicKey).getBytes());
+            String dataValue = SM2.sm2Encrypt(params.toString(), publicKey);
             data.put("data", dataValue);
 
             HttpHeaders httpHeaders = new HttpHeaders();
@@ -154,15 +155,17 @@ public class UnionPayRequestApiComponent {
             httpHeaders.set("Accept", "application/json");
             httpHeaders.set("Authorization", getAccessToken());
             // 请求
-            HttpEntity<String> httpEntity = new HttpEntity<>(params.toString(), httpHeaders);
+            LOGGER.error("银联接口url:"+url);
+            LOGGER.error("银联接口入参:"+params);
+            HttpEntity<String> httpEntity = new HttpEntity<>(data.toString(), httpHeaders);
             ResponseEntity<String> response = restTemplate.postForEntity(url, httpEntity, String.class);
             JSONObject resp = JSONObject.parseObject(response.getBody());
 
             if ("20000000".equals(resp.getString("errCode"))) {
                 String privateKey = unionPayApiConfigurationProperties.getPrivateKey();
-
-                String respData = SM2Utils.decrypt(new String(Base64.getDecoder().decode(resp.getString("data"))),
+                String respData = SM2.sm2Decrypt(resp.getString("data"),
                         privateKey);
+                LOGGER.error("银联接口返参:"+respData);
                 return JSONObject.parseObject(respData, UnionPayResponse.class);
             } else {
                 throw new SystemException("银联信息验证异常[" + resp.getString("errCode") + ":" +
@@ -245,16 +248,18 @@ public class UnionPayRequestApiComponent {
             httpHeaders.set("Content-Type", "application/json;charset=UTF-8");
             httpHeaders.set("Accept", "application/json");
             // 请求
+            LOGGER.error("银联获取token-url:"+accessTokenUrl);
+            LOGGER.error("银联获取token-入参:"+params);
             HttpEntity<JSONObject> httpEntity = new HttpEntity<>(params, httpHeaders);
             ResponseEntity<String> response = restTemplate.postForEntity(accessTokenUrl, httpEntity, String.class);
             JSONObject resp = JSONObject.parseObject(response.getBody());
-
+            LOGGER.error("银联获取token-返参:"+resp);
             if ("0000".equals(resp.getString("errCode"))) {
                 String token = resp.getString("accessToken");
                 int expiresIn = resp.getIntValue("expiresIn"); //失效时间
                 Map<String, String> map = new HashMap<>();
-                map.put(UNION_PAY_MAP, "OPEN-ACCESS-TOKEN AccessToken=\"" + token + "\", appId=\"" + appId + "\"");
-                redisTemplate.opsForHash().putAll(UNION_PAY_ACCESS_TOKEN_KEY, map);
+                map.put(UNION_PAY_ACCESS_TOKEN_KEY, "OPEN-ACCESS-TOKEN AccessToken=" + token + ", AppId=" + appId );
+                redisTemplate.opsForHash().putAll(UNION_PAY_MAP, map);
                 redisTemplate.expire(UNION_PAY_ACCESS_TOKEN_KEY, expiresIn - 30, TimeUnit.SECONDS);
                 return token;
             } else {

+ 225 - 0
src/main/java/com/ydtech/utils/Base64.java

@@ -0,0 +1,225 @@
+package com.ydtech.utils;
+
+public final class Base64 {
+    static private final int BASELENGTH = 128;
+    static private final int LOOKUPLENGTH = 64;
+    static private final int TWENTYFOURBITGROUP = 24;
+    static private final int EIGHTBIT = 8;
+    static private final int SIXTEENBIT = 16;
+    static private final int FOURBYTE = 4;
+    static private final int SIGN = -128;
+    static private final char PAD = '=';
+    static private final boolean fDebug = false;
+    static final private byte[] base64Alphabet = new byte[BASELENGTH];
+    static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
+
+    static {
+        for (int i = 0; i < BASELENGTH; ++i) {
+            base64Alphabet[i] = -1;
+        }
+        for (int i = 'Z'; i >= 'A'; i--) {
+            base64Alphabet[i] = (byte) (i - 'A');
+        }
+        for (int i = 'z'; i >= 'a'; i--) {
+            base64Alphabet[i] = (byte) (i - 'a' + 26);
+        }
+
+        for (int i = '9'; i >= '0'; i--) {
+            base64Alphabet[i] = (byte) (i - '0' + 52);
+        }
+        base64Alphabet['+'] = 62;
+        base64Alphabet['/'] = 63;
+        for (int i = 0; i <= 25; i++) {
+            lookUpBase64Alphabet[i] = (char) ('A' + i);
+        }
+        for (int i = 26, j = 0; i <= 51; i++, j++) {
+            lookUpBase64Alphabet[i] = (char) ('a' + j);
+        }
+        for (int i = 52, j = 0; i <= 61; i++, j++) {
+            lookUpBase64Alphabet[i] = (char) ('0' + j);
+        }
+        lookUpBase64Alphabet[62] = (char) '+';
+        lookUpBase64Alphabet[63] = (char) '/';
+    }
+
+    private static boolean isWhiteSpace(char octect) {
+        return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
+    }
+
+    private static boolean isPad(char octect) {
+        return (octect == PAD);
+    }
+
+    private static boolean isData(char octect) {
+        return (octect < BASELENGTH && base64Alphabet[octect] != -1);
+    }
+
+    /**
+     * Encodes hex octects into Base64
+     *
+     * @param binaryData Array containing binaryData
+     * @return Encoded Base64 array
+     */
+    public static String encode(byte[] binaryData) {
+        if (binaryData == null) {
+            return null;
+        }
+        int lengthDataBits = binaryData.length * EIGHTBIT;
+        if (lengthDataBits == 0) {
+            return "";
+        }
+        int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
+        int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
+        int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
+        char encodedData[] = null;
+        encodedData = new char[numberQuartet * 4];
+        byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        if (fDebug) {
+        }
+        for (int i = 0; i < numberTriplets; i++) {
+            b1 = binaryData[dataIndex++];
+            b2 = binaryData[dataIndex++];
+            b3 = binaryData[dataIndex++];
+            if (fDebug) { }
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+            byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
+            if (fDebug) { }
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
+        }
+
+        // form integral number of 6-bit groups
+        if (fewerThan24bits == EIGHTBIT) {
+            b1 = binaryData[dataIndex];
+            k = (byte) (b1 & 0x03);
+            if (fDebug) { }
+            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
+            encodedData[encodedIndex++] = PAD;
+            encodedData[encodedIndex++] = PAD;
+        } else if (fewerThan24bits == SIXTEENBIT) {
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            l = (byte) (b2 & 0x0f);
+            k = (byte) (b1 & 0x03);
+            byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
+            byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
+            encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
+            encodedData[encodedIndex++] = PAD;
+        }
+        return new String(encodedData);
+    }
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param encoded string containing Base64 data
+     * @return Array containind decoded data.
+     */
+    public static byte[] decode(String encoded) {
+        if (encoded == null) {
+            return null;
+        }
+        char[] base64Data = encoded.toCharArray();
+        // remove white spaces
+        int len = removeWhiteSpace(base64Data);
+        if (len % FOURBYTE != 0) {
+            return null;//should be divisible by four
+        }
+        int numberQuadruple = (len / FOURBYTE);
+        if (numberQuadruple == 0) {
+            return new byte[0];
+        }
+        byte decodedData[] = null;
+        byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
+        char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
+        int i = 0;
+        int encodedIndex = 0;
+        int dataIndex = 0;
+        decodedData = new byte[(numberQuadruple) * 3];
+        for (; i < numberQuadruple - 1; i++) {
+            if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
+                    || !isData((d3 = base64Data[dataIndex++]))
+                    || !isData((d4 = base64Data[dataIndex++]))) {
+                return null;
+            }//if found "no data" just return null
+            b1 = base64Alphabet[d1];
+            b2 = base64Alphabet[d2];
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
+        }
+        if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
+            return null;//if found "no data" just return null
+        }
+        b1 = base64Alphabet[d1];
+        b2 = base64Alphabet[d2];
+        d3 = base64Data[dataIndex++];
+        d4 = base64Data[dataIndex++];
+        if (!isData((d3)) || !isData((d4))) {//Check if they are PAD characters
+            if (isPad(d3) && isPad(d4)) {
+                if ((b2 & 0xf) != 0)//last 4 bits should be zero
+                {
+                    return null;
+                }
+                byte[] tmp = new byte[i * 3 + 1];
+                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
+                tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+                return tmp;
+            } else if (!isPad(d3) && isPad(d4)) {
+                b3 = base64Alphabet[d3];
+                if ((b3 & 0x3) != 0)//last 2 bits should be zero
+                {
+                    return null;
+                }
+                byte[] tmp = new byte[i * 3 + 2];
+                System.arraycopy(decodedData, 0, tmp, 0, i * 3);
+                tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+                tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+                return tmp;
+            } else {
+                return null;
+            }
+        } else { //No PAD e.g 3cQl
+            b3 = base64Alphabet[d3];
+            b4 = base64Alphabet[d4];
+            decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
+            decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+            decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
+        }
+        return decodedData;
+    }
+
+    /**
+     * remove WhiteSpace from MIME containing encoded Base64 data.
+     *
+     * @param data the byte array of base64 data (with WS)
+     * @return the new length
+     */
+    private static int removeWhiteSpace(char[] data) {
+        if (data == null) {
+            return 0;
+        }
+        // count characters that's not whitespace
+        int newSize = 0;
+        int len = data.length;
+        for (int i = 0; i < len; i++) {
+            if (!isWhiteSpace(data[i])) {
+                data[newSize++] = data[i];
+            }
+        }
+        return newSize;
+    }
+}

+ 343 - 0
src/main/java/com/ydtech/utils/SM2.java

@@ -0,0 +1,343 @@
+package com.ydtech.utils;
+
+
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECPoint;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+
+public class SM2 {
+    private final static Logger LOGGER = LoggerFactory.getLogger(SM2.class);
+
+
+    public static String sm2Encrypt(String plainText, String pubKey) {
+        byte[] data = SM2.encrypt(plainText, pubKey);
+        String enData = Base64.encode(data);
+        return enData;
+    }
+
+    public static String sm2Decrypt(String plainText, String priKey) {
+        byte[] encryptData = Base64.decode(plainText);
+        String rawData = SM2.decrypt(encryptData, priKey);
+        return rawData;
+    }
+
+    //国密办文件中推荐的椭圆曲线相关参数
+
+    private static BigInteger n = new BigInteger(
+            "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "7203DF6B" + "21C6052B" + "53BBF409" + "39D54123", 16);
+    private static BigInteger p = new BigInteger(
+            "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFF", 16);
+    private static BigInteger a = new BigInteger(
+            "FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFC", 16);
+    private static BigInteger b = new BigInteger(
+            "28E9FA9E" + "9D9F5E34" + "4D5A9E4B" + "CF6509A7" + "F39789F5" + "15AB8F92" + "DDBCBD41" + "4D940E93", 16);
+    private static BigInteger gx = new BigInteger(
+            "32C4AE2C" + "1F198119" + "5F990446" + "6A39C994" + "8FE30BBF" + "F2660BE1" + "715A4589" + "334C74C7", 16);
+    private static BigInteger gy = new BigInteger(
+            "BC3736A2" + "F4F6779C" + "59BDCEE3" + "6B692153" + "D0A9877C" + "C62A4740" + "02DF32E5" + "2139F0A0", 16);
+    private static final int DIGEST_LENGTH = 32;
+    private static SecureRandom random = new SecureRandom();
+    private static ECCurve.Fp curve = new ECCurve.Fp(p, a, b);
+    private static ECPoint G = curve.createPoint(gx, gy);
+    private static ECDomainParameters ecc_bc_spec = new ECDomainParameters(curve, G, n);
+
+    /**
+     * 以16进制打印字节数组
+     *
+     * @param b
+     */
+    public static void printHexString(byte[] b) {
+        for (int i = 0; i < b.length; i++) {
+            String hex = Integer.toHexString(b[i] & 0xFF);
+            if (hex.length() == 1) {
+                hex = '0' + hex;
+            }
+            System.out.print(hex.toUpperCase());
+        }
+        System.out.println();
+    }
+
+    /**
+     * 随机数生成器
+     *
+     * @param max
+     * @return
+     */
+    private static BigInteger random(BigInteger max) {
+
+        BigInteger r = new BigInteger(256, random);
+        while (r.compareTo(max) >= 0) {
+            r = new BigInteger(128, random);
+
+        }
+        return r;
+    }
+
+    /**
+     * 判断字节数组是否全0
+     *
+     * @param buffer
+     * @return
+     */
+    private static boolean allZero(byte[] buffer) {
+        for (int i = 0; i < buffer.length; i++) {
+            if (buffer[i] != 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 公钥加密
+     *
+     * @param input     加密原文
+     * @param pubKeyStr 公钥
+     * @return
+     */
+    public static byte[] encrypt(String input, String pubKeyStr) {
+        ECPoint publicKey = curve.decodePoint(hexStr2Bytes(pubKeyStr));
+        byte[] inputBuffer = new byte[0];
+        try {
+            inputBuffer = input.getBytes("UTF8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        byte[] C1Buffer;
+        ECPoint kpb;
+        byte[] t;
+        do {
+            /* 1 产生随机数k,k属于[1, n-1] */
+            BigInteger k = random(n);
+            /* 2 计算椭圆曲线点C1 = [k]G = (x1, y1) */
+            ECPoint C1 = G.multiply(k);
+            C1Buffer = C1.getEncoded(false);
+            /*
+             * 3 计算椭圆曲线点 S = [h]Pb
+             */
+            BigInteger h = ecc_bc_spec.getH();
+            if (h != null) {
+                ECPoint S = publicKey.multiply(h);
+                if (S.isInfinity()) {
+                    throw new IllegalStateException();
+                }
+            }
+            /* 4 计算 [k]PB = (x2, y2) */
+            kpb = publicKey.multiply(k).normalize();
+            /* 5 计算 t = KDF(x2||y2, klen) */
+            byte[] kpbBytes = kpb.getEncoded(false);
+            t = KDF(kpbBytes, inputBuffer.length);
+        } while (allZero(t));
+        /* 6 计算C2=M^t */
+        byte[] C2 = new byte[inputBuffer.length];
+        for (int i = 0; i < inputBuffer.length; i++) {
+            C2[i] = (byte) (inputBuffer[i] ^ t[i]);
+        }
+        /* 7 计算C3 = Hash(x2 || M || y2) */
+        byte[] C3 = sm3hash(kpb.getXCoord().toBigInteger().toByteArray(), inputBuffer,
+                kpb.getYCoord().toBigInteger().toByteArray());
+        /* 8 输出密文 C=C1 || C2 || C3 */
+        byte[] encryptResult = new byte[C1Buffer.length + C2.length + C3.length];
+        System.arraycopy(C1Buffer, 0, encryptResult, 0, C1Buffer.length);
+        System.arraycopy(C2, 0, encryptResult, C1Buffer.length, C2.length);
+        System.arraycopy(C3, 0, encryptResult, C1Buffer.length + C2.length, C3.length);
+        return encryptResult;
+    }
+
+    /**
+     * 私钥解密
+     *
+     * @param encryptData 密文数据字节数组
+     * @param priKeyStr   解密私钥
+     * @return
+     */
+    public static String decrypt(byte[] encryptData, String priKeyStr) {
+
+
+        BigInteger privateKey = new BigInteger(priKeyStr, 16);
+
+        byte[] C1Byte = new byte[65];
+        System.arraycopy(encryptData, 0, C1Byte, 0, C1Byte.length);
+
+        ECPoint C1 = curve.decodePoint(C1Byte).normalize();
+
+        /*
+         * 计算椭圆曲线点 S = [h]C1 是否为无穷点
+         */
+        BigInteger h = ecc_bc_spec.getH();
+        if (h != null) {
+            ECPoint S = C1.multiply(h);
+            if (S.isInfinity()) {
+                throw new IllegalStateException();
+            }
+        }
+        /* 计算[dB]C1 = (x2, y2) */
+        ECPoint dBC1 = C1.multiply(privateKey).normalize();
+
+        /* 计算t = KDF(x2 || y2, klen) */
+        byte[] dBC1Bytes = dBC1.getEncoded(false);
+        int klen = encryptData.length - 65 - DIGEST_LENGTH;
+        byte[] t = KDF(dBC1Bytes, klen);
+
+        if (allZero(t)) {
+            System.err.println("all zero");
+            throw new IllegalStateException();
+        }
+
+        /* 计算M'=C2^t */
+        byte[] M = new byte[klen];
+        for (int i = 0; i < M.length; i++) {
+            M[i] = (byte) (encryptData[C1Byte.length + i] ^ t[i]);
+        }
+
+        /*  计算 u = Hash(x2 || M' || y2) 判断 u == C3是否成立 */
+        byte[] C3 = new byte[DIGEST_LENGTH];
+
+
+        System.arraycopy(encryptData, encryptData.length - DIGEST_LENGTH, C3, 0, DIGEST_LENGTH);
+        byte[] u = sm3hash(dBC1.getXCoord().toBigInteger().toByteArray(), M,
+                dBC1.getYCoord().toBigInteger().toByteArray());
+
+        if (Arrays.equals(u, C3)) {
+            try {
+                return new String(M, "UTF8");
+            } catch (UnsupportedEncodingException e) {
+                e.printStackTrace();
+            }
+            return null;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * 判断是否在范围内
+     *
+     * @param param
+     * @param min
+     * @param max
+     * @return
+     */
+    private static boolean between(BigInteger param, BigInteger min, BigInteger max) {
+        if (param.compareTo(min) >= 0 && param.compareTo(max) < 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 判断生成的公钥是否合法
+     *
+     * @param publicKey
+     * @return
+     */
+    private static boolean checkPublicKey(ECPoint publicKey) {
+        if (!publicKey.isInfinity()) {
+            BigInteger x = publicKey.getXCoord().toBigInteger();
+            BigInteger y = publicKey.getYCoord().toBigInteger();
+            if (between(x, new BigInteger("0"), p) && between(y, new BigInteger("0"), p)) {
+                BigInteger xResult = x.pow(3).add(a.multiply(x)).add(b).mod(p);
+                BigInteger yResult = y.pow(2).mod(p);
+                if (yResult.equals(xResult) && publicKey.multiply(n).isInfinity()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 字节数组拼接
+     *
+     * @param params
+     * @return
+     */
+    private static byte[] join(byte[]... params) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        byte[] res = null;
+        try {
+            for (int i = 0; i < params.length; i++) {
+                baos.write(params[i]);
+            }
+            res = baos.toByteArray();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return res;
+    }
+
+    /**
+     * sm3摘要
+     * @param params
+     * @return
+     */
+    private static byte[] sm3hash(byte[]... params) {
+        byte[] res = null;
+        try {
+            res = SM3.hash(join(params));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return res;
+    }
+
+    /**
+     * 密钥派生函数
+     * @param Z
+     * @param klen 生成klen字节数长度的密钥
+     * @return
+     */
+    private static byte[] KDF(byte[] Z, int klen) {
+        int ct = 1;
+        int end = (int) Math.ceil(klen * 1.0 / 32);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            for (int i = 1; i < end; i++) {
+                baos.write(sm3hash(Z, SM3.toByteArray(ct)));
+                ct++;
+            }
+            byte[] last = sm3hash(Z, SM3.toByteArray(ct));
+            if (klen % 32 == 0) {
+                baos.write(last);
+            } else {
+                baos.write(last, 0, klen % 32);
+            }
+            return baos.toByteArray();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static byte[] hexStr2Bytes(String src) {
+        int l = src.length() / 2;
+        byte[] ret = new byte[l];
+        for (int i = 0; i < l; ++i) {
+            int m = i * 2 + 1;
+            int n = m + 1;
+            ret[i] = uniteBytes(src.substring(i * 2, m), src.substring(m, n));
+        }
+        return ret;
+    }
+
+    private static byte uniteBytes(String src0, String src1) {
+        byte b0 = Byte.decode("0x" + src0);
+        b0 = (byte) (b0 << 4);
+        byte b1 = Byte.decode("0x" + src1);
+        byte ret = (byte) (b0 | b1);
+        return ret;
+    }
+
+
+}

+ 0 - 109
src/main/java/com/ydtech/utils/SM2Utils.java

@@ -1,109 +0,0 @@
-package com.ydtech.utils;
-
-
-import com.ydtech.modules.admin.components.UnionPayRequestApiComponent;
-import org.bouncycastle.crypto.engines.SM2Engine;
-import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
-import org.bouncycastle.crypto.params.ECPublicKeyParameters;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
-import org.bouncycastle.crypto.util.PrivateKeyFactory;
-import org.bouncycastle.crypto.util.PublicKeyFactory;
-import org.bouncycastle.jce.ECNamedCurveTable;
-import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
-import org.bouncycastle.jce.spec.ECNamedCurveSpec;
-import org.bouncycastle.jce.spec.ECParameterSpec;
-import org.bouncycastle.jce.spec.ECPublicKeySpec;
-import org.bouncycastle.util.encoders.Hex;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.math.BigInteger;
-import java.security.KeyFactory;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.ECPrivateKeySpec;
-import java.util.Base64;
-
-public class SM2Utils {
-    private final static Logger LOGGER = LoggerFactory.getLogger(SM2Utils.class);
-
-
-    /**
-     * 公钥转换
-     * @param publicKeyString 公钥字符串
-     * @return ECPublicKeyParameters
-     * @throws Exception
-     */
-    public static ECPublicKeyParameters getPublicKeyFromString (String publicKeyString) throws Exception {
-        byte[] keyBytes = Base64.getDecoder().decode(publicKeyString);
-        ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
-        ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(ecSpec.getCurve().decodePoint(keyBytes), ecSpec);
-        PublicKey publicKey =  KeyFactory.getInstance("EC","BC").generatePublic(publicKeySpec);
-        return (ECPublicKeyParameters) PublicKeyFactory.createKey(publicKey.getEncoded());
-    }
-
-    /**
-     * 私钥转换
-     * @param privateKeyString 私钥字符串
-     * @return ECPrivateKeyParameters
-     * @throws Exception
-     */
-    public static ECPrivateKeyParameters getPrivateKeyFromString (String privateKeyString) throws Exception{
-        byte[] keyBytes = Base64.getDecoder().decode(privateKeyString);
-        ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
-        ECNamedCurveSpec param =  new ECNamedCurveSpec("sm2p256v1", ecSpec.getCurve(), ecSpec.getG(), ecSpec.getN());
-        KeyFactory keyFactory = KeyFactory.getInstance("EC","BC");
-        PrivateKey privateKey = keyFactory.generatePrivate(new ECPrivateKeySpec(new BigInteger(1, keyBytes) , param));
-        return (ECPrivateKeyParameters) PrivateKeyFactory.createKey(privateKey.getEncoded());
-    }
-
-
-    /**
-     * SM2加密算法
-     *
-     * @param publicKeyString 公钥字符串
-     * @param data      明文数据
-     * @return
-     */
-    public static String encrypt(String data, String publicKeyString) throws Exception{
-        try {
-            byte[] bytes = data.getBytes();
-            ECPublicKeyParameters publicKey = getPublicKeyFromString(publicKeyString);
-
-            SM2Engine engine = new SM2Engine();
-            engine.init(true, new ParametersWithRandom(publicKey));
-
-            byte[] arrayOfBytes =engine.processBlock(bytes,0, bytes.length);
-            return Hex.toHexString(arrayOfBytes);
-        } catch (Exception e) {
-            LOGGER.error("SM2加密时出现异常:",e);
-            throw new Exception("SM2加密时出现异常", e);
-        }
-    }
-
-
-
-    /**
-     * SM2解密算法
-     * @param data    hex格式密文
-     * @param privateKeyString    密钥PrivateKey型
-     * @return              明文
-     */
-    public static String decrypt(String data, String privateKeyString)  throws Exception{
-        try {
-            byte[] bytes = data.getBytes();
-            ECPrivateKeyParameters privateKeyParameters = getPrivateKeyFromString(privateKeyString);
-
-            SM2Engine sm2Engine = new SM2Engine();
-            // 设置sm2为解密模式
-            sm2Engine.init(false, privateKeyParameters);
-            byte[] arrayOfBytes = sm2Engine.processBlock(bytes, 0, bytes.length);
-            return new String(arrayOfBytes);
-        } catch (Exception e) {
-            LOGGER.error("SM2解密时出现异常:" ,e);
-            throw new Exception("SM2解密时出现异常", e);
-        }
-    }
-
-
-}

+ 213 - 0
src/main/java/com/ydtech/utils/SM3.java

@@ -0,0 +1,213 @@
+package com.ydtech.utils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Arrays;
+
+/**
+ * SM3杂凑算法实现
+ */
+public class SM3 {
+
+    private static char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
+            '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+    private static final String ivHexStr = "7380166f 4914b2b9 172442d7 da8a0600 a96f30bc 163138aa e38dee4d b0fb0e4e";
+    private static final BigInteger IV = new BigInteger(ivHexStr.replaceAll(" ",
+            ""), 16);
+    private static final Integer Tj15 = Integer.valueOf("79cc4519", 16);
+    private static final Integer Tj63 = Integer.valueOf("7a879d8a", 16);
+    private static final byte[] FirstPadding = {(byte) 0x80};
+    private static final byte[] ZeroPadding = {(byte) 0x00};
+
+    private static int T(int j) {
+        if (j >= 0 && j <= 15) {
+            return Tj15.intValue();
+        } else if (j >= 16 && j <= 63) {
+            return Tj63.intValue();
+        } else {
+            throw new RuntimeException("data invalid");
+        }
+    }
+
+    private static Integer FF(Integer x, Integer y, Integer z, int j) {
+        if (j >= 0 && j <= 15) {
+            return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue());
+        } else if (j >= 16 && j <= 63) {
+            return Integer.valueOf((x.intValue() & y.intValue())
+                    | (x.intValue() & z.intValue())
+                    | (y.intValue() & z.intValue()));
+        } else {
+            throw new RuntimeException("data invalid");
+        }
+    }
+
+    private static Integer GG(Integer x, Integer y, Integer z, int j) {
+        if (j >= 0 && j <= 15) {
+            return Integer.valueOf(x.intValue() ^ y.intValue() ^ z.intValue());
+        } else if (j >= 16 && j <= 63) {
+            return Integer.valueOf((x.intValue() & y.intValue())
+                    | (~x.intValue() & z.intValue()));
+        } else {
+            throw new RuntimeException("data invalid");
+        }
+    }
+
+    private static Integer P0(Integer x) {
+        return Integer.valueOf(x.intValue()
+                ^ Integer.rotateLeft(x.intValue(), 9)
+                ^ Integer.rotateLeft(x.intValue(), 17));
+    }
+
+    private static Integer P1(Integer x) {
+        return Integer.valueOf(x.intValue()
+                ^ Integer.rotateLeft(x.intValue(), 15)
+                ^ Integer.rotateLeft(x.intValue(), 23));
+    }
+
+    private static byte[] padding(byte[] source) throws IOException {
+        if (source.length >= 0x2000000000000000l) {
+            throw new RuntimeException("src data invalid.");
+        }
+        long l = source.length * 8;
+        long k = 448 - (l + 1) % 512;
+        if (k < 0) {
+            k = k + 512;
+        }
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        baos.write(source);
+        baos.write(FirstPadding);
+        long i = k - 7;
+        while (i > 0) {
+            baos.write(ZeroPadding);
+            i -= 8;
+        }
+        baos.write(long2bytes(l));
+        return baos.toByteArray();
+    }
+
+    private static byte[] long2bytes(long l) {
+        byte[] bytes = new byte[8];
+        for (int i = 0; i < 8; i++) {
+            bytes[i] = (byte) (l >>> ((7 - i) * 8));
+        }
+        return bytes;
+    }
+
+    public static byte[] hash(byte[] source) throws IOException {
+        byte[] m1 = padding(source);
+        int n = m1.length / (512 / 8);
+        byte[] b;
+        byte[] vi = IV.toByteArray();
+        byte[] vi1 = null;
+        for (int i = 0; i < n; i++) {
+            b = Arrays.copyOfRange(m1, i * 64, (i + 1) * 64);
+            vi1 = CF(vi, b);
+            vi = vi1;
+        }
+        return vi1;
+    }
+
+    private static byte[] CF(byte[] vi, byte[] bi) throws IOException {
+        int a, b, c, d, e, f, g, h;
+        a = toInteger(vi, 0);
+        b = toInteger(vi, 1);
+        c = toInteger(vi, 2);
+        d = toInteger(vi, 3);
+        e = toInteger(vi, 4);
+        f = toInteger(vi, 5);
+        g = toInteger(vi, 6);
+        h = toInteger(vi, 7);
+
+        int[] w = new int[68];
+        int[] w1 = new int[64];
+        for (int i = 0; i < 16; i++) {
+            w[i] = toInteger(bi, i);
+        }
+        for (int j = 16; j < 68; j++) {
+            w[j] = P1(w[j - 16] ^ w[j - 9] ^ Integer.rotateLeft(w[j - 3], 15))
+                    ^ Integer.rotateLeft(w[j - 13], 7) ^ w[j - 6];
+        }
+        for (int j = 0; j < 64; j++) {
+            w1[j] = w[j] ^ w[j + 4];
+        }
+        int ss1, ss2, tt1, tt2;
+        for (int j = 0; j < 64; j++) {
+            ss1 = Integer
+                    .rotateLeft(
+                            Integer.rotateLeft(a, 12) + e
+                                    + Integer.rotateLeft(T(j), j), 7);
+            ss2 = ss1 ^ Integer.rotateLeft(a, 12);
+            tt1 = FF(a, b, c, j) + d + ss2 + w1[j];
+            tt2 = GG(e, f, g, j) + h + ss1 + w[j];
+            d = c;
+            c = Integer.rotateLeft(b, 9);
+            b = a;
+            a = tt1;
+            h = g;
+            g = Integer.rotateLeft(f, 19);
+            f = e;
+            e = P0(tt2);
+        }
+        byte[] v = toByteArray(a, b, c, d, e, f, g, h);
+        for (int i = 0; i < v.length; i++) {
+            v[i] = (byte) (v[i] ^ vi[i]);
+        }
+        return v;
+    }
+
+    private static int toInteger(byte[] source, int index) {
+        StringBuilder valueStr = new StringBuilder("");
+        for (int i = 0; i < 4; i++) {
+            valueStr.append(hexDigits[(byte) ((source[index * 4 + i] & 0xF0) >> 4)]);
+            valueStr.append(hexDigits[(byte) (source[index * 4 + i] & 0x0F)]);
+        }
+        return Long.valueOf(valueStr.toString(), 16).intValue();
+
+    }
+
+    private static byte[] toByteArray(int a, int b, int c, int d, int e, int f,
+                                      int g, int h) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(32);
+        baos.write(toByteArray(a));
+        baos.write(toByteArray(b));
+        baos.write(toByteArray(c));
+        baos.write(toByteArray(d));
+        baos.write(toByteArray(e));
+        baos.write(toByteArray(f));
+        baos.write(toByteArray(g));
+        baos.write(toByteArray(h));
+        return baos.toByteArray();
+    }
+
+    public static byte[] toByteArray(int i) {
+        byte[] byteArray = new byte[4];
+        byteArray[0] = (byte) (i >>> 24);
+        byteArray[1] = (byte) ((i & 0xFFFFFF) >>> 16);
+        byteArray[2] = (byte) ((i & 0xFFFF) >>> 8);
+        byteArray[3] = (byte) (i & 0xFF);
+        return byteArray;
+    }
+
+    private static String byteToHexString(byte b) {
+        int n = b;
+        if (n < 0) {
+            n = 256 + n;
+        }
+        int d1 = n / 16;
+        int d2 = n % 16;
+        return "" + hexDigits[d1] + hexDigits[d2];
+    }
+
+    public static String byteArrayToHexString(byte[] b) {
+        StringBuffer resultSb = new StringBuffer();
+        for (int i = 0; i < b.length; i++) {
+            resultSb.append(byteToHexString(b[i]));
+        }
+        return resultSb.toString();
+    }
+
+    public static void main(String[] args) throws IOException {
+        System.out.println(SM3.byteArrayToHexString(SM3.hash("sm3算法测试".getBytes())));
+    }
+}

+ 6 - 6
src/main/resources/application-pre.yml

@@ -23,13 +23,13 @@ baidu:
 #银联API参数配置
 unionPay:
   api:
-    appId: 111
-    appKey: 1111
-    accessTokenUrl: https://test-api-open.chinaums.com/v2/token/acces
-    factorVerifyUrl: https://test-api-open.chinaums.com/v1/datacenter/smartverification/encrypted/mobile/2factor/verify
-    bankCardVerifyUrl: https://test-api-open.chinaums.com/v1/datacenter/smartverification/encrypted/bankcard/verify
+    appId: 10037e6f8e41423b01912b657e5c116c
+    appKey: 1110043747cb40f4911096480ea93f3e
+    accessTokenUrl: https://api-mop.chinaums.com/v2/token/access
+    threeFactorVerifyUrl: https://api-mop.chinaums.com/v1/datacenter/smartverification/encrypted/mobile/3factor/verify
+    bankCardVerifyUrl: https://api-mop.chinaums.com/v1/datacenter/smartverification/encrypted/bankcard/verify
     publicKey: 0414bb7e9c3914bb65b85079b1dc6e1ca2a0ee04dd9bb55b2f8d31704a6dec0cca695739e128f9c931330e1c0f493be4d56244236434c9d344c4716ba64a4470ba
-    privateKey:
+    privateKey: 24ebfde60aecb81214605050763926ec342c487859a8da57d5badcd3c1559336
 aly:
   cbit:
     url_pre: http://223.70.186.115:80/CBIT/API/insCBIT

+ 6 - 6
src/main/resources/application-prod.yml

@@ -23,13 +23,13 @@ baidu:
 #银联API参数配置
 unionPay:
   api:
-    appId: 111
-    appKey: 1111
-    accessTokenUrl: https://test-api-open.chinaums.com/v2/token/acces
-    factorVerifyUrl: https://test-api-open.chinaums.com/v1/datacenter/smartverification/encrypted/mobile/2factor/verify
-    bankCardVerifyUrl: https://test-api-open.chinaums.com/v1/datacenter/smartverification/encrypted/bankcard/verify
+    appId: 10037e6f8e41423b01912b657e5c116c
+    appKey: 1110043747cb40f4911096480ea93f3e
+    accessTokenUrl: https://api-mop.chinaums.com/v2/token/access
+    threeFactorVerifyUrl: https://api-mop.chinaums.com/v1/datacenter/smartverification/encrypted/mobile/3factor/verify
+    bankCardVerifyUrl: https://api-mop.chinaums.com/v1/datacenter/smartverification/encrypted/bankcard/verify
     publicKey: 0414bb7e9c3914bb65b85079b1dc6e1ca2a0ee04dd9bb55b2f8d31704a6dec0cca695739e128f9c931330e1c0f493be4d56244236434c9d344c4716ba64a4470ba
-    privateKey:
+    privateKey: 24ebfde60aecb81214605050763926ec342c487859a8da57d5badcd3c1559336
 
 aly:
   cbit:

+ 5 - 5
src/main/resources/application-test.yml

@@ -25,13 +25,13 @@ baidu:
 #银联API参数配置
 unionPay:
   api:
-    appId: 111
-    appKey: 1111
-    accessTokenUrl: https://test-api-open.chinaums.com/v2/token/acces
-    factorVerifyUrl: https://test-api-open.chinaums.com/v1/datacenter/smartverification/encrypted/mobile/2factor/verify
+    appId: 10037e6f8e41423b01912b657e5c116c
+    appKey: 1110043747cb40f4911096480ea93f3e
+    accessTokenUrl: https://test-api-open.chinaums.com/v2/token/access
+    threeFactorVerifyUrl: https://test-api-open.chinaums.com/v1/datacenter/smartverification/encrypted/mobile/3factor/verify
     bankCardVerifyUrl: https://test-api-open.chinaums.com/v1/datacenter/smartverification/encrypted/bankcard/verify
     publicKey: 0414bb7e9c3914bb65b85079b1dc6e1ca2a0ee04dd9bb55b2f8d31704a6dec0cca695739e128f9c931330e1c0f493be4d56244236434c9d344c4716ba64a4470ba
-    privateKey:
+    privateKey: 24ebfde60aecb81214605050763926ec342c487859a8da57d5badcd3c1559336
 
 aly:
   cbit: