5 changed files with 353 additions and 3 deletions
@ -0,0 +1,235 @@ |
|||
package qianmu.container.util; |
|||
|
|||
/** |
|||
* Created by Android Studio. |
|||
* User: linzhibin |
|||
* Date: 2025/11/26 |
|||
* Time: 19:22 |
|||
*/ |
|||
class Base64Util { |
|||
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] = '+'; |
|||
lookUpBase64Alphabet[63] = '/'; |
|||
} |
|||
|
|||
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); |
|||
} |
|||
|
|||
/** |
|||
* 将十六进制八位字节编码为Base64 |
|||
* |
|||
* @param binaryData 包含二进制数据的数组 |
|||
* @return 编码Base64字符串 |
|||
*/ |
|||
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 = new char[numberQuartet * 4]; |
|||
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; |
|||
int encodedIndex = 0; |
|||
int dataIndex = 0; |
|||
if (fDebug) { |
|||
System.out.println("number of triplets = " + numberTriplets); |
|||
} |
|||
for (int i = 0; i < numberTriplets; i++) { |
|||
b1 = binaryData[dataIndex++]; |
|||
b2 = binaryData[dataIndex++]; |
|||
b3 = binaryData[dataIndex++]; |
|||
if (fDebug) { |
|||
System.out.println("b1= " + b1 + ", b2= " + b2 + ", b3= " + b3); |
|||
} |
|||
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) { |
|||
System.out.println("val2 = " + val2); |
|||
System.out.println("k4 = " + (k << 4)); |
|||
System.out.println("vak = " + (val2 | (k << 4))); |
|||
} |
|||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; |
|||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; |
|||
encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; |
|||
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; |
|||
} |
|||
if (fewerThan24bits == EIGHTBIT) { |
|||
b1 = binaryData[dataIndex]; |
|||
k = (byte) (b1 & 0x03); |
|||
if (fDebug) { |
|||
System.out.println("b1=" + b1); |
|||
System.out.println("b1<<2 = " + (b1 >> 2)); |
|||
} |
|||
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); |
|||
} |
|||
|
|||
/** |
|||
* 将Base64数据解码为八位字节 |
|||
* |
|||
* @param encoded 包含Base64数据的字符串 |
|||
* @return 包含解码数据的数组. |
|||
*/ |
|||
public static byte[] decode(String encoded) { |
|||
if (encoded == null) { |
|||
return null; |
|||
} |
|||
char[] base64Data = encoded.toCharArray(); |
|||
//删除空白
|
|||
int len = removeWhiteSpace(base64Data); |
|||
//应该可以被四整除
|
|||
if (len % FOURBYTE != 0) { |
|||
return null; |
|||
} |
|||
int numberQuadruple = (len / FOURBYTE); |
|||
if (numberQuadruple == 0) { |
|||
return new byte[0]; |
|||
} |
|||
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; |
|||
byte[] 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; |
|||
}//没有数据直接返回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;////没有数据直接返回null
|
|||
} |
|||
b1 = base64Alphabet[d1]; |
|||
b2 = base64Alphabet[d2]; |
|||
d3 = base64Data[dataIndex++]; |
|||
d4 = base64Data[dataIndex++]; |
|||
if (!isData((d3)) || !isData((d4))) { |
|||
//检查是否为填充字符
|
|||
if (isPad(d3) && isPad(d4)) { |
|||
if ((b2 & 0xf) != 0) {//最后四位应为0
|
|||
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){//最后2位应为零
|
|||
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; |
|||
} |
|||
|
|||
/** |
|||
* 从包含编码Base64数据的MIME中删除空白 |
|||
* |
|||
* @param data base64数据的字节数组(带空白) |
|||
* @return 新的长度 |
|||
*/ |
|||
private static int removeWhiteSpace(char[] data) { |
|||
if (data == null) { |
|||
return 0; |
|||
} |
|||
int newSize = 0; |
|||
int len = data.length; |
|||
for (int i = 0; i < len; i++) { |
|||
if (!isWhiteSpace(data[i])) { |
|||
data[newSize++] = data[i]; |
|||
} |
|||
} |
|||
return newSize; |
|||
} |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
package qianmu.container.util; |
|||
|
|||
import java.nio.charset.StandardCharsets; |
|||
import java.security.InvalidKeyException; |
|||
import java.security.MessageDigest; |
|||
import java.security.NoSuchAlgorithmException; |
|||
import java.util.Base64; |
|||
import java.util.Locale; |
|||
|
|||
import javax.crypto.Mac; |
|||
import javax.crypto.spec.SecretKeySpec; |
|||
|
|||
import cn.hutool.core.util.ObjectUtil; |
|||
import qianmu.container.data.DeviceData; |
|||
import qianmu.container.data.FloorData; |
|||
|
|||
/** |
|||
* Created by Android Studio. |
|||
* User: linzhibin |
|||
* Date: 2025/11/26 |
|||
* Time: 19:16 |
|||
*/ |
|||
public class SignUtil { |
|||
|
|||
private static final String SignAlgorithm = "HmacSHA256"; |
|||
|
|||
public static Boolean checkSign(String activationCode, String projectCode, Long timeStrap, String sk) { |
|||
if (ObjectUtil.isEmpty(sk)) { |
|||
return false; |
|||
} |
|||
long thisTime = System.currentTimeMillis(); |
|||
// 大于/小于5min则过期
|
|||
long diff = thisTime - timeStrap; |
|||
if (diff < -1000 * 60 * 5 || diff > 1000 * 60 * 5) { |
|||
return false; |
|||
} |
|||
String anObject = buildSk(activationCode, projectCode, timeStrap); |
|||
boolean equals = sk.equals(anObject); |
|||
return equals; |
|||
} |
|||
|
|||
/** |
|||
* activationCode+timeStrap作为ak,projectCode为盐值 |
|||
*/ |
|||
public static String buildSk(String activationCode, String projectCode, Long timeStrap) { |
|||
String ak = activationCode + timeStrap; |
|||
return sign(projectCode, ak); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 生成签名 哈希后转16进制再sha256加密,最后转base64 |
|||
* @param secret 秘钥 |
|||
* @param content 待签名内容 |
|||
*/ |
|||
public static String sign(String secret, String content) { |
|||
try { |
|||
Mac mac = Mac.getInstance(SignAlgorithm); |
|||
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), SignAlgorithm)); |
|||
String data = toHex(hash(content)); |
|||
byte[] signByte = mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); |
|||
|
|||
return Base64Util.encode(toHex(signByte).getBytes()); |
|||
} catch (NoSuchAlgorithmException | InvalidKeyException ex) { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public static byte[] hash(String text) { |
|||
try { |
|||
MessageDigest md = MessageDigest.getInstance("SHA-256"); |
|||
md.update(text.getBytes(StandardCharsets.UTF_8)); |
|||
|
|||
return md.digest(); |
|||
} catch (NoSuchAlgorithmException ex) { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public static String toHex(byte[] data) { |
|||
StringBuilder sb = new StringBuilder(data.length * 2); |
|||
for (byte b : data) { |
|||
String hex = Integer.toHexString(b); |
|||
if (hex.length() == 1) { |
|||
sb.append("0"); |
|||
} else if (hex.length() == 8) { |
|||
hex = hex.substring(6); |
|||
} |
|||
sb.append(hex); |
|||
} |
|||
|
|||
return sb.toString().toLowerCase(Locale.getDefault()); |
|||
} |
|||
|
|||
|
|||
public static void main(String[] args) { |
|||
long timeStrap = System.currentTimeMillis(); |
|||
String regKey = DeviceData.getDeviceInfo(DeviceData.HINT_REG_KEY); |
|||
String mallCode = FloorData.getMallCode(); |
|||
System.out.println(SignUtil.buildSk(regKey, mallCode, timeStrap)); |
|||
System.out.println(timeStrap); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue