package com.xiaopeng.order.utils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; /** * 蓝牙解锁Token生成工具类 * 根据设计方案实现Token的生成 */ @Component public class BluetoothUnlockTokenUtil { private static final String HMAC_ALGORITHM = "HmacSHA256"; private static final int RANDOM_BYTES_LENGTH = 16; // 16字节随机数 private static final int HMAC_BYTES_LENGTH = 16; // 16字节HMAC (取前16字节) private static final int LONG_BYTE_SIZE = 8; // Long类型占用8字节 @Value("${bluetooth.unlock.secret:123456}") private String secret; /** * 生成蓝牙解锁Token * * @param userId 用户ID * @param orderId 订单ID * @param vehicleId 车辆ID * @param expirationTime 订单预估结束时间 * @return 64字符的Token (32字符随机数 + 32字符HMAC) */ public String generateBluetoothUnlockToken(long userId, long orderId, long vehicleId, long expirationTime) { try { // 1. 使用安全随机数生成器生成16字节随机数 SecureRandom secureRandom = new SecureRandom(); byte[] randomBytes = new byte[RANDOM_BYTES_LENGTH]; secureRandom.nextBytes(randomBytes); // 2. 拼接二进制数据:随机数(16字节) + userId(8字节) + orderId(8字节) + vehicleId(8字节) + expirationTime(8字节) byte[] dataToSign = new byte[RANDOM_BYTES_LENGTH + 4 * LONG_BYTE_SIZE]; // 复制随机数 System.arraycopy(randomBytes, 0, dataToSign, 0, RANDOM_BYTES_LENGTH); // 复制userId (转换为字节数组) longToBytes(userId, dataToSign, RANDOM_BYTES_LENGTH); // 复制orderId (转换为字节数组) longToBytes(orderId, dataToSign, RANDOM_BYTES_LENGTH + LONG_BYTE_SIZE); // 复制vehicleId (转换为字节数组) longToBytes(vehicleId, dataToSign, RANDOM_BYTES_LENGTH + 2 * LONG_BYTE_SIZE); // 复制expirationTime (转换为字节数组) longToBytes(expirationTime, dataToSign, RANDOM_BYTES_LENGTH + 3 * LONG_BYTE_SIZE); // 3. 使用服务器端密钥对上述48字节计算HMAC-SHA256 byte[] hmacFull = calculateHmac(secret, dataToSign); // 4. 取HMAC结果的前16字节 byte[] hmacPrefix = new byte[HMAC_BYTES_LENGTH]; System.arraycopy(hmacFull, 0, hmacPrefix, 0, HMAC_BYTES_LENGTH); // 5. 最终Token = 十六进制(随机数) + 十六进制(HMAC前16字节) String randomHex = bytesToHex(randomBytes); String hmacHex = bytesToHex(hmacPrefix); return randomHex + hmacHex; } catch (NoSuchAlgorithmException | InvalidKeyException e) { throw new RuntimeException("生成Token失败", e); } } /** * 将long值转换为8字节,并存入指定位置 * * @param value 要转换的long值 * @param dest 目标字节数组 * @param offset 偏移量 */ private static void longToBytes(long value, byte[] dest, int offset) { for (int i = 0; i < LONG_BYTE_SIZE; i++) { dest[offset + i] = (byte) ((value >> (8 * (LONG_BYTE_SIZE - 1 - i))) & 0xFF); } } /** * 计算HMAC-SHA256 * * @param secret 密钥 * @param data 待计算的数据 * @return HMAC-SHA256结果 * @throws NoSuchAlgorithmException * @throws InvalidKeyException */ private static byte[] calculateHmac(String secret, byte[] data) throws NoSuchAlgorithmException, InvalidKeyException { SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(), HMAC_ALGORITHM); Mac mac = Mac.getInstance(HMAC_ALGORITHM); mac.init(secretKeySpec); return mac.doFinal(data); } /** * 将字节数组转换为十六进制字符串 * * @param bytes 字节数组 * @return 十六进制字符串 */ private static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { String hex = Integer.toHexString(b & 0xFF); if (hex.length() == 1) { sb.append('0'); } sb.append(hex); } return sb.toString(); } }