---
title: 免加密卡，鲲鹏TEE内生国密高性价比参考实践-官方技术文章-鲲鹏社区
description: 鲲鹏安全计算密码模块是基于华为自研可信执行环境操作系统（Trusted Execution Environment OS， TEE OS）提供的一个密码模块套件，该模块分为CA/TA两部分，TA部分内置于TEE OS中，CA部分包括API接口库和密钥管理工具，旨在为安全业务提供便捷、安全的密码算法能力。 本密码模块符合GM/T 0018-2012和2023标准，为公钥密码基础设施应用中的通用密码服
keywords: 免加密卡,TEE,内生国密高性,价比参考实践,官方技术文章,鲲鹏社区,非商用声明,方案介绍
url: https://www.hikunpeng.com/developer/techArticles/20260310-1?envFlag=1
section: (其他)
---

# 免加密卡，鲲鹏TEE内生国密高性价比参考实践-官方技术文章-鲲鹏社区

URL: https://www.hikunpeng.com/developer/techArticles/20260310-1?envFlag=1
描述: 鲲鹏安全计算密码模块是基于华为自研可信执行环境操作系统（Trusted Execution Environment OS， TEE OS）提供的一个密码模块套件，该模块分为CA/TA两部分，TA部分内置于TEE OS中，CA部分包括API接口库和密钥管理工具，旨在为安全业务提供便捷、安全的密码算法能力。 本密码模块符合GM/T 0018-2012和2023标准，为公钥密码基础设施应用中的通用密码服
关键词: 免加密卡,TEE,内生国密高性,价比参考实践,官方技术文章,鲲鹏社区,非商用声明,方案介绍

官方技术文章 [了解详情](https://www.hikunpeng.com/zh/developer/techArticles)

免加密卡，鲲鹏TEE内生国密高性价比参考实践

免加密卡，鲲鹏TEE内生国密高性价比参考实践

案例分享安装部署鲲鹏处理器TEE

发表于 2026/03/10

0

# 1 非商用声明

该文档提供的内容为参考实践，仅供用户参考使用，用户可参考实践文档构建自己的软件，按需进行安全、可靠性加固，但不建议直接将相关Demo或镜像文件集成到商用产品中。

# 2 方案介绍

## 2.1 背景介绍

数字化转型背景下，数据加密已成为各行业安全运营的核心要求，广泛适用于通用数据防护、VPN 网关链路安全、证券交易、银行与监管机构（人行、证监）数据交互及应用核心数据加密等场景。此类场景需要保障数据机密性与完整性的同时，亦需满足国家密码标准的合规要求。

长期以来，上述国密合规加密需求主要依赖外置密码卡、密码机等硬件设备实现，但传统方案存在显著痛点：一是部署成本高，设备采购、适配及后期维护投入大；二是集成复杂度高，需定制化开发适配业务系统，影响上线效率；三是扩展灵活性不足，难以快速响应业务扩容需求；四是运维管理繁琐，需专业团队保障设备运行与密钥管理。

在此背景下，市场亟需一种兼具国密合规性、高性价比与便捷部署特性的加密解决方案，突破传统外置硬件设备的应用局限，满足各行业数字化转型中对安全、高效、低成本加密技术的迫切需求。

## 2.2 方案简介

### 概述

鲲鹏安全计算密码模块是基于华为自研可信执行环境操作系统（Trusted Execution Environment OS， TEE OS）提供的一个密码模块套件，该模块分为CA/TA两部分，TA部分内置于TEE OS中，CA部分包括API接口库和密钥管理工具，旨在为安全业务提供便捷、安全的密码算法能力。

### 方案优势

本密码模块符合GM/T 0018-2012和2023标准，已通过国密二级认证要求，为公钥密码基础设施应用中的通用密码服务层提供基础密码服务，包括密钥生成，单一的密码运算，文件管理等服务。本密码设备内嵌于服务器，可在不新增密码卡、密码机的前提下提供标准密码接口，具有高可靠、低成本、高易用的特点。

### 软件架构

TEE OS是基于TrustZone架构的可信安全隔离执行环境，密码模块管理单元随TEE OS启动而加载运行。密码运算运行在安全侧，REE侧提供标准SDF接口支持和调用。

图1鲲鹏安全计算密码模块架构图

libsdf.so：部署在REE侧，提供标准的SDF接口。将密码应用的密码运算请求转发到TEE OS上的密码模块管理单元中运行。

华为TEE OS架构详见系统架构 [了解详情](https://www.hikunpeng.com/document/detail/zh/kunpengcctrustzone/trustzone/twp/kunpengtrustzone_19_0007.html)。

### 原理描述

鲲鹏安全计算密码模块运行在华为自研的安全TEE OS基础之上，REE侧提供符合GM/T 0018-2012和2023标准规范的接口库文件libsdf.so，用户应用调用libsdf.so接口后，请求被转发到TEE侧，由密码模块管理单元进行处理，算法服务单元基于TEE OS底层实现具体的密码运算，安全存储服务单元提供数据加密保存。

图2SDF架构原理

# 3 使用指导

## 3.1 环境要求

鲲鹏安全计算密码模块支持的机型及配套版本如表1 硬件环境要求 [了解详情](https://www.hikunpeng.com/#table4542192101610)所示。

表1硬件环境要求

| 配套项 | 说明 |
| --- | ---|
| CPU | 鲲鹏920新型号处理器（ 7270Z、5253Z、5252Z、5235Z、5230Z等） |
| BIOS固件 | 21.23版本及以上获取链接 |
| BMC固件 | 5.05.12.11版本及以上获取链接 |
| CCOS固件 | 1.0版本及以上，请参见表 软件包获取获取TEE OS hpm固件包解压后获取该固件包，固件包名称为ccos_image_*.hpm。 |
| License支持 | 需要激活Kunpeng TEE Function |
| 操作系统 | 内核版本4.19、5.10的openEuler和麒麟系统 |

相关软件包获取方式如表2 软件包获取所示。

表2软件包获取

| 软件包名称 | 版本（分支）要求 | 软件包说明 | 获取方法 |
| --- | --- | --- | ---|
| itrustee_tzdriver | iTrustee_7.9.1_release | iTrustee REE侧Patch包源码。 | 获取链接 |
| itrustee_client | iTrustee_7.9.1_release | iTrustee REE侧Patch包源码。 | 获取链接 |
| libboundscheck | master | 内存边界检查的工具库。 | 获取链接 |
| BoostKit-boostcc-ccos-1.2.zip | 1.2 | TEE OS hpm固件包，适用于鲲鹏主板S920X20机型升级。 | 获取链接 |

## 3.2部署流程

请参考鲲鹏BoostKit机密计算>商密应用>开发指南>准备环境章节:链接 [了解详情](https://www.hikunpeng.com/document/detail/zh/kunpengcctrustzone/cca/devg/Kunpeng_ommercialcryptography_16_0011.html)

## 3.3接口说明及示例

请参考鲲鹏BoostKit机密计算>商密应用>开发指南章节：链接 [了解详情](https://www.hikunpeng.com/document/detail/zh/kunpengcctrustzone/cca/devg/Kunpeng_ommercialcryptography_16_0026.html)

# 4 典型场景参考实践

## 4.1银行与三方机构数据交互场景

### 4.1.1场景需求

该场景使用物理机部署TEE套件并且使用SM4算法进行加解密开发。

### 4.1.2环境信息说明

本参考实践的调优验证所涉及的详细软硬件信息如表1 软件信息和表2 硬件信息。

表1软件信息

| 类别 | 软件版本 |
| --- | ---|
| OS | OpenEuler 22.03 LTS-SP4 |
| BMC固件 | 5.05.12.25 |
| TEE OS固件 | 1.2 |
| BIOS固件 | 21.23 |
| itrustee_tzdriver | 7.9.1 |
| itrustee_client | 7.9.1 |

表2硬件信息

| 类别 | 硬件描述 |
| --- | ---|
| CPU | 鲲鹏7270z |
| 内存 | 384GB |
| 硬盘 | 4*1.5TB NVMe硬盘 |
| 主板 | S920X20 |

### 4.1.3部署流程

部署流程图如下：

#### 4.1.3.1导入liscense

TEE特性启用受License控制，本章节主要介绍如何在iBMC导入License。

在特性使用前请确认iBMC许可证管理界面中“Kunpeng TEE Function”和“Kunpeng accelerator* Function”已处于开启状态。

说明：如果测试使用需要申请License，请联系华为业务负责人申请。对于商业项目使用，需要购买TEE License

步骤1登录iBMC网页，iBMC管理>许可证信息>设备ESN码。

将设备ESN码反馈给华为业务负责人并获取License文件

步骤2登录iBMC网页，iBMC管理>许可证信息。

在未导入License的情况下，Kunpeng TEE Function和Kunpeng accelerator* Function处于“已”状态。

步骤3单击“安装”，选择获取的License。

成功导入License后，Kunpeng TEE Function和Kunpeng accelerator*处于"已开启"状态，并显示对应截止日期。

----结束

#### 4.1.3.2修改服务器BIOS配置

当前TEE与BIOS特性"超线程"，"内存3路通道交织"不兼容，且TEE默认是的。若需使用TEE，则需在BIOS菜单中以上特性并打开"TEE Support"特性。

步骤1进入iBMC首页>虚拟控制台>启动虚拟控制台

步骤2重启服务器，按照提示按"Del"后输入BIOS密码进入BIOS界面。

步骤33路通道交织。

1. 在BIOS界面中依次选择Advanced>Memory Configuration，按"Enter"键进入Memory Configuration界面。

2. 在Memory Configuration界面，选择"Channel Interleaving 3Way"，将其设置为"Disabled"。

步骤4SMT2。

1. 在BIOS界面中依次单击"Advanced>Power And Performance Configuration>CPU PM Control"，按"Enter"键进入CPU PM Control界面。

2. 在CPU PM Control界面将"SMT2"设置为"Disabled"。

步骤5打开TEE。

1. 在BIOS界面中依次单击"Security>TEE Configuration"，进入TEE Configuration界面。

2. 在TEE Configuration界面中选择"Support TEE"，并将其设置为"Enabled"。

3. TEE Memoty Size按照实际业务进行设置，本参考实践及对应应用场景该配置设置为128GB。

----结束

#### 4.1.3.3配置内生国密运行环境

步骤1获取itrustee_client源码。

```
git clone https://gitcode.com/openeuler/itrustee_client.git -b iTrustee_7.9.1_release
```

步骤2获取itrustee_tzdriver源码。

```
git clone https://gitcode.com/openeuler/itrustee_tzdriver.git -b iTrustee_7.9.1_release
```

步骤3获取libboundscheck源码。

```
git clone https://gitcode.com/openeuler/libboundscheck.git
```

步骤4将libboundscheck源码分别复制到“itrustee_client/”目录和“itrustee_tzdriver/”目录下，用于itrustee_tzdriver，itrustee_client中的makefile编译时候的调用。

```
cp -rf libboundscheck itrustee_client
cp -rf libboundscheck itrustee_tzdriver
```

步骤5安装编译依赖。

```
yum install openssl-devel zlib-devel kernel-devel-$(uname -r)
```

步骤6编译itrustee_tzdriver。

```
cd itrustee_tzdriver && make CPU_GROUP_BINDING=y
```

步骤7在编译tzdriver后，将tzdriver.ko复制到指定目录。

```
mkdir -p "/lib/modules/$(uname -r)/kernel/drivers/trustzone/"
cp tzdriver.ko /lib/modules/$(uname -r)/kernel/drivers/trustzone
```

步骤8编译itrustee_client。

```
cd ..
cd itrustee_client
make
make install
```

步骤9根据获取链接 [了解详情](https://www.hikunpeng.com/zh/developer/download?title=%E6%9C%BA%E5%AF%86%E8%AE%A1%E7%AE%97&subTitle=CCOS)获取BoostKit-boostcc-ccos-1.2.zip，解压zip包后获得hpm固件、kunpeng_sec_drv.sec和sdf-utils*.rpm。

步骤10安装sdf-utils。

```
rpm -ivh sdf-utils*.rpm
```

步骤11升级ccos固件，将解压后的ccos_image_1.2.hpm上传到iBMC进行固件升级。

步骤12部署SEC驱动文件。

```
mkdir -p /var/itrustee/tee_dynamic_drv/crypto/
cp kunpeng_sec_drv.sec /var/itrustee/tee_dynamic_drv/crypto/
```

步骤13加载tzdriver.ko。

```
insmod /lib/modules/$(uname -r)/kernel/drivers/trustzone/tzdriver.ko
```

步骤14运行teecd进程。

```
nohup /usr/bin/teecd &
```

----结束

#### 4.1.3.4初始化内置密钥

用户应用调用libsdf.so接口访问内置密钥前需要先生成内置密钥。

为了方便用户创建和管理内置密钥，该模块提供了密钥管理工具，支持密钥创建，删除，修改、查询，导入，导出，恢复出厂设置等。

以下是密钥创建的流程展示：

步骤1使用sudo执行，进入密钥管理工具首页。

```
sudo /usr/bin/tee_key_manage 
```

步骤2可按上下方向键将箭头移动到想要执行的选项上，然后按“Enter”键选中进入。

- 密钥创建演示。以加密密钥为例，选择“Create Key>Encryption Key”后输入下标和密码。

说明：创建内置密钥时会对密码复杂度进行校验，需要至少包含大写字母、小写字母、数字、特殊字符中的至少两种，长度大于8位。为提升安全性，密码需要定期修改。

----结束

说明：若内置密钥配置成功则说明SDF接口调用成功，内生国密开发运行环境配置完成。

#### 4.1.3.5常见报错及解决

问题1：银河麒麟V10SP3 执行insmod /lib/modules/$(uname -r)/kernel/drivers/trustzone/tzdriver.ko指令报错insmod: ERROR: could not insert module /lib/modules/4.19.90-89.11.v2401.ky10.aarch64/kernel/drivers/trustzone/tzdriver.ko: Unknown symbol in module

原因：查看dmesg日志

[11328.394516] tzdriver: Unknown symbol __stack_chk_guard (err -2)

[11328.394580] tzdriver: Unknown symbol __stack_chk_fail (err -2)

该问题实际是编译环境与运行环境在堆栈保护机制上的不匹配导致：tzdriver.ko模块在编译时启用了堆栈保护，但当前运行的内核环境中没有导出这两个符号，导致模块加载失败。

解决方案：进入到itrustee_tzdriver以及itrustee_client目录，编辑Makefile文件，查询-fstack-protector-strong选项并去除，去除后重新编译

问题2：执行加载tzdriver.ko步骤报错

[root@host30 c00666192]# insmod /lib/modules/$(uname -r)/kernel/drivers/trustzone/tzdriver.ko

insmod: ERROR: could not insert module /lib/modules/5.10.0-216.0.0.115.oe2203sp4.aarch64/kernel/drivers/trustzone/tzdriver.ko: Operation not permitted

原因：固件/CCOS等固件版本不配套

解决方案：需要升级BIOS固件版本至21.23，获取链接 [了解详情](https://support.huawei.com/enterprise/zh/software/263636300-ESW2001500002)

需要升级ccos固件版本至1.2，获取链接 [了解详情](https://www.hikunpeng.com/zh/developer/download?title=%E6%9C%BA%E5%AF%86%E8%AE%A1%E7%AE%97&subTitle=CCOS)

直接在BMC首页点击固件升级（使用每个包后缀为.hpm的文件进行升级），进入升级页面

### 4.1.4开发示例

#### 接口调用示例

如下提供示例代码演示了接口的调用，以SM4加密密钥为例，包括密钥的创建、密钥标识码的修改、密钥访问权限路径添加与删除、导出导入密钥，删除密钥。

示例1代码：

```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/sha.h>
#include "sdf.h"
#define MAX_KEY_FILE_SIZE (1024 * 1024)
#define EXPORT_SALT "123456789"
#define CA_PATH "./test_sdf"
#define USER_NAME "root"
#define PASSWD "Huawei123." //密码用于测试使用
#define NEWPASSWD "Huawei321." //密码用于测试使用
#define ALG_FLAG_OFFSET 20
int generate_sm4_key(void *session, unsigned int key_id, unsigned char *password) {
    int ret;
    // SM4加密密钥
    unsigned int enc_info = SDF_SM4 << ALG_FLAG_OFFSET | key_id;
    ret = ECM_GenerateKey(session, enc_info, password, strlen(password));
    if (ret != SDR_OK) {
        printf("ECM_GenerateKey SM4 key failed, ret = 0x%x\n", ret);
        return ret;
    }
    return ret;
}
int delete_sm4_key(void *session, unsigned int key_id) {
    int ret;
    unsigned int enc_info = SDF_SM4 << ALG_FLAG_OFFSET | key_id;
    ret = ECM_DeleteKey(session, enc_info);
    if (ret != SDR_OK) {
        printf("ECM_DeleteKey SM4 key_index %d failed, ret = 0x%x\n", key_id, ret);
    }
    return ret;
}
int export_import_sm4_key(void *session, unsigned int key_id1, unsigned int key_id2) {
    int ret = -1;
    uint8_t *key_data = NULL;
    uint32_t key_data_len = MAX_KEY_FILE_SIZE;
    unsigned int enc_key_info1 = SDF_SM4 << ALG_FLAG_OFFSET | key_id1;
    unsigned int enc_key_info2 = SDF_SM4 << ALG_FLAG_OFFSET | key_id2;
    
    key_data = (uint8_t *)malloc(key_data_len);
    if (key_data == NULL) {
        printf("malloc failed\n");
        return -1;
    }
    
    // 导出导入SM4加密密钥
    ret = ECM_ExportKey(session, enc_key_info1, key_data, &key_data_len, EXPORT_SALT);
    if (ret) {
        printf("ECM_ExportKey SM4 key failed, ret = 0x%x, %d\n", ret, key_data_len);
        goto free;
    }
    
    ret = ECM_ImportKey(session, enc_key_info2, key_data, key_data_len, EXPORT_SALT);
    if (ret) {
        printf("ECM_ImportKey SM4 key failed, ret = 0x%x\n", ret);
        goto free;
    }
    
free:
    free(key_data);
    return ret;
}
int change_sm4_passwd(void *session, unsigned int key_id, char *passwd, char *new_passwd) {
    int ret;
    unsigned int enc_info = SDF_SM4 << ALG_FLAG_OFFSET | key_id;
    ret = ECM_ChangePassword(session, enc_info, passwd, strlen(passwd), new_passwd, strlen(new_passwd));
    if (ret != SDR_OK) {
        printf("ECM_ChangePassword SM4 key failed, ret = 0x%x\n", ret);
    }
    return ret;
}
void calculate_cainfo(const char *ca_path, const char *user_name, unsigned char hash[32]) {
    char plan[256] = {0};
    memcpy(plan, user_name, strlen(user_name));
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, ca_path, strlen(ca_path));
    SHA256_Update(&sha256, plan, 256);
    SHA256_Final(hash, &sha256);
    for (size_t i = 0; i < 32; i++) {
        printf("%02x", hash[i]);
    }
    printf("\n");
}
int main() {
    int ret = -1;
    int key_idx1 = 106, key_idx2 = 102; // SM4密钥索引建议使用101-200范围
    void *device = NULL, *session = NULL;
    
    ret = SDF_OpenDevice(&device);
    if (ret != SDR_OK) {
        printf("sdf open device failed, ret = 0x%x\n", ret);
        return ret;
    }
    
    ret = ECM_OpenSession(device, &session);
    if (ret != SDR_OK) {
        printf("sdf open session failed, ret = 0x%x\n", ret);
        goto close;
    }
    
    // 生成SM4密钥
    ret = generate_sm4_key(session, key_idx1, PASSWD);
    if (ret != SDR_OK) {
        printf("generate_sm4_key failed, ret = 0x%x\n", ret);
        goto close;
    }
    printf("generate SM4 key success\n");
    
    // 修改SM4密钥密码
    ret = change_sm4_passwd(session, key_idx1, PASSWD, NEWPASSWD);
    if (ret != SDR_OK) {
        printf("change SM4 key passwd failed, ret = 0x%x\n", ret);
        goto close;
    }
    printf("change SM4 key passwd success\n");
    
    // 导出导入SM4密钥
    ret = export_import_sm4_key(session, key_idx1, key_idx2);
    if (ret != SDR_OK) {
        printf("export_import_sm4_key failed, ret = 0x%x\n", ret);
        goto close;
    }
    printf("export SM4 key %d, import to key %d success\n", key_idx1, key_idx2);
    
    // 删除SM4密钥
    ret = delete_sm4_key(session, key_idx1);
    if (ret != SDR_OK) {
        printf("delete SM4 key %d failed, ret = 0x%x\n", key_idx1, ret);
        goto close;
    }
    
    ret = delete_sm4_key(session, key_idx2);
    if (ret != SDR_OK) {
        printf("delete SM4 key %d failed, ret = 0x%x\n", key_idx2, ret);
        goto close;
    }
    printf("delete SM4 key success\n");

    // 重新设置SM4密钥
    // 将下面代码注释去掉后，可以再次生成SM4密钥，可以用于示例2代码中使用SM4算法的接口调用，用于SM4算法单包加解密，多包加解密等操作
    /* ret = generate_sm4_key(session, key_idx1, PASSWD);
    if (ret != SDR_OK) {
        printf("generate_sm4_key failed, ret = 0x%x\n", ret);
        goto close;
    }
    printf("generate SM4 key success\n"); */
    
close:
    if (session != NULL) {
        (void)SDF_CloseSession(session);
    }
    if (device != NULL) {
        (void)SDF_CloseDevice(device);
    }
    return ret;
}
```

如下提供示例代码演示了接口的调用，以SM4算法为例，包括了设备和会话管理，单包加解密，多包加解密等操作

示例2代码：

```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include "sdf.h"
// 配置参数结构体
typedef struct {
    int jobs;          // 进程数
    int threads;       // 线程数
    char* work_dir;    // 结果输出目录
    int time_limit;    // 压测时长(秒)
    int data_size;     // 数据处理长度(bytes)
    unsigned int alg_id; // 算法ID
    unsigned int key_index; // 密钥索引
    char* password;    // 密钥密码
} Config;
// 线程参数结构体
typedef struct {
    Config* config;
    int thread_id;
} ThreadArgs;
// 生成随机数据
void generate_random_data(unsigned char* data, int size) {
    for(int i = 0; i < size; i++) {
        data[i] = rand() % 256;
    }
}
// 初始化SDF会话
static int init_sdf_session(void** hDevice, void** hSession) {
    int ret = SDF_OpenDevice(hDevice);
    if (ret != SDR_OK) return ret;
    return SDF_OpenSession(*hDevice, hSession);
}
// 释放SDF资源
static void cleanup_sdf(void* hSession, void* hDevice) {
    if (hSession) SDF_CloseSession(hSession);
    if (hDevice) SDF_CloseDevice(hDevice);
}
// 获取密钥权限
static int get_key_access(void* hSession, unsigned int keyIndex, const char* password) {
    return SDF_GetKEKAccessRight(hSession, keyIndex, 
                               (unsigned char*)password, strlen(password));
}
// 生成密钥
static int generate_key(void* hSession, unsigned int keyBits, 
                       unsigned int algId, unsigned int kekIndex,
                       unsigned char* keyData, unsigned int* keyLength,
                       void** hKey) {
    return SDF_GenerateKeyWithKEK(hSession, keyBits, algId, 
                                 kekIndex, keyData, keyLength, hKey);
}
// 单包加解密测试
void* single_packet_test(void* arg) {
    ThreadArgs* args = (ThreadArgs*)arg;
    Config* config = args->config;
    
    printf("Thread %d: 开始单包加解密测试 [数据大小: %d字节]\n", 
           args->thread_id, config->data_size);
    
    srand(time(NULL));
    
    unsigned char* data = malloc(config->data_size);
    unsigned char* enc_data = malloc(config->data_size);
    unsigned char* dec_data = malloc(config->data_size);
    
    generate_random_data(data, config->data_size);
    
    void* hDevice = NULL;
    void* hSession = NULL;
    void* hKey = NULL;
    int ret = init_sdf_session(&hDevice, &hSession);
    if (ret != SDR_OK) goto cleanup;
    
    ret = get_key_access(hSession, config->key_index, config->password);
    if (ret != SDR_OK) {
        printf("Thread %d: 获取密钥权限失败, 错误码: 0x%x\n", args->thread_id, ret);
        goto cleanup;
    }
    
    unsigned char keyData[16];
    unsigned int keyLength = sizeof(keyData);
    ret = generate_key(hSession, 128, config->alg_id, 106, 
                      keyData, &keyLength, &hKey);
    if (ret != SDR_OK || hKey == NULL) {
        printf("Thread %d: 生成密钥失败\n", args->thread_id);
        goto cleanup;
    }
    
    unsigned char iv[16] = {0};  // 直接声明并初始化
    unsigned char* pIV = (config->alg_id == SGD_SM4_CBC) ? iv : NULL;
    unsigned int enc_data_len = config->data_size;
    
    ret = SDF_Encrypt(hSession, hKey, config->alg_id, iv, 
                     data, config->data_size, enc_data, &enc_data_len);
    if (ret != SDR_OK) {
        printf("Thread %d: 加密失败, 错误码: 0x%x\n", args->thread_id, ret);
        goto cleanup;
    }
    
    unsigned int dec_data_len = config->data_size;
    ret = SDF_Decrypt(hSession, hKey, config->alg_id, iv, 
                     enc_data, enc_data_len, dec_data, &dec_data_len);
    if (ret != SDR_OK) {
        printf("Thread %d: 解密失败, 错误码: 0x%x\n", args->thread_id, ret);
        goto cleanup;
    }
    
    printf("Thread %d: %s\n", args->thread_id, 
          memcmp(data, dec_data, config->data_size) == 0 ? 
          "单包加解密测试成功" : "解密结果不匹配");
    
cleanup:
    free(data);
    free(enc_data);
    free(dec_data);
    if (hKey) SDF_DestroyKey(hSession, hKey);
    cleanup_sdf(hSession, hDevice);
    return NULL;
}
// 多包加解密测试
void* multi_packet_test(void* arg) {
    ThreadArgs* args = (ThreadArgs*)arg;
    Config* config = args->config;
    
    printf("Thread %d: 开始多包加解密测试 [数据大小: %d字节]\n", 
           args->thread_id, config->data_size);
    
    srand(time(NULL));
    
    unsigned char* data = malloc(config->data_size);
    unsigned char* enc_data = malloc(config->data_size * 2);
    unsigned char* dec_data = malloc(config->data_size);
    unsigned char iv[16] = {0};
    
    generate_random_data(data, config->data_size);
    
    void* hDevice = NULL;
    void* hSession = NULL;
    void* hKey = NULL;
    int ret = init_sdf_session(&hDevice, &hSession);
    if (ret != SDR_OK) goto cleanup;
    
    ret = get_key_access(hSession, config->key_index, config->password);
    if (ret != SDR_OK) {
        printf("Thread %d: 获取密钥权限失败, 错误码: 0x%x\n", args->thread_id, ret);
        goto cleanup;
    }
    
    unsigned char keyData[16];
    unsigned int keyLength = sizeof(keyData);
    ret = generate_key(hSession, 128, config->alg_id, 106, 
                      keyData, &keyLength, &hKey);
    if (ret != SDR_OK || hKey == NULL) {
        printf("Thread %d: 生成密钥失败\n", args->thread_id);
        goto cleanup;
    }
    
    // 加密处理
    unsigned int total_enc_len = 0;
    unsigned int chunk_size = config->data_size / 4;
    
    ret = SDF_EncryptInit(hSession, hKey, config->alg_id, iv, sizeof(iv));
    if (ret != SDR_OK) {
        printf("Thread %d: 加密初始化失败, 错误码: 0x%x\n", args->thread_id, ret);
        goto cleanup;
    }
    
    for (int i = 0; i < 4; i++) {
        unsigned int chunk_enc_len = chunk_size;
        ret = SDF_EncryptUpdate(hSession, data + i * chunk_size, 
                               chunk_size, enc_data + total_enc_len, 
                               &chunk_enc_len);
        if (ret != SDR_OK) {
            printf("Thread %d: 加密块%d失败, 错误码: 0x%x\n", args->thread_id, i+1, ret);
            goto cleanup;
        }
        total_enc_len += chunk_enc_len;
    }
    
    unsigned int last_enc_len = 0;
    ret = SDF_EncryptFinal(hSession, enc_data + total_enc_len, &last_enc_len);
    if (ret != SDR_OK) {
        printf("Thread %d: 最后加密块失败, 错误码: 0x%x\n", args->thread_id, ret);
        goto cleanup;
    }
    total_enc_len += last_enc_len;
    
    // 解密处理
    unsigned int total_dec_len = 0;
    
    ret = SDF_DecryptInit(hSession, hKey, config->alg_id, iv, sizeof(iv));
    if (ret != SDR_OK) {
        printf("Thread %d: 解密初始化失败, 错误码: 0x%x\n", args->thread_id, ret);
        goto cleanup;
    }
    
    for (int i = 0; i < 4; i++) {
        unsigned int chunk_dec_len = chunk_size;
        ret = SDF_DecryptUpdate(hSession, enc_data + i * chunk_size, 
                               chunk_size, dec_data + total_dec_len, 
                               &chunk_dec_len);
        if (ret != SDR_OK) {
            printf("Thread %d: 解密块%d失败, 错误码: 0x%x\n", args->thread_id, i+1, ret);
            goto cleanup;
        }
        total_dec_len += chunk_dec_len;
    }
    
    unsigned int last_dec_len = 0;
    ret = SDF_DecryptFinal(hSession, dec_data + total_dec_len, &last_dec_len);
    if (ret != SDR_OK) {
        printf("Thread %d: 最后解密块失败, 错误码: 0x%x\n", args->thread_id, ret);
        goto cleanup;
    }
    total_dec_len += last_dec_len;
    
    printf("Thread %d: %s\n", args->thread_id, 
          memcmp(data, dec_data, config->data_size) == 0 ? 
          "多包加解密测试成功" : "解密结果不匹配");
    
cleanup:
    free(data);
    free(enc_data);
    free(dec_data);
    if (hKey) SDF_DestroyKey(hSession, hKey);
    cleanup_sdf(hSession, hDevice);
    return NULL;
}
int main(int argc, char** argv) {
    Config config = {
        .data_size = 1024,
        .time_limit = 30,
        .threads = 1,
        .key_index = 106,
        .password = "Huawei123.",
        .alg_id = SGD_SM4_ECB
    };
    pthread_t thread;
    ThreadArgs args = {&config, 0};
    
    // 执行单包测试
    pthread_create(&thread, NULL, single_packet_test, &args);
    pthread_join(thread, NULL);
    
    // 执行多包测试
    pthread_create(&thread, NULL, multi_packet_test, &args);
    pthread_join(thread, NULL);
    
    return 0;
}
```

#### 测试代码执行示例

步骤1下载本模块的接口头文件。

```
git clone https://gitcode.com/openeuler/itrustee_sdk.git
```

步骤2在代码中引用sdf.h头文件，即可进行应用开发，参考上文示例。

步骤3完毕后可参考如下命令编译：本实践接口头文件所在目录为/home/itrustee_sdk/include/SDF，可根据下载头文件时的目录替换成实际路径。

```
gcc -o sm4_create_password sm4_create_password.c -I /home/itrustee_sdk/include/SDF/ -lsdf  -lcrypto #示例1 参考编译指令
gcc -o sm4_test sm4_test.c -I /home/itrustee_sdk/include/SDF/ -lsdf -ldl -lpthread  #示例2 参考编译指令
```

步骤4执行应用，可成功执行SM4算法的加解密，签名验证等测试。

```
./sm4_test
```

----结束

注意：使用SM4算法的接口调用的时候，要注意使用的索引跟密钥是对应SM4算法的而非SM2算法的，这里有两种设置索引以及密钥的方式

方式1：参考3.1.4开发示例中"示例1代码"进行设置，将"重新设置SM4密钥"下面代码注释去掉并运行示例1代码，即可完成SM4密钥的设置。

方式2：参考3.1.3.4初始化内置密钥，执行sudo /usr/bin/tee_key_manage命令进入密钥管理工具首页后，选择"Create Key>Encryption Key" 进行设置

## 4.2 金融安全网关

### 4.2.1场景需求

该场景使用虚拟机部署TEE套件并且使用SM2算法进行加解密开发。

### 4.2.2环境信息说明

本参考实践的调优验证所涉及的详细软硬件信息如表1 软件信息和表2 硬件信息。

表1软件信息

| 类别 | 软件版本 |
| --- | ---|
| 物理机OS | OpenEuler 22.03 LTS-SP4 |
| 虚拟机OS | OpenEuler 22.03 LTS-SP4 |
| BMC固件 | 5.05.12.25 |
| TEE OS固件 | 1.2 |
| BIOS固件 | 21.23 |
| itrustee_tzdriver | 7.9.1 |
| itrustee_client | 7.9.1 |
| qemu | 6.2.0 |
| tee-gp-proxy | 源码tag 25.3.0 |

表2硬件信息

| 类别 | 硬件描述 |
| --- | ---|
| CPU | 鲲鹏7270Z |
| 内存 | 384GB |
| 硬盘 | 4*1.5TB NVMe硬盘 |
| 主板 | S920X20 |

### 4.2.3部署流程

部署流程图如下：

#### 4.2.3.1导入license

请参考章节3.1.3.1 导入license

#### 4.2.3.2修改服务器BIOS配置

请参考章节3.1.3.2修改服务器BIOS配置

#### 4.2.3.3物理机环境搭建

步骤1安装依赖

```
yum install git gcc patch make  kernel-devel-$(uname -r) ninja-build 
yum install glib2 glib2-devel pixman-devel 
yum install openssl-devel zlib-devel
yum install libxml2-devel libvirt-devel
```

步骤2编译libboundscheck

获取libboundscheck源码

```
git clone https://gitcode.com/openeuler/libboundscheck.git
```

步骤3编译vtzb_proxy

获取vtzb_proxy源码

```
git clone -b 25.3.0 https://gitcode.com/openeuler/tee-gp-proxy.git #使用25.3.0版本与下文itrustee_tzdriver,itrustee_client 7.9.1版本进行配套
cp -rf libboundscheck tee-gp-proxy/trustzone-awared-vm/Host/vtzb_proxy
```

【注意】此处tee-gp-proxy使用tag为25.3.0的版本，与下文itrustee_tzdriver，itrustee_client源码分支iTrustee_7.9.1_release配套使用

修改tee-gp-proxy/trustzone-awared-vm/Host/vtzb_proxy/serial_port.h中的SERIAL_PORT_NUM参数，默认值为64，修改成实际串口数量，一个VM对应一个虚拟串口

```
#define SERIAL_PORT_NUM			 15
```

编译tee-gp-proxy

```
cd tee-gp-proxy/trustzone-awared-vm/Host/vtzb_proxy 
make 
sudo cp ./vtz_proxy /usr/bin/vtz_proxy
```

步骤4编译安装itrustee_tzdriver

获取itrustee_tzdriver源码

```
git clone https://gitcode.com/openeuler/itrustee_tzdriver.git -b iTrustee_7.9.1_release
```

进入itrustee_tzdriver的根目录，使能补丁文件，补丁文件路径按照实际路径修改

```
cd itrustee_tzdriver
git am ../tee-gp-proxy/trustzone-awared-vm/Host/tzdriver-0001-support-virtual-machine.patch 
git am ../tee-gp-proxy/trustzone-awared-vm/Host/tzdriver-0002-support-get-uid-from-vm.patch 
```

回到上层目录，将libboundscheck源码复制到“itrustee_tzdriver/”目录下，用于itrustee_tzdriver中打补丁后的makefile编译时候的调用

```
cd ..
cp -rf libboundscheck itrustee_tzdriver
```

编译itrustee_tzdriver。

```
cd itrustee_tzdriver && make CPU_GROUP_BINDING=y
```

在编译tzdriver后，将tzdriver.ko复制到指定目录。

```
mkdir -p "/lib/modules/$(uname -r)/kernel/drivers/trustzone/"
cp tzdriver.ko /lib/modules/$(uname -r)/kernel/drivers/trustzone
```

步骤5编译安装itrustee_client

获取itrustee_client源码

```
git clone https://gitcode.com/openeuler/itrustee_client.git -b iTrustee_7.9.1_release
```

进入itrustee_client的根目录，使能补丁文件，补丁文件路径按照实际路径修改

```
cd itrustee_client
git am ../tee-gp-proxy/trustzone-awared-vm/Host/client-0001-add-vm-uid-in-TC_NS_ClientContext.patch
```

回到上层目录，将libboundscheck源码复制到“itrustee_client/”目录下，用于itrustee_client中打补丁后的makefile编译时候的调用

```
cd ..
cp -rf libboundscheck itrustee_client
```

编译itrustee_client。

```
cd itrustee_client
make
make install
```

步骤6根据获取链接 [了解详情](https://www.hikunpeng.com/zh/developer/download?title=%E6%9C%BA%E5%AF%86%E8%AE%A1%E7%AE%97&subTitle=CCOS)获取BoostKit-boostcc-ccos-1.2.zip，解压zip包后获得hpm固件、kunpeng_sec_drv.sec和sdf-utils*.rpm

步骤7安装sdf-utils。

```
rpm -ivh sdf-utils*.rpm
```

步骤8升级ccos固件，将解压后的ccos_image_1.2.hpm上传到iBMC进行固件升级

步骤9部署SEC驱动文件

```
mkdir -p /var/itrustee/tee_dynamic_drv/crypto/
cp kunpeng_sec_drv.sec /var/itrustee/tee_dynamic_drv/crypto/
```

----结束

#### 4.2.3.4虚拟机环境搭建

以下步骤以采用开源OS虚拟化平台为示例，对于使用超融合虚拟化平台需要有一定的适配工作，详见下文章节中的"注意"部分。

#### 4.2.3.4.1编译qemu

#### 注意：此处若使用超融合平台部署虚拟化则需要超融合厂商进行适配，使用源码编译打补丁的方式编译安装qemu

步骤1获取qemu v6.2.0源码

```
git clone -b v6.2.0 https://git.qemu.org/git/qemu.git
```

若gitlab源下载缓慢，则可以考虑使用gitee源进行下载

```
git clone -b v6.2.0 https://gitee.com/nothing-wrong/qemu.git
```

由于编译的时候代码会主动使用gitlab的源进行拉取其他模块，因此手动将.gitmodules文件中的克隆地址换成gitee源的地址再编译。

以下文本为替换源后的.gitmodules文件文本，复制代码后将其重命名为.gitmodules，上传到qemu文件夹下替换原来的.gitmodules文件

```
[submodule "roms/seabios"]
	path = roms/seabios
	url = https://gitee.com/nothing-wrong/seabios.git
[submodule "roms/SLOF"]
	path = roms/SLOF
	url = https://gitee.com/nothing-wrong/SLOF.git
[submodule "roms/ipxe"]
	path = roms/ipxe
	url = https://gitee.com/nothing-wrong/ipxe.git
[submodule "roms/openbios"]
	path = roms/openbios
	url = https://gitee.com/nothing-wrong/openbios.git
[submodule "roms/qemu-palcode"]
	path = roms/qemu-palcode
	url = https://gitee.com/nothing-wrong/qemu-palcode.git
[submodule "roms/sgabios"]
	path = roms/sgabios
	url = https://gitee.com/nothing-wrong/sgabios.git
[submodule "dtc"]
	path = dtc
	url = https://gitee.com/nothing-wrong/dtc.git
[submodule "roms/u-boot"]
	path = roms/u-boot
	url = https://gitee.com/nothing-wrong/u-boot.git
[submodule "roms/skiboot"]
	path = roms/skiboot
	url = https://gitee.com/nothing-wrong/skiboot.git
[submodule "roms/QemuMacDrivers"]
	path = roms/QemuMacDrivers
	url = https://gitee.com/nothing-wrong/QemuMacDrivers.git
[submodule "ui/keycodemapdb"]
	path = ui/keycodemapdb
	url = https://gitee.com/nothing-wrong/keycodemapdb.git
[submodule "capstone"]
	path = capstone
	url = https://gitee.com/nothing-wrong/capstone.git
[submodule "roms/seabios-hppa"]
	path = roms/seabios-hppa
	url = https://gitee.com/nothing-wrong/seabios-hppa.git
[submodule "roms/u-boot-sam460ex"]
	path = roms/u-boot-sam460ex
	url = https://gitee.com/nothing-wrong/u-boot-sam460ex.git
[submodule "tests/fp/berkeley-testfloat-3"]
	path = tests/fp/berkeley-testfloat-3
	url = https://gitee.com/nothing-wrong/berkeley-testfloat-3.git
[submodule "tests/fp/berkeley-softfloat-3"]
	path = tests/fp/berkeley-softfloat-3
	url = https://gitee.com/nothing-wrong/berkeley-softfloat-3.git
[submodule "roms/edk2"]
	path = roms/edk2
	url = https://gitee.com/nothing-wrong/edk2.git
[submodule "slirp"]
	path = slirp
	url = https://gitee.com/nothing-wrong/libslirp.git
[submodule "roms/opensbi"]
	path = roms/opensbi
	url = https://gitee.com/nothing-wrong/opensbi.git
[submodule "roms/qboot"]
	path = roms/qboot
	url = https://gitee.com/nothing-wrong/qboot.git
[submodule "meson"]
	path = meson
	url = https://gitee.com/nothing-wrong/meson.git
[submodule "roms/vbootrom"]
	path = roms/vbootrom
	url = https://gitee.com/nothing-wrong/vbootrom.git

```

步骤2应用补丁文件

进入目标目录qemu的根目录，补丁文件路径按照实际路径修改

```
cd qemu
git am ../tee-gp-proxy/trustzone-awared-vm/Host/qemu-00*.patch
```

步骤3编译qemu

```
mkdir build
cd build 
../configure --target-list=aarch64-softmmu --disable-werror 
make -j
```

----结束

#### 4.2.3.4.2虚拟机配置

注意：此处若使用超融合平台部署虚拟化则需要超融合厂商进行适配，修改虚拟机配置文件，添加虚拟串口，映射路径

步骤1安装依赖

```
yum -y install edk2-aarch64.noarch libvirt
```

步骤2下载虚拟机qcow2镜像, 本参考实践以openEuler22.03-LTS-SP4为例

下载链接 [了解详情](https://repo.openeuler.org/openEuler-22.03-LTS-SP4/virtual_machine_img/aarch64/)

解压镜像压缩包

```
yum install xz
unxz openEuler-22.03-LTS-SP4-aarch64.qcow2.xz
```

步骤3启用libvirtd服务

```
systemctl start libvirtd.service
setenforce 0
```

步骤4修改虚拟机配置文件

新建虚拟机配置文件

```
vim vm-libvirt.xml
```

参考以下内容修改虚拟机配置文件

```
<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
    <!-- 虚拟机名称 -->
    <name>nvm-ta-1</name>
    <!-- 虚拟机内存 -->
    <memory unit='GiB'>32</memory>
    <!-- 虚拟处理器的个数 -->
    <vcpu>16</vcpu>
    <os>
        <type arch='aarch64' machine='virt'>hvm</type>
        <!-- 这里是arm架构的 -->
        <loader readonly='yes' type='pflash'>/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw</loader>
        <!-- 指定nvram文件路径，用于存储UEFI启动配置 保证文件名不冲突即可 -->
        <nvram>/var/lib/libvirt/qemu/nvram/openEulerVM.fd</nvram>
    </os>
    <features>
        <acpi/>
        <gic version='3'/>
    </features>
    <cpu mode='host-passthrough'>
    </cpu>
    <iothreads>1</iothreads>
    <clock offset='utc'/>
    <on_poweroff>destroy</on_poweroff>
    <on_reboot>restart</on_reboot>
    <on_crash>restart</on_crash>
    <devices>
        <!-- 创建软链接指向编译qemu后build目录下的qemu-system-aarch64 -->
            <emulator>/home/zhangqiang/qemu/build/aarch64-softmmu/qemu-system-aarch64</emulator>(修改成对应的路径)
        <disk type='file' device='disk'>
            <driver name='qemu' type='qcow2' iothread="1"/>
            <!-- 镜像路径 -->
            <source file='/home/zhangqiang/xunihua/openEuler-22.03-LTS-SP4-aarch64.qcow2'/>(修改成对应的路径)
            <target dev='vda' bus='virtio'/>
            <boot order='1'/>
        </disk>
        <!--
        <disk type='file' device='cdrom'>
            <driver name='qemu' type='raw'/>
            <source file='/home/z50040113/target/openEuler-22.03-LTS-SP1-aarch64-dvd.iso'/>
            <readonly/>
            <target dev='sdb' bus='scsi'/>
            <boot order='2'/>
        </disk>
        -->
        <interface type='network'>
            <!-- 建议配置network NAT网络模式 -->
            <source network='default'/>
            <model type='virtio'/>
        </interface>
        <console type='pty'/>
        <video>
               <model type='virtio'/>
        </video>
        <controller type='scsi' index='0' model='virtio-scsi'/>
        <controller type='usb' model='ehci'/>
        <input type='tablet' bus='usb'/>
        <input type='keyboard' bus='usb'/>
        <!-- port 为vnc连接端口，auth 为连接密码 -->
        <!--
        <graphics type='vnc' port='5901' listen='0.0.0.0' password='on' auth='passwd1234!'/>
        -->
    </devices>
    <qemu:commandline>
     <!-- 添加一个虚拟串口，映射到host路径/tmp/vm_vtzb_sock0 -->
     <qemu:arg value='-chardev'/>
     <qemu:arg value='socket,path=/tmp/vm_vtzb_sock1,server=on,wait=off,id=vm01_vtzb_sock'/>
     <qemu:arg value='-device'/>
     <qemu:arg value='virtio-serial'/>
     <qemu:arg value='-device'/>
     <qemu:arg value='virtserialport,chardev=vm01_vtzb_sock,name=vtzf_serialport0'/>
     <!--
     <qemu:arg value='-monitor'/>
     <qemu:arg value='telnet:127.0.0.1:5556,server=on,wait=off'/>
     -->
    </qemu:commandline>
    <seclabel type='dynamic' model='dac' relabel='yes'/>
</domain>
```

多虚机配置

```
 <qemu:arg value='-chardev'/>
 <qemu:arg value='socket,path=/var/vtzb/vm_vtzb_sock1,server=on,wait=off,id=vm01_vtzb_sock'/>
 <qemu:arg value='-device'/>
 <qemu:arg value='virtio-serial'/>
 <qemu:arg value='-device'/>
 <qemu:arg value='virtserialport,chardev=vm01_vtzb_sock,name=vtzf_serialport0'/>
```

1. 在虚机配置文件中修改如上代码：path按照/var/vtzb/vm_vtzb_sock0，/var/vtzb/vm_vtzb_sock1，/var/vtzb/vm_vtzb_sock2且接着上一个虚机配置文件中的path有序递增，且数字不能大于配置文件中的 max_vm_count
2. 修改id与chardev 一致且唯一
3. name 恒为vtzf_serialport0

生效虚拟机配置文件

```
virsh define vm-libvirt.xml (名称与上面新建的配置文件保持一致)
```

步骤5启用虚拟机

```
virsh start nvm-ta-1(名称与配置文件保持一致)
```

步骤6登录虚拟机

openEuler镜像默认账号为root, 默认密码为openEuler12#$

```
virsh console nvm-ta-1
```

#### ----结束

#### 4.2.3.4.3配置虚拟机内生国密运行环境

以下步骤在虚拟机中执行

步骤1安装依赖

```
yum install make kernel-devel-$(uname -r) git gcc openssl-devel
```

步骤2下载tee-gp-proxy仓库，其中包含vtzdriver与virtio(5.10内核)源码

```
git clone -b 25.3.0 https://gitcode.com/openeuler/tee-gp-proxy.git
```

【注意】此处tee-gp-proxy使用tag为25.3.0的版本，与下文itrustee_tzdriver，itrustee_client源码分支iTrustee_7.9.1_release配套使用

步骤3编译libboundscheck

获取libboundscheck源码

```
git clone https://gitcode.com/openeuler/libboundscheck.git
```

复制到tee-gp-proxy下

```
cp -rf libboundscheck tee-gp-proxy/trustzone-awared-vm/VM/vtzdriver
```

步骤4编译安装itrustee_client

获取itrustee_client源码

```
git clone https://gitcode.com/openeuler/itrustee_client.git -b iTrustee_7.9.1_release
```

进入itrustee_client的根目录，使能补丁文件，补丁文件路径按照实际路径修改

```
cd itrustee_client
git am ../tee-gp-proxy/trustzone-awared-vm/Host/client-0001-add-vm-uid-in-TC_NS_ClientContext.patch
```

回到上层目录，将libboundscheck源码复制到“itrustee_client/”目录下，用于itrustee_client中打补丁后的makefile编译时候的调用

```
cd ..
cp -rf libboundscheck itrustee_client
```

编译itrustee_client。

```
cd itrustee_client
make
make install
```

步骤5根据获取链接 [了解详情](https://www.hikunpeng.com/zh/developer/download?title=%E6%9C%BA%E5%AF%86%E8%AE%A1%E7%AE%97&subTitle=CCOS)获取BoostKit-boostcc-ccos-1.2.zip，解压zip包后获得kunpeng_sec_drv.sec和sdf-utils*.rpm

步骤6安装sdf-utils

```
rpm -ivh sdf-utils*.rpm
```

步骤7部署SEC驱动文件

```
mkdir -p /var/itrustee/tee_dynamic_drv/crypto/
cp kunpeng_sec_drv.sec /var/itrustee/tee_dynamic_drv/crypto/
```

步骤8编译virtio_console.ko并加载（仅5.10内核需要执行此步骤）

```
cd ..
cd tee-gp-proxy/trustzone-awared-vm/VM/virtio/char
make
mkdir -p /lib/modules/$(uname -r)/kernel/drivers/trustzone
cp virtio_console.ko /lib/modules/$(uname -r)/kernel/drivers/trustzone
rmmod virtio_console
insmod /lib/modules/$(uname -r)/kernel/drivers/trustzone/virtio_console.ko
```

步骤9编译vtzdriver并加载vtzfdriver.ko,vtzfdriver加载后不可卸载, 如需卸载请重启

```
cd tee-gp-proxy/trustzone-awared-vm/VM/vtzdriver
make
mkdir -p /lib/modules/$(uname -r)/kernel/drivers/trustzone
cp vtzfdriver.ko /lib/modules/$(uname -r)/kernel/drivers/trustzone
insmod /lib/modules/$(uname -r)/kernel/drivers/trustzone/vtzfdriver.ko
```

#### ----结束

#### 4.2.3.5加载驱动

虚拟机使用TEE运行环境需要保证物理机HOST端以及虚拟机GUEST端都加载驱动，若重启则需要重新加载

步骤1物理机Host端加载驱动

```
insmod /lib/modules/$(uname -r)/kernel/drivers/trustzone/tzdriver.ko
nohup /usr/bin/teecd &
nohup /usr/bin/vtz_proxy &
```

步骤2虚拟机Guest端加载驱动

重新加载virtio_console模块（仅5.10内核需要执行此步骤)

```
rmmod virtio_console
insmod /lib/modules/$(uname -r)/kernel/drivers/trustzone/virtio_console.ko
```

加载vtzfdriver.ko和teecd

```
insmod /lib/modules/$(uname -r)/kernel/drivers/trustzone/vtzfdriver.ko
nohup /usr/bin/teecd &
```

#### ----结束

说明：若加载驱动成功且teecd进行运行成功则说明环境配置是正常的，相应的组件以及驱动编译安装是成功的。

#### 4.2.3.6初始化内置密钥

参考章节3.1.3.4 初始化内置密钥

说明：若内置密钥配置成功则说明SDF接口调用成功，内生国密开发运行环境配置完成。

#### 4.2.3.7常见报错及解决

问题1：在进行物理机环境搭建执行步骤3编译itrustee_tzdriver时报错函数未定义

原因：makefile中存在条件判断逻辑，若系统已经内置了vsnprintf_s函数，则不使用本地 libboundscheck，若没有内置，则使用本地 libboundscheck

解决方案：进入itrustee_tzdriver源码目录，执行以下命令将条件判断逻辑注释掉

```
sed -i '29,33s/^/#/' Makefile
sed -i '37s/^/#/' Makefile
```

重新执行make进行编译

```
make CPU_GROUP_BINDING=y
```

问题2：定义虚拟机配置文件后启动虚拟机报错 qemu-system-aarch64: -device virtio-net-pci,netdev=hostnet0,id=net0,mac=52:54:00:3e:fc:c2,bus=pci.2,addr=0x0: failed to find romfile "efi-virtio.rom"

原因：网络设备缺少 efi-virtio.rom文件

解决方案：使用指令find / -name "*virtio*.rom" 2>/dev/null | grep -i efi查询文件位置，若不存在，则使用yum install edk2-aarch64下载该文件。

编辑虚拟机xml配置文件，在网络设备添加 <rom file='/usr/share/qemu/efi-virtio.rom'/>(具体路径以文件在服务器的路径为准)

### 4.2.4开发示例

#### 接口调用示例

如下提供示例代码演示了接口的使用方式，以SM2用户密钥为例，包括密钥的创建、密钥标识码的修改、密钥访问权限路径添加与删除、导出导入密钥，删除密钥。

示例1代码：

```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/sha.h>
#include "sdf.h"

#define MAX_KEY_FILE_SIZE (1024 * 1024)
#define EXPORT_SALT "123456789"
#define CA_PATH "./test_sdf"
#define USER_NAME "root"
#define PASSWD "Huawei123."
#define NEWPASSWD "Huawei321."
#define ALG_FLAG_OFFSET 20

int generate_key(void *session, unsigned int key_id, unsigned char *password)
{
    int ret;
    // sm2签名及加密密钥
    unsigned int sign_info = SDF_SM2_SIGN << ALG_FLAG_OFFSET | key_id;
    unsigned int enc_info = SDF_SM2_ENC << ALG_FLAG_OFFSET | key_id;
    ret = ECM_GenerateKey(session, sign_info, password, strlen(password));
    if (ret != SDR_OK) {
        printf("ECM_GenerateKey sign key failed, ret = 0x%x\n", ret);
        return ret;
    }
    
    ret = ECM_GenerateKey(session, enc_info, password, strlen(password));
    if (ret != SDR_OK) {
        printf("ECM_GenerateKey enc key failed, ret = 0x%x\n", ret);
        return ret;
    }
    return ret;
}
int delete_key(void *session, unsigned int key_id)
{
    int ret;
    unsigned int sign_info = SDF_SM2_SIGN << ALG_FLAG_OFFSET | key_id;
    unsigned int enc_info = SDF_SM2_ENC << ALG_FLAG_OFFSET | key_id;
    ret = ECM_DeleteKey(session, sign_info);
    if (ret != SDR_OK) {
        printf("ECM_DeleteKey key_index %d failed, ret = 0x%x\n", key_id, ret);
        return ret;
    }
    ret = ECM_DeleteKey(session, enc_info);
    if (ret != SDR_OK) {
        printf("ECM_DeleteKey key_index %d failed, ret = 0x%x\n", key_id, ret);
    }
    return ret;
}
int export_import_key(void *session, unsigned int key_id1, unsigned int key_id2)
{
    int ret = -1;
    uint8_t *key_data = NULL;
    uint32_t key_data_len = MAX_KEY_FILE_SIZE;
    unsigned int sign_key_info1 = SDF_SM2_SIGN << ALG_FLAG_OFFSET | key_id1;
    unsigned int sign_key_info2 = SDF_SM2_SIGN << ALG_FLAG_OFFSET | key_id2;
    unsigned int enc_key_info1 = SDF_SM2_ENC << ALG_FLAG_OFFSET | key_id1;
    unsigned int enc_key_info2 = SDF_SM2_ENC << ALG_FLAG_OFFSET | key_id2;
    key_data = (uint8_t *)malloc(key_data_len);
    if (key_data == NULL) {
        printf("malloc failed\n");
        return -1;
    }
    // 导出导入签名密钥
    ret = ECM_ExportKey(session, sign_key_info1, key_data, &key_data_len, EXPORT_SALT);
    if (ret) {
        printf("ECM_ExportKey sign key failed, ret = 0x%x, %d\n", ret, key_data_len);
        goto free;
    }
    ret = ECM_ImportKey(session, sign_key_info2, key_data, key_data_len, EXPORT_SALT);
    if (ret) {
        printf("ECM_ImportKey sign key failed, ret = 0x%x\n", ret);
        goto free;
    }
    // 导出导入加密密钥
    ret = ECM_ExportKey(session, enc_key_info1, key_data, &key_data_len, EXPORT_SALT);
    if (ret) {
        printf("ECM_ExportKey enc key failed, ret = 0x%x, %d\n", ret, key_data_len);
        goto free;
    }
    ret = ECM_ImportKey(session, enc_key_info2, key_data, key_data_len, EXPORT_SALT);
    if (ret) {
        printf("ECM_ImportKey enc key failed, ret = 0x%x\n", ret);
        goto free;
    }
free:
    free(key_data);
    return ret;
}
int change_passwd(void *session, unsigned int key_id, char *passwd, char *new_passwd)
{
    int ret;
    unsigned int sign_info = SDF_SM2_SIGN << ALG_FLAG_OFFSET | key_id;
    unsigned int enc_info = SDF_SM2_ENC << ALG_FLAG_OFFSET | key_id;
    ret = ECM_ChangePassword(session, sign_info, passwd, strlen(passwd), new_passwd, strlen(new_passwd));
    if (ret != SDR_OK) {
        printf("ECM_ChangePassword sign key failed, ret = 0x%x\n", ret);
        return ret;
    }
    ret = ECM_ChangePassword(session, enc_info, passwd, strlen(passwd), new_passwd, strlen(new_passwd));
    if (ret != SDR_OK) {
        printf("ECM_ChangePassword sign key failed, ret = 0x%x\n", ret);
    }
    return ret;
}
void calculate_cainfo(const char *ca_path, const char *user_name, unsigned char hash[32])
{
    char plan[256] = {0};
    memcpy(plan, user_name, strlen(user_name));
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, ca_path, strlen(ca_path));
    SHA256_Update(&sha256, plan, 256);
    SHA256_Final(hash, &sha256);
    for (size_t i = 0; i < 32; i++) {
        printf("%02x", hash[i]);
    }
    printf("\n");
}
int main()
{
    int ret = -1;
    int key_idx1 = 1, key_idx2 = 2;
    void *device = NULL, *session = NULL;
    ret = SDF_OpenDevice(&device);
    if (ret != SDR_OK) {
        printf("sdf open device failed, ret = 0x%x\n", ret);
        return ret;
    }
    ret = ECM_OpenSession(device, &session);
    if (ret != SDR_OK) {
        printf("sdf open session failed, ret = 0x%x\n", ret);
        goto close;
    }
    ret = generate_key(session, key_idx1, PASSWD);
    if (ret != SDR_OK) {
        printf("generate_key failed, ret = 0x%x\n", ret);
        goto close;
    }
    printf("generate key success\n");
    // 修改密码
    ret = change_passwd(session, key_idx1, PASSWD, NEWPASSWD);
    if (ret != SDR_OK) {
        printf("change key passwd failed, ret = 0x%x\n", ret);
        goto close;
    }
    printf("change passwd success\n");
   
    // 导入密钥到key_idx2
    ret = export_import_key(session, key_idx1, key_idx2);
    if (ret != SDR_OK) {
        printf("export_import_key failed, ret = 0x%x\n", ret);
        goto close;
    }
    printf("export key %d, import to key %d success\n", key_idx1, key_idx2);
    ret = delete_key(session, key_idx1);
    if (ret != SDR_OK) {
        printf("ECM_DeleteKey failed, ret = 0x%x\n", ret);
        goto close;
    }
    ret = delete_key(session, key_idx2);
    if (ret != SDR_OK) {
        printf("ECM_DeleteKey failed, ret = 0x%x\n", ret);
        goto close;
    }
    printf("delete key success\n");
close:
    if (session != NULL) {
        (void)SDF_CloseSession(session);
    }
    if (device != NULL) {
        (void)SDF_CloseDevice(device);
    }
    return ret;
}
```

如下提供示例代码演示了接口的调用，以SM2算法为例，包括了设备和会话管理，加解密，验证签名等操作

示例2代码：

```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "sdf.h"

// 定义配置结构体
typedef struct {
    unsigned int index; // 秘钥索引
    char* password;    // 秘钥密码
    int data_size;     // 数据处理长度(bytes)
    int test_type;     // 测试类型: 0-加密, 1-签名
} Config;

// 定义线程参数结构体
typedef struct {
    Config* config;
    int thread_id;
} ThreadArgs;

// 生成随机数据函数
static void generate_random_data(unsigned char* data, int size) {
    FILE* urandom = fopen("/dev/urandom", "r");
    if (urandom) {
        fread(data, 1, size, urandom);
        fclose(urandom);
    }
}

// 执行SM2加密测试
static void* perform_sm2_encryption(void* arg) {
    ThreadArgs* args = (ThreadArgs*)arg;
    Config* config = args->config;
    
    // 初始化测试数据
    unsigned char* data = malloc(config->data_size);
    generate_random_data(data, config->data_size);
    
    // 打开设备和会话
    void* hDevice = NULL;
    void* hSession = NULL;
    SDF_OpenDevice(&hDevice);
    SDF_OpenSession(hDevice, &hSession);
    SDF_GetPrivateKeyAccessRight(hSession, config->index, 
                               (unsigned char*)config->password, 
                               strlen(config->password));
    
    printf("Thread %d: 开始执行SM2加密测试 [数据大小: %d字节]\n", 
           args->thread_id, config->data_size);
    
    // 执行加密和解密操作
    ECCCipher cipher;
    unsigned char decrypted[config->data_size];
    unsigned int decrypted_size = config->data_size;
    
    SDF_ExternalEncrypt_ECC(hSession, SDF_SM2_ENC, NULL, data, config->data_size, &cipher);
    SDF_ExternalDecrypt_ECC(hSession, SDF_SM2_ENC, NULL, &cipher, decrypted, &decrypted_size);
    
    printf("Thread %d: SM2加密测试完成\n", args->thread_id);
    
    // 释放资源
    SDF_ReleasePrivateKeyAccessRight(hSession, config->index);
    SDF_CloseSession(hSession);
    SDF_CloseDevice(hDevice);
    free(data);
    
    return NULL;
}

// 执行SM2签名验证测试
static void* perform_sm2_sign_verify(void* arg) {
    ThreadArgs* args = (ThreadArgs*)arg;
    Config* config = args->config;
    
    // 初始化测试数据
    unsigned char* data = malloc(config->data_size);
    generate_random_data(data, config->data_size);
    
    // 打开设备和会话
    void* hDevice = NULL;
    void* hSession = NULL;
    SDF_OpenDevice(&hDevice);
    SDF_OpenSession(hDevice, &hSession);
    SDF_GetPrivateKeyAccessRight(hSession, config->index, 
                               (unsigned char*)config->password, 
                               strlen(config->password));
    
    printf("Thread %d: 开始执行SM2签名验证测试 [数据大小: %d字节]\n", 
           args->thread_id, config->data_size);
    
    // 执行签名和验证操作
    ECCSignature signature;
    SDF_InternalSign_ECC(hSession, config->index, data, config->data_size, &signature);
    SDF_InternalVerify_ECC(hSession, config->index, data, config->data_size, &signature);
    
    printf("Thread %d: SM2签名验证测试完成\n", args->thread_id);
    
    // 释放资源
    SDF_ReleasePrivateKeyAccessRight(hSession, config->index);
    SDF_CloseSession(hSession);
    SDF_CloseDevice(hDevice);
    free(data);
    
    return NULL;
}

// 设备检查函数
static int check_device() {
    void* hDeviceHandle = NULL;
    int ret = SDF_OpenDevice(&hDeviceHandle);
    if (ret != SDR_OK) {
        printf("设备检查失败, 错误码: 0x%X\n", ret);
        return -1;
    }
    printf("设备检查成功\n");
    SDF_CloseDevice(hDeviceHandle);
    return 0;
}

// 主函数
int main(int argc, char* argv[]) {
    // 添加设备检查
    if (check_device() != 0) {
        printf("请先检查设备驱动和权限设置\n");
        return 1;
    }
    
    // 初始化配置参数
    Config config = {0};
    config.index = 1;         // 默认秘钥索引
    config.password = "Huawei12#$"; // 默认密码
    config.data_size = 1024;  // 默认数据大小
    
    // 创建线程执行测试
    pthread_t thread;
    ThreadArgs args;
    args.config = &config;
    args.thread_id = 0;
    
    // 执行SM2加密测试
    config.test_type = 0;
    pthread_create(&thread, NULL, perform_sm2_encryption, &args);
    pthread_join(thread, NULL);
    
    // 执行SM2签名验证测试
    config.test_type = 1;
    pthread_create(&thread, NULL, perform_sm2_sign_verify, &args);
    pthread_join(thread, NULL);
    
    return 0;
}
```

#### 测试代码执行示例

步骤1下载本模块的接口头文件。

```
git clone https://gitcode.com/openeuler/itrustee_sdk.git
```

步骤2在代码中引用sdf.h头文件，即可进行应用开发，参考上文示例。

步骤3完毕后可参考如下命令编译：

本实践接口头文件所在目录为/home/itrustee_sdk/include/SDF，可根据下载头文件时的目录替换成实际路径

```
gcc -o sm2_create_password sm2_create_password.c -I /home/itrustee_sdk/include/SDF/ -lsdf  -lcrypto #示例1 参考编译指令
gcc -o sm2_test sm2_test.c -I /home/itrustee_sdk/include/SDF/ -lsdf -ldl -lpthread  #示例2 参考编译指令
```

步骤4执行应用，可成功执行SM2算法的加解密，签名验证等测试。

```
./sm2_test
```

----结束

本页内容
