SDK 使用指南

使用 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 算法加密并返回给客户端,客户端在本地解密密文获得计算结果,通过这种方式实现全链路隐私保护。

c3s.002
  • 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,服务平台无法获取用户隐私数据。

c3s.003
  • 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 算法的解密。支持数据一方加密后多方解密。

c3s.004
  • 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();