使用 C3S 服务,首先您需要通过编写 WASM 智能合约将业务逻辑写成可信计算应用 TAPP。TAPP 编写完成后,需要使用 MYCDT 编译工具将 TAPP 编译成 WASM 字节码。之后,通过 MYTF SDK 将 TAPP 安装上传至 MYTF 可信计算引擎中。最后,通过调用 TAPP 接口来执行 TAPP并获得执行结果。本文仅介绍如何使用 SDK 调用 TAPP ,并提供相应的示例代码以供参考。有关如何编写 TAPP,具体参考 可信计算应用开发。
调用可信计算应用分为以下四步:
搭建开发环境
使用 SDK 首先需要配置开发环境依赖,请检查并配置以下四项依赖:
JDK 1.8 及以上版本,可在终端运行
java -version
查看当前 Java 版本。Maven 3.5.4 及以上版本,在终端运行
mvn -v
查看当前 Maven 版本。Java 运行环境需要支持 BouncyCastle。
引用 SDK JAR 包并配置 SDK 间接依赖。
SDK 间接依赖如下所示:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.61</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
初始化配置信息
访问密钥
您可以获取阿里云账号的访问密钥(包括AccessKey ID 和 AccessKey Secret),用于 C3S 接口的访问。
// 模拟用户 accessId 和 accessPrivateKey
String userAccessId = "A...L";
String userAccessPrivateKey = "z...x";
配置信息
AliCloudClientConfig config = new AliCloudClientConfig();
config.setAccessId(accessId);
config.setAccessPrivateKey(accessPrivateKey);
config.setMytfTrustLevel(0);
// 选填,使用数据授权特性和可升级 TAPP 特性时需要,需要自己生成密钥并用密码加密
// 用户身份密钥绑定的 DID 标识
config.setIdentityPrivateKeyDIDIndex(userDID);
// 用户身份密钥,输入被加密的私钥全文
config.setIdentityPrivateKey(userIdentityPriKey);
// 用户身份密钥的加密密码
config.setIdentityPrivateKeyPW(password);
// 选填, SecretPrivateKey 用来保证用户请求时的端到端加密,如果不填客户端会自动生成
// 若自动生成,则在 sdk 重启时会被丢弃,可能会导致之前的请求结果无法解密。请注意此问题。
// 用户加密密钥,输入被加密的私钥全文
config.setSecretPrivateKey(userSecretPriKey);
// 用户加密密钥的加密密码
config.setSecretPrivateKeyPW(password2);
IAS 根证书
String trustedIASCert = "-----BEGIN CERTIFICATE-----\n"
+ "MIIFSzCCA7OgAwIBAgIJANEHdl0yo7CUMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV\n"
+ "BAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNV\n"
+ "BAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQDDCdJbnRlbCBTR1ggQXR0ZXN0\n"
+ "YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwIBcNMTYxMTE0MTUzNzMxWhgPMjA0OTEy\n"
+ "MzEyMzU5NTlaMH4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwL\n"
+ "U2FudGEgQ2xhcmExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQD\n"
+ "DCdJbnRlbCBTR1ggQXR0ZXN0YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwggGiMA0G\n"
+ "CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCfPGR+tXc8u1EtJzLA10Feu1Wg+p7e\n"
+ "LmSRmeaCHbkQ1TF3Nwl3RmpqXkeGzNLd69QUnWovYyVSndEMyYc3sHecGgfinEeh\n"
+ "rgBJSEdsSJ9FpaFdesjsxqzGRa20PYdnnfWcCTvFoulpbFR4VBuXnnVLVzkUvlXT\n"
+ "L/TAnd8nIZk0zZkFJ7P5LtePvykkar7LcSQO85wtcQe0R1Raf/sQ6wYKaKmFgCGe\n"
+ "NpEJUmg4ktal4qgIAxk+QHUxQE42sxViN5mqglB0QJdUot/o9a/V/mMeH8KvOAiQ\n"
+ "byinkNndn+Bgk5sSV5DFgF0DffVqmVMblt5p3jPtImzBIH0QQrXJq39AT8cRwP5H\n"
+ "afuVeLHcDsRp6hol4P+ZFIhu8mmbI1u0hH3W/0C2BuYXB5PC+5izFFh/nP0lc2Lf\n"
+ "6rELO9LZdnOhpL1ExFOq9H/B8tPQ84T3Sgb4nAifDabNt/zu6MmCGo5U8lwEFtGM\n"
+ "RoOaX4AS+909x00lYnmtwsDVWv9vBiJCXRsCAwEAAaOByTCBxjBgBgNVHR8EWTBX\n"
+ "MFWgU6BRhk9odHRwOi8vdHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9jb250ZW50\n"
+ "L0NSTC9TR1gvQXR0ZXN0YXRpb25SZXBvcnRTaWduaW5nQ0EuY3JsMB0GA1UdDgQW\n"
+ "BBR4Q3t2pn680K9+QjfrNXw7hwFRPDAfBgNVHSMEGDAWgBR4Q3t2pn680K9+Qjfr\n"
+ "NXw7hwFRPDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkq\n"
+ "hkiG9w0BAQsFAAOCAYEAeF8tYMXICvQqeXYQITkV2oLJsp6J4JAqJabHWxYJHGir\n"
+ "IEqucRiJSSx+HjIJEUVaj8E0QjEud6Y5lNmXlcjqRXaCPOqK0eGRz6hi+ripMtPZ\n"
+ "sFNaBwLQVV905SDjAzDzNIDnrcnXyB4gcDFCvwDFKKgLRjOB/WAqgscDUoGq5ZVi\n"
+ "zLUzTqiQPmULAQaB9c6Oti6snEFJiCQ67JLyW/E83/frzCmO5Ru6WjU4tmsmy8Ra\n"
+ "Ud4APK0wZTGtfPXU7w+IBdG5Ez0kE1qzxGQaL4gINJ1zMyleDnbuS8UicjJijvqA\n"
+ "152Sq049ESDz+1rRGc2NVEqh1KaGXmtXvqxXcTB+Ljy5Bw2ke0v8iGngFBPqCTVB\n"
+ "3op5KBG3RjbF6RRSzwzuWfL7QErNC8WEy5yDVARzTA5+xmBc388v9Dm21HGfcC8O\n"
+ "DD+gT9sSpssq0ascmvH49MOgjt1yoysLtdCtJW/9FZpoOypaHx0R+mJTLwPXVMrv\n"
+ "DaVzWh5aiEx+idkSGMnX\n"
+ "-----END CERTIFICATE-----";
安装可信应用
安装 TAPP 会将 TAPP 字节码部署到 MYTF 可信计算引擎中,用户需要指定 TappId 和 TappVersion 来唯一标识 TAPP。
// 启动client会获取 C3S 平台信息,安装和执行 TAPP 前必须要调用 startup() 接口
AliCloudMytfClient client = new AliCloudMytfClient(config);
Boolean ifSuccess = client.startup();
Assert.assertTrue(ifSuccess);
// 获取 MYTFInfo 信息
MYTFInfo mytfInfo = client.getMYTFInfo();
Assert.assertNotNull(mytfInfo);
// 安装 TAPP 前,准备 TAPP 信息,TEST_CONTRACT_FILE 为编译后的 TAPP 文件路径
String tappId = "example"+ new Random().nextInt(1000000);
Integer tappVersion = 2;
byte[] bytecodes = FileUtils.readFileToByteArray(new File(TEST_CONTRACT_FILE));
// 构造安装 TAPP 请求
TappInstallRequest tappInstallRequest = TappInstallRequest.builder()
.newTapp(tappId, tappVersion, bytecodes)
.build();
// 发送安装 TAPP 请求
TappInstallResponse tappInstallResponse = client.installTapp(tappInstallRequest);
System.out.println(tappInstallResponse);
Assert.assertTrue(tappInstallResponse.isSuccess());
// 查询安装过的 TAPP 信息并保存在本地,执行 TAPP 前必须要调用 getTappInfo() 接口
TappInfo tappInfo = client.getTappInfo(tappId, tappVersion);
Assert.assertNotNull(tappInfo);
执行可信应用
下面是使用 SDK 执行可信应用开发的参考示例,您可根据实际业务需求参考该示例进行开发。
普通执行请求
TAPP 接口示例
INTERFACE std::string TestSayHi() {
return "hi";
}
INTERFACE std::string TestRepeat(const std::string& msg) {
return msg;
}
INTERFACE uint32_t TestAdd(const uint32_t& num1, const uint32_t& num2) {
return num1 + num2;
}
INTERFACE std::vector<std::string> TestStringVector(const std::string& data) {
std::vector<std::string> ret;
int err_code = 2;
ret.push_back("OK");
ret.push_back("FAIL");
ret.push_back(data);
ret.push_back(std::to_string(err_code));
return ret;
}
INTERFACE std::string TestOutput(const std::string& data) {
std::string log_buffer = "log buffer";
log_buffer.append("1234");
log_buffer.append("abc");
Require(false, log_buffer.c_str());
return data;
}
SDK 使用示例
// 构造 TAPP 执行合约接口"TestSayHi",调用无参数的 TAPP 接口
String testReqMethod = "TestSayHi";
TappExecuteRequest tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.build();
// 发送 TAPP 执行请求
TappExecuteResponse tappExecuteResponse = client.executeTapp(tappExecuteRequest);
// 解析 TAPP 执行结果
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
System.out.println("result : " + tappExecuteResponse.getReturnValue().toUtf8String());
// 构造 TAPP 执行合约接口"TestAdd",调用有参数的 TAPP 接口
testReqMethod = "TestAdd";
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addUint32(BigInteger.valueOf(12))
.addUint32(BigInteger.valueOf(11))
.build();
// 发送 TAPP 执行请求
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
// 解析 TAPP 执行结果
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
System.out.println("result : " + tappExecuteResponse.getReturnValue().toUint32());
// 构造 TAPP 执行合约接口"TestStringVector",调用有参数的 TAPP 接口
testReqMethod = "TestStringVector";
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addString("{hello mytf!}")
.build();
// 发送 TAPP 执行请求
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
// 解析 TAPP 执行结果
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
System.out.println("result : " + tappExecuteResponse.getReturnValue().toUtf8StringList().toString());
// 构造 TAPP 执行合约接口"TestOutput",调用有参数的 TAPP 接口
String testOutputData = "abc";
testReqMethod = "TestOutput";
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addString(testOutputData)
.build();
// 发送 TAPP 执行请求
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
// 执行 TAPP 异常,查看执行异常信息
System.out.println("responseCode: " + tappExecuteResponse.getResponseCode());
System.out.println("output: " + tappExecuteResponse.getOutputString());
端到端加密请求
用户提交可信计算执行任务时,可以选择端到端加密发送。SDK 会将用户在本地构造的执行请求通过 ECIES 算法进行加密,并将加密后的请求发送给 MYTF。MYTF 接收到请求后会在可信执行环境内将密文解密并进行计算。计算完成后,MYTF 将结果同样通过 ECIES 算法加密并返回给客户端,客户端在本地解密密文获得计算结果,通过这种方式实现全链路隐私保护。
SDK 使用示例
// 构造执行 TAPP 请求
testReqMethod = "TestRepeat";
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addString("hello mytf!")
.build();
// 发送端到端加密的执行请求
tappExecuteResponse = client.executeTappPrivately(tappExecuteRequest);
// 检查是否执行成功
Assert.assertNotNull(tappExecuteResponse);
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
// 获取执行结果
Assert.assertEquals("hello mytf!", tappExecuteResponse.getReturnValue().toUtf8String());
格式转换工具
TAPP 接口示例
// 使用 rapidjson 第三方库对 json 进行构造和解析
INTERFACE std::string TestJson() {
using namespace::rapidjson;
std::vector<std::string> ret;
std::string stringFromStream = R"({
"dictVersion": 1,
"content":
[
{"key": "word1", "value": "单词1"} ,
{"key": "word2", "value": "单词2"} ,
{"key": "word5", "value": "单词3"}
]
})";
// ---------------------------- read json --------------------
// parse json from string.
using rapidjson::Document;
Document doc;
doc.Parse<0>(stringFromStream.c_str());
if (doc.HasParseError()) {
rapidjson::ParseErrorCode code = doc.GetParseError();
Require(code == kParseErrorNone, "parse from string");
}
// use values in parse result.
using rapidjson::Value;
Value& v = doc["dictVersion"];
if (v.IsInt()) {
print("%d\n", v.GetInt());
}
Value& contents = doc["content"];
if (contents.IsArray()) {
for (size_t i = 0; i < contents.Size(); ++i) {
Value& v = contents[i];
Require(v.IsObject(), "parse error");
if (v.HasMember("key") && v["key"].IsString()) {
print("%s\n", v["key"].GetString());
}
if (v.HasMember("value") && v["value"].IsString()) {
print("%s\n", v["value"].GetString());
}
}
}
// ---------------------------- write json --------------------
print("add a value into array\n");
Value item(Type::kObjectType);
item.AddMember("key", "word2", doc.GetAllocator());
item.AddMember("value", "单词2", doc.GetAllocator());
contents.PushBack(item, doc.GetAllocator());
// convert dom to string.
StringBuffer buffer; // in rapidjson/stringbuffer.h
Writer<StringBuffer> writer(buffer); // in rapidjson/writer.h
doc.Accept(writer);
print("%s\n", buffer.GetString());
ret.push_back(buffer.GetString());
// ---------------------------- add member to new json --------------------
rapidjson::Document doc1;
doc1.SetObject();
rapidjson::Document::AllocatorType &allocator = doc1.GetAllocator();
rapidjson::Value data_a_json;
rapidjson::Value data_b_json;
std::string a = "abc";
std::string b = "bcd";
data_a_json.SetString(a.c_str(), (int) a.size(), allocator);
data_b_json.SetString(b.c_str(), (int) b.size(), allocator);
doc1.AddMember("new_member_a", data_a_json, allocator);
doc1.AddMember("new_member_b", data_a_json, allocator);
// add item to array
rapidjson::Value items(rapidjson::kArrayType);
int item_count = 10;
std::string item = "cde";
for(int i = 0; i < item_count; ++i) {
rapidjson::Value value;
value.SetString(item.c_str(), item.length(), allocator);
items.PushBack(value, allocator);
}
doc1.AddMember("items", items, allocator);
StringBuffer buffer2;
Writer<StringBuffer> writer2(buffer2);
doc1.Accept(writer2);
ret.push_back(buffer2.GetString().c_str());
ret.push_back("OK");
return ret;
}
INTERFACE std::string TestBase64Encode(std::string data) {
std::string out;
CryptoErrorCode err = Base64Encode(data, out);
if (err != CryptoErrorCode::kSuccess) {
print("failed to base64encode: %d", err);
return std::to_string(err);
}
return out;
}
INTERFACE std::string TestBase64Decode(std::string data) {
std::string out;
CryptoErrorCode err = Base64Decode(data, out);
if (err != CryptoErrorCode::kSuccess) {
print("failed to base64decode: %d", err);
return std::to_string(err);
}
return out;
}
SDK 使用示例
// 测试Base64编码解码
// 构造执行 TAPP 请求,调用 TAPP 接口 "TestBase64Encode"
String plaindata = "this is test for Base64Encode & Base64Decode";
testReqMethod = "TestBase64Encode";
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addString(plaindata)
.build();
// 发送 TAPP 执行请求
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
// 解析 TAPP 执行结果
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
String base64String = tappExecuteResponse.getReturnValue().toUtf8String();
System.out.println("plaindata: " + new String(Base64.decode(base64String)));
// 构造执行 TAPP 请求,调用 TAPP 接口 "TestBase64Decode"
testReqMethod = "TestBase64Decode";
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addString(base64String)
.build();
// 发送 TAPP 执行请求
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
// 解析 TAPP 执行结果
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
System.out.println("plaindata: " + tappExecuteResponse.getReturnValue().toUtf8String());
散列函数
TAPP 接口示例
INTERFACE std::vector<std::string> TestHash(std::string msg) {
std::vector<std::string> result;
std::string sha256hash;
CryptoErrorCode err = Sha256(msg, sha256hash);
if (err != CryptoErrorCode::kSuccess) {
print("failed to sha256: %d", err);
result.push_back(std::to_string(err));
return result;
}
result.push_back(Bin2Hex(sha256hash));
std::string sm3hash;
err = Sm3_256(msg, sm3hash);
if (err != CryptoErrorCode::kSuccess) {
print("failed to SM3_256: %d", err);
result.push_back(std::to_string(err));
return result;
}
print("sm3_256 hash: %s", Bin2Hex(sm3hash).c_str());
result.push_back(Bin2Hex(sm3hash));
return result;
}
INTERFACE std::vector<std::string> TestHMAC(std::string msg, std::string secret) {
std::vector<std::string> result;
std::string hmac_sha256_out;
CryptoErrorCode err = Hmac(MdType::kSha256, msg, secret, hmac_sha256_out);
if (err != CryptoErrorCode::kSuccess) {
print("failed to do hmac: %d", err);
result.push_back(std::to_string(err));
return result;
}
result.push_back(Bin2Hex(hmac_sha256_out));
std::string hmac_sha1_out;
err = Hmac(MdType::kSha1, msg, secret, hmac_sha1_out);
if (err != CryptoErrorCode::kSuccess) {
print("failed to do hmac: %d", err);
result.push_back(std::to_string(err));
return result;
}
result.push_back(Bin2Hex(hmac_sha1_out));
return result;
}
SDK 使用示例
// 测试哈希算法
// 本地计算Sha256hash和Sm3hash
String plaindata = "this is test for hash";
byte[] expectedSha256hash = Hash.sha256(plaindata.getBytes());
byte[] expectedSm3hash = Hash.sm3_256(plaindata.getBytes());
// 构造执行 TAPP 请求,调用 TAPP 接口 "TestHash"
testReqMethod = "TestHash";
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addBytes(plaindata.getBytes())
.build();
// 发送 TAPP 执行请求
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
// 解析 TAPP 执行结果
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
System.out.println("plaindata: " + Hex.toHexString(plaindata.getBytes()));
System.out.println("expectedSha256hash: " + Hex.toHexString(expectedSha256hash));
System.out.println("sha256Hash: " + tappExecuteResponse.getReturnValue().toUtf8StringList().get(0));
System.out.println("expectedSm3hash: " + Hex.toHexString(expectedSm3hash));
System.out.println("sm3256hash: " + tappExecuteResponse.getReturnValue().toUtf8StringList().get(1));
// 测试HMAC算法
plaindata = "this is a test for hmac";
String secret = "12345678";
// 本地计算HmacSha256和HmacSha1
byte[] expectedHmacSha256 = Hash.hmac_sha256(plaindata.getBytes(), secret.getBytes());
byte[] expectedHmacSha1 = Hash.hmac_sha1(plaindata.getBytes(), secret.getBytes());
// 构造执行 TAPP 请求,调用 TAPP 接口 "TestHMAC"
testReqMethod = "TestHMAC";
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addBytes(plaindata.getBytes())
.addBytes(secret.getBytes())
.build();
// 发送 TAPP 执行请求
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
// 解析 TAPP 执行结果
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
System.out.println("Hmacsha256 : " + tappExecuteResponse.getReturnValue().toUtf8StringList().get(0));
System.out.println("expectedHmacsha256: " + Hex.toHexString(expectedHmacSha256));
System.out.println("Hmacsha1 : " + tappExecuteResponse.getReturnValue().toUtf8StringList().get(1));
System.out.println("expectedHmacsha1: " + Hex.toHexString(expectedHmacSha1));
RSA 签名和验签
TAPP 内部可以使用该函数进行 RSA 签名和验签。
TAPP 接口示例
INTERFACE std::string TestRsaSign(std::string prikey, std::string digest_name, std::string data) {
std::string signature;
int res_code = RsaSign(prikey, digest_name, data, signature);
if(res_code != 1){
print("Failed to sign data by RSA: %d", res_code);
}
return signature;
}
INTERFACE int TestRsaVerify(std::string pubkey, std::string digest_name, std::string data, std::string signature) {
return RsaVerify(pubkey, digest_name, data, signature);
}
SDK 使用示例
// 测试RSA签名和验签
String plainData = "this is test for rsaSign & rsaVerify";
UserKeyPair userKeyPair = UserKeyFactory.generateKeyPair(KeyTypeEnum.RSA_4096_KEY);
byte[] sig = RSATool.RSASign(plainData.getBytes(), userKeyPair.getPrivateKey().getEncoded());
Assert.assertTrue(RSATool.RSAVerify(plainData.getBytes(), sig, userKeyPair.getPublicKey().getEncoded()));
String priKey = Base64.toBase64String(userKeyPair.getPrivateKey().getEncoded());
String pubKey = Base64.toBase64String(userKeyPair.getPublicKey().getEncoded());
String RSAPriKeyPEM = CryptoUtils.priKey2PemString(userKeyPair.getPrivateKey());
//构造 TAPP 执行请求,测试 TAPP "TestRsaSign" 接口
String testReqMethod = "TestRsaSign";
TappExecuteRequest tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addString(RSAPriKeyPEM)
.addString("SHA256")
.addString(plainData)
.build();
//发送 TAPP 执行请求
TappExecuteResponse tappExecuteResponse = client.executeTapp(tappExecuteRequest);
//解析 TAPP 执行响应
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
String signature = tappExecuteResponse.getReturnValue().toUtf8String();
System.out.println("signature: " + signature);
Assert.assertEquals(Base64.toBase64String(sig), signature);
//构造 TAPP 执行请求,测试 TAPP "TestRsaVerify" 接口
testReqMethod = "TestRsaVerify";
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addString(pubKey)
.addString("SHA256")
.addString(plainData)
.addString(Base64.toBase64String(sig))
.build();
//发送 TAPP 执行请求
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
//解析 TAPP 执行响应
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
Assert.assertEquals(1, tappExecuteResponse.getReturnValue().toInt32());
TAPP ECDSA 签名
TAPP 接口示例
INTERFACE std::vector<std::string> TestTappEcdsaSign(std::string data) {
std::vector<std::string> ret;
std::string signature;
int err_code = 1;
err_code = TappEcdsaSign(data, signature, KEY_ALGO_TYPE::ECDSA_RAW_SECP256K1_KEY);
if(err_code != 1){
ret.push_back(std::to_string(err_code));
return ret;
}
std::string base64_sig;
err_code = Base64Encode(signature, base64_sig);
if(err_code != 1){
ret.push_back(std::to_string(err_code));
return ret;
}
ret.push_back(std::to_string(err_code));
ret.push_back(base64_sig);
return ret;
}
SDK 使用示例
//构造 TAPP 执行请求,测试 TAPP "TestTappEcdsaSign" 接口
String plainData = "this is test for TappECDSASign";
testReqMethod = "TestTappEcdsaSign";
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addString(plainData)
.build();
//发送 TAPP 执行请求
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
//解析 TAPP 执行响应
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
String errorCode = tappExecuteResponse.getReturnValue().toUtf8StringList().get(0);
Assert.assertTrue(errorCode.equals("1"));
String ecdsaSignature = tappExecuteResponse.getReturnValue().toUtf8StringList().get(1);
byte[] tappSignPK = tappInfo.getVerificationKeys().get(KeyTypeEnum.ECDSA_RAW_SECP256K1_KEY);
System.out.println("signature: " + Hex.toHexString(Base64.decode(ecdsaSignature)));
System.out.println("verify ret: "+ ECDSATool.ECDSAVerify(plainData.getBytes(), tappSignPK, Base64.decode(ecdsaSignature)));
Envelope 信封加密解密
TAPP 内部可以使用该函数进行基于 ECIES-SECP256K1 算法的加密解密。支持多方用户数据加密后,由第三方服务平台融合多方加密数据并请求执行 TAPP,服务平台无法获取用户隐私数据。
TAPP 接口示例
INTERFACE std::string TestEnvelopeOpen(const std::string& enc_data) {
std::string plain_data;
int res_code = EnvelopeOpen(enc_data, plain_data);
if(res_code != 1){
print("Failed to open envelope: %d", res_code);
plain_data = "Failed";
}
return plain_data;
}
INTERFACE std::string TestEnvelopeBuild(const std::string& plain_data, const std::string& pk) {
std::string ret_envelope;
int res_code = EnvelopeBuild(pk, plain_data, ret_envelope);
if(res_code != 1){
print("Failed to open envelope: %d", res_code);
ret_envelope = "Failed";
}
return ret_envelope;
}
SDK 使用示例
// 获取 TAPP 信息
TappInfo tappInfo = client.getTappInfo(tappId, tappVersion);
// 本地重新生成新的 EC 公私钥对,构造密文信封
String plainData = "this is test for envelopeOpen & envelopeBuild";
UserKeyPair eciesKey = UserKeyFactory.generateKeyPair(KeyTypeEnum.ECIES_SECP256K1_KEY);
byte[] tappEnvelope = EnvelopeUtils.buildTappEnvelope(tappInfo.getEncryptionKeys().get(KeyTypeEnum.ECIES_SECP256K1_KEY), eciesKey.getPrivateKey().getEncoded(), plainData.getBytes());
// 构造 TAPP 执行请求, 测试 TAPP 'TestEnvelopeOpen' 接口
String testReqMethod = "TestEnvelopeOpen";
TappExecuteRequest tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addBytes(tappEnvelope)
.build();
// 发送 TAPP 执行请求,合约内执行 tapp 信封解密
TappExecuteResponse tappExecuteResponse = client.executeTapp(tappExecuteRequest);
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
// 校验本地明文是否与合约内解密的明文相等
Assert.assertEquals(plainData, tappExecuteResponse.getReturnValue().toUtf8String());
// 构造密文信封
tappEnvelope = EnvelopeUtils.buildTappEnvelope(tappInfo, eciesKey, plainData.getBytes()).tlvEncode();
// 构造 TAPP 执行请求, 测试 TAPP 'TestEnvelopeOpen' 接口
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addBytes(tappEnvelope)
.build();
// 发送 TAPP 执行请求,合约内执行 tapp 信封解密
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
Assert.assertEquals(plainData, tappExecuteResponse.getReturnValue().toUtf8String());
// 使用本地SDK生成的隐私密钥构造密文信封
tappEnvelope = client.getKeyStore().buildTappEnvelope(tappInfo, plainData.getBytes());
// 构造 TAPP 执行请求, 测试 TAPP 'TestEnvelopeOpen' 接口
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addBytes(tappEnvelope)
.build();
// 发送 TAPP 执行请求
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
Assert.assertEquals(plainData, tappExecuteResponse.getReturnValue().toUtf8String());
// 构造 TAPP 执行请求, 测试 TAPP 'TestEnvelopeBuild' 接口
testReqMethod = "TestEnvelopeBuild";
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addBytes(plainData.getBytes())
.addBytes(eciesKey.getPublicKey().getEncoded())
.build();
// 发送 TAPP 执行请求
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
// 解析 TAPP 执行结果
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
byte[] tappExecutedEnvelope = tappExecuteResponse.getReturnValue().toBytes();
byte[] envelopeRecoverPlainData = EnvelopeUtils.openTappEnvelope(tappInfo.getEncryptionKeys().get(KeyTypeEnum.ECIES_SECP256K1_KEY), eciesKey.getPrivateKey().getEncoded(), tappExecutedEnvelope);
Assert.assertTrue(Arrays.equals(plainData.getBytes(), envelopeRecoverPlainData));
// 构造 TAPP 执行请求, 测试 TAPP 'TestEnvelopeBuild' 接口
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addBytes(plainData.getBytes())
.addBytes(client.getKeyStore().getSecretKeypair().getPublicKey().getEncoded())
.build();
// 发送 TAPP 执行请求
tappExecuteResponse = client.executeTapp(tappExecuteRequest);
// 解析 TAPP 执行结果
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
tappExecutedEnvelope = tappExecuteResponse.getReturnValue().toBytes();
envelopeRecoverPlainData = client.getKeyStore().openTappEnvelope(tappInfo, tappExecutedEnvelope);
Assert.assertTrue(Arrays.equals(plainData.getBytes(), envelopeRecoverPlainData));
ECElgamalEnvelope 信封解密
TAPP 内部可以使用该函数进行基于 ECElgamal-SECP256K1 算法的解密。支持数据一方加密后多方解密。
TAPP 接口示例
INTERFACE std::string TestEcElgamalEnvelopeOpen(std::string prikey, std::string cipher_data) {
uint32_t curve_type = 0;
std::string plain_data;
int res_code = ECElgamalEnvelopeOpen(curve_type, prikey, cipher_data, plain_data);
if(res_code != 1){
print("Failed to open envelope: %d", res_code);
plain_data = "Failed";
}
return plain_data;
}
INTERFACE uint32_t TestEcElgamalEnvelopeBatchOpen(std::string enc_prikey, std::vector<std::string> cipher_data_batch) {
uint32_t curve_type = 0;
uint32_t success_count = 0;
std::string plain_data;
std::string plain_prikey;
int res_code = EnvelopeOpen(enc_prikey, plain_prikey);
if(res_code != 1){
print("Failed to open prikey envelope: %d", res_code);
return 0;
}
for(auto cipher_data: cipher_data_batch) {
if(cipher_data==""){
continue;
}
int res_code = ECElgamalEnvelopeOpen(curve_type, plain_prikey, Hex2Bin(cipher_data), plain_data);
if(res_code != 1){
print("Failed to ecelgamal open envelope: %d", res_code);
success_count++;
}
}
return success_count;
}
SDK 使用示例
// 本地构造公私钥对、明文 plainBytes
byte[] plainBytes = "this is test for ecElgamalEnvelopeOpen".getBytes();
int pkSize = 3;
String[] publicKeys = new String[pkSize];
UserKeyPair user1Keypair = UserKeyFactory.generateKeyPair(KeyTypeEnum.ECELGAMAL_SECP256K1_KEY);
UserKeyPair user2Keypair = UserKeyFactory.generateKeyPair(KeyTypeEnum.ECELGAMAL_SECP256K1_KEY);
UserKeyPair user3Keypair = UserKeyFactory.generateKeyPair(KeyTypeEnum.ECELGAMAL_SECP256K1_KEY);
publicKeys[0] = Base64.toBase64String(user1Keypair.getRawPublicKey());
publicKeys[1] = Base64.toBase64String(user2Keypair.getRawPublicKey());
publicKeys[2] = Base64.toBase64String(user3Keypair.getRawPublicKey());
// 本地对明文数据进行 ECElgamalEncrypt 加密
byte[] ciphertext = ECElgamalTool.ECElgamalEncrypt(CryptoSuiteTypeEnum.SECP256K1, publicKeys, plainBytes);
System.out.println("ciphertext: " + Hex.toHexString(ciphertext));
byte[] plaintext = ECElgamalTool.ECElgamalDecrypt(CryptoSuiteTypeEnum.SECP256K1, user3Keypair.getRawPrivateKey(), ciphertext);
System.out.println("recovered plaintext:" + Hex.toHexString(plaintext));
// 构造执行 TAPP 请求
String testReqMethod = "TestEcElgamalEnvelopeOpen";
tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.addBytes(user3Keypair.getRawPrivateKey())
.addBytes(ciphertext)
.build();
// 发送执行 TAPP 请求
TappExecuteResponse tappExecuteResponse = client.executeTapp(tappExecuteRequest);
// 解析 TAPP 执行结果
Assert.assertTrue(tappExecuteResponse.isRequestSuccess());
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
assertEquals(plainBytes, tappExecuteResponse.getReturnValue().toBytes());
TAPP LOG
Tapp 中可以通过 API LOG_DEBUG 等打印信息,调用者可以查看日志信息。SDK 中相关的操作包括:安装时指定Tapp 打印的日志级别,执行 tapp 后查看其中的 tapp。TAPP LOG每行长度限制512,超长会截断。
String testReqMethod = "TestLog";
byte[] bytecodes = FileUtils.readFileToByteArray(new File(TEST_CONTRACT_FILE));
// 安装 tapp,设置日志级别为INFO
// 那么在 tapp 执行过程中,低于 INFO 级别的LOG(如 LOG_DEBUG)将不会生效
TappInstallRequest tappInstallRequest = TappInstallRequest.builder()
.newTapp(tappId, tappVersion, bytecodes)
.setTappLogLevel(TappLogLevelEnum.TAPP_LOG_INFO)
.upgradeable()
.build();
TappInstallResponse tappInstallResponse = client.installTapp(tappInstallRequest);
System.out.println(tappInstallResponse);
Assert.assertTrue(tappInstallResponse.isSuccess());
// 检验 tappInfo 中包含设置的 log level: info
TappInfo tappInfo = client.getTappInfo(tappId, tappVersion);
Assert.assertNotNull(tappInfo);
Assert.assertEquals(tappInfo.getTappProperty().getTappLogLevel(), TappLogLevelEnum.TAPP_LOG_INFO);
// 再次执行,会得到 info level 级别的 log
TappExecuteRequest tappExecuteRequest = TappExecuteRequest.builder()
.defaultRequest(tappId, tappVersion, testReqMethod)
.build();
TappExecuteResponse tappExecuteResponse = client.executeTapp(tappExecuteRequest);
Assert.assertTrue(tappExecuteResponse.isExecuteSuccess());
String logs = tappExecuteResponse.getTappLog();