源于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"]);
?>