From: Xia Kaixiang Date: Thu, 20 Feb 2020 03:58:49 +0000 (+0800) Subject: 车载码测试 X-Git-Tag: 1.0.12^2~7 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=a228a7da733cee7a17f4b398619d23ebee16d733;p=epayment%2Ffood_payapi.git 车载码测试 --- diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/PbocAlgorithem.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/PbocAlgorithem.java index 49da1147..3c26f8be 100644 --- a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/PbocAlgorithem.java +++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/PbocAlgorithem.java @@ -2,8 +2,8 @@ package com.supwisdom.dlpay.busqrcode; import java.util.Arrays; -import static com.supwisdom.dlpay.busQRcode.CryptoUtil.desDecryptECB; -import static com.supwisdom.dlpay.busQRcode.CryptoUtil.desEncryptECB; +import static com.supwisdom.dlpay.busqrcode.CryptoUtil.desDecryptECB; +import static com.supwisdom.dlpay.busqrcode.CryptoUtil.desEncryptECB; public class PbocAlgorithem { 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 5f8a7afd..93f6b609 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 @@ -1,12 +1,11 @@ package com.supwisdom.dlpay.busqrcode; import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator; -import com.supwisdom.dlpay.util.TOTP; + import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.security.InvalidKeyException; @@ -23,7 +22,7 @@ public class QrCode { 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 = 30; + public static final int TOTP_OFFSET = 10; //TODO: 前后偏移10步,30*10=300s=5m 约前后5分钟内totp有效 // static final ArrayList DEFAULT_SCOPES = new ArrayList<>(); // static { @@ -203,10 +202,10 @@ public class QrCode { 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)); + if (null != qrBuilder.sKey) logger.info("sKey=" + encodeHex(qrBuilder.sKey).toUpperCase()); if (null != qrBuilder.prefix) logger.info("prefix=" + qrBuilder.prefix); - logger.info("rootkey=" + encodeHex(qrBuilder.rootKey)); + logger.info("rootkey=" + encodeHex(qrBuilder.rootKey).toUpperCase()); logger.info("======================================================="); } @@ -233,7 +232,7 @@ public class QrCode { logger.info("appid=" + qrBuilder.appid); 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)); + if (null != qrBuilder.sKey) logger.info("sKey=" + encodeHex(qrBuilder.sKey).toUpperCase()); if (null != qrBuilder.prefix) logger.info("prefix=" + qrBuilder.prefix); logger.info("======================================================="); } @@ -273,7 +272,7 @@ public class QrCode { if (null == qrBuilder.sKey) { throw new RuntimeException("sKey is null"); } - final String encdata = uid + qrBuilder.appid + amount + transdate + transtime + "{" + encodeHex(qrBuilder.sKey) + "}"; + final String encdata = uid + qrBuilder.appid + amount + transdate + transtime + "{" + encodeHex(qrBuilder.sKey).toUpperCase() + "}"; return encodeHex(sha256(encdata.getBytes())).toUpperCase(); } diff --git a/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/TOTP.java b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/TOTP.java new file mode 100644 index 00000000..450ca253 --- /dev/null +++ b/bus-qrcode/src/main/java/com/supwisdom/dlpay/busqrcode/TOTP.java @@ -0,0 +1,264 @@ +package com.supwisdom.dlpay.busqrcode; + +/** + Copyright (c) 2011 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, is permitted pursuant to, and subject to the license + terms contained in, the Simplified BSD License set forth in Section + 4.c of the IETF Trust's Legal Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info). + */ + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.lang.reflect.UndeclaredThrowableException; +import java.math.BigInteger; +import java.security.GeneralSecurityException; + +/** + * This is an example implementation of the OATH TOTP algorithm. Visit + * www.openauthentication.org for more information. + * + * @author Johan Rydell, PortWise, Inc. + */ +public class TOTP { + private static final int[] DIGITS_POWER + // 0 1 2 3 4 5 6 7 8 + = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 }; + + private TOTP() { + } + + /** + * This method uses the JCE to provide the crypto algorithm. HMAC computes a + * Hashed Message Authentication Code with the crypto hash algorithm as a + * parameter. + * + * @param crypto + * : the crypto algorithm (HmacSHA1, HmacSHA256, HmacSHA512) + * @param keyBytes + * : the bytes to use for the HMAC key + * @param text + * : the message or text to be authenticated + */ + private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) { + try { + Mac hmac; + hmac = Mac.getInstance(crypto); + + SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW"); + hmac.init(macKey); + + return hmac.doFinal(text); + } catch (GeneralSecurityException gse) { + throw new UndeclaredThrowableException(gse); + } + } + + /** + * This method converts a HEX string to Byte[] + * + * @param hex + * : the HEX string + * + * @return: a byte array + */ + private static byte[] hexStr2Bytes(String hex) { + // Adding one byte to get the right conversion + // Values starting with "0" can be converted + byte[] bArray = new BigInteger("10" + hex, 16).toByteArray(); + + // Copy all the REAL bytes, not the "first" + byte[] ret = new byte[bArray.length - 1]; + + for (int i = 0; i < ret.length; i++) + ret[i] = bArray[i + 1]; + + return ret; + } + + /** + * This method generates a TOTP value for the given set of parameters. + * + * @param key + * : the shared secret, HEX encoded + * @param time + * : a value that reflects a time + * @param returnDigits + * : number of digits to return + * + * @return: a numeric String in base 10 that includes + * {@link truncationDigits} digits + */ + public static String generateTOTP(String key, String time, + String returnDigits) { + return generateTOTP(key, time, returnDigits, "HmacSHA1"); + } + + /** + * This method generates a TOTP value for the given set of parameters. + * + * @param key + * : the shared secret, HEX encoded + * @param time + * : a value that reflects a time + * @param returnDigits + * : number of digits to return + * + * @return: a numeric String in base 10 that includes + * {@link truncationDigits} digits + */ + public static String generateTOTP256(String key, String time, + String returnDigits) { + return generateTOTP(key, time, returnDigits, "HmacSHA256"); + } + + /** + * This method generates a TOTP value for the given set of parameters. + * + * @param key + * : the shared secret, HEX encoded + * @param time + * : a value that reflects a time + * @param returnDigits + * : number of digits to return + * + * @return: a numeric String in base 10 that includes + * {@link truncationDigits} digits + */ + public static String generateTOTP512(String key, String time, + String returnDigits) { + return generateTOTP(key, time, returnDigits, "HmacSHA512"); + } + + /** + * This method generates a TOTP value for the given set of parameters. + * + * @param key + * : the shared secret, HEX encoded + * @param time + * : a value that reflects a time + * @param returnDigits + * : number of digits to return + * @param crypto + * : the crypto function to use + * + * @return: a numeric String in base 10 that includes + * {@link truncationDigits} digits + */ + public static String generateTOTP(String key, String time, + String returnDigits, String crypto) { + int codeDigits = Integer.decode(returnDigits).intValue(); + String result = null; + + // Using the counter + // First 8 bytes are for the movingFactor + // Compliant with base RFC 4226 (HOTP) + while (time.length() < 16) + time = "0" + time; + + // Get the HEX in a Byte[] + byte[] msg = hexStr2Bytes(time); + byte[] k = hexStr2Bytes(key); + byte[] hash = hmac_sha(crypto, k, msg); + + // put selected bytes into result int + int offset = hash[hash.length - 1] & 0xf; + + int binary = ((hash[offset] & 0x7f) << 24) + | ((hash[offset + 1] & 0xff) << 16) + | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff); + + int otp = binary % DIGITS_POWER[codeDigits]; + + result = Integer.toString(otp); + + while (result.length() < codeDigits) { + result = "0" + result; + } + + return result; + } + + public static void main(String[] args) { + long X = 30; + long T0 = 0; + String steps = "0"; + long time = System.currentTimeMillis() / 1000; + System.out.println(time); + long T = (time - T0) / X; + steps = Long.toHexString(T).toUpperCase(); + while (steps.length() < 16) { + steps = "0" + steps; + } + String seed = "9e8bb3d2df73741c041aef39e37ee015fc98233720a1350fcd67d5c3027896ac"; + String totp = TOTP.generateTOTP(seed, steps, "8", "HmacSHA256"); + System.out.println(totp); + + /* + + + SecureRandom a = new SecureRandom(); + byte[] b = a.generateSeed(8); + // Seed for HMAC-SHA1 - 20 bytes + String seed = "3132333435363738393031323334353637383930"; + + // Seed for HMAC-SHA256 - 32 bytes + //String seed32 = "3132333435363738393031323334353637383930313233343536373839303132"; + String seed32 = "e5b72370f61687c8075b8383266f4fbcbb3b55da178eed76329666600c134093"; + + // Seed for HMAC-SHA512 - 64 bytes + String seed64 = "3132333435363738393031323334353637383930" + + "3132333435363738393031323334353637383930" + + "3132333435363738393031323334353637383930" + "31323334"; + long T0 = 0; + long X = 30; + long[] testTime = { 59L, 1111111109L, 1111111111L, 1234567890L, + 2000000000L, 20000000000L }; + + String steps = "0"; + DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + + try { + System.out.println("+---------------+-----------------------+" + + "------------------+--------+--------+"); + System.out.println("| Time(sec) | Time (UTC format) " + + "| Value of T(Hex) | TOTP | Mode |"); + System.out.println("+---------------+-----------------------+" + + "------------------+--------+--------+"); + + for (int i = 0; i < testTime.length; i++) { + long T = (testTime[i] - T0) / X; + steps = Long.toHexString(T).toUpperCase(); + + while (steps.length() < 16) + steps = "0" + steps; + + String fmtTime = String.format("%1$-11s", testTime[i]); + String utcTime = df.format(new Date(testTime[i] * 1000)); + System.out.print("| " + fmtTime + " | " + utcTime + " | " + + steps + " |"); + System.out.println(generateTOTP(seed, steps, "8", "HmacSHA1") + + "| SHA1 |"); + System.out.print("| " + fmtTime + " | " + utcTime + " | " + + steps + " |"); + System.out.println(generateTOTP(seed32, steps, "8", + "HmacSHA256") + "| SHA256 |"); + System.out.print("| " + fmtTime + " | " + utcTime + " | " + + steps + " |"); + System.out.println(generateTOTP(seed64, steps, "8", + "HmacSHA512") + "| SHA512 |"); + + System.out.println("+---------------+-----------------------+" + + "------------------+--------+--------+"); + } + } catch (final Exception e) { + System.out.println("Error : " + e); + } + + */ + } +} \ No newline at end of file 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 e69de29b..9e8bae1a 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 @@ -0,0 +1,61 @@ +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.*; + +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); + } +} \ No newline at end of file