API Reference
English

客户端密封结果

版本说明

  • Web SDK 5.1.0及以上
  • Android SDK 5.1.0及以上
  • iOS SDK 5.1.0及以上

密封客户端结果说明

通常客户接入设备指纹应用到业务场景分为两部分,第一步客户的应用需要集成 SDK 先采集设备信息,由同盾返回设备信息黑盒。第二步客户端应用获取到黑盒传递给风控中台,由风控中台通过黑盒调设备指纹服务获取设备ID 及设备信息进行决策。

密封客户端结果,是在客户应用接入SDK 的时候就可以选择是否直接返回加密的设备信息,如果确认由前端应用获取,同盾 SDK 会直接返回匿名ID、黑盒及加密的设备信息,由客户的风控中台通过密钥解密设备信息,进行决策,省略了风控中台与设备指纹服务交互的步骤。

优点:

  • 减少了端与端之前交互的延迟,解密信息直接在您的服务端应用,无需调 API 接口
  • 提高了数据的安全性,黑产无法恶意读取客户端密封结果

配置密封客户端结果

如果您想使用密封客户端结果,需要完成以下步骤

  1. 在客户平台-开发者-密钥&SDK 里创建加密密钥并启用
  2. 前端初始化 SDK 会获取到密封结果,将密封结果发送到服务端
  3. 您的服务端需要接收密封结果并且通过密钥解密

创建加密密钥

导航到客户平台-开发者-密钥&SDK-加密密钥


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

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


服务端解密密封结果

  • 不要在客户端解密密封结果
  • 解密功能设计仅在后端进行。如果尝试在客户端解密密封结果,意味着任何人都可以看到加密密钥。这会泄漏敏感信息同时引入新的黑产攻击

前端发送客户端密封结果到服务端后,需要使用上面生成的加密密钥进行解密。

解密密封结果代码参考:

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将不再返回加密的设备详情。禁用加密密钥之前,请确保您的集成可以支持处理未加密的数据结果