liujie
2026-06-09 c96fbe6b0aeb413a17c429133ee6d81d138144b9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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();
    }
 
}