源于Github的Python版本
后面转成了Java版本,依赖于Hutool工具类

package org.jeecg.modules.third.finance.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.Header;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.ocean.rawsdk.util.Base64;
import com.google.common.collect.Lists;
import org.springframework.http.MediaType;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;

public class QImeiUtil {
    public static String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEIxgwoutfwoJxcGQeedgP7FG9qaIuS0qzfR8gWkrkTZKM2iWHn2ajQpBRZjMSoSf6+KJGvar2ORhBfpDXyVtZCKpqLQ+FLkpncClKVIrBwv6PHyUvuCb0rIarmgDnzkfQAqVufEtR64iazGDKatvJ9y6B9NMbHddGSAUmRTCrHQIDAQAB";
    public static String SECRET_KEY = "qimei_qq_androidpzAuCmaFAaFaHrdakPjLIEqKrGnSOOvH";
    public static String SECRET = "ZdJqM15EeO2zWc08";
    public static String APP_KEY = "0AND0HD6FE4HY80F";
    public static String APPID = "qimei_qq_android";

    public static String rsaEncrypt(String content) {
        RSA rsa = SecureUtil.rsa(null, Base64.decodeBase64(PUBLIC_KEY.getBytes()));
        return rsa.encryptBase64(content, KeyType.PublicKey);
    }

    /**
     * @param device JSON字符串
     * @return
     */
    public static List<String> genQuery(String device) {
        String cryptKey = RandomUtil.randomString("abcdef1234567890", 16);
        String nonce = RandomUtil.randomString("abcdef1234567890", 16);
        long ts = System.currentTimeMillis();
        String key = rsaEncrypt(cryptKey);
        String params = new AES(Mode.CBC, Padding.PKCS5Padding, cryptKey.getBytes(), cryptKey.getBytes()).encryptBase64(device);

        String extra = "{\"appKey\":\"" + APP_KEY + "\"}";
        String sign = DigestUtil.md5Hex(key + params + ts + nonce + SECRET + extra);
        Map<String, Object> reqParam = MapUtil.newHashMap();
        reqParam.put("key", key);
        reqParam.put("params", params);
        reqParam.put("time", Long.toString(ts));
        reqParam.put("nonce", nonce);
        reqParam.put("sign", sign);
        reqParam.put("extra", extra);

        Map<String, Object> data = MapUtil.newHashMap();
        data.put("app", 0);
        data.put("os", 1);
        data.put("qimeiParams", reqParam);

        return Lists.newArrayList(JSON.toJSONString(data), String.valueOf(ts / 1000));
    }

    public static String genRandomPayload() {
        StringBuilder beaconId = new StringBuilder();
        String timeMonth = DateUtil.formatDate(DateUtil.beginOfMonth(new Date()));
        int rand1 = RandomUtil.randomInt(100000, 999999);
        int rand2 = RandomUtil.randomInt(100000000, 999999999);
        List<Integer> numList = Lists.newArrayList(1, 2, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30, 33, 34, 37, 38);
        
        IntStream.rangeClosed(1, 41).forEach(i -> {
            if (numList.contains(i)) {
                beaconId.append("k").append(i).append(":").append(timeMonth).append(rand1).append(".").append(rand2);
            } else if (i == 3) {
                beaconId.append("k3:0000000000000000");
            } else if (i == 4) {
                beaconId.append("k4:").append(RandomUtil.randomString("123456789abcdef", 16));
            } else {
                beaconId.append("k").append(i).append(":").append(RandomUtil.randomInt(0, 9999));
            }
            beaconId.append(";");
        });
        
        List<String> brandList = Lists.newArrayList("VIVO", "Xiaomi", "OPPO", "HUAWEI");
        String brand = brandList.get(RandomUtil.randomInt(brandList.size() - 1));
        int fixedRandSeconds = RandomUtil.randomInt(0, 14400);
        String dateTime = DateUtil.formatDateTime(DateUtil.offsetSecond(new Date(), -fixedRandSeconds));

        Map<String, Object> reserved = MapUtil.newHashMap();
        reserved.put("harmony", "0");
        reserved.put("clone", "0");
        reserved.put("containe", "");
        reserved.put("oz", "UhYmelwouA+V2nPWbOvLTgN2\\/m8jwGB+yUB5v9tysQg=");
        reserved.put("oo", "Xecjt+9S1+f8Pz2VLSxgpw==");
        reserved.put("kelong", "0");
        reserved.put("uptimes", dateTime);
        reserved.put("multiUser", "0");
        reserved.put("bod", brand);
        reserved.put("brd", brand);
        reserved.put("dv", "PCRT00");
        reserved.put("firstLevel", "");
        reserved.put("manufact", brand);
        reserved.put("name", "PCRT00");
        reserved.put("host", "se.infra");
        reserved.put("kernel", "Linux localhost 4.14.253-android+ #754 SMP Wed Nov 9 17:04:03 CST 2022 armv8");

        Map<String, Object> result = MapUtil.newHashMap();
        result.put("androidId", "BRAND.141613.779");
        result.put("platformId", 1);
        result.put("appKey", APP_KEY);
        result.put("appVersion", "12.6.0.12");
        result.put("beaconIdSrc", beaconId.toString());
        result.put("brand", brand);
        result.put("channelId", "10003505");
        result.put("cid", "");
        result.put("imei", genRandomImei());
        result.put("imsi", "");
        result.put("mac", "");
        result.put("model", "");
        result.put("networkType", "unknown");
        result.put("oaid", "");
        result.put("osVersion", "Android 11.0,level 30");
        result.put("qimei", "");
        result.put("qimei36", "");
        result.put("sdkVersion", "1.2.13.6");
        result.put("targetSdkVersion", "29");
        result.put("audit", "");
        result.put("userId", "{}");
        result.put("packageId", "com.tencent.qqmusic");
        result.put("deviceType", "Phone");
        result.put("sdkName", "");
        result.put("reserved", JSON.toJSONString(reserved));

        return JSON.toJSONString(result);

    }

    public static String genRandomImei() {
        int tac = RandomUtil.randomInt(100000, 999999);
        int snr = RandomUtil.randomInt(100000, 999999);
        String imeiWithoutChecksum = tac + "" + snr;
        int checksum = calculateLuhnChecksum(imeiWithoutChecksum);
        return imeiWithoutChecksum + "" + checksum;
    }

    public static int calculateLuhnChecksum(String numberStr) {
        char[] digits = numberStr.toCharArray();
        int totalSum = 0;
        boolean isSecondDigit = false;

        for (int i = digits.length - 1; i >= 0; i--) {
            int digit = Character.getNumericValue(digits[i]);
            if (isSecondDigit) {
                digit *= 2;
                if (digit > 9) {
                    digit -= 9;
                }
            }
            totalSum += digit;
            isSecondDigit = !isSecondDigit;
        }
        return (10 - (totalSum % 10)) % 10;
    }

    public static String getQImei() {
        List<String> dataList = genQuery(genRandomPayload());
        String data = dataList.get(0);
        String ts = dataList.get(1);
        String sign = DigestUtil.md5Hex(SECRET_KEY + ts);
        String url = "https://api.tencentmusic.com/tme/trpc/proxy";

        HttpResponse response = HttpUtil.createPost(url)
                .header(Header.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .header(Header.USER_AGENT, "QQMusiC")
                .header("appid", APPID)
                .header("service", "trpc.tme_datasvr.qimeiproxy.QimeiProxy")
                .header("method", "GetQimei")
                .header("sign", sign)
                .header("timestamp", ts)
                .body(data)
                .execute();
        String body = response.body();
        System.err.println(body);
        if (response.isOk()) {
            JSONObject dataObj = JSON.parseObject(body);
            return dataObj.getString("data");
        }
        return null;
    }

    public static void main(String[] args) {
        String qImei = getQImei();
        System.err.println(qImei);
    }
}

还有个PHP版本

<?php

const PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEIxgwoutfwoJxcGQeedgP7FG9qaIuS0qzfR8gWkrkTZKM2iWHn2ajQpBRZjMSoSf6+KJGvar2ORhBfpDXyVtZCKpqLQ+FLkpncClKVIrBwv6PHyUvuCb0rIarmgDnzkfQAqVufEtR64iazGDKatvJ9y6B9NMbHddGSAUmRTCrHQIDAQAB\n-----END PUBLIC KEY-----";
const APP_KEY = "0AND0HD6FE4HY80F";
const SECRET = "ZdJqM15EeO2zWc08";
const HOST = "api.tencentmusic.com";
const METHOD = "GetQimei";
const SERVICE = "trpc.tme_datasvr.qimeiproxy.QimeiProxy";
const APPID = "qimei_qq_android";
const SECRET_KEY = "qimei_qq_androidpzAuCmaFAaFaHrdakPjLIEqKrGnSOOvH";

function rsa_encrypt($content) {
    $publicKey = openssl_get_publickey(PUBLIC_KEY);
    openssl_public_encrypt($content, $encrypted, $publicKey, OPENSSL_PKCS1_PADDING);
    return $encrypted;
}

class AES {
    const BLOCK_SIZE = 16;

    private $key;

    public function __construct($key) {
        $this->key = $key;
    }

    private function pad($v) {
        $padding_size = self::BLOCK_SIZE - strlen($v) % self::BLOCK_SIZE;
        return $v . str_repeat(chr($padding_size), $padding_size);
    }

    private function unpad($v) {
        return substr($v, 0, -ord($v[strlen($v) - 1]));
    }

    public function encrypt($content) {
        $iv = $this->key;
        return openssl_encrypt($this->pad($content), 'AES-128-CBC', $this->key, OPENSSL_RAW_DATA, $iv);
    }

    public function decrypt($content) {
        $iv = $this->key;
        return $this->unpad(openssl_decrypt($content, 'AES-128-CBC', $this->key, OPENSSL_RAW_DATA, $iv));
    }
}

function random_string($length, $chars) {
    $result = '';
    for ($i = 0; $i < $length; $i++) {
        $result .= $chars[rand(0, strlen($chars) - 1)];
    }
    return $result;
}

function calc_md5(...$args) {
    return md5(implode('', $args));
}

function gen_query($device) {
    $crypt_key = random_string(16, "abcdef1234567890");
    $nonce = random_string(16, "abcdef1234567890");
    $ts = time() * 1000;
    $key = base64_encode(rsa_encrypt($crypt_key));
    $aes = new AES($crypt_key);
    $params = base64_encode($aes->encrypt(json_encode($device)));
    $extra = '{"appKey":"' . APP_KEY . '"}';
    $sign = calc_md5($key, $params, (string)$ts, $nonce, SECRET, $extra);
    $data = array(
        "app" => 0,
        "os" => 1,
        "qimeiParams" => array(
            "key" => $key,
            "params" => $params,
            "time" => (string)$ts,
            "nonce" => $nonce,
            "sign" => $sign,
            "extra" => $extra
        )
    );
    return array($data, (string)(int)($ts / 1000));
}

function gen_random_payload() {
    $beacon_id = "";
    $time_month = date("Y-m") . "-01";
    $rand1 = rand(100000, 999999);
    $rand2 = rand(100000000, 999999999);

    for ($i = 1; $i <= 41; $i++) {
        if (in_array($i, array(1, 2, 13, 14, 17, 18, 21, 22, 25, 26, 29, 30, 33, 34, 37, 38))) {
            $beacon_id .= "k$i:$time_month$rand1.$rand2";
        } elseif ($i == 3) {
            $beacon_id .= "k3:0000000000000000";
        } elseif ($i == 4) {
            $beacon_id .= "k4:" . random_string(16, "123456789abcdef");
        } else {
            $beacon_id .= "k$i:" . rand(0, 9999);
        }
        $beacon_id .= ";";
    }
    $brand = array("VIVO", "Xiaomi", "OPPO", "HUAWEI")[array_rand(array("VIVO", "Xiaomi", "OPPO", "HUAWEI"))];
    // var_dump(array_rand(array("VIVO", "Xiaomi", "OPPO", "HUAWEI")));
    $fixed_rand_seconds = rand(0, 14400);
    $current_time = new DateTime();
    // var_dump($current_time);
    $time_result = $current_time->modify("-$fixed_rand_seconds seconds");
    // var_dump($time_result);
    $formatted_time = $time_result->format("Y-m-d H:i:s");
    $reserved = array(
        "harmony" => "0",
        "clone" => "0",
        "containe" => "",
        "oz" => "UhYmelwouA+V2nPWbOvLTgN2/m8jwGB+yUB5v9tysQg=",
        "oo" => "Xecjt+9S1+f8Pz2VLSxgpw==",
        "kelong" => "0",
        "uptimes" => $formatted_time,
        "multiUser" => "0",
        "bod" => $brand,
        "brd" => $brand,
        "dv" => "PCRT00",
        "firstLevel" => "",
        "manufact" => $brand,
        "name" => "PCRT00",
        "host" => "se.infra",
        "kernel" => "Linux localhost 4.14.253-android+ #754 SMP Wed Nov 9 17:04:03 CST 2022 armv8"
    );
    $result = array(
        "androidId" => "BRAND.141613.779",
        "platformId" => 1,
        "appKey" => APP_KEY,
        "appVersion" => "12.6.0.12",
        "beaconIdSrc" => $beacon_id,
        "brand" => $brand,
        "channelId" => "10003505",
        "cid" => "",
        "imei" => gen_random_imei(),
        "imsi" => "",
        "mac" => "",
        "model" => "",
        "networkType" => "unknown",
        "oaid" => "",
        "osVersion" => "Android 11.0,level 30",
        "qimei" => "",
        "qimei36" => "",
        "sdkVersion" => "1.2.13.6",
        "targetSdkVersion" => "29",
        "audit" => "",
        "userId" => "{}",
        "packageId" => "com.tencent.qqmusic",
        "deviceType" => "Phone",
        "sdkName" => "",
        "reserved" => json_encode($reserved, JSON_UNESCAPED_UNICODE)
    );
    
    //var_dump(json_encode($result));
    return $result;
}

function gen_random_imei() {
    $tac = rand(100000, 999999);
    $snr = rand(100000, 999999);
    $imei_without_checksum = "$tac$snr";
    $checksum = calculate_luhn_checksum($imei_without_checksum);
    return "$imei_without_checksum$checksum";
}

function calculate_luhn_checksum($number_str) {
    $digits = str_split($number_str);
    $total_sum = 0;
    $is_second_digit = false;

    for ($i = count($digits) - 1; $i >= 0; $i--) {
        $digit = (int)$digits[$i];
        if ($is_second_digit) {
            $digit *= 2;
            if ($digit > 9) {
                $digit -= 9;
            }
        }
        $total_sum += $digit;
        $is_second_digit = !$is_second_digit;
    }

    return (10 - $total_sum % 10) % 10;
}

class QImeiResult {
    public $q16;
    public $q36;

    public function __construct($q16, $q36) {
        $this->q16 = $q16;
        $this->q36 = $q36;
    }
}

function get() {
    list($data, $ts) = gen_query(gen_random_payload());
    $sign = calc_md5(SECRET_KEY, $ts);
    $url = "https://" . HOST . "/tme/trpc/proxy";

    $options = array(
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($data),
        CURLOPT_HTTPHEADER => array(
            "Content-Type: application/json",
            "Host: " . HOST,
            "method: " . METHOD,
            "service: " . SERVICE,
            "appid: " . APPID,
            "sign: " . $sign,
            "user-agent: QQMusic",
            "timestamp: " . $ts
        )
    );

    $curl = curl_init();
    curl_setopt_array($curl, $options);
    $response = curl_exec($curl);
    curl_close($curl);

    return $response;
}

// Example usage:
$response = get();
print(json_decode($response,true)["data"]);

?>
最后修改:2024 年 03 月 14 日
如果觉得我的文章对你有用,请随意赞赏