更新时间:2025-07-11 GMT+08:00
分享

Linux系统上QingTian Enclave应用的开发

QingTian Enclave SDK

QingTian Enclave SDK由一系列开源库组成,以便您开发自己的QingTian Enclave应用程序。其中包括QingTian安全模块(QingTian Security Module,QTSM)提供的qtsm-lib函数库。此外,SDK集成了KMS接口,该接口内置了获取证明文档及调用华为云KMS相关服务的功能。在典型使用案例里,我们描述了在QingTian Enclave调用KMS解密接口的示例。

表1 接口介绍

类型

接口

接口描述

libqtsm接口

qtsm_describe_pcr

查询指定index的PCR数据信息

qtsm_extend_pcr

扩展指定index对应的PCR值

qtsm_lock_pcr

锁定指定index的PCR数据信息

qtsm_lock_pcrs

批量锁定指定index对应的PCR值

qtsm_get_describe

获取qtsm信息

qtsm_get_attestation

获取Attestation Doc

qtsm_get_random

获取硬件随机数

KMS接口

kms_generate_datakey_blocking

生成新的密钥对,获取公钥与私钥

kms_generate_datakey_blocking_with_proxy

集成qtproxy代理的密钥对获取接口

kms_gen_random_blocking

获取随机数

kms_gen_random_blocking_with_proxy

集成qtproxy代理的随机数获取接口

kms_decrypt_data_blocking

解密数据

kms_decrypt_data_blocking_with_proxy

集成qtproxy代理的解密数据接口

源码可以在开源仓库https://gitee.com/HuaweiCloudDeveloper/huawei-qingtian/tree/master/enclave免费获取,您可以基于测试示例开发自己的QingTian Enclave应用程序。

Vsock通信示例

本节主要通过vsock示例来介绍如何在Linux环境下开发QingTian Enclave应用程序。本节中提供的vsock程序只支持在Linux环境下运行。

通过该vsock程序,可以帮助开发者了解到父虚拟机和QingTian Enclave间如何进行消息传递从而实现双方交互。该vsock程序带有Server和Client参数,通过指定该参数可以指定父虚拟机或者QingTian Enclave虚拟机扮演相应角色。在该vsock程序中,客户端程序会发送一段简单的文本信息通过vsock通道传递给服务端程序,同时服务端程序会监听相应的vsock通道,一旦收到来自客户端的消息后,会将其收到的消息打印在终端界面上。

在下面介绍中,我们将说明如何让QingTian Enclave扮演服务端等待并接收来自客户端父虚拟机的hello world信息:

  1. 编写一个SocketCommunication.py程序:
    #!/usr/local/env python3
    import argparse
    import socket
    import sys
    
    CID_DEFAULT = 3
    PORT_DEFAULT = 9999
    TIMEOUT = 5
    BLACKLOG_DEFAULT = 5
    
    class Client:
        def __init__(self, cid, port):
            self.clientAddr = (cid, port)
            self.connect()
    
        def connect(self):
            self.socket = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)
            self.socket.settimeout(TIMEOUT)
            print("connecting to the server")
            try:
                self.socket.connect(self.clientAddr)
            except socket.error:
                print("client's socket connection err")
                sys.exit(1)
    
        def send(self, msg):
            print("client sends hello to the server")
            self.socket.sendall(msg)
    
        def disconnect(self):
            self.socket.close()
    
        def receiveData(self):
            while True:
                try:
                    message = self.socket.recv().decode()
                except (socket.error, UnicodeDecodeError):
                    break
                if message:
                    print(message, end = " ", flush = True)
            print()
    
    def clientHandler(args):
        client = Client(args.cid, args.port)
        message = "Hello world"
        client.send(message.encode())
        client.disconnect()
    
    class Server:
        def __init__(self, port):
            self.socket = socket.socket(socket.AF_VSOCK, socket.SOCK_STREAM)
            self.serverAddr = (socket.VMADDR_CID_ANY, port)
            self.socket.bind(self.serverAddr)
            self.socket.listen(BLACKLOG_DEFAULT)
    
        def receiveData(self):
            while True:
                print("waiting for a connection")
                (conn, clientAddr) = self.socket.accept()
                try:
                    print("connection from ", clientAddr)
                    while True:
                        try: 
                            data = conn.recv(256).decode()
                        except (socket.error, UnicodeDecodeError):
                            break
                        if data:
                            print("data: ", data)
                        else:
                            print("connection close")
                            break
                finally:
                    conn.close()
    
    def serverHandler(args):
        server = Server(args.port)
        server.receiveData()
    
    
    def main():
        parser = argparse.ArgumentParser(description = "Hello world demo", prog='SocketCommunication')
        subparsers = parser.add_subparsers(description = "Communication roles")
        parserClient = subparsers.add_parser("Client", description = "Client",
                                            help = "Communicate with server using a given cid and port.")
        parserClient.add_argument("-c", "--cid", default = CID_DEFAULT, type = int, help = "Client's Cid")
        parserClient.add_argument("-p", "--port", default = PORT_DEFAULT, type = int, help = "Client's port")
        parserClient.set_defaults(func = clientHandler)
        parserServer = subparsers.add_parser("Server", description = "Server", help = "Listen on a given port")
        parserServer.add_argument("-p", "--port", default = PORT_DEFAULT, type = int, help = "Server's Port")
        parserServer.set_defaults(func = serverHandler)
        if len(sys.argv) < 2:
            parser.print_usage()
            sys.exit(1)
        args = parser.parse_args()
        args.func(args)
    
    
    if __name__ == "__main__":
        main()
  1. 创建一个Dockerfile文件,内容如下:
    #start the Docker image from ubuntu
    FROM ubuntu:22.04
    WORKDIR /home/builder
    # COPY vsocket example
    COPY . vsocket
    # install relative dependencies
    RUN apt-get update && \
        apt-get install python3 -y && \
        apt-get install gcc -y && \
        apt-get install gawk -y
    # Launch a client
    CMD ["python3", "/home/builder/vsocket/SocketCommunication.py","Server","-p 9999"]
  1. 构建docker镜像:
    sudo docker build -t vsock-sample-client -f Dockerfile .
  1. 将docker镜像转化为QingTian Enclave镜像:
    qt enclave make-img --docker-uri vsock-sample-client --eif vsock_sample.eif
  1. 使用vsock_sample.eif以debug模式启动QingTian Enclave:
    qt enclave start --cpus 2 --mem 4096 --eif vsock_sample.eif --debug-mode --cid 4
    然后使用qt enclave console命令查看QingTian Enclave内只读终端输出:
    qt enclave console --enclave-id 0
    waiting for a connection
  1. 然后另起一个父虚拟机终端,启动客户端程序:
    python3 SocketCommunication.py Client -c 4 -p 9999
  1. 当服务端程序收到来着vsock的消息后,会打印如下消息到终端上:
    connection from  (3, 4180219645)
    data:  Hello world
    connection close
    waiting for a connection

KMS接口调用和权限配置示例

本节主要基于开源示例代码来介绍如何在QingTian Enclave应用程序中调用KMS接口并配置权限。本节中提供的示例程序只支持在Linux环境下运行。

在配置前,请先购买一台C7t规格的弹性云服务器作为QingTian Enclave父虚拟机并完成相关配置,详细内容,请参见快速入门

  1. 从gitee仓库下载huawei-qingtian-enclave代码。

    本示例以qtsm-java-sdk为例进行介绍。

    1. 执行以下命令,下载huawei-qingtian-enclave代码。

      cd /home

      git clone https://gitee.com/heathjay/huawei-qingtian.git

      执行以下命令,切到newjay-java-sdk分支。

      cd /home/huawei-qingtian

      git checkout newjay-java-sdk

  2. 根据用例,获取相关参数。

    测试可参考:https://gitee.com/HuaweiCloudDeveloper/huawei-qingtian/pulls/21/files

    表2中获取的参数需填入测试文件文件中。

    表2 KMS接口参数

    参数名称

    含义

    获取方式

    ak

    Access Key ID

    详细内容,请参见API签名指南

    sk

    Secret Access Key

    project_id

    项目ID

    获取方法请参见获取项目ID

    key_id

    KMS中的keyID

    host

    KMS endpoint

  1. 通过IAM创建自定义身份策略。

    使用管理员权限账号创建自定义身份策略“enclave-test-kms-api”,详细操作,请参见创建自定义策略

    需要配置的自定义身份策略内容如下:

    {
      "Version": "5.0",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "kms::generateRandom",
            "kms:cmk:createDataKey",
            "kms:cmk:decryptData",
            "kms:cmk:decryptDataKey"
          ],
          "Condition": {
            "StringEqualsIgnoreCase": {
              "kms:RecipientAttestation/PCR0": [
                "8f2cbfb3930e59c6de5c4caff0a3f4c0457e8956bfb4556a7ca1f5f4614a741eeee39ae10447eb5baee48d49e6c1cb6c",
                "ff7ba807a385b49fc1c3346bb47215aef503dee6df22d32f733e22b90a9bc4b22424ca7de1a3537ac9608d7ebe461d67",
                "a28e765550d6ad1188860d30167b1fdb9e29c8da825543861bc76ef1e8427fac6b444ec6a1847fc2c22deae8170c2e67"
              ],
              "kms:RecipientAttestation/PCR8": [
                "a9add94b0ecbbd992baded2176370ecf3bfed2cb39b2ec547512b5174279799f2036fa0b8577bdaf503836178bd11ee2"
              ]
            }
          }
        },
        {
          "Effect": "Allow",
          "Action": [
            "kms:cmk:encryptData",
            "kms:cmk:encryptDataKey"
          ]
        }
      ]
    }

    其中,PCR0和PCR8的值需要在完成QingTian Enclave镜像制作后,进行配置。

  2. 创建IAM用户并授予权限。
    1. 在IAM控制台,单击“体验新版控制台”。
    2. 在“用户”页面创建IAM用户“enclave-test-kms-uers”并授予“enclave-test-kms-api”权限。
      图1 创建IAM用户(1)
      图2 创建IAM用户(2)
      图3 为用户授权
    3. 获取“enclave-test-kms-uers”用户的AK、SK。

      在用户的“安全设置”页签可新增并下载访问密钥,请参见创建IAM用户的访问密钥

      从下载的访问密钥文件中,可获取用户的AK、SK取值。

    4. 将AK、SK赋值到测试程序对应参数。

      测试程序:/home/huawei-qingtian/enclave/qtsm-sdk-java/kms-cms-java/com/huawei/src/test/TestKmsCmsProxy.java

      图4 赋值AK、SK
  3. 获取KMS的Endpoint节点。
    1. 通过地区和终端节点获取KMS在不同区域的终端节点。
    2. 将KMS的Endpoint赋值到文件huawei-qingtian/enclave/qtsm-sdk-java/kms-cms-java/com/huawei/src/test/TestKmsCmsProxy.java中对应的host字符串变量:
      图5 赋值Endpoint
  4. 获取项目ID。
    1. 通过控制台获取项目ID,详细操作,请参见获取项目ID
    2. 将获取的项目ID赋值到文件/home/huawei-qingtian/enclave/qtsm-sdk-java/kms-cms-java/com/huawei/src/test/TestKmsCmsProxy.java中对应的uriPrefix字符串变量:
      图6 赋值项目ID
  5. 获取密钥ID。
    1. 通过数据加密控制台,创建密钥并获取ID。详细操作,请参见创建自定义密钥
    2. 将获取的密钥ID赋值到文件/home/huawei-qingtian/enclave/qtsm-sdk-java/kms-cms-java/com/huawei/src/test/TestKmsCmsProxy.java中对应的uriPrefix字符串变量:
      图7 赋值密钥ID
    3. 使用密钥加密一段明文,例如“hello world!”,详细操作,请参见使用自定义密钥在线加解密小数据
    4. 将加密后的密文填写到文件/home/huawei-qingtian/enclave/qtsm-sdk-java/kms-cms-java/com/huawei/src/test/TestKmsCmsProxy.java中对应的dataInputStr中:
      图8 填写密文
  6. 进入到/home/huawei-qingtian/enclave/qtsm-sdk-java/kms-cms-java/scripts目录,执行sh build_image.sh脚本构建QingTian Enclave镜像。
    图9 执行脚本

    在该目录下有文件kms-demo.eif,包含pcr0和pcr8的值。

  7. 生成镜像签名密钥和公钥。
    openssl ecparam -out private-key.pem -name secp384r1 -genkey
    openssl req -new -key private-key.pem -out ssl.csr
    openssl x509 -req -days 365 -in ssl.csr -signkey private-key.pem -out server.pem
  8. 启动proxy工具。
    /usr/local/bin/qingtian/enclave/qt_proxy -l 8000 -a kms.ap-southeast-3.myhuaweicloud.com -p 443 &
  9. 使用nc-vsock工具登录Enclave,进行调试。
    1. 由于该用例的QingTian Enclave中启动了一个vsock的服务端(监听9999端口),所以可以从QingTian Enclave父虚拟机内通过vsock客户端登录到Enclave进行调试。
      huawei-qingtian/enclave/qtsm-sdk-java/kms-cms-java/scripts/build_kms_demo.sh
    2. 使用nc-vsock进入Enclave。
      /home/huawei-qingtian/nc-vsock/nc-vsock 4 9999
      图10 进入Enclave
    3. 在enclave中执行如下命令:
      cd /home/builder/enclave/qtsm-sdk-java/kms-cms-java/target
      # We can perform it manually
      java -cp .:../lib/lombok-1.18.26.jar:../lib/junit-4.13.1.jar -Djava.library.path=./lib com.huawei.src.test.TestKmsCmsProxy
      图11 报错信息

      执行命令后,

      • 若发现该用户无权限,如图11所示。需根据Enclave镜像的PCR0和PCR8修改自定义身份策略,执行步骤11.d
      • 若执行成功,执行步骤11.e
    4. (可选)修改自定义策略,大概30秒~90秒生效。
      执行命令后,若发现该用户无权限,如图11所示。需根据Enclave镜像的PCR0和PCR8修改自定义策略策略。
      图12 修改自定义策略
    5. 在enclave中重新执行如下命令,调试KMS接口:
      cd /home/builder/enclave/qtsm-sdk-java/kms-cms-java/target
      # We can perform it manually
      java -cp .:../lib/lombok-1.18.26.jar:../lib/junit-4.13.1.jar -Djava.library.path=./lib com.huawei.src.test.TestKmsCmsProxy
    6. KMS接口调试成功,查看调试结果。
      图13 解密接口
      图14 数据密钥接口
      图15 随机数接口

相关文档