版本说明
- Web SDK 5.1.0及以上
- Android SDK 5.1.0及以上
- iOS SDK 5.1.0及以上
密封客户端结果说明
通常客户接入设备指纹应用到业务场景分为两部分,第一步客户的应用需要集成 SDK 先采集设备信息,由同盾返回设备信息黑盒。第二步客户端应用获取到黑盒传递给风控中台,由风控中台通过黑盒调设备指纹服务获取设备ID 及设备信息进行决策。
密封客户端结果,是在客户应用接入SDK 的时候就可以选择是否直接返回加密的设备信息,如果确认由前端应用获取,同盾 SDK 会直接返回匿名ID、黑盒及加密的设备信息,由客户的风控中台通过密钥解密设备信息,进行决策,省略了风控中台与设备指纹服务交互的步骤。
优点:
- 减少了端与端之前交互的延迟,解密信息直接在您的服务端应用,无需调 API 接口
- 提高了数据的安全性,黑产无法恶意读取客户端密封结果

配置密封客户端结果
如果您想使用密封客户端结果,需要完成以下步骤
- 在客户平台-开发者-密钥&SDK 里创建加密密钥并启用
- 前端初始化 SDK 会获取到密封结果,将密封结果发送到服务端
- 您的服务端需要接收密封结果并且通过密钥解密
创建加密密钥
导航到客户平台-开发者-密钥&SDK-加密密钥

- 点击创建加密密钥
- 输入密钥名称
- 选择前端加密的 appKey 绑定,支持绑定多个
- 选择密钥状态
- 点击保存

复制 加密密钥 提供给服务端使用

服务端解密密封结果
- 不要在客户端解密密封结果
- 解密功能设计仅在后端进行。如果尝试在客户端解密密封结果,意味着任何人都可以看到加密密钥。这会泄漏敏感信息同时引入新的黑产攻击
前端发送客户端密封结果到服务端后,需要使用上面生成的加密密钥进行解密。
解密密封结果代码参考:
import base64
import json
import zstandard as zstd
import io
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
def aes_decrypt(source, key_str):
try:
# Base64 解码
data = base64.b64decode(source)
key_bytes = base64.b64decode(key_str)
# 验证最小长度 (IV12 + TAG16 + Zstd最小头5)
if len(data) < 12 + 16 + 5:
raise ValueError("Data too short for valid payload")
# 提取各部分
iv = data[:12]
auth_tag = data[-16:]
ciphertext = data[12:-16]
# AES-GCM 解密
cipher = Cipher(
algorithms.AES(key_bytes),
modes.GCM(iv, auth_tag),
backend=default_backend()
)
decryptor = cipher.decryptor()
compressed = decryptor.update(ciphertext) + decryptor.finalize()
# 验证Zstd帧头
if compressed[:4] != b'\x28\xb5\x2f\xfd': # Magic Number
raise ValueError("Invalid Zstd header")
# 流式解压适配分块压缩
dctx = zstd.ZstdDecompressor()
with dctx.stream_reader(io.BytesIO(compressed)) as reader:
decompressed = reader.read() # 自动处理未知大小
# 返回JSON字符串
device_info = decompressed.decode('utf-8')
return json.dumps(device_info)
except Exception as e:
print(f"AES256-ZSTD decrypt fail! {e}")
return None
# encryptionKey
keyStr = "your encryptionKey"
# sealedResult
source = "your sealedResult"
result = aes_decrypt(source, keyStr)
print(result)
package cn.tongdun.brick.common.util.tdcipher.Utils;
import cn.tongdun.brick.common.util.JsonUtil;
import com.github.luben.zstd.ZstdInputStream;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
@Slf4j
public class test {
// 测试用例
public static void main(String[] args) throws Exception {
// encryptionKey
String keyStr = "your encryptionKey";
// sealedResult
String encryptSource = "your sealedResult";
String aesDecrypt = aesDecrypt(encryptSource, keyStr);
System.out.println(aesDecrypt);
}
/**
* AES256GCM解密
*
* @param source 后端返回加密后数据
* @param keyStr 平台生成加密密钥
* @return 设备详情Json
*/
public static String aesDecrypt(String source, String keyStr) {
try {
// 后端返回加密后数据
byte[] data = Base64.getDecoder().decode(source);
/*
生成密钥
*/
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
SecretKey key = new SecretKeySpec(keyBytes, "AES");
/*
提取加密后数据中iv和加密数据
*/
if (data.length < 12) {
throw new IllegalArgumentException();
}
byte[] iv = Arrays.copyOfRange(data, 0, 12);
byte[] ciphertext = Arrays.copyOfRange(data, 12, data.length);
/*
解密
*/
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec);
byte[] dataBeforeEncryption = cipher.doFinal(ciphertext);
/*
解压数据
*/
byte[] unzipData;
try (ByteArrayInputStream bis = new ByteArrayInputStream(dataBeforeEncryption);
ZstdInputStream zis = new ZstdInputStream(bis);
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[4096];
int len;
while ((len = zis.read(buffer)) > 0) {
bos.write(buffer, 0, len);
}
unzipData = bos.toByteArray();
}
/*
返回结果
*/
String deviceInfo = new String(unzipData, StandardCharsets.UTF_8);
return JsonUtil.writeValueAsString(deviceInfo);
} catch (Exception e) {
log.error("AES256 decrypt fail!", e);
return null;
}
}
// 添加 Maven 依赖:
// <dependency>
// <groupId>com.github.luben</groupId>
// <artifactId>zstd-jni</artifactId>
// <version>1.5.5-4</version>
// </dependency>
}
package main
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/klauspost/compress/zstd"
)
func main() {
// encryptionKey
var keyStr = "your encryptionKey"
// sealedResult
var encryptSource = "your sealedResult"
fmt.Println(AESDecrypt(encryptSource, keyStr))
}
func AESDecrypt(source, keyStr string) (string, error) {
// Base64 解码
data, _ := base64.StdEncoding.DecodeString(source)
keyBytes, _ := base64.StdEncoding.DecodeString(keyStr)
// 提取 IV 和密文
if len(data) < 12 {
return "", fmt.Errorf("invalid data length")
}
iv := data[:12]
ciphertext := data[12:]
// AES-GCM 解密
block, _ := aes.NewCipher(keyBytes)
gcm, _ := cipher.NewGCM(block)
compressed, err := gcm.Open(nil, iv, ciphertext, nil)
if err != nil {
return "", err
}
// Zstandard 解压缩
decoder, _ := zstd.NewReader(nil)
defer decoder.Close()
decompressed, err := decoder.DecodeAll(compressed, nil)
if err != nil {
return "", err
}
// 返回 JSON 字符串
deviceInfo := string(decompressed)
result, _ := json.Marshal(deviceInfo)
return string(result), nil
}
// 依赖: go get github.com/klauspost/compress/zstd
const crypto = require('crypto');
const zstd = require('@bokuweb/zstd-wasm');
// 定义常量
const MIN_DATA_LENGTH = 28; // 12(IV) + 16(tag) = 28
const ZSTD_MAGIC_NUMBER = 0xFD2FB528;
async function aesDecrypt(source, keyStr) {
try {
// Base64 解码
const data = Buffer.from(source, 'base64');
const keyBytes = Buffer.from(keyStr, 'base64');
// 验证最小长度要求
if (data.length < MIN_DATA_LENGTH) {
throw new Error(`Data too short (${data.length} bytes). Minimum required: ${MIN_DATA_LENGTH} bytes`);
}
// 提取各部分
const iv = data.subarray(0, 12);
const authTag = data.subarray(data.length - 16);
const ciphertext = data.subarray(12, data.length - 16);
// 创建解密器
const decipher = crypto.createDecipheriv('aes-256-gcm', keyBytes, iv);
decipher.setAuthTag(authTag);
// 解密数据 - 正确处理流式解密
const chunks = [];
chunks.push(decipher.update(ciphertext));
try {
chunks.push(decipher.final());
} catch (e) {
throw new Error('Failed to finalize AES decryption', e);
}
const decrypted = Buffer.concat(chunks);
// 初始化 Zstd
await zstd.init();
// 解压缩数据
let decompressed;
try {
if (decrypted.length >= 4 && decrypted.readUInt32LE(0) !== ZSTD_MAGIC_NUMBER) {
decompressed = decrypted;
} else {
decompressed = zstd.decompress(decrypted);
}
} catch (e) {
decompressed = decrypted;
}
// 返回 JSON 字符串
try {
const decoder = new TextDecoder('utf-8');
return decoder.decode(decompressed);
} catch (e) {
throw new Error('Failed to decode decrypted data as UTF-8', e);
}
} catch (e) {
return null;
}
}
async function testDecryption() {
// encryptionKey
const KeyStr = 'your encryptionKey';
// sealedResult
const Source = 'your sealedResult';
const result = await aesDecrypt(Source, KeyStr);
if (result) {
try {
// 尝试解析嵌套的JSON
const parsed = JSON.parse(result);
if (typeof parsed === 'string') {
const innerParsed = JSON.parse(parsed);
console.log('Double-parsed result:', innerParsed);
} else {
console.log('Parsed result:', parsed);
}
} catch (e) {
console.error('Error parsing JSON:', e);
console.log('Raw result:', result);
}
} else {
console.log('Decryption failed');
}
}
testDecryption();
//npm install @bokuweb/zstd-wasm
<?php
function aesDecrypt($source, $keyStr) {
try {
// Base64 解码
$data = base64_decode($source);
$keyBytes = base64_decode($keyStr);
// 提取 IV 和密文
if (strlen($data) < 12) {
throw new Exception("Invalid data");
}
$iv = substr($data, 0, 12);
$ciphertext = substr($data, 12);
// 提取认证标签 (最后16字节)
$tag = substr($ciphertext, -16);
$ciphertext = substr($ciphertext, 0, -16);
// AES-GCM 解密
$compressed = openssl_decrypt(
$ciphertext,
'aes-256-gcm',
$keyBytes,
OPENSSL_RAW_DATA,
$iv,
$tag
);
if ($compressed === false) {
throw new Exception("AES decryption failed");
}
// Zstandard 解压缩
$decompressed = zstd_uncompress($compressed);
if ($decompressed === false) {
throw new Exception("Zstd decompression failed");
}
// 返回 JSON 字符串
return json_encode($decompressed);
} catch (Exception $e) {
error_log("AES256 decrypt fail! " . $e->getMessage());
return null;
}
}
// encryptionKey
$testKey = 'your encryptionKey';
// sealedResult
$testData = 'your sealedResult';
$result = aesDecrypt($testData, $testKey);
var_dump($result);
// 安装扩展:
// pecl install zstd
// 并在php.ini中添加: extension=zstd
?>
// zstd解压代码
const fzstd = (function() {
var _e = {};
'use strict';
var r = ArrayBuffer, t = Uint8Array, e = Uint16Array, n = Int16Array, a = Uint32Array, s = Int32Array,
i = function(r, e, n) {
if (t.prototype.slice) return t.prototype.slice.call(r, e, n);
(null == e || e < 0) && (e = 0), (null == n || n > r.length) && (n = r.length);
var a = new t(n - e);
return a.set(r.subarray(e, n)), a;
}, o = function(r, e, n, a) {
if (t.prototype.fill) return t.prototype.fill.call(r, e, n, a);
for ((null == n || n < 0) && (n = 0), (null == a || a > r.length) && (a = r.length); n < a; ++n) r[n] = e;
return r;
}, u = function(r, e, n, a) {
if (t.prototype.copyWithin) return t.prototype.copyWithin.call(r, e, n, a);
for ((null == n || n < 0) && (n = 0), (null == a || a > r.length) && (a = r.length); n < a;) r[e++] = r[n++];
};
_e.ZstdErrorCode = {
InvalidData: 0,
WindowSizeTooLarge: 1,
InvalidBlockType: 2,
FSEAccuracyTooHigh: 3,
DistanceTooFarBack: 4,
UnexpectedEOF: 5
};
var h = ['invalid zstd data', 'window size too large (>2046MB)', 'invalid block type', 'FSE accuracy too high', 'match distance too far back', 'unexpected EOF'],
f = function(r, t, e) {
var n = Error(t || h[r]);
if (n.code = r, Error.captureStackTrace && Error.captureStackTrace(n, f), !e) throw n;
return n;
}, l = function(r, t, e) {
for (var n = 0, a = 0; n < e; ++n) a |= r[t++] << (n << 3);
return a;
}, v = function(r, t) {
return (r[t] | r[t + 1] << 8 | r[t + 2] << 16 | r[t + 3] << 24) >>> 0;
}, c = function(r, e) {
var n = r[0] | r[1] << 8 | r[2] << 16;
if (3126568 == n && 253 == r[3]) {
var a = r[4], i = a >> 5 & 1, o = a >> 2 & 1, u = 3 & a, h = a >> 6;
8 & a && f(0);
var c = 6 - i, b = 3 == u ? 4 : u, y = l(r, c, b), p = h ? 1 << h : i, w = l(r, c += b, p) + (1 == h && 256),
g = w;
if (!i) {
var d = 1 << 10 + (r[5] >> 3);
g = d + (d >> 3) * (7 & r[5]);
}
g > 2145386496 && f(1);
var m = new t((1 == e ? w || g : e ? 0 : g) + 12);
return m[0] = 1, m[4] = 4, m[8] = 8, {
b: c + p,
y: 0,
l: 0,
d: y,
w: e && 1 != e ? e : m.subarray(12),
e: g,
o: new s(m.buffer, 0, 3),
u: w,
c: o,
m: Math.min(131072, g)
};
}
if (25481893 == (n >> 4 | r[3] << 20)) return v(r, 4) + 8;
f(0);
}, b = function(r) {
for (var t = 0; 1 << t <= r; ++t) ;
return t - 1;
}, y = function(a, s, i) {
var o = 4 + (s << 3), u = 5 + (15 & a[s]);
u > i && f(3);
for (var h = 1 << u, l = h, v = -1, c = -1, y = -1, p = h, w = new r(512 + (h << 2)), g = new n(w, 0, 256), d = new e(w, 0, 256), m = new e(w, 512, h), z = 512 + (h << 1), E = new t(w, z, h), k = new t(w, z + h); v < 255 && l > 0;) {
var A = b(l + 1), T = o >> 3, x = (1 << A + 1) - 1, F = (a[T] | a[T + 1] << 8 | a[T + 2] << 16) >> (7 & o) & x,
S = (1 << A) - 1, B = x - l - 1, I = F & S;
if (I < B ? (o += A, F = I) : (o += A + 1, F > S && (F -= B)), g[++v] = --F, -1 == F ? (l += F, E[--p] = v) : l -= F, !F) do {
var U = o >> 3;
c = (a[U] | a[U + 1] << 8) >> (7 & o) & 3, o += 2, v += c;
} while (3 == c);
}
(v > 255 || l) && f(0);
for (var D = 0, M = (h >> 1) + (h >> 3) + 3, W = h - 1, O = 0; O <= v; ++O) {
var j = g[O];
if (j < 1) d[O] = -j; else for (y = 0; y < j; ++y) {
E[D] = O;
do {
D = D + M & W;
} while (D >= p);
}
}
for (D && f(0), y = 0; y < h; ++y) {
var C = d[E[y]]++, H = k[y] = u - b(C);
m[y] = (C << H) - h;
}
return [o + 7 >> 3, { b: u, s: E, n: k, t: m }];
}, p = function(r, n) {
var a = 0, s = -1, i = new t(292), u = r[n], h = i.subarray(0, 256), l = i.subarray(256, 268),
v = new e(i.buffer, 268);
if (u < 128) {
var c = y(r, n + 1, 6), p = c[1], w = c[0] << 3, g = r[n += u];
g || f(0);
for (var d = 0, m = 0, z = p.b, E = z, k = (++n << 3) - 8 + b(g); !((k -= z) < w);) {
var A = k >> 3;
if (h[++s] = p.s[d += (r[A] | r[A + 1] << 8) >> (7 & k) & (1 << z) - 1], (k -= E) < w) break;
h[++s] = p.s[m += (r[A = k >> 3] | r[A + 1] << 8) >> (7 & k) & (1 << E) - 1], z = p.n[d], d = p.t[d], E = p.n[m], m = p.t[m];
}
++s > 255 && f(0);
} else {
for (s = u - 127; a < s; a += 2) {
var T = r[++n];
h[a] = T >> 4, h[a + 1] = 15 & T;
}
++n;
}
var x = 0;
for (a = 0; a < s; ++a) (I = h[a]) > 11 && f(0), x += I && 1 << I - 1;
var F = b(x) + 1, S = 1 << F, B = S - x;
for (B & B - 1 && f(0), h[s++] = b(B) + 1, a = 0; a < s; ++a) {
var I;
++l[h[a] = (I = h[a]) && F + 1 - I];
}
var U = new t(S << 1), D = U.subarray(0, S), M = U.subarray(S);
for (v[F] = 0, a = F; a > 0; --a) {
var W = v[a];
o(M, a, W, v[a - 1] = W + l[a] * (1 << F - a));
}
for (v[0] != S && f(0), a = 0; a < s; ++a) {
var O = h[a];
if (O) {
var j = v[O];
o(D, a, j, v[O] = j + (1 << F - O));
}
}
return [n, { n: M, b: F, s: D }];
}, w = y(new t([81, 16, 99, 140, 49, 198, 24, 99, 12, 33, 196, 24, 99, 102, 102, 134, 70, 146, 4]), 0, 6)[1],
g = y(new t([33, 20, 196, 24, 99, 140, 33, 132, 16, 66, 8, 33, 132, 16, 66, 8, 33, 68, 68, 68, 68, 68, 68, 68, 68, 36, 9]), 0, 6)[1],
d = y(new t([32, 132, 16, 66, 102, 70, 68, 68, 68, 68, 36, 73, 2]), 0, 5)[1], m = function(r, t) {
for (var e = r.length, n = new s(e), a = 0; a < e; ++a) n[a] = t, t += 1 << r[a];
return n;
}, z = new t(new s([0, 0, 0, 0, 16843009, 50528770, 134678020, 202050057, 269422093]).buffer, 0, 36), E = m(z, 0),
k = new t(new s([0, 0, 0, 0, 0, 0, 0, 0, 16843009, 50528770, 117769220, 185207048, 252579084, 16]).buffer, 0, 53),
A = m(k, 3), T = function(r, t, e) {
var n = r.length, a = t.length, s = r[n - 1], i = (1 << e.b) - 1, o = -e.b;
s || f(0);
for (var u = 0, h = e.b, l = (n << 3) - 8 + b(s) - h, v = -1; l > o && v < a;) {
var c = l >> 3;
t[++v] = e.s[u = (u << h | (r[c] | r[c + 1] << 8 | r[c + 2] << 16) >> (7 & l)) & i], l -= h = e.n[u];
}
l == o && v + 1 == a || f(0);
}, x = function(r, t, e) {
var n = 6, a = t.length + 3 >> 2, s = a << 1, i = a + s;
T(r.subarray(n, n += r[0] | r[1] << 8), t.subarray(0, a), e), T(r.subarray(n, n += r[2] | r[3] << 8), t.subarray(a, s), e), T(r.subarray(n, n += r[4] | r[5] << 8), t.subarray(s, i), e), T(r.subarray(n), t.subarray(i), e);
}, F = function(r, n, a) {
var s, u = n.b, h = r[u], l = h >> 1 & 3;
n.l = 1 & h;
var v = h >> 3 | r[u + 1] << 5 | r[u + 2] << 13, c = (u += 3) + v;
if (1 == l) {
if (u >= r.length) return;
return n.b = u + 1, a ? (o(a, r[u], n.y, n.y += v), a) : o(new t(v), r[u]);
}
if (!(c > r.length)) {
if (0 == l) return n.b = c, a ? (a.set(r.subarray(u, c), n.y), n.y += v, a) : i(r, u, c);
if (2 == l) {
var m = r[u], F = 3 & m, S = m >> 2 & 3, B = m >> 4, I = 0, U = 0;
F < 2 ? 1 & S ? B |= r[++u] << 4 | (2 & S && r[++u] << 12) : B = m >> 3 : (U = S, S < 2 ? (B |= (63 & r[++u]) << 4, I = r[u] >> 6 | r[++u] << 2) : 2 == S ? (B |= r[++u] << 4 | (3 & r[++u]) << 12, I = r[u] >> 2 | r[++u] << 6) : (B |= r[++u] << 4 | (63 & r[++u]) << 12, I = r[u] >> 6 | r[++u] << 2 | r[++u] << 10)), ++u;
var D = a ? a.subarray(n.y, n.y + n.m) : new t(n.m), M = D.length - B;
if (0 == F) D.set(r.subarray(u, u += B), M); else if (1 == F) o(D, r[u++], M); else {
var W = n.h;
if (2 == F) {
var O = p(r, u);
I += u - (u = O[0]), n.h = W = O[1];
} else W || f(0);
(U ? x : T)(r.subarray(u, u += I), D.subarray(M), W);
}
var j = r[u++];
if (j) {
255 == j ? j = 32512 + (r[u++] | r[u++] << 8) : j > 127 && (j = j - 128 << 8 | r[u++]);
var C = r[u++];
3 & C && f(0);
for (var H = [g, d, w], L = 2; L > -1; --L) {
var Z = C >> 2 + (L << 1) & 3;
if (1 == Z) {
var q = new t([0, 0, r[u++]]);
H[L] = { s: q.subarray(2, 3), n: q.subarray(0, 1), t: new e(q.buffer, 0, 1), b: 0 };
} else 2 == Z ? (u = (s = y(r, u, 9 - (1 & L)))[0], H[L] = s[1]) : 3 == Z && (n.t || f(0), H[L] = n.t[L]);
}
var G = n.t = H, J = G[0], K = G[1], N = G[2], P = r[c - 1];
P || f(0);
var Q = (c << 3) - 8 + b(P) - N.b, R = Q >> 3, V = 0, X = (r[R] | r[R + 1] << 8) >> (7 & Q) & (1 << N.b) - 1,
Y = (r[R = (Q -= K.b) >> 3] | r[R + 1] << 8) >> (7 & Q) & (1 << K.b) - 1,
$ = (r[R = (Q -= J.b) >> 3] | r[R + 1] << 8) >> (7 & Q) & (1 << J.b) - 1;
for (++j; --j;) {
var _ = N.s[X], rr = N.n[X], tr = J.s[$], er = J.n[$], nr = K.s[Y], ar = K.n[Y], sr = 1 << nr,
ir = sr + ((r[R = (Q -= nr) >> 3] | r[R + 1] << 8 | r[R + 2] << 16 | r[R + 3] << 24) >>> (7 & Q) & sr - 1);
R = (Q -= k[tr]) >> 3;
var or = A[tr] + ((r[R] | r[R + 1] << 8 | r[R + 2] << 16) >> (7 & Q) & (1 << k[tr]) - 1);
R = (Q -= z[_]) >> 3;
var ur = E[_] + ((r[R] | r[R + 1] << 8 | r[R + 2] << 16) >> (7 & Q) & (1 << z[_]) - 1);
if (R = (Q -= rr) >> 3, X = N.t[X] + ((r[R] | r[R + 1] << 8) >> (7 & Q) & (1 << rr) - 1), R = (Q -= er) >> 3, $ = J.t[$] + ((r[R] | r[R + 1] << 8) >> (7 & Q) & (1 << er) - 1), R = (Q -= ar) >> 3, Y = K.t[Y] + ((r[R] | r[R + 1] << 8) >> (7 & Q) & (1 << ar) - 1), ir > 3) n.o[2] = n.o[1], n.o[1] = n.o[0], n.o[0] = ir -= 3; else {
var hr = ir - (0 != ur);
hr ? (ir = 3 == hr ? n.o[0] - 1 : n.o[hr], hr > 1 && (n.o[2] = n.o[1]), n.o[1] = n.o[0], n.o[0] = ir) : ir = n.o[0];
}
for (L = 0; L < ur; ++L) D[V + L] = D[M + L];
M += ur;
var fr = (V += ur) - ir;
if (fr < 0) {
var lr = -fr, vr = n.e + fr;
for (lr > or && (lr = or), L = 0; L < lr; ++L) D[V + L] = n.w[vr + L];
V += lr, or -= lr, fr = 0;
}
for (L = 0; L < or; ++L) D[V + L] = D[fr + L];
V += or;
}
if (V != M) for (; M < D.length;) D[V++] = D[M++]; else V = D.length;
a ? n.y += V : D = i(D, 0, V);
} else if (a) {
if (n.y += B, M) for (L = 0; L < B; ++L) D[L] = D[M + L];
} else M && (D = i(D, M));
return n.b = c, D;
}
f(2);
}
}, S = function(r, e) {
if (1 == r.length) return r[0];
for (var n = new t(e), a = 0, s = 0; a < r.length; ++a) {
var i = r[a];
n.set(i, s), s += i.length;
}
return n;
};
function B(r, t) {
for (var e = [], n = +!t, a = 0, s = 0; r.length;) {
var i = c(r, n || t);
if ('object' == typeof i) {
for (n ? (t = null, i.w.length == i.u && (e.push(t = i.w), s += i.u)) : (e.push(t), i.e = 0); !i.l;) {
var o = F(r, i, t);
o || f(5), t ? i.e = i.y : (e.push(o), s += o.length, u(i.w, 0, o.length), i.w.set(o, i.w.length - o.length));
}
a = i.b + 4 * i.c;
} else a = i;
r = r.subarray(a);
}
return S(e, s);
}
_e.decompress = B;
var I = function() {
function r(r) {
this.ondata = r, this.c = [], this.l = 0, this.z = 0;
}
return r.prototype.push = function(r, e) {
if ('number' == typeof this.s) {
var n = Math.min(r.length, this.s);
r = r.subarray(n), this.s -= n;
}
var a = r.length + this.l;
if (!this.s) {
if (e) {
if (!a) return void this.ondata(new t(0), !0);
a < 5 && f(5);
} else if (a < 18) return this.c.push(r), void (this.l = a);
if (this.l && (this.c.push(r), r = S(this.c, a), this.c = [], this.l = 0), 'number' == typeof (this.s = c(r))) return this.push(r, e);
}
if ('number' != typeof this.s) {
if (a < (this.z || 3)) return e && f(5), this.c.push(r), void (this.l = a);
if (this.l && (this.c.push(r), r = S(this.c, a), this.c = [], this.l = 0), !this.z && a < (this.z = 2 & r[this.s.b] ? 4 : 3 + (r[this.s.b] >> 3 | r[this.s.b + 1] << 5 | r[this.s.b + 2] << 13))) return e && f(5), this.c.push(r), void (this.l = a);
for (this.z = 0; ;) {
var s = F(r, this.s);
if (!s) {
e && f(5);
var i = r.subarray(this.s.b);
return this.s.b = 0, this.c.push(i), void (this.l += i.length);
}
if (this.ondata(s, !1), u(this.s.w, 0, s.length), this.s.w.set(s, this.s.w.length - s.length), this.s.l) {
var o = r.subarray(this.s.b);
return this.s = 4 * this.s.c, void this.push(o, e);
}
}
} else e && f(5);
}, r;
}();
_e.Decompress = I;
return _e;
})();
// sealedResult
const encryptSource = 'your sealedResult';
// encryptionKey
const keyStr = 'your encryptionKey';
// Base64字符串转ArrayBuffer
function base64ToArrayBuffer(base64) {
const binaryString = atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes;
}
// ArrayBuffer转UTF-8字符串
function arrayBufferToString(buffer) {
return new TextDecoder('utf-8').decode(buffer);
}
// AES-GCM解密函数
async function aesGCMDecrypt(ciphertext, keyBytes, iv) {
const key = await crypto.subtle.importKey(
'raw',
keyBytes,
{ name: 'AES-GCM' },
false,
['decrypt']
);
const decrypted = await crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: iv
},
key,
ciphertext
);
return new Uint8Array(decrypted);
}
// 主函数:解密并解压
async function decryptAndUncompress(source, keyStr) {
try {
// Base64解码加密数据
const encryptedData = base64ToArrayBuffer(source);
if (encryptedData.length < 12) {
throw new Error('加密数据长度不足,无法提取IV');
}
// 提取前12字节作为IV
const iv = encryptedData.slice(0, 12);
// 剩余部分作为密文
const ciphertext = encryptedData.slice(12);
// 解码密钥
const keyBytes = base64ToArrayBuffer(keyStr);
// AES-GCM解密
const decryptedData = await aesGCMDecrypt(ciphertext, keyBytes, iv);
// 使用fzstd解压Zstd压缩数据
const decompressedData = fzstd.decompress(decryptedData);
// 转换为字符串
const result = arrayBufferToString(decompressedData);
console.log(' 解密并解压成功:', result);
return result;
} catch (error) {
console.error(' 解密或解压失败:', error);
throw error;
}
}
// 调用主函数
decryptAndUncompress(encryptSource, keyStr)
.then(result => {
// 这里可以处理结果,比如显示在页面上
console.log(' 最终结果:', result);
})
.catch(err => {
console.error(err);
});
密封结果解密示例:
{
"device_risk_score": 4,
"code": 200,
"channel": "apitest",
"device_risk_label": [
"adb_link"
],
"device_risk_tools": {
"running_risk_tools_type": [],
"installed_risk_tools_type": []
},
"device_detail": {
"app_version": "5.0.0",
"language": "zh",
"installed_packages": "[{\"package\":\"cn.tongdun.mobrisk.demo\",\"name\":\"-\"}]",
"mcc": "",
"wifi_ip": "",
"device_svn": "20",
"available_storage": 109139550208,
"cpu_type": "",
"dns_address": "",
"host": "cn-west-hcd-5a-ad305db651716345895703-78bb978c87-7thzf",
"model": "TAS-AL00",
"brand": "HUAWEI",
"hardware": "kirin990",
"longitude": 0.0,
"cell_ip": "",
"ip": "10.57.30.10",
"screen_resolution": "1080x2340",
"cpu_hardware": "",
"charge_state": "full",
"up_time": 110874673,
"audio_mode": 0,
"aid": "00000000-0000-0000-0000-000000000000",
"startup_time": 1745295692850,
"bssid": "",
"latitude": 0.0,
"running_packages": "cn.tongdun.mobrisk.demo",
"proxy_info": "",
"device_name": "HWTAS",
"total_memory": 7925583872,
"vpn_true_ip": "",
"ipv6": "",
"system_version": "12",
"baseband_version": "21C20B715S000C000",
"fp_version": "5.0.0",
"country_iso": "cn,-",
"current_time": 1745406567523,
"product": "TAS-AL00",
"mnc": "",
"sim_operator": "中国电信,-",
"battery_level": 100,
"vpn_ip": "",
"api_version": "31",
"time_zone": "UTC+08:00",
"sign_md5": "03ce925f76ceb40e1c2ed8bfe3fcdd0c480a7ba8689ff814c601edfbf79e339e",
"screen_brightness": 31,
"carrier": "-,-",
"package_name": "cn.tongdun.mobrisk.demo",
"available_memory": 4362240000,
"total_storage": 117555855360,
"android_id": "9223e63cc64ba3ed",
"network_type": "wifi"
},
"message": "success",
"device_os": "Android",
"device_history_risk_label": [
{
"last_time": "1745312268033",
"label": "device_info_tampered"
},
{
"last_time": "1744612104302",
"label": "debugger_detected"
},
{
"last_time": "1743389182833",
"label": "abnormal_time"
},
{
"last_time": "1739946217228",
"label": "short_uptime"
},
{
"last_time": "1745464165274",
"label": "adb_link"
},
{
"last_time": "1741937366712",
"label": "replay_attacks"
}
],
"anonymous_id": "Iop9ffi1Pvs7N39968k617537Q3GK8TJ39fe63d7b78e0361",
"sequence_id": "1745476264739000X0497006B4905617"
}
禁用密封结果
您只需禁用客户平台的加密密钥即可关闭密封的客户端结果,禁用之后,SDK-API将不再返回加密的设备详情。禁用加密密钥之前,请确保您的集成可以支持处理未加密的数据结果
