From: kaixiang.xia Date: Wed, 28 Oct 2020 10:19:09 +0000 (+0800) Subject: 大理二维码设计修改 X-Git-Tag: 1.0.29^2~12 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=03919bd86ed2a5c295cdfa4b699e1b9ec468f774;p=epayment%2Ffood_payapi.git 大理二维码设计修改 --- diff --git a/bus-qrcode/build.gradle b/bus-qrcode/build.gradle index 4132b554..aa6c2900 100644 --- a/bus-qrcode/build.gradle +++ b/bus-qrcode/build.gradle @@ -7,7 +7,7 @@ plugins { group = rootProject.group -def sdkVersion = gitVersion() +def sdkVersion = '1.0.0' sourceCompatibility = 1.8 targetCompatibility = 1.8 @@ -15,7 +15,7 @@ publishing { publications { mavenJava(MavenPublication) { groupId = project.group - artifactId = 'bus-qrcode' + artifactId = 'dlsmk-qrcode' version = sdkVersion from components.java } @@ -37,18 +37,17 @@ publishing { dependencies { implementation "org.apache.commons:commons-lang3:3.7" implementation 'com.eatthepath:java-otp:0.1.0' - implementation 'org.slf4j:slf4j-api:1.7.25' + implementation 'org.slf4j:slf4j-api:1.7.26' implementation 'commons-codec:commons-codec:1.9' - runtime 'org.slf4j:slf4j-parent:1.7.25' - runtime 'org.slf4j:slf4j-simple:1.7.25' + runtime 'org.slf4j:slf4j-parent:1.7.26' testImplementation 'junit:junit:4.12' } jar { enabled = true - baseName = "bus-qrcode" + baseName = "dlsmk-qrcode" manifest { - attributes('Bus-QRcode-Version': sdkVersion) + attributes('dlsmk-qrcode-version': sdkVersion) } } diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/BinUtil.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/BinUtil.java index b04ecd43..9b00aa0f 100644 --- a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/BinUtil.java +++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/BinUtil.java @@ -58,10 +58,10 @@ public class BinUtil { } public static String encodeBase64(byte[] data) { - return new Base64(true).encodeAsString(data); + return Base64.encodeBase64URLSafeString(data); } public static byte[] decodeBase64(String data) { - return new Base64(true).decode(data); + return Base64.decodeBase64(data); } } diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/CryptoUtil.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/CryptoUtil.java index 839e3c01..2998c854 100644 --- a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/CryptoUtil.java +++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/CryptoUtil.java @@ -83,7 +83,7 @@ public class CryptoUtil { private static byte[] doAESCipher(byte[] key, byte[] iv, byte[] data, int mode) { try { - Cipher keyCipher = Cipher.getInstance("AES/CFB/NoPadding"); + Cipher keyCipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); IvParameterSpec ivSpec = new IvParameterSpec(iv); SecretKeySpec secret = new SecretKeySpec(key, "AES"); keyCipher.init(mode, secret, ivSpec); diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/QrCode.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/QrCode.java index fdbbf727..c6717035 100644 --- a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/QrCode.java +++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/QrCode.java @@ -8,6 +8,8 @@ import org.slf4j.LoggerFactory; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.*; @@ -19,15 +21,26 @@ import static com.supwisdom.dlpay.busqrcode.CryptoUtil.*; public class QrCode { private static Logger logger = LoggerFactory.getLogger(QrCode.class); - public static final String DELIMITER = ":"; - public static final String SCOPE_DELIMITER = ","; - public static final String DEFAULT_SCOPE = "1"; //默认仅支持 1-大理车载消费场景 - public static final int TOTP_OFFSET = 10; //TODO: 前后偏移10步,30*10=300s=5m 约前后5分钟内totp有效 - -// static final ArrayList DEFAULT_SCOPES = new ArrayList<>(); -// static { -// DEFAULT_SCOPES.add("1"); //默认仅支持 1-大理车载消费场景 -// } + public static final String FIELD_UID = "uid"; + public static final String FIELD_CARDNO = "cardNo"; + public static final String FIELD_CARDTYPE = "cardType"; + private static final String FIELD_TOTP = "totp"; + private static final String FIELD_RANDOM = "random"; + private static final String FIELD_SIGN = "sign"; + private static final String DELIMITER = ":"; + public static final long timeStep = 5; //totp步长 5s + public static final int totpDigits = 8; //totp取的位数 + + static final ArrayList qrDataKeys = new ArrayList<>(); + + static { + qrDataKeys.add(FIELD_UID); + qrDataKeys.add(FIELD_CARDNO); + qrDataKeys.add(FIELD_CARDTYPE); + qrDataKeys.add(FIELD_TOTP); + qrDataKeys.add(FIELD_RANDOM); + qrDataKeys.add(FIELD_SIGN); + } private Builder qrBuilder; @@ -36,79 +49,85 @@ public class QrCode { } public static class Builder { - private String appid; - private String uid; - private String scope; - private String seed = "a8aae1a41ffab089428e8e21381cc34af3b614ac7b7cba50f58470b8e50c6072"; - private boolean resetSeed = false; - private byte[] iv = decodeHex("55b6f5b3287c535f8274a99354676d0b"); - private boolean resetIv = false; + private String uid; //手机注册的uid 系统用户唯一ID + private String cardNo; //市民卡号 + private String cardType; //公交卡类别 + private String prefix; //码前缀 + private String mac; + private byte[] rootKey; - private byte[] sKey; - private byte[] des3Key; - private String prefix; - private boolean debug; + private byte[] iv = decodeHex("55b6f5b3287c535f8274b99354676d0e"); + private String seed = "125ea2f97689988b6501"; + private boolean rootKeySet = false; + private boolean resetIv = false; + private boolean resetSeed = false; + + private boolean debug = false; public QrCode create() { return new QrCode(this); } - public Builder appid(String appid) { - if (StringUtils.isBlank(appid) || !StringUtils.isAsciiPrintable(appid)) { - throw new RuntimeException("appid is invalid"); + public Builder rootKey(String rootKey) { + if (StringUtils.isBlank(rootKey)) { + throw new RuntimeException("rootKey is empty"); } - this.appid = appid; - this.des3Key = md5(appid.getBytes()); + this.rootKey = decodeBase64(rootKey); + rootKeySet = true; return this; } - public Builder uid(String uid) { - if (StringUtils.isBlank(uid) || !StringUtils.isAsciiPrintable(uid)) { - throw new RuntimeException("uid is invalid"); + public Builder iv(String iv) { + if (StringUtils.isBlank(iv)) { + throw new RuntimeException("iv is empty"); } - this.uid = uid; + this.iv = decodeHex(iv); + resetIv = true; return this; } - public Builder scope(String scope) { - if (StringUtils.isBlank(scope)) { - throw new RuntimeException("scope is empty"); + public Builder seed(String seed) { + if (StringUtils.isBlank(seed)) { + throw new RuntimeException("seed is empty"); } - this.scope = scope; + this.seed = seed; + this.resetSeed = true; return this; } - public Builder rootkey(String key) { - this.rootKey = decodeHex(key); + public Builder uid(String uid) { + if (StringUtils.isBlank(uid) || !StringUtils.isAsciiPrintable(uid)) { + throw new RuntimeException("uid is invalid"); + } + this.uid = uid; return this; } - public Builder rootkey(String appid, String skey){ - if (StringUtils.isBlank(appid) || !StringUtils.isAsciiPrintable(appid)) { - throw new RuntimeException("appid is invalid"); + public Builder cardNo(String cardNo) { + if (StringUtils.isBlank(cardNo) || !StringUtils.isAsciiPrintable(cardNo)) { + throw new RuntimeException("cardNo is invalid"); } - if (StringUtils.isBlank(skey)) { - throw new RuntimeException("sKey is empty"); - } - this.appid = appid; - this.sKey = decodeHex(skey); - this.des3Key = md5(appid.getBytes()); - this.rootKey = desEncryptECB(this.des3Key, this.sKey); + this.cardNo = cardNo; return this; } - public Builder seed(String s) { - if (StringUtils.isBlank(s)) { - throw new RuntimeException("seed is empty"); + public Builder cardType(String cardType) { + if (StringUtils.isBlank(cardType) || !StringUtils.isAsciiPrintable(cardType)) { + throw new RuntimeException("cardType is invalid"); } - this.seed = s; - this.resetSeed = true; + this.cardType = cardType; return this; } - public Builder ivHex(String v) { - this.iv = decodeHex(v); - this.resetIv = true; + public Builder card(String cardNo, String cardType) { + if (StringUtils.isBlank(cardNo) || !StringUtils.isAsciiPrintable(cardNo)) { + throw new RuntimeException("cardNo is invalid"); + } + if (StringUtils.isBlank(cardType) || !StringUtils.isAsciiPrintable(cardType)) { + throw new RuntimeException("cardType is invalid"); + } + this.cardNo = cardNo; + this.cardType = cardType; return this; } @@ -156,32 +175,26 @@ public class QrCode { private TimeBasedOneTimePasswordGenerator totpGenerator(int passwordLength) { try { - return new TimeBasedOneTimePasswordGenerator(30, TimeUnit.SECONDS, passwordLength, + return new TimeBasedOneTimePasswordGenerator(timeStep, TimeUnit.SECONDS, passwordLength, TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA256); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("TOTP Error", e); } } - private boolean checkScope(List scopes) { - return scopes.contains(DEFAULT_SCOPE); - } - - private boolean verifyTotp(String totp, String secret, int offset) { - String second = "30"; - String seed = secret; + private boolean verifyTotp(String totp, String seed, int offset) { long T0 = 0; String[] keys = new String[offset * 2 + 1]; long time = Calendar.getInstance().getTime().getTime() / 1000; for (int i = 0; i < keys.length; i++) { String steps = "0"; try { - long T = (time - T0) / Long.parseLong(second); + long T = (time - T0) / timeStep; steps = Long.toHexString(T + (i - offset)).toUpperCase(); while (steps.length() < 16) { steps = "0" + steps; } - String key = TOTP.generateTOTP(secret, steps, "8", TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA256); + String key = TOTP.generateTOTP(seed, steps, String.valueOf(totpDigits), TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA256); keys[i] = key; } catch (final Exception e) { System.out.println("Error : " + e); @@ -195,87 +208,210 @@ public class QrCode { return false; } + private byte[] getSignFactor(String data) { + return sha256(("{dlsmk_}" + data).getBytes()); + } + + private byte[] byteConcat(byte[] first, byte[] second) { + try { + final ByteArrayOutputStream output = new ByteArrayOutputStream(); + output.write(first); + output.write(second); + return output.toByteArray(); + } catch (IOException e) { + throw new RuntimeException("byte concat method error!!!"); + } + } + + private byte[] byteConcat(byte[] first, byte[] second, byte[] third) { + try { + final ByteArrayOutputStream output = new ByteArrayOutputStream(); + output.write(first); + output.write(second); + output.write(third); + return output.toByteArray(); + } catch (IOException e) { + throw new RuntimeException("byte concat method error!!!"); + } + } + public String qrcode() { if (qrBuilder.debug) { - logger.info("appid=" + qrBuilder.appid); logger.info("uid=" + qrBuilder.uid); - logger.info("scope=" + qrBuilder.scope); - if (qrBuilder.resetSeed) logger.info("seed=" + qrBuilder.seed); - if (qrBuilder.resetIv) logger.info("iv=" + encodeHex(qrBuilder.iv)); - if (null != qrBuilder.sKey) logger.info("sKey=" + encodeHex(qrBuilder.sKey).toUpperCase()); + logger.info("cardNo=" + qrBuilder.cardNo); + logger.info("cardType=" + qrBuilder.cardNo); if (null != qrBuilder.prefix) logger.info("prefix=" + qrBuilder.prefix); - logger.info("rootkey=" + encodeHex(qrBuilder.rootKey).toUpperCase()); + if (qrBuilder.resetSeed) logger.info("seed=" + qrBuilder.seed); + if (qrBuilder.rootKeySet) logger.info("rootKey=" + encodeBase64(qrBuilder.rootKey)); + if (qrBuilder.resetIv) logger.info("iv=" + encodeHex(qrBuilder.iv)); logger.info("======================================================="); } - final String randomStr = getRandomString(6); //随机数 - final String totp = genTOTPWithSeed(qrBuilder.seed, 8); - final String encDataPlain = new StringJoin(DELIMITER) + if (null == qrBuilder.uid || null == qrBuilder.cardNo || null == qrBuilder.cardType) { + throw new RuntimeException("QR code params is null"); + } + + if (!qrBuilder.rootKeySet) { + throw new RuntimeException("QR decode root key must be set!"); + } + + final String randomStr = getRandomString(2); //随机数 + final String totp = genTOTPWithSeed(qrBuilder.seed, totpDigits); + final byte[] factor = getSignFactor(qrBuilder.uid); + final String qrData = new StringJoin(DELIMITER) .add(qrBuilder.uid) - .add(qrBuilder.scope) + .add(qrBuilder.cardNo) + .add(qrBuilder.cardType) .add(totp) .add(randomStr).toString(); - - final byte[] encData = aesEncryptCFB(qrBuilder.rootKey, encDataPlain.getBytes(), qrBuilder.iv); - final String code = encodeBase64(encData); if (qrBuilder.debug) { - logger.info("encdata plain= " + encDataPlain); + logger.info("qrData = " + qrData); } - + final byte[] sign = sha256(byteConcat(qrData.getBytes(), factor)); + final byte[] encDataPlain = byteConcat(qrData.getBytes(), DELIMITER.getBytes(), sign); + final byte[] encData = aesEncryptCFB(qrBuilder.rootKey, encDataPlain, qrBuilder.iv); + final String code = encodeBase64(encData); + String result = code; if (qrBuilder.prefix != null) { - return qrBuilder.prefix + code; + result = qrBuilder.prefix + code; + } + if (qrBuilder.debug) { + logger.info("QR code = " + result); } - return code; + return result; } - public String decodeUid(String qrcode) { + private Map decode(String qrcode) { if (qrBuilder.debug) { - logger.info("appid=" + qrBuilder.appid); + logger.info("Start parsing QR code= "+ qrcode); + if (null != qrBuilder.prefix) logger.info("prefix=" + qrBuilder.prefix); + if (qrBuilder.resetSeed) logger.info("seed=" + qrBuilder.seed); + if (qrBuilder.rootKeySet) logger.info("rootKey=" + encodeBase64(qrBuilder.rootKey)); if (qrBuilder.resetIv) logger.info("iv=" + encodeHex(qrBuilder.iv)); - if (null != qrBuilder.sKey) logger.info("sKey=" + encodeHex(qrBuilder.sKey).toUpperCase()); - if (null != qrBuilder.prefix) logger.info("prefix=" + qrBuilder.prefix); logger.info("======================================================="); } - if (null == qrBuilder.rootKey) { - throw new RuntimeException("qr decode root key must not be null"); + + if (null == qrcode) { + throw new RuntimeException("QR code must not be null"); + } + + if (!qrBuilder.rootKeySet) { + throw new RuntimeException("QR decode root key must be set!"); } + if (qrBuilder.prefix != null) { qrcode = qrcode.substring(qrBuilder.prefix.length()); } - final String encDataPlain = new String(aesDecryptCFB(qrBuilder.rootKey, decodeBase64(qrcode), qrBuilder.iv)); + + byte[] code = aesDecryptCFB(qrBuilder.rootKey, decodeBase64(qrcode), qrBuilder.iv); + if (null == code) { + throw new RuntimeException("Unable to recognize QR code!"); + } + final String encData = new String(code); + if (qrBuilder.debug) { + logger.info("QR code data: " + encData ); + } + final String[] fields = encData.split(DELIMITER); + if (fields.length < 6) { + throw new RuntimeException("QR code plain text format error!"); + } + Map result = new HashMap<>(); + for (int i = 0; i < fields.length && i < qrDataKeys.size(); ++i) { + result.put(qrDataKeys.get(i), fields[i]); + } + final String qrData; + { + final StringJoin sj = new StringJoin(DELIMITER); + for (int i = 0; i < 5; ++i) { + sj.add(result.get(qrDataKeys.get(i))); + } + qrData = sj.toString(); + } + final String uid = result.get(FIELD_UID); + final byte[] factor = getSignFactor(uid); + final byte[] calcSign = sha256(byteConcat(qrData.getBytes(), factor)); if (qrBuilder.debug) { - logger.info("Decode data : <" + encDataPlain + ">"); + logger.info("calcSign=" + new String(calcSign)); + logger.info("sign=" + result.get(FIELD_SIGN)); } - String[] fields = encDataPlain.split(DELIMITER); - if (fields.length < 4) { - throw new RuntimeException("qrcode plain text format error!"); + if (!new String(calcSign).equalsIgnoreCase(result.get(FIELD_SIGN))) { + throw new RuntimeException("QR code sign was not matched!"); } - String uid = fields[0]; - String[] scopes = fields[1].split(SCOPE_DELIMITER); - String totp = fields[2]; - if (!verifyTotp(totp, qrBuilder.seed, TOTP_OFFSET)) { - throw new RuntimeException("qrcode is invalid!"); //TOTP校验 + return result; + } + + /** + * 返回码数据不校验totp + * */ + public Map decodeWithoutTotpCheck(String qrcode) { + final Map data = decode(qrcode); + if (null == data || data.isEmpty()) { + throw new RuntimeException("QR code parsing failure!"); } - if (!checkScope(Arrays.asList(scopes))) { - throw new RuntimeException("qrcode is not supported!"); //应用场景校验 + this.qrBuilder.mac = new StringJoin(DELIMITER) + .add(data.get(FIELD_UID)) + .add(data.get(FIELD_CARDNO)) + .add(data.get(FIELD_CARDTYPE)) + .add(data.get(FIELD_TOTP)) + .add(data.get(FIELD_RANDOM)).toString(); //计算流水TAC的因子 + + Map result = new HashMap<>(); + result.put(FIELD_UID, data.get(FIELD_UID)); + result.put(FIELD_CARDNO, data.get(FIELD_CARDNO)); + result.put(FIELD_CARDTYPE, data.get(FIELD_CARDTYPE)); + result.put(FIELD_TOTP, data.get(FIELD_TOTP)); //不校验totp,直接返回 + return result; + } + + /** + * 返回码数据校验totp + * */ + public Map decodeWithTotpCheck(String qrcode) { + return decodeWithTotpCheck(qrcode, 3); //默认15s失效 + } + + /** + * 返回码数据校验totp + * */ + public Map decodeWithTotpCheck(String qrcode, int offset) { + final Map data = decode(qrcode); + if (null == data || data.isEmpty()) { + throw new RuntimeException("QR code parsing failure!"); + } + + //校验totp + if (!verifyTotp(data.get(FIELD_TOTP), this.qrBuilder.seed, offset)) { + throw new RuntimeException("qrcode is invalid!"); //二维码已无效 } - return uid; + this.qrBuilder.mac = new StringJoin(DELIMITER) + .add(data.get(FIELD_UID)) + .add(data.get(FIELD_CARDNO)) + .add(data.get(FIELD_CARDTYPE)) + .add(data.get(FIELD_TOTP)) + .add(data.get(FIELD_RANDOM)).toString(); //计算流水TAC的因子 + + Map result = new HashMap<>(); + result.put(FIELD_UID, data.get(FIELD_UID)); + result.put(FIELD_CARDNO, data.get(FIELD_CARDNO)); + result.put(FIELD_CARDTYPE, data.get(FIELD_CARDTYPE)); + return result; } - public String tac(String uid, Integer amount, String transdate, String transtime) { - if (null == qrBuilder.appid) { - throw new RuntimeException("appid is null"); + public String tac(Integer amount, String transDate, String transTime) { + if (null == this.qrBuilder.mac) { + throw new RuntimeException("Please decode QR code first!"); } - if (null == qrBuilder.sKey) { - throw new RuntimeException("sKey is null"); + if (this.qrBuilder.debug) { + logger.info("mac = " + this.qrBuilder.mac); } - final String encdata = uid + qrBuilder.appid + amount + transdate + transtime + "{" + encodeHex(qrBuilder.sKey).toUpperCase() + "}"; - return encodeHex(sha256(encdata.getBytes())).toUpperCase(); + + final String encData = this.qrBuilder.mac + amount + transDate + transTime + "{dlsmk}"; + return encodeBase64(sha256(encData.getBytes())).toLowerCase(); } } diff --git a/bus-qrcode/src/test/java/com/supwisdom/dlpay/busqrcode/QrcodeTest.java b/bus-qrcode/src/test/java/com/supwisdom/dlpay/busqrcode/QrcodeTest.java index 9e8bae1a..f67a8282 100644 --- a/bus-qrcode/src/test/java/com/supwisdom/dlpay/busqrcode/QrcodeTest.java +++ b/bus-qrcode/src/test/java/com/supwisdom/dlpay/busqrcode/QrcodeTest.java @@ -1,61 +1,37 @@ package com.supwisdom.dlpay.busqrcode; import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import static com.supwisdom.dlpay.busqrcode.BinUtil.*; -import static com.supwisdom.dlpay.busqrcode.CryptoUtil.*; -import static org.junit.Assert.*; +import java.util.Map; public class QrcodeTest { - private Logger logger = LoggerFactory.getLogger(QrcodeTest.class); - - @Test - public void testBusQrcode() { - String prefix = "dlsmk_"; - String appid = "10001"; - String uid = "ff8080816cf13e79016cf15d63f40011"; - String scope = "1,2"; - String rkey = "0A5DE6CE985D43989B7EBE64AD8EB9C3"; - Integer amount = 100; - String transdate = "20200220"; - String transtime = "090921"; - - String skey = encodeHex(desDecryptECB(md5(appid.getBytes()), decodeHex(rkey))).toUpperCase(); - logger.info("skey=[" + skey + "]"); - String rootKey = encodeHex(desEncryptECB(md5(appid.getBytes()), decodeHex(skey))).toUpperCase(); - assertEquals("rootkey与skey互为3des加密关系错误!", rkey, rootKey); - - QrCode encQr = QrCode.builder() - .appid(appid) - .rootkey(rkey) - .uid(uid) - .scope(scope) - .prefix(prefix) - .debug(true) - .create(); - String qrcode = encQr.qrcode(); - logger.info("qrcode=[" + qrcode + "]"); - - QrCode decQr = QrCode.builder() - .rootkey(appid, skey) - .prefix(prefix) - .debug(true) - .create(); - -// try { -// Thread.sleep(6*60*1000); //totp测试 -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } - - String decUid = decQr.decodeUid(qrcode); - assertEquals("解码uid错误!", uid, decUid); - - String tac = decQr.tac(uid, amount, transdate, transtime); - String tacData = uid + appid + amount + transdate + transtime + "{" + skey + "}"; - String testTac = encodeHex(sha256(tacData.getBytes())).toUpperCase(); - assertEquals("tac计算错误", tac, testTac); + @Test + public void testBusQrcode() { + final String uid = "0a5de6ce985d43989b7ebe64ad8eb9c3"; + final String cardNo = "00001252"; + final String cardtype = "80"; + final String rootKeyB64 = "Vbb1syh8U1+CdLmTVGdtDiVvKBQ81n4GmgBEO/ohSbU="; + final String ivHex = "55b6f5b3287c535f8274b99354676d0e"; + + try { + QrCode handler = QrCode.builder() + .rootKey(rootKeyB64) + .iv(ivHex) + .uid(uid) + .card(cardNo, cardtype) + .debug(true) + .create(); + final String qrcode = handler.qrcode(); + + QrCode decHandler = QrCode.builder() + .rootKey(rootKeyB64) + .iv(ivHex) + .debug(true).create(); + Map result = decHandler.decodeWithTotpCheck(qrcode); + String tac = decHandler.tac(1000, "20201028", "144521"); + + } catch (Exception e) { + e.printStackTrace(); } + } } \ No newline at end of file diff --git a/payapi/build.gradle b/payapi/build.gradle index 5d8338c9..f10aab85 100644 --- a/payapi/build.gradle +++ b/payapi/build.gradle @@ -57,6 +57,16 @@ docker { docker.dependsOn(jar) +repositories { + maven { + url "http://ykt-nx.supwisdom.com/repository/ecard-repo/" + credentials { + username 'ecard' + password 'Ecard4SUP' + } + } +} + dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-redis' @@ -114,6 +124,9 @@ dependencies { /*支付宝SDK*/ implementation group: 'com.alipay.sdk', name: 'alipay-sdk-java', version: '3.7.110.ALL' + /*大理二维码jar*/ + implementation 'com.supwisdom:dlsmk-qrcode:1.0.0' + annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" annotationProcessor 'org.projectlombok:lombok:1.18.8' compileOnly 'org.projectlombok:lombok:1.18.8' diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/card_service.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/card_service.kt index e99baab9..f8c4e5be 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/card_service.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/card_service.kt @@ -33,4 +33,7 @@ interface CardService { @Transactional(rollbackFor = arrayOf(Exception::class)) fun updateCardTransStatus(cardno:String,status:String): ApiResponse + + @Transactional(rollbackFor = arrayOf(Exception::class), readOnly = true) + fun findCardByUseridAndCardphyid(userid: String, cardtype: String, cardphyid: String?): TCard? } \ No newline at end of file diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/card_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/card_service_impl.kt index 79dbbb98..a94e50b9 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/card_service_impl.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/card_service_impl.kt @@ -26,10 +26,13 @@ import javax.persistence.PersistenceContext class CardServiceImpl : CardService { @Autowired lateinit var cardDao: CardDao + @Autowired lateinit var personDao: PersonDao + @Autowired - lateinit var citizencardLossApplyDao:CitizencardLossApplyDao + lateinit var citizencardLossApplyDao: CitizencardLossApplyDao + @Autowired lateinit var systemUtilService: SystemUtilService @@ -124,8 +127,8 @@ class CardServiceImpl : CardService { query.setParameter("endtime", enddate) query.unwrap(NativeQueryImpl::class.java).setResultTransformer(Transformers.aliasToBean(CitizenCardInfo::class.java)) val list = query.resultList as List - resp.retcode=0 - resp.retmsg="OK" + resp.retcode = 0 + resp.retmsg = "OK" resp.cards = list return resp } @@ -172,7 +175,7 @@ class CardServiceImpl : CardService { override fun getCardByCardNoOrUserid(param: QueryCardParam): TCard? { if (!StringUtil.isEmpty(param.cardno)) { return cardDao.findCardByCardnoAndCardtype(param.cardno, param.cardtype) - }else if (!StringUtil.isEmpty(param.userid)) { + } else if (!StringUtil.isEmpty(param.userid)) { return cardDao.findCardByUseridAndCardtype(param.userid, param.cardtype) } return null @@ -223,4 +226,12 @@ class CardServiceImpl : CardService { this.retmsg = "ok" } } + + override fun findCardByUseridAndCardphyid(userid: String, cardtype: String, cardphyid: String?): TCard? { + return if (cardphyid.isNullOrEmpty()) { + cardDao.findCardByUseridAndCardtype(userid, cardtype) + } else { + cardDao.findBankcardByCitizencard(userid, cardtype, cardphyid) + } + } } \ No newline at end of file diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt index 38b6374a..cbdeaefd 100644 --- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt +++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/qrcode_srvice_impl.kt @@ -7,6 +7,7 @@ import com.supwisdom.dlpay.api.bean.QrcodeParam import com.supwisdom.dlpay.api.service.CardService import com.supwisdom.dlpay.api.service.QRCodeService import com.supwisdom.dlpay.api.service.UserService +import com.supwisdom.dlpay.busqrcode.QrCode import com.supwisdom.dlpay.framework.service.SystemUtilService import com.supwisdom.dlpay.framework.util.MD5 import com.supwisdom.dlpay.framework.util.TradeDict @@ -64,33 +65,66 @@ class QRCodeServiceImpl:QRCodeService{ resp.retmsg = "银行卡未签约" return resp } + val cityCard = cardService.findCardByUseridAndCardphyid(bankCard.userid, ConstantUtil.CARDTYPE_CITIZENCARD, bankCard.cardphyid) + if (null == cityCard) { + resp.retcode = 1 + resp.retmsg = "用户未绑定市民卡" + return resp + } else if (TradeDict.STATUS_NORMAL != cityCard.status) { + resp.retcode = 1 + resp.retmsg = "市民卡已注销" + return resp + } else if (TradeDict.STATUS_NORMAL != cityCard.transStatus) { + resp.retcode = 1 + resp.retmsg = when (cityCard.transStatus) { + TradeDict.STATUS_UNUSE -> "市民卡未启用!" + TradeDict.STATUS_LOST -> "市民卡已挂失!" + TradeDict.STATUS_LOCKED -> "市民卡已锁定!" + TradeDict.STATUS_FROZEN -> "市民卡已冻结!" + else -> "市民卡状态异常!" + } + return resp + } - val totp = QrCodeTotpUtil.generateTOTP(muser.secertkey) - val rowdata = QrcodeRawData() - rowdata.userid = muser.userid - rowdata.setTotp(totp) - val orgData = Gson().toJson(rowdata) - val publicKey = RSAKeysGenerate.getPublicKey(muser.rsapublic) - val encdata = org.apache.commons.codec.binary.Base64.encodeBase64String(RSAKeysGenerate.encryptbyte(publicKey, orgData)) - val qrcode = AesUtil.encryptCFB("$uid:$encdata", rootkey, iv, "AES/CFB/NoPadding") - - val key = MD5.encodeByMD5ToURLSafeBase64(qrcode) - redisTemplate.opsForValue().set(key,qrcode, Duration.ofSeconds(20)) + val handle = QrCode.builder() + .rootKey(rootkey) + .iv(iv) + .uid(uid) + .card(cityCard.cardno, cityCard.busCardType ?: "80") + .create() + val qrcode = handle.qrcode() resp.retcode = 0 - resp.retmsg = key + resp.retmsg = qrcode return resp + +// val totp = QrCodeTotpUtil.generateTOTP(muser.secertkey) +// val rowdata = QrcodeRawData() +// rowdata.userid = muser.userid +// rowdata.setTotp(totp) +// val orgData = Gson().toJson(rowdata) +// val publicKey = RSAKeysGenerate.getPublicKey(muser.rsapublic) +// val encdata = org.apache.commons.codec.binary.Base64.encodeBase64String(RSAKeysGenerate.encryptbyte(publicKey, orgData)) +// val qrcode = AesUtil.encryptCFB("$uid:$encdata", rootkey, iv, "AES/CFB/NoPadding") +// +// val key = MD5.encodeByMD5ToURLSafeBase64(qrcode) +// redisTemplate.opsForValue().set(key,qrcode, Duration.ofSeconds(20)) + +// resp.retcode = 0 +// resp.retmsg = key +// return resp } override fun encodeCode(param: QrcodeParam): ApiResponse { - var resp = ApiResponse() + val resp = ApiResponse() val uid = param.uid val rootkey = systemUtilService.getBusinessValue("aes.cfb.rootkey") val iv = systemUtilService.getBusinessValue("aes.cfb.iv") - if (rootkey.isNullOrEmpty()||iv.isNullOrEmpty()) { + if (rootkey.isNullOrEmpty() || iv.isNullOrEmpty()) { resp.retcode = 1 resp.retmsg = "秘钥未配置" return resp } + val totp = QrCodeTotpUtil.generateTOTP(param.secertkey) val rowdata = QrcodeRawData() rowdata.userid = param.userid