端云互信SDK是一套基于TEE构建的数据安全传输工具,旨在保障端侧应用与云上服务,或云上服务之间的互相信任和数据安全传输。
端云互信SDK分为数据发送端(client)和数据接收端(server)两部分,其使用场景如下图所示。
支持语言 | 下载地址 | 使用指引 |
---|---|---|
Python | 点击前往Python | |
C++ | 点击前往C++ | |
Java | 点击前往Java | |
JS | 点击前往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系统为应用自动创建的密钥,该密钥将应用于端云互信传输。
可在操作一栏对端云互信互信应用进行修改和删除,删除时需二次确认。
{ "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_attested_pods": [ { "cluster_id": "ccrqduf03q1qab0saeivg", "namespace": "default", "deployment": "jsc-server-example", "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_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)
secure_channel_client.attest_server()
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)
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)
src_file_name = "***" des_file_name = "***" enc_key = secure_channel_client.encrypt_file(src_file_name, des_file_name, "t") #t 是对文本文件按行加密,b 是对二进制文件整体加密
# 发送请求到服务端 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)
云云数据传输时或未启用签名时不需要关注该功能。
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) 。。。
代码说明
压缩包包含 sln、vcxproj 项目文件、jsc 文件夹(源代码)、example 文件夹(示例程序)、thirdparty-src 文件夹(3 个依赖库源代码)、thirdparty-build 文件夹(OpenSSL 依赖库编译产物)。
prebuild.ps1 脚本介绍:这个脚本主要是用来解压 thirdparty 并编译 OpenSSL(编译产物在 thirdparty-build/openssl...,编译主项目时会用到)用户如果不需要重新编译OpenSSL,则不需要使用此脚本。
代码编译
依赖:
编译产物:
数据加密
#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; }
数据加密
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); } } }
数据加密
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_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)
# 获取请求参数 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)
# 获取请求参数 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 是对二进制文件整体加密
# 获取请求参数 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")
# 获取请求参数 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