You need to enable JavaScript to run this app.
导航
端云互信服务
最近更新时间:2025.03.14 21:35:18首次发布时间:2025.01.10 20:58:29
我的收藏
有用
有用
无用
无用

概述

端云互信SDK是一套基于TEE构建的数据安全传输工具,旨在保障端侧应用与云上服务,或云上服务之间的互相信任和数据安全传输。
端云互信SDK分为数据发送端(client)和数据接收端(server)两部分,其使用场景如下图所示。

SDK下载地址

支持语言

下载地址

使用指引

Python

0.1.7.17版本

点击前往Python

C++

1.2版本

点击前往C++

Java

1.0版本

点击前往Java

JS

1.0版本

点击前往JS

相关术语

术语

含义

TrustZone

TrustZone是ARM针对消费电子设备设计的一种硬件架构,TrustZone将硬件和软件资源划分为安全(Secure World)和非安全(Normal World)两个世界,所有需要保密的操作在安全世界执行,其余操作在非安全世界执行。

Secure World

TrustZone的安全区域(世界)

Normal World

TrustZone的非安全区域(世界)

TA

Trusted Application,运行在TEE安全区域中的服务

QSEE

高通芯片的TrustZone方案

M-TEE

MTK芯片的TrustZone方案

管理端云互信应用

创建端云互信应用

注:只有注册过的应用才可以启用签名/验签功能,如果不需要签名字段,本步骤可省略。

进入端云互信一级目录,点击创建应用。输入合法的应用名称,应用描述;自定义应用的秘钥名称和信息,点击确定,创建成功后应用列表出现新增记录。

查看端云互信应用

进入应用列表,按需查看PCC系统为应用自动创建的密钥,该密钥将应用于端云互信传输。

修改和删除

可在操作一栏对端云互信互信应用进行修改和删除,删除时需二次确认。

端云互信SDK配置

发送端配置

机密容器服务

{
  "ra_url": "open.volcengineapi.com", //火山引擎地址
  "ra_service_name": "test_pcc",  //接收端服务在Jeddak PCC上部署时的服务名称
  "ra_policy_id": "16a371d7-130b-58eb-9e95-b5b816559ad8", //远程证明时的策略ID
  "ra_uid": "**", //用户的火山账号ID
  "bytedance_top_info": "{\"ak\": \"**\", \"sk\": \"**==\", \"service\": \"pcc_test\"}", // 用户火山账号的AK和SK信息
  "attest_interval": 1800,  //SDK自动做RA的时间间隔
  "root_key_config": "{\"test_id_test_name\": \"./myPrivateKey.pem\"}" // 数据发送端对自己做签名时,给的appinfo和私钥文件地址
}

参数说明

  • ra_service_name仅适用部署在机密容器内的服务,获取方式见下图。
  • ra_policy_id是做远程证明时的策略ID,获取方式见下图。
  • ra_uid是用户的账号ID,获取方式见下图:
  • bytedance_top_info是用户访问火山引擎的配置信息,包括AK和SK等。
  • attest_interval自动进行远程证明的时间间隔,单位秒
  • root_key_config:用户的RootKey配置,格式为{app_info: RootKey私钥文件地址},如果不需要签名,root_key_config可以不提供。

普通容器服务

{
    ....
    "ra_attested_pods": [
        {
          "cluster_id": "ccrqduf03q1qab0saeivg",
          "namespace": "default",
          "deployment": "jsc-server-example",
          "pod": "random"
        }
      ]
      ....
  }

参数说明

  • cluster_id是接收端服务在火山PCC部署时所在集群的集群ID,获取方式见下图:
  • namespace是接收端服务部署的命名空间名称,deployment是接收端服务部署的名称,这两个参数的获取方式如下。
  • pod是接收端服务所对应实例的名称,可填入具体的实例名称,也可填入random。

接收端配置

{
  "tks_url": "open.volcengineapi.com", //服务端TKS服务地址
  "tks_app_id": "2100278143", // TKS密钥的appid,一般等于用户的火山账号ID
  "tks_ring_id": "86aa20b9-bb5c-492f-a048-2fbceb1247b0",  //密钥的密钥环ID
  "tks_key_id": "e1187037-e454-420e-a68f-4bac56fbdcc0", //密钥的keyid
  "bytedance_top_info": "{\"ak\": \"**\", \"sk\": \"**\", \"service\": \"pcc_test\"}", //访问TKS时,用户的火山账号信息
  "refresh_interval": 600, //密钥刷新的时间间隔
  "root_key_config": "[{\"app_info\": \"test_id_test_name\", \"pub_key\":\"./myPublicKey.pem\"}]" //数据发送端如果启用了身份签名,接收端进行验签时的公钥文件信息
}

参数说明
如果不启用验签功能,root_key_config可以不配置;
如果启用了签名,且已经在PCC的端云互信页面中注册了公钥,则pub_key可以不指定。

加解密数据调用方法

发送端调用SDK加密数据

Python

  1. 初始化SDK
  • 加载本地配置文件
sdk_config_file_name = "./client_config.json"
secure_channel_config = jsc.ClientConfig.from_file(sdk_config_file_name)
secure_channel_client = jsc.Client(secure_channel_config)
  • 从环境变量中加载配置
env_conf_key = "***"
secure_channel_config = jsc.ClientConfig.from_env(env_conf_key)
secure_channel_client = jsc.Client(secure_channel_config)
  1. 对数据接收端发起RA验证,当有attest_interval配置项时,用户不需要主动发起RA,SDK会自动发起。
secure_channel_client.attest_server()
  1. 数据加密
test_msg = "我是待加密字符串"
encrypted_msg, enc_key = secure_channel_client.encrypt_with_response(test_msg)
# encrypted_msg 是加密后的密文

# send(encrypted_msg) 或直接解密encrypted_msg

plaintext = enc_key.decrypt(encrypted_msg)
print(plaintext)   # plaintext = b"我是待加密字符串"

# --------------------------------------------------------

test_msg = "我是待加密字符串"
encrypted_msg = secure_channel_client.encrypt(test_msg)
send(encrypted_msg)
  1. 数据解密
test_msg = "我是待加密字符串"
encrypted_msg, enc_key = secure_channel_client.encrypt_with_response(test_msg)
plaintext = enc_key.decrypt(encrypted_msg)

# --------------------------------------------------------

test_msg = "我是待加密字符串"
encrypted_msg, enc_key = secure_channel_client.encrypt_with_response(test_msg)

receive_response = send_to_server(encrypted_msg)
plaintext = enc_key.decrypt(receive_response)
  1. 对文件加密
src_file_name = "***"
des_file_name = "***"
enc_key = secure_channel_client.encrypt_file(src_file_name, des_file_name, "t")  #t 是对文本文件按行加密,b 是对二进制文件整体加密
  1. 流式请求加密
# 发送请求到服务端
test_msg = "我是待发送数据"
encrypted_msg, enc_key = secure_channel_client.encrypt_with_response(test_msg)
params = {"query": encrypted_msg}

with requests.post(url=url, json=params, stream=True) as r:
    for line in r.iter_lines():
        if line:
            res = line.decode().strip()
            enc_msg = base64.b64decode(res)
            plaintext = enc_key.decrypt(enc_msg)
            print("解密应答:", plaintext)
  1. 签名计算

云云数据传输时或未启用签名时不需要关注该功能。

params: Dict[str, str] = {}
params["enc_msg"] = secure_channel_client.encrypt(msg)
params["app_info"] = app_info

timestamp = str(int(datetime.now(timezone.utc).timestamp()))
sign = secure_channel_client.gen_sign(app_info, timestamp)
header = {"Timestamp": timestamp, "Sign": sign}

response = requests.post(url, json=params, headers=header)
。。。

C++

代码说明
压缩包包含 sln、vcxproj 项目文件、jsc 文件夹(源代码)、example 文件夹(示例程序)、thirdparty-src 文件夹(3 个依赖库源代码)、thirdparty-build 文件夹(OpenSSL 依赖库编译产物)。

prebuild.ps1 脚本介绍:这个脚本主要是用来解压 thirdparty 并编译 OpenSSL(编译产物在 thirdparty-build/openssl...,编译主项目时会用到)用户如果不需要重新编译OpenSSL,则不需要使用此脚本。

代码编译
依赖:

  • Visual Studio 2022
  • 选择 C++ 桌面开发组件、Windows 11 SDK ≥ 10.0.26100

编译产物:

  • x64/Debug 或 x64/Release 文件夹
  • 3 个 DLL

数据加密

#include <iostream>
#include <string>
#include <vector>

#include <jsc/client.hpp>
#include <jsc/client_config.hpp>
#include <jsc/client_key.hpp>
#include <jsc/detail/encoding.hpp>
#include <jsc/logger.hpp>

#include <httplib.h>
#include <nlohmann/json.hpp>

using namespace std;
using namespace jsc;
using namespace jsc::detail;

Log *g_logger = nullptr;

constexpr char const *kClientConfigPath = "client_config.json";

constexpr char const *kServerAddr = "http://101.126.153.180:8080";

void test_utils() {
  // base64 encode decode test
  std::string str = "i am a test string";
  std::vector<uint8_t> vec = std::vector<uint8_t>(str.begin(), str.end());

  std::string ret = base64_encode(vec);
  std::cout << "base64_encode ret=" << ret << std::endl;

  vec = base64_decode(ret);
  std::cout << "ret=" << std::string(vec.begin(), vec.end()) << std::endl;

  std::cout << "hex_ret=" << hex_encode(vec) << std::endl;
}

void test_attest_server() {
  ClientConfig client_config = ClientConfig::from_file(kClientConfigPath);
  g_logger->debug(to_string(client_config.attest_interval));
  g_logger->debug(client_config.root_key_config);
  g_logger->debug(client_config.log_dir);
  g_logger->debug(client_config.ra_url);
  g_logger->debug(client_config.bytedance_top_info);
  Client client = Client(client_config);
  client.attest_server();
}

void test_encrypt() {
  ClientConfig client_config = ClientConfig::from_file(kClientConfigPath);

  Client client = Client(client_config);
  client.attest_server();

  std::string plaintext = "我是待加密字符串";
  std::string ret = client.encrypt(plaintext);
  std::cout << "encrypt ret=" << ret << std::endl;
}

void test_request_1() {
  ClientConfig client_config = ClientConfig::from_file(kClientConfigPath);

  Client client = Client(client_config);
  std::string msg = "我是机密信息";
  std::string enc_msg = client.encrypt(msg);

  // 构造并发送网络请求
  httplib::Headers headers = {};
  nlohmann::json request = {{"enc_msg", enc_msg}};
  std::string request_body = request.dump();

  httplib::Client cli(kServerAddr);
  auto res =
      cli.Post("/request_example_1", headers, request_body, "application/json");
  if (!res) {
    std::cout << "Network error: " << httplib::to_string(res.error())
              << std::endl;
    return;
  }
  if (res->status != 200) {
    std::cout << "Response status: " << res->status << std::endl;
    return;
  }

  nlohmann::json response_json = nlohmann::json::parse(res->body);
  std::cout << "server res:" << response_json << std::endl;
}

void test_request_2() {
  ClientConfig client_config = ClientConfig::from_file(kClientConfigPath);

  Client client = Client(client_config);
  std::string msg = "我是机密信息";
  EncryptResult encrypt_info = client.encrypt_with_response(msg);

  // 构造并发送网络请求
  httplib::Headers headers = {};
  nlohmann::json request = {{"enc_msg", encrypt_info.ciphertext}};
  std::string request_body = request.dump();

  httplib::Client cli(kServerAddr);
  auto res =
      cli.Post("/request_example_1", headers, request_body, "application/json");
  if (!res) {
    std::cout << "Network error: " << httplib::to_string(res.error())
              << std::endl;
    return;
  }
  if (res->status != 200) {
    std::cout << "Response status: " << res->status << std::endl;
    return;
  }

  nlohmann::json response_json = nlohmann::json::parse(res->body);
  std::cout << "server res:" << response_json << std::endl;

  // 对服务端的应答中的加密信息进行解密
  std::string enc_res = response_json["msg"].get<std::string>();
  std::vector<uint8_t> enc_res_vect = base64_decode(enc_res);
  enc_res = std::string(enc_res_vect.begin(), enc_res_vect.end());
  std::cout << "res=" << enc_res << std::endl;
  std::vector<uint8_t> vec = encrypt_info.response_key.decrypt(enc_res);
  std::cout << "res=" << std::string(vec.begin(), vec.end()) << std::endl;
}

void test_sign() {
  ClientConfig client_config = ClientConfig::from_file(kClientConfigPath);

  Client client = Client(client_config);
  std::string msg = "我是机密信息";
  std::string enc_msg = client.encrypt(msg);

  // 构造并发送网络请求
  std::string app_info = "test_id_test_name";
  nlohmann::json request = {{"enc_msg", enc_msg}, {"app_info", app_info}};

  auto now = std::chrono::system_clock::now();
  auto timestamp =
      std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch())
          .count();
  std::string timestamp_str = std::to_string(timestamp);

  timestamp_str = "1739186828";
  std::string sign = client.gen_sign(app_info, timestamp_str);
  std::cout << "timestamp_str=" << timestamp_str << ", sign=" << sign
            << std::endl;

  httplib::Headers headers = {{"Timestamp", timestamp_str}, {"Sign", sign}};

  std::string request_body = request.dump();

  httplib::Client cli(kServerAddr);
  auto res =
      cli.Post("/sign_example_1", headers, request_body, "application/json");
  if (!res) {
    std::cout << "Network error: " << httplib::to_string(res.error())
              << std::endl;
    return;
  }
  if (res->status != 200) {
    std::cout << "Response status: " << res->status << std::endl;
    return;
  }

  nlohmann::json response_json = nlohmann::json::parse(res->body);
  std::cout << "Response: " << response_json << std::endl;
}

int main() {
  cout << "in jsc_client_cpp example" << endl;

  g_logger = new Log("log_name", "./test.log");
  g_logger->info("aaa");

  try {
    // test_utils();

    // test_attest_server();

    test_encrypt();

    test_request_1();

    test_request_2();

    test_sign();

  } catch (std::exception const &e) {
    std::cerr << e.what() << '\n';
  } catch (...) {
    std::cerr << "Unknown error\n";
  }
  return 0;
}

Java

数据加密

package com.bytedance.jeddak_secure_channel;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;

public class Example {
    public static void main(String[] args) throws IOException {
        if (args.length < 1) {
            System.err.println("Usage: jeddak_secure_channel.Example <path to client config>");
            return;
        }
        ClientConfig config = ClientConfig.fromFile(Paths.get(args[0]));
        try (Client client = new Client(config)) {
            client.attestServer("My_Secret_Token");

            String message = "Hello, World!";
            EncryptResult encrypted = client.encryptWithResponse(message);

            URL url = new URL("http://localhost:8080/request_example_2");
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();

            String response;
            try {
                connection.setRequestMethod("POST");
                connection.setRequestProperty("Content-Type", "text/plain");

                connection.setDoOutput(true);
                connection
                        .getOutputStream()
                        .write(encrypted.ciphertext.getBytes(StandardCharsets.UTF_8));

                Reader responseBody =
                        new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8);
                response = encrypted.responseKey.decryptString(responseBody);
            } finally {
                connection.disconnect();
            }

            System.out.println(response);
        }
    }
}

JS

数据加密

import { Client, ClientConfig, normalizeConfig } from "./client";
import { readFile } from "node:fs/promises";
import { join } from "node:path";

async function main() {
  const configContents = await readFile(
    join(__dirname, "../example/client_config.json"),
    "utf8",
  );
  const config: ClientConfig = normalizeConfig(JSON.parse(configContents));
  const client = new Client(config);

  await client.attestServer("My_Secret_Token");

  const message = "Hello, World!";
  const [encrypted, responseKey] = await client.encryptWithResponse(message);
  const response = await fetch("http://localhost:8080/request_example_2", {
    method: "POST",
    headers: {
      "Content-Type": "text/plain",
    },
    body: encrypted,
  });

  const decrypted = await responseKey.decrypt(await response.text());
  console.log(new TextDecoder().decode(decrypted));

  client.close();
}

main();

接收端调用SDK解密数据

Python

  1. 初始化SDK
  • 通过本地配置文件初始化
sdk_config_file_name = "./server_config.json"
secure_channel_config = jsc.ServerConfig.from_file(sdk_config_file_name)
secure_channel_server = jsc.Server(secure_channel_config)
  • 从环境变量中加载配置
env_conf_key = "***"
secure_channel_config = jsc.ServerConfig.from_env(env_conf_key)
secure_channel_server = jsc.Server(secure_channel_config)
  1. 数据解密
# 获取请求参数
params = request.get_json()

encrypt_msg = params.get("enc_msg")
plaintext, encrypt_key = secure_channel_server.decrypt_with_response(encrypt_msg)
plaintext = plaintext.decode()
print("收到明文数据:", plaintext)
  1. 解密客户端发过来的加密文件
# 获取请求参数
params = request.form

# save file
encrypted_path = "./src_encrypted_file"
file_1 = request.files["file1"]
file_1.save(encrypted_path)

# 解密文件
file_enc_key = params["file_enc_key"]  # 文件加密的密钥信息
output_path = "./des_plaintext_file"
secure_channel_server.decrypt_file(file_enc_key, encrypted_path, output_path, "t") #t 是对文本文件按行加密,b 是对二进制文件整体加密
  1. 流式请求处理
# 获取请求参数
params = request.get_json()

# 解密请求中的密文数据
encrypt_msg = params.get("query")
plaintext, encrypt_key = secure_channel_server.decrypt_with_response(encrypt_msg)
plaintext = plaintext.decode()
print("收到请求数据:", plaintext)

# 数据处理
res_data = "我是要返回给客户端的数据"

# 发送响应
def generate_blocks():
    for i in range(5):
        res_msg = encrypt_key.encrypt(res_data).encode()
        res_msg = base64.b64encode(res_msg).decode()
        yield f"{res_msg}\n"
        time.sleep(1)

return Response(generate_blocks(), status=200, mimetype="text/plain")
  1. 对客户端请求的签名进行验签,如为机密数据传输则无需关注此功能。
# 获取请求参数
app_info = params.get("app_info")
timestamp = headers.get("Timestamp")
sign = headers.get("Sign")

assert not secure_channel_server.verify_sign(app_info, timestamp, sign) == True