diff --git a/OAT.xml b/OAT.xml
index 4471a41dc398916305a03ae6b29a3c6d5ae185fe..fb2b2507c7e18d6f832870e95c5c39323ce4b8a2 100644
--- a/OAT.xml
+++ b/OAT.xml
@@ -179,10 +179,10 @@ Note:If the text contains special characters, please escape them according to th
-
-
-
-
+
+
+
+
@@ -201,6 +201,8 @@ Note:If the text contains special characters, please escape them according to th
+
+
@@ -448,10 +450,10 @@ Note:If the text contains special characters, please escape them according to th
-
-
-
-
+
+
+
+
diff --git a/code/BasicFeature/Security/KeyManager/.gitignore b/code/BasicFeature/Security/KeyManager/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/.gitignore
@@ -0,0 +1,12 @@
+/node_modules
+/oh_modules
+/local.properties
+/.idea
+**/build
+/.hvigor
+.cxx
+/.clangd
+/.clang-format
+/.clang-tidy
+**/.test
+/.appanalyzer
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/AppScope/app.json5 b/code/BasicFeature/Security/KeyManager/AppScope/app.json5
new file mode 100644
index 0000000000000000000000000000000000000000..f9b5a8318e55ac131f19e817dd06b041310d6f69
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/AppScope/app.json5
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ "app": {
+ "bundleName": "com.samples.keymanager",
+ "vendor": "example",
+ "versionCode": 1000000,
+ "versionName": "1.0.0",
+ "icon": "$media:layered_image",
+ "label": "$string:app_name"
+ }
+}
diff --git a/code/BasicFeature/Security/KeyManager/AppScope/resources/base/element/string.json b/code/BasicFeature/Security/KeyManager/AppScope/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..33b7ce7028ebb625a58c756d9579651751e0d296
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/AppScope/resources/base/element/string.json
@@ -0,0 +1,8 @@
+{
+ "string": [
+ {
+ "name": "app_name",
+ "value": "KeyManager"
+ }
+ ]
+}
diff --git a/code/BasicFeature/Security/KeyManager/AppScope/resources/base/media/background.png b/code/BasicFeature/Security/KeyManager/AppScope/resources/base/media/background.png
new file mode 100644
index 0000000000000000000000000000000000000000..923f2b3f27e915d6871871deea0420eb45ce102f
Binary files /dev/null and b/code/BasicFeature/Security/KeyManager/AppScope/resources/base/media/background.png differ
diff --git a/code/BasicFeature/Security/KeyManager/AppScope/resources/base/media/foreground.png b/code/BasicFeature/Security/KeyManager/AppScope/resources/base/media/foreground.png
new file mode 100644
index 0000000000000000000000000000000000000000..97014d3e10e5ff511409c378cd4255713aecd85f
Binary files /dev/null and b/code/BasicFeature/Security/KeyManager/AppScope/resources/base/media/foreground.png differ
diff --git a/code/BasicFeature/Security/KeyManager/AppScope/resources/base/media/layered_image.json b/code/BasicFeature/Security/KeyManager/AppScope/resources/base/media/layered_image.json
new file mode 100644
index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/AppScope/resources/base/media/layered_image.json
@@ -0,0 +1,7 @@
+{
+ "layered-image":
+ {
+ "background" : "$media:background",
+ "foreground" : "$media:foreground"
+ }
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/README.md b/code/BasicFeature/Security/KeyManager/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..0ce012c6605ba279234f248c43ba83eb3698109f
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/README.md
@@ -0,0 +1,346 @@
+# 通用密钥管理
+
+### 介绍
+
+本示例介绍了如何使用AES和RSA进行数据加解密,并模拟了服务端和客户端的加解密通信流程。
+
+### 效果图预览
+
+
+
+**使用说明**:
+
+1. 点击输入框,输入任何内容,点击“发送”按钮,页面将会展示客户端和服务端的加解密通信过程。
+2. 客户端会在2s后调用模拟服务端的方法,页面将会展示签名验证数据的过程。
+
+### 实现思路
+
+1. 创建页面,添加List用于显示加解密流程信息,添加TextInput用于输入数据。详细代码可参考[Index.ets](./entry/src/main/ets/pages/KeyManager.ets)。
+
+ ```typescript
+ @Entry
+ @Component
+ struct KeyManager {
+ ...
+
+ build() {
+ RelativeContainer() {
+ Row() {
+ TextInput({ text: this.message, placeholder: '请输入内容' })
+ .id('input')
+ .onChange((value: string) => {
+ this.message = value;
+ })
+ .layoutWeight(4)
+ ...
+ }
+ ...
+
+ List() {
+ ForEach(this.messageArray, (message: Message) => {
+ ListItem() {
+ Row() {
+ Text(message.message)
+ .padding(10)
+ .fontColor(message.position === MessagePosition.Left ? Color.Blue : Color.Black)
+ .constraintSize({
+ maxWidth: '80%'
+ })
+ .borderRadius(10)
+ .backgroundColor("#F1F1F1")
+ .margin({
+ left: message.position === MessagePosition.Left ? 10 : 0,
+ right: message.position === MessagePosition.Right ? 10 : 0
+ })
+ }
+ ...
+ }
+ })
+ }
+ ...
+ }
+
+ }
+ ```
+
+2. 分别实现AES密钥生成方法和加解密方法。详细代码可参考[AesUtils.ets](./entry/src/main/ets/utils/AesUtils.ets)。
+
+ ```typescript
+ /**
+ * 生成AES密钥
+ * @returns AES密钥的16进制字符串
+ */
+ export async function generateAesKey(): Promise {
+ ...
+ }
+
+ /**
+ * AES解密
+ * @param encryptedMessage 已经加密的数据
+ * @param aesKey AES密钥
+ * @returns 解密后的数据
+ */
+ export async function aesGcmDecrypt(encryptedMessage: string, aesKey: string): Promise {
+ ...
+ }
+
+ /**
+ * AES加密
+ * @param textString 需要加密的数据
+ * @param aesKey AES密钥
+ * @returns
+ */
+ export async function aesGcmEncrypt(textString: string, aesKey: string): Promise {
+ ...
+ }
+ ```
+
+3. 分别实现RSA密钥生成方法、加解密方法、签名验证方法。详细代码可参考[RsaUtils.ets](./entry/src/main/ets/utils/RsaUtils.ets)。
+
+ ```typescript
+ /**
+ * 生成RSA密钥对
+ * @returns RSA密钥对
+ */
+ export async function generateRsaKey(): Promise {
+ ...
+ }
+
+ /**
+ * RSA加密
+ * @param data 需要加密的数据
+ * @param publicKey 公钥
+ * @returns 完成加密的数据
+ */
+ export async function rsaEncryption(data: string, publicKey: string): Promise {
+ ...
+ }
+
+ /**
+ * RSA解密
+ * @param encryptedData 经过加密的数据
+ * @param privateKey 私钥
+ * @returns 解密后的数据
+ */
+ export async function rsaDecryption(encryptedData: string, privateKey: string): Promise {
+ ...
+ return "";
+ }
+
+ /**
+ * 验证签名
+ * @param encryptedData 待验证的数据
+ * @param singedData 签名信息
+ * @param publicKey 公钥
+ * @returns 签名验证是否通过
+ */
+ export async function verify(encryptedData: string, singedData: string, publicKey: string): Promise {
+ ...
+ }
+
+ /**
+ * 签名
+ * @param data 需要签名的数据
+ * @param privateKey 私钥
+ * @returns 签名信息
+ */
+ export async function sign(data: string, privateKey: string): Promise {
+ ...
+ }
+ ```
+
+4. 在客户端中进行AES密钥生成、加解密以及RSA加密。详细代码可参考[LocalClient.ets](./entry/src/main/ets/client/LocalClient.ets)。
+
+ ```typescript
+ // 客户端下载公钥
+ async downloadPublicKey() {
+ let publicKeyResult: SignedData = JSON.parse(await this.server.downloadPublicKey());
+ // 验证公钥签名
+ if (await verify(publicKeyResult.encryptedMessage, publicKeyResult.signedMessage,
+ publicKeyResult.encryptedMessage)) {
+ this.publicKey = publicKeyResult.encryptedMessage;
+ }
+ if (this.publicKey === "") {
+ sendProcessMessage("获取公钥失败", MessagePosition.Right);
+ throw new Error('downloadPublicKey failed');
+ }
+ sendProcessMessage("获取公钥成功", MessagePosition.Right);
+ }
+
+ // 发送数据到服务端
+ async sendMessageToServer(message: string): Promise {
+ sendProcessMessage("开始发送数据到服务端...", MessagePosition.Right);
+ // 消息加密
+ let encryptionMessage: string = await this.encryption(message);
+ sendProcessMessage("发送加密数据到服务端...", MessagePosition.Right);
+ // 发送服务端
+ await this.server.receiveMessageFromClient(encryptionMessage);
+ }
+
+ // 从服务端接收数据
+ async receiveMessageFromServer(): Promise {
+ sendProcessMessage("开始接收服务端数据...", MessagePosition.Right);
+ let signMessage: string = await this.server.sendMessageToClient();
+ // 验证签名
+ let signData: SignedData = JSON.parse(signMessage);
+ sendProcessMessage("开始验证签名...", MessagePosition.Right);
+ let isVerified: boolean = await verify(signData.encryptedMessage, signData.signedMessage, this.publicKey);
+ if (isVerified) {
+ sendProcessMessage("签名验证成功,开始解密...", MessagePosition.Right);
+ let decryptedMessage = await aesGcmDecrypt(signData.encryptedMessage, this.aesKey);
+ sendProcessMessage("解密成功,解密结果:" + decryptedMessage, MessagePosition.Right);
+ return decryptedMessage;
+ }
+ sendProcessMessage("签名验证失败", MessagePosition.Right);
+ return "签名验证失败";
+ }
+
+ // 加密
+ async encryption(message: string): Promise {
+ if (!this.publicKey) {
+ sendProcessMessage("本地未找到公钥,开始下载...", MessagePosition.Right);
+ // 获取公钥
+ await this.downloadPublicKey();
+ sendProcessMessage("公钥下载成功", MessagePosition.Right);
+ }
+ if (this.aesKey === "") {
+ sendProcessMessage("本地未找到AES密钥,开始生成...", MessagePosition.Right);
+ // 生成AES密钥
+ this.aesKey = await generateAesKey();
+ sendProcessMessage(`AES密钥生成成功,密钥:${this.aesKey},开始发送到服务端...`, MessagePosition.Right);
+ // 将AES密钥发送到服务端
+ await this.sendAesKeyToServer();
+ }
+ sendProcessMessage(`客户端开始加密数据...`, MessagePosition.Right);
+ let encryptionResult: string = await aesGcmEncrypt(message, this.aesKey);
+ sendProcessMessage(`客户端加密成功,加密结果:${encryptionResult}`, MessagePosition.Right);
+ // 使用AES加密数据
+ return encryptionResult;
+ }
+
+ // 发送AES密钥到服务端
+ async sendAesKeyToServer() {
+ sendProcessMessage(`AES密钥进行加密...`, MessagePosition.Right);
+ // 对AES密钥使用公钥进行加密
+ let encryptedAesKey: string = await rsaEncryption(this.aesKey, this.publicKey);
+ sendProcessMessage(`加密成功,加密结果:${encryptedAesKey},开始发送到服务端...`, MessagePosition.Right);
+ // 将加密后的AES密钥发送到服务端
+ await this.server.receiveKeyFromClient(encryptedAesKey);
+ }
+ ```
+
+5. 在客户端中进行AES密钥生成、加解密以及RSA加密。详细代码可参考[LocalClient.ets](./entry/src/main/ets/client/LocalClient.ets)。
+
+ ```typescript
+ //生成证书
+ async createKey() {
+ sendProcessMessage("模拟服务端开始生成证书", MessagePosition.Left);
+ let rsaKey: RsaKey | undefined = await generateRsaKey()
+ if (rsaKey) {
+ this.publicKey = rsaKey.publicKey;
+ this.privateKey = rsaKey.privateKey;
+ sendProcessMessage("模拟服务端生成证书成功,公钥:" + this.publicKey, MessagePosition.Left);
+ }
+ }
+
+ // 模拟公钥下载
+ async downloadPublicKey(): Promise {
+ if (this.publicKey === "") {
+ // 创建密钥
+ await this.createKey();
+ }
+ // 对公钥进行签名
+ let signResult: string = await sign(this.publicKey, this.privateKey);
+ let publicKeyResult: SignedData = { encryptedMessage: this.publicKey, signedMessage: signResult };
+ return JSON.stringify(publicKeyResult);
+ }
+
+ async receiveKeyFromClient(encryptedAesKey: string) {
+ sendProcessMessage(`模拟服务端接收到AES密钥:${encryptedAesKey}`, MessagePosition.Left);
+ this.aesKey = await rsaDecryption(encryptedAesKey, this.privateKey);
+ sendProcessMessage(`模拟服务端AES密钥解密成功,AES密钥为:${this.aesKey}`, MessagePosition.Left);
+ Logger.info(TAG, 'receive key from client success, server aesKey:' + this.aesKey);
+ }
+
+ // 获取客户端消息
+ async receiveMessageFromClient(message: string): Promise {
+ sendProcessMessage("模拟服务端接收到数据,开始解密...", MessagePosition.Left);
+ // 解密
+ let decryptedMessage = await aesGcmDecrypt(message, this.aesKey);
+ sendProcessMessage(`模拟服务端解密成功,解密结果:${decryptedMessage}`, MessagePosition.Left);
+ // 存储
+ this.messageStorage.push(decryptedMessage);
+ }
+
+ async sendMessageToClient(): Promise {
+ let needSendMessage: string = "这是模拟服务端返回的测试数据," + this.messageStorage[this.messageStorage.length-1];
+ sendProcessMessage(`模拟服务端开始发送数据,将要发送的数据是:${needSendMessage}`, MessagePosition.Left);
+ sendProcessMessage(`模拟服务端开始加密数据:${needSendMessage}`, MessagePosition.Left);
+ let encryptedMessage: string = await aesGcmEncrypt(needSendMessage, this.aesKey);
+ sendProcessMessage(`模拟服务端加密成功,加密结果:${encryptedMessage}`, MessagePosition.Left);
+ // 签名
+ sendProcessMessage(`模拟服务端开始签名数据...`, MessagePosition.Left);
+ let signedMessage: string = await sign(encryptedMessage, this.privateKey);
+ sendProcessMessage(`模拟服务端签名成功,签名结果:${signedMessage}`, MessagePosition.Left);
+ let signData: SignedData = { encryptedMessage: encryptedMessage, signedMessage: signedMessage };
+ // 发送消息
+ return JSON.stringify(signData);
+ }
+ ```
+
+### 工程结构&模块类型
+
+```
+KeyManger // har类型
+|---client
+| |---LocalClient.ets // 模拟客户端
+|---pages
+| |---KeyManager.ets // 应用页面
+|---server
+| |---LocalMockServer.ets // 模拟服务端
+|---utils
+| |---AesUtils.ets // AES加密工具类
+| |---Logger.ets // 日志打印类
+| |---RsaUtils.ets // RSA加密工具类
+| |---Utils.ets // 其他公用方法
+```
+
+### 模块依赖
+
+无
+
+### 相关权限
+
+无
+
+### 约束与限制
+
+1. 本示例仅支持标准系统上运行。
+
+2. 本示例为Stage模型,从API version 12开始支持。SDK版本号:5.0.0.71 Release,镜像版本号:OpenHarmony 5.0.1.107。
+
+3. 本示例需要使用DevEco Studio 5.0.4 Release (Build Version: 5.0.11.100, built on March 28, 2025)编译运行。
+
+### 下载
+
+如需单独下载本工程,执行如下命令:
+
+```shell
+git init
+git config core.sparsecheckout true
+echo code/BasicFeature/Security/KeyManager/ > .git/info/sparse-checkout
+git remote add origin https://gitee.com/openharmony/applications_app_samples.git
+git pull origin master
+```
+
+### 参考资料
+
+[加解密算法库框架cryptoFramework](https://docs.openharmony.cn/pages/v5.1/zh-cn/application-dev/reference/apis-crypto-architecture-kit/js-apis-cryptoFramework.md#cryptoframeworkcreatesymkeygenerator)
+
+[使用RSA非对称密钥(PKCS1模式)加解密](https://docs.openharmony.cn/pages/v5.1/zh-cn/application-dev/security/CryptoArchitectureKit/crypto-rsa-asym-encrypt-decrypt-pkcs1.md)
+
+[使用AES对称密钥(GCM模式)加解密](https://docs.openharmony.cn/pages/v5.1/zh-cn/application-dev/security/CryptoArchitectureKit/crypto-aes-sym-encrypt-decrypt-gcm.md)
+
+[使用RSA密钥对(PKCS1模式)签名验签](https://docs.openharmony.cn/pages/v5.1/zh-cn/application-dev/security/CryptoArchitectureKit/crypto-rsa-sign-sig-verify-pkcs1.md)
+
+
diff --git a/code/BasicFeature/Security/KeyManager/build-profile.json5 b/code/BasicFeature/Security/KeyManager/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..49f50afc4ef59767dc9e7f60b48e0992c911be2a
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/build-profile.json5
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ "app": {
+ "signingConfigs": [],
+ "products": [
+ {
+ "name": "default",
+ "signingConfig": "default",
+ "compatibleSdkVersion": 12,
+ "compileSdkVersion": 12,
+ "runtimeOS": "OpenHarmony",
+ "buildOption": {
+ "strictMode": {
+ "caseSensitiveCheck": true,
+ "useNormalizedOHMUrl": true
+ }
+ }
+ }
+ ],
+ "buildModeSet": [
+ {
+ "name": "debug",
+ },
+ {
+ "name": "release"
+ }
+ ]
+ },
+ "modules": [
+ {
+ "name": "entry",
+ "srcPath": "./entry",
+ "targets": [
+ {
+ "name": "default",
+ "applyToProducts": [
+ "default"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/code-linter.json5 b/code/BasicFeature/Security/KeyManager/code-linter.json5
new file mode 100644
index 0000000000000000000000000000000000000000..ed05653cca31b61d64cf6471529eaf50d4f70709
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/code-linter.json5
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ "files": [
+ "**/*.ets"
+ ],
+ "ignore": [
+ "**/src/ohosTest/**/*",
+ "**/src/test/**/*",
+ "**/src/mock/**/*",
+ "**/node_modules/**/*",
+ "**/oh_modules/**/*",
+ "**/build/**/*",
+ "**/.preview/**/*"
+ ],
+ "ruleSet": [
+ "plugin:@performance/recommended",
+ "plugin:@typescript-eslint/recommended"
+ ],
+ "rules": {
+ "@security/no-unsafe-aes": "error",
+ "@security/no-unsafe-hash": "error",
+ "@security/no-unsafe-mac": "warn",
+ "@security/no-unsafe-dh": "error",
+ "@security/no-unsafe-dsa": "error",
+ "@security/no-unsafe-ecdsa": "error",
+ "@security/no-unsafe-rsa-encrypt": "error",
+ "@security/no-unsafe-rsa-sign": "error",
+ "@security/no-unsafe-rsa-key": "error",
+ "@security/no-unsafe-dsa-key": "error",
+ "@security/no-unsafe-dh-key": "error",
+ "@security/no-unsafe-3des": "error"
+ }
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/common_secret_key_manager.gif b/code/BasicFeature/Security/KeyManager/common_secret_key_manager.gif
new file mode 100644
index 0000000000000000000000000000000000000000..5a6e91e0ad3ee83a3f67b1ca907745ab2c62cc7d
Binary files /dev/null and b/code/BasicFeature/Security/KeyManager/common_secret_key_manager.gif differ
diff --git a/code/BasicFeature/Security/KeyManager/entry/.gitignore b/code/BasicFeature/Security/KeyManager/entry/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/.gitignore
@@ -0,0 +1,6 @@
+/node_modules
+/oh_modules
+/.preview
+/build
+/.cxx
+/.test
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/build-profile.json5 b/code/BasicFeature/Security/KeyManager/entry/build-profile.json5
new file mode 100644
index 0000000000000000000000000000000000000000..e7569e3056e27af38e9991b7ea73ec10f3ba8a05
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/build-profile.json5
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ "apiType": "stageMode",
+ "buildOption": {
+ },
+ "buildOptionSet": [
+ {
+ "name": "release",
+ "arkOptions": {
+ "obfuscation": {
+ "ruleOptions": {
+ "enable": false,
+ "files": [
+ "./obfuscation-rules.txt"
+ ]
+ }
+ }
+ }
+ },
+ ],
+ "targets": [
+ {
+ "name": "default"
+ },
+ {
+ "name": "ohosTest",
+ }
+ ]
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/hvigorfile.ts b/code/BasicFeature/Security/KeyManager/entry/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/hvigorfile.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { hapTasks } from '@ohos/hvigor-ohos-plugin';
+
+export default {
+ system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
+ plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
+}
diff --git a/code/BasicFeature/Security/KeyManager/entry/obfuscation-rules.txt b/code/BasicFeature/Security/KeyManager/entry/obfuscation-rules.txt
new file mode 100644
index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/obfuscation-rules.txt
@@ -0,0 +1,23 @@
+# Define project specific obfuscation rules here.
+# You can include the obfuscation configuration files in the current module's build-profile.json5.
+#
+# For more details, see
+# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5
+
+# Obfuscation options:
+# -disable-obfuscation: disable all obfuscations
+# -enable-property-obfuscation: obfuscate the property names
+# -enable-toplevel-obfuscation: obfuscate the names in the global scope
+# -compact: remove unnecessary blank spaces and all line feeds
+# -remove-log: remove all console.* statements
+# -print-namecache: print the name cache that contains the mapping from the old names to new names
+# -apply-namecache: reuse the given cache file
+
+# Keep options:
+# -keep-property-name: specifies property names that you want to keep
+# -keep-global-name: specifies names that you want to keep in the global scope
+
+-enable-property-obfuscation
+-enable-toplevel-obfuscation
+-enable-filename-obfuscation
+-enable-export-obfuscation
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/oh-package.json5 b/code/BasicFeature/Security/KeyManager/entry/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..c9cb6c8174858277c9b0d465a51547dcab16d5ff
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/oh-package.json5
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ "name": "entry",
+ "version": "1.0.0",
+ "description": "Please describe the basic information.",
+ "main": "",
+ "author": "",
+ "license": "",
+ "dependencies": {}
+}
+
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/ets/client/LocalClient.ets b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/client/LocalClient.ets
new file mode 100644
index 0000000000000000000000000000000000000000..5ee352210e42d8bc5be814dd25e4f3c4fe6fa978
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/client/LocalClient.ets
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { LocalMockServer } from '../server/LocalMockServer';
+import { aesGcmDecrypt, aesGcmEncrypt, generateAesKey } from '../utils/AesUtils';
+import { rsaEncryption, verify } from '../utils/RsaUtils';
+import { MessagePosition, sendProcessMessage } from '../utils/Utils';
+
+const TAG: string = 'LocalClient';
+
+class SignedData {
+ encryptedMessage: string = "";
+ signedMessage: string = "";
+}
+
+/**
+ * 本地客户端进行加密、验签
+ */
+export class LocalClient {
+ private server: LocalMockServer = new LocalMockServer();
+ private publicKey: string = "";
+ private aesKey: string = "";
+
+ // 客户端下载公钥
+ async downloadPublicKey() {
+ let publicKeyResult: SignedData = JSON.parse(await this.server.downloadPublicKey());
+ // 验证公钥签名
+ if (await verify(publicKeyResult.encryptedMessage, publicKeyResult.signedMessage,
+ publicKeyResult.encryptedMessage)) {
+ this.publicKey = publicKeyResult.encryptedMessage;
+ }
+ if (this.publicKey === "") {
+ sendProcessMessage("获取公钥失败", MessagePosition.Right);
+ throw new Error('downloadPublicKey failed');
+ }
+ sendProcessMessage("获取公钥成功", MessagePosition.Right);
+ }
+
+ // 发送数据到服务端
+ async sendMessageToServer(message: string): Promise {
+ sendProcessMessage("开始发送数据到服务端...", MessagePosition.Right);
+ // 消息加密
+ let encryptionMessage: string = await this.encryption(message);
+ sendProcessMessage("发送加密数据到服务端...", MessagePosition.Right);
+ // 发送服务端
+ await this.server.receiveMessageFromClient(encryptionMessage);
+ }
+
+ // 从服务端接收数据
+ async receiveMessageFromServer(): Promise {
+ sendProcessMessage("开始接收服务端数据...", MessagePosition.Right);
+ let signMessage: string = await this.server.sendMessageToClient();
+ // 验证签名
+ let signData: SignedData = JSON.parse(signMessage);
+ sendProcessMessage("开始验证签名...", MessagePosition.Right);
+ let isVerified: boolean = await verify(signData.encryptedMessage, signData.signedMessage, this.publicKey);
+ if (isVerified) {
+ sendProcessMessage("签名验证成功,开始解密...", MessagePosition.Right);
+ let decryptedMessage = await aesGcmDecrypt(signData.encryptedMessage, this.aesKey);
+ sendProcessMessage("解密成功,解密结果:" + decryptedMessage, MessagePosition.Right);
+ return decryptedMessage;
+ }
+ sendProcessMessage("签名验证失败", MessagePosition.Right);
+ return "签名验证失败";
+ }
+
+ // 加密
+ async encryption(message: string): Promise {
+ if (!this.publicKey) {
+ sendProcessMessage("本地未找到公钥,开始下载...", MessagePosition.Right);
+ // 获取公钥
+ await this.downloadPublicKey();
+ sendProcessMessage("公钥下载成功", MessagePosition.Right);
+ }
+ if (this.aesKey === "") {
+ sendProcessMessage("本地未找到AES密钥,开始生成...", MessagePosition.Right);
+ // 生成AES密钥
+ this.aesKey = await generateAesKey();
+ sendProcessMessage(`AES密钥生成成功,密钥:${this.aesKey},开始发送到服务端...`, MessagePosition.Right);
+ // 将AES密钥发送到服务端
+ await this.sendAesKeyToServer();
+ }
+ sendProcessMessage(`客户端开始加密数据...`, MessagePosition.Right);
+ let encryptionResult: string = await aesGcmEncrypt(message, this.aesKey);
+ sendProcessMessage(`客户端加密成功,加密结果:${encryptionResult}`, MessagePosition.Right);
+ // 使用AES加密数据
+ return encryptionResult;
+ }
+
+ // 发送AES密钥到服务端
+ async sendAesKeyToServer() {
+ sendProcessMessage(`AES密钥进行加密...`, MessagePosition.Right);
+ // 对AES密钥使用公钥进行加密
+ let encryptedAesKey: string = await rsaEncryption(this.aesKey, this.publicKey);
+ sendProcessMessage(`加密成功,加密结果:${encryptedAesKey},开始发送到服务端...`, MessagePosition.Right);
+ // 将加密后的AES密钥发送到服务端
+ await this.server.receiveKeyFromClient(encryptedAesKey);
+ }
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/ets/entryability/EntryAbility.ets b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/entryability/EntryAbility.ets
new file mode 100644
index 0000000000000000000000000000000000000000..3b2f952c0de46b4376ecaf31f40514f3a5ef9870
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/entryability/EntryAbility.ets
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { window } from '@kit.ArkUI';
+
+const DOMAIN = 0x0000;
+
+export default class EntryAbility extends UIAbility {
+ onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
+ this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate');
+ }
+
+ onDestroy(): void {
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy');
+ }
+
+ onWindowStageCreate(windowStage: window.WindowStage): void {
+ // Main window is created, set main page for this ability
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
+
+ windowStage.loadContent('pages/KeyManager', (err) => {
+ if (err.code) {
+ hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
+ return;
+ }
+ hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
+ });
+ }
+
+ onWindowStageDestroy(): void {
+ // Main window is destroyed, release UI related resources
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
+ }
+
+ onForeground(): void {
+ // Ability has brought to foreground
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground');
+ }
+
+ onBackground(): void {
+ // Ability has back to background
+ hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground');
+ }
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets
new file mode 100644
index 0000000000000000000000000000000000000000..0a97e21bd7a15599af76a806695860ff1eb0ebfe
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { hilog } from '@kit.PerformanceAnalysisKit';
+import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit';
+
+const DOMAIN = 0x0000;
+
+export default class EntryBackupAbility extends BackupExtensionAbility {
+ async onBackup() {
+ hilog.info(DOMAIN, 'testTag', 'onBackup ok');
+ await Promise.resolve();
+ }
+
+ async onRestore(bundleVersion: BundleVersion) {
+ hilog.info(DOMAIN, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion));
+ await Promise.resolve();
+ }
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/ets/pages/KeyManager.ets b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/pages/KeyManager.ets
new file mode 100644
index 0000000000000000000000000000000000000000..63819e4f98837cb638459f737c3de0a21b4a6f83
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/pages/KeyManager.ets
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { LocalClient } from '../client/LocalClient';
+import { KeyboardAvoidMode } from '@kit.ArkUI';
+import { emitter } from '@kit.BasicServicesKit';
+import { Message, messageEvent, MessagePosition, sendProcessMessage } from '../utils/Utils';
+import { inputMethod } from '@kit.IMEKit';
+
+/**
+ * 模拟数据加密传输流程
+ * 1. 请求服务端,获取公钥。
+ * 2. 服务端生成RSA密钥,用私钥对公钥签名后返回给客户端。
+ * 3. 客户端验证公钥签名。
+ * 4. 生成AES密钥,通过公钥加密后发送到服务端
+ * 5. 服务端解密并存储AES密钥
+ * 6. 客户端使用AES密钥对数据进行加密,发送给服务端
+ * 7. 服务端使用AES密钥对数据进行解密,存储
+ * 8. 服务端使用AES密钥对数据进行加密
+ * 9. 服务端使用私钥对数据签名,并发送给客户端
+ * 10. 客户端使用公钥对数据验签,判断是否成功
+ * 11. 验签成功后使用AES密钥解密数据
+ */
+
+@Entry
+@Component
+struct KeyManager {
+ @State message: string = "";
+ private client: LocalClient = new LocalClient();
+ @State messageArray: Message[] = [];
+
+ aboutToAppear(): void {
+ this.getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE);
+
+ emitter.on(messageEvent, (data: emitter.EventData) => {
+ let message: Message = new Message();
+ message.message = data.data?.message;
+ message.position = data.data?.position;
+ this.messageArray.push(message);
+ });
+ }
+
+ build() {
+ RelativeContainer() {
+ Row() {
+ TextInput({ text: this.message, placeholder: $r('app.string.please_input') })
+ .id('input')
+ .onChange((value: string) => {
+ this.message = value;
+ })
+ .layoutWeight(4)
+ Button($r('app.string.send'))
+ .id('button')
+ .onClick(() => {
+ inputMethod.getController().stopInputSession();
+ this.sendMessage(this.message);
+ this.message = "";
+ })
+ .layoutWeight(1)
+ .margin({
+ left: 10
+ })
+ }
+ .padding({
+ left: 10,
+ right: 10
+ })
+ .alignRules({
+ bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
+ left: { anchor: '__container__', align: HorizontalAlign.Start },
+ right: { anchor: '__container__', align: HorizontalAlign.End }
+ })
+ .height(60)
+ .alignItems(VerticalAlign.Center)
+ .width('100%')
+ .id("row_input")
+
+ List() {
+ ForEach(this.messageArray, (message: Message) => {
+ ListItem() {
+ Row() {
+ Text(message.message)
+ .padding(10)
+ .fontColor(message.position === MessagePosition.Left ? Color.Blue : Color.Black)
+ .constraintSize({
+ maxWidth: '80%'
+ })
+ .borderRadius(10)
+ .backgroundColor("#F1F1F1")
+ .margin({
+ left: message.position === MessagePosition.Left ? 10 : 0,
+ right: message.position === MessagePosition.Right ? 10 : 0
+ })
+ }
+ .width('100%')
+ .justifyContent(message.position === MessagePosition.Left ? FlexAlign.Start : FlexAlign.End)
+ .margin({
+ top: 10
+ })
+ }
+ })
+ }.id('list')
+ .scrollBar(BarState.Off)
+ .alignRules({
+ bottom: { anchor: 'row_input', align: VerticalAlign.Top },
+ top: { anchor: '__container__', align: VerticalAlign.Top },
+ left: { anchor: '__container__', align: HorizontalAlign.Start },
+ right: { anchor: '__container__', align: HorizontalAlign.End }
+ })
+ .expandSafeArea([SafeAreaType.KEYBOARD])
+
+ }
+ .height('100%')
+ .width('100%')
+ }
+
+ async sendMessage(message: string) {
+ sendProcessMessage("需要发送给服务端的数据是:" + message, MessagePosition.Right);
+ await this.client.sendMessageToServer(message);
+
+ setTimeout(async () => {
+ await this.client.receiveMessageFromServer();
+ }, 2000)
+ }
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/ets/server/LocalMockServer.ets b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/server/LocalMockServer.ets
new file mode 100644
index 0000000000000000000000000000000000000000..da91749820b6d0e6d26362ed6779ca569215910e
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/server/LocalMockServer.ets
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Logger from '../utils/Logger';
+import { aesGcmDecrypt, aesGcmEncrypt } from '../utils/AesUtils';
+import { generateRsaKey, rsaDecryption, RsaKey, sign } from '../utils/RsaUtils';
+import { emitter } from '@kit.BasicServicesKit';
+import { messageEvent, MessagePosition, sendProcessMessage } from '../utils/Utils';
+
+const TAG: string = 'LocalMockServer';
+
+class SignedData {
+ encryptedMessage: string = "";
+ signedMessage: string = "";
+}
+
+/**
+ * 模拟服务端进行RSA密钥生成、解密、签名
+ */
+export class LocalMockServer {
+ private publicKey: string = "";
+ private privateKey: string = "";
+ private messageStorage: string[] = [];
+ private aesKey: string = "";
+
+ //生成证书
+ async createKey() {
+ sendProcessMessage("模拟服务端开始生成证书", MessagePosition.Left);
+ let rsaKey: RsaKey | undefined = await generateRsaKey()
+ if (rsaKey) {
+ this.publicKey = rsaKey.publicKey;
+ this.privateKey = rsaKey.privateKey;
+ sendProcessMessage("模拟服务端生成证书成功,公钥:" + this.publicKey, MessagePosition.Left);
+ }
+ }
+
+ // 模拟公钥下载
+ async downloadPublicKey(): Promise {
+ if (this.publicKey === "") {
+ // 创建密钥
+ await this.createKey();
+ }
+ // 对公钥进行签名
+ let signResult: string = await sign(this.publicKey, this.privateKey);
+ let publicKeyResult: SignedData = { encryptedMessage: this.publicKey, signedMessage: signResult };
+ return JSON.stringify(publicKeyResult);
+ }
+
+ async receiveKeyFromClient(encryptedAesKey: string) {
+ sendProcessMessage(`模拟服务端接收到AES密钥:${encryptedAesKey}`, MessagePosition.Left);
+ this.aesKey = await rsaDecryption(encryptedAesKey, this.privateKey);
+ sendProcessMessage(`模拟服务端AES密钥解密成功,AES密钥为:${this.aesKey}`, MessagePosition.Left);
+ Logger.info(TAG, 'receive key from client success, server aesKey:' + this.aesKey);
+ }
+
+ // 获取客户端消息
+ async receiveMessageFromClient(message: string): Promise {
+ sendProcessMessage("模拟服务端接收到数据,开始解密...", MessagePosition.Left);
+ // 解密
+ let decryptedMessage = await aesGcmDecrypt(message, this.aesKey);
+ sendProcessMessage(`模拟服务端解密成功,解密结果:${decryptedMessage}`, MessagePosition.Left);
+ // 存储
+ this.messageStorage.push(decryptedMessage);
+ }
+
+ async sendMessageToClient(): Promise {
+ let needSendMessage: string = "这是模拟服务端返回的测试数据," + this.messageStorage[this.messageStorage.length-1];
+ sendProcessMessage(`模拟服务端开始发送数据,将要发送的数据是:${needSendMessage}`, MessagePosition.Left);
+ sendProcessMessage(`模拟服务端开始加密数据:${needSendMessage}`, MessagePosition.Left);
+ let encryptedMessage: string = await aesGcmEncrypt(needSendMessage, this.aesKey);
+ sendProcessMessage(`模拟服务端加密成功,加密结果:${encryptedMessage}`, MessagePosition.Left);
+ // 签名
+ sendProcessMessage(`模拟服务端开始签名数据...`, MessagePosition.Left);
+ let signedMessage: string = await sign(encryptedMessage, this.privateKey);
+ sendProcessMessage(`模拟服务端签名成功,签名结果:${signedMessage}`, MessagePosition.Left);
+ let signData: SignedData = { encryptedMessage: encryptedMessage, signedMessage: signedMessage };
+ // 发送消息
+ return JSON.stringify(signData);
+ }
+}
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/ets/utils/AesUtils.ets b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/utils/AesUtils.ets
new file mode 100644
index 0000000000000000000000000000000000000000..45c469f0c11a130440b89b54ee7ebe99f51bbfc6
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/utils/AesUtils.ets
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { cryptoFramework } from "@kit.CryptoArchitectureKit";
+import Logger from "./Logger";
+import { arrayBufferToString, fromHexString, stringToUint8Array, TAG, uint8ArrayToShowStr } from "./Utils";
+
+// 加密数据结构
+class EncryptionData {
+ aesGcmTag: string = "";
+ encryptedText: string = "";
+}
+
+/**
+ * 生成AES密钥
+ * @returns AES密钥的16进制字符串
+ */
+export async function generateAesKey(): Promise {
+ try {
+ // 创建对称密钥生成器
+ let symKeyGenerator: cryptoFramework.SymKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256');
+ // 通过密钥生成器随机生成对称密钥
+ let symKey = await symKeyGenerator.generateSymKey();
+ // 获取对称密钥的二进制数据,输出长度为256bit的字节流
+ let encodedKey: cryptoFramework.DataBlob = symKey.getEncoded();
+ let data: Uint8Array = encodedKey.data;
+ // 将二进制数据转为16进制string。
+ return uint8ArrayToShowStr(data);
+ } catch (error) {
+ Logger.error(TAG, 'AES create failed');
+ return "";
+ }
+}
+
+/**
+ * AES解密
+ * @param encryptedMessage 已经加密的数据
+ * @param aesKey AES密钥
+ * @returns 解密后的数据
+ */
+export async function aesGcmDecrypt(encryptedMessage: string, aesKey: string): Promise {
+ let encryptionData: EncryptionData = JSON.parse(encryptedMessage);
+ try {
+ // 创建对称密钥生成器实例
+ let symKeyGenerator: cryptoFramework.SymKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256');
+ // 将AES密钥字符串转换为密钥类型
+ let symKeyBlob: cryptoFramework.DataBlob = { data: fromHexString(aesKey) };
+ let key: cryptoFramework.SymKey = await symKeyGenerator.convertKey(symKeyBlob);
+ // 指定算法名称(含密钥长度)、加密模式以及填充方法的组合
+ let cipherAlgName: string = 'AES256|GCM|PKCS7';
+ let cipher: cryptoFramework.Cipher = cryptoFramework.createCipher(cipherAlgName);
+ // 适用于GCM模式的加密参数
+ let globalGcmParams: cryptoFramework.GcmParamsSpec = genGcmParamsSpec(encryptionData.aesGcmTag);
+ // 表示进行加密操作
+ let mode: cryptoFramework.CryptoMode = cryptoFramework.CryptoMode.DECRYPT_MODE;
+ // Cipher中init、update、doFinal为三段式接口,需要成组使用。其中init和doFinal必选,update可选。
+ // 初始化加解密的cipher对象
+ await cipher.init(mode, key, globalGcmParams);
+ // 将明文转换为Uint8Array,用于后续加密操作
+ let plainText: cryptoFramework.DataBlob = { data: fromHexString(encryptionData.encryptedText) };
+ // 分段更新加密或者解密数据操作,可选方法
+ let cipherTextBlob: cryptoFramework.DataBlob = await cipher.update(plainText);
+ await cipher.doFinal(null);
+ let tmpArr: Uint8Array = cipherTextBlob.data;
+ // 将加密后的密文转换为字符串
+ let decryptedText: string = arrayBufferToString(tmpArr);
+ return decryptedText;
+ } catch (error) {
+ Logger.error(TAG, `AES decrypt failed, ${error.code}, ${error.message}`);
+ return "";
+ }
+}
+
+/**
+ * AES加密
+ * @param textString 需要加密的数据
+ * @param aesKey AES密钥
+ * @returns
+ */
+export async function aesGcmEncrypt(textString: string, aesKey: string): Promise {
+ try {
+ // 创建对称密钥生成器实例
+ let symKeyGenerator: cryptoFramework.SymKeyGenerator = cryptoFramework.createSymKeyGenerator('AES256');
+ // 将AES密钥字符串转换为密钥类型
+ let symKeyBlob: cryptoFramework.DataBlob = { data: fromHexString(aesKey) };
+ let key: cryptoFramework.SymKey = await symKeyGenerator.convertKey(symKeyBlob);
+ // 指定算法名称(含密钥长度)、加密模式以及填充方法的组合
+ let cipherAlgName: string = 'AES256|GCM|PKCS7';
+ let cipher: cryptoFramework.Cipher = cryptoFramework.createCipher(cipherAlgName);
+ // 适用于GCM模式的加密参数
+ let globalGcmParams: cryptoFramework.GcmParamsSpec = genGcmParamsSpec();
+ // 表示进行加密操作
+ let mode: cryptoFramework.CryptoMode = cryptoFramework.CryptoMode.ENCRYPT_MODE;
+ // Cipher中init、update、doFinal为三段式接口,需要成组使用。其中init和doFinal必选,update可选。
+ // 初始化加解密的cipher对象
+ await cipher.init(mode, key, globalGcmParams);
+ // 将明文转换为Uint8Array,用于后续加密操作
+ let plainText: cryptoFramework.DataBlob = { data: stringToUint8Array(textString) };
+ // 分段更新加密或者解密数据操作,可选方法
+ let cipherTextBlob: cryptoFramework.DataBlob = await cipher.update(plainText);
+ let tmpArr: Uint8Array = cipherTextBlob.data;
+ // 将加密后的密文转换为16进制字符串
+ let encryptedText: string = uint8ArrayToShowStr(tmpArr);
+ // 用于处理剩余数据和本次传入的数据,并最终结束加密或解密操作。
+ // 对于GCM模式的对称加密:一次加密流程中,如果将每一次update和doFinal的结果拼接起来,会得到“密文+authTag”,即末尾的16字节(GCM模式)或12字节(CCM模式)是authTag,
+ // 而其余部分均为密文。(也就是说,如果doFinal的data参数传入null,则doFinal的结果就是authTag)
+ // authTag需要填入解密时的GcmParamsSpec;密文则作为解密时的入参data。
+ let authTag: cryptoFramework.DataBlob = await cipher.doFinal(null);
+ let tmoTagArr: Uint8Array = authTag.data;
+ let aesGcmTag: string = uint8ArrayToShowStr(tmoTagArr);
+ // 将AES的加密结果转换为JSON对象
+ let aesEncryptResult: EncryptionData = { aesGcmTag: aesGcmTag, encryptedText: encryptedText };
+ return JSON.stringify(aesEncryptResult);
+ } catch (error) {
+ Logger.error(TAG, `AES encrypt failed, ${error.code}, ${error.message}`);
+ return "";
+ }
+}
+
+/**
+ * AES加密参数
+ * @param authTag 加解密参数,加密数据时不需要传参,会在调用cipher的doFinal方法时生成,解密时需要传入
+ * @returns
+ */
+export function genGcmParamsSpec(authTag?: string): cryptoFramework.GcmParamsSpec {
+ let arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 12 bytes
+ let dataIv = new Uint8Array(arr);
+ let ivBlob: cryptoFramework.DataBlob = { data: dataIv };
+
+ arr = [0, 0, 0, 0, 0, 0, 0, 0]; // 8 bytes
+ let dataAad = new Uint8Array(arr);
+ let aadBlob: cryptoFramework.DataBlob = { data: dataAad };
+
+ arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // 16 bytes
+ let dataTag = new Uint8Array(arr);
+ // GCM的authTag在加密时从doFinal结果中获取,在解密时填入init函数的params参数中
+ let tagBlob: cryptoFramework.DataBlob = { data: authTag ? fromHexString(authTag) : dataTag };
+
+ let gcmParamsSpec: cryptoFramework.GcmParamsSpec = {
+ iv: ivBlob,
+ aad: aadBlob,
+ authTag: tagBlob,
+ algName: 'GcmParamsSpec'
+ };
+ return gcmParamsSpec;
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/ets/utils/Logger.ets b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/utils/Logger.ets
new file mode 100644
index 0000000000000000000000000000000000000000..2ce64f4033b709ae7da1feac2ccae81c32ae04af
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/utils/Logger.ets
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { hilog } from '@kit.PerformanceAnalysisKit';
+
+const TAG = 'KeyManager';
+
+class Logger {
+ private domain: number;
+ private prefix: string;
+ private format: string = '%{public}s, %{public}s';
+
+ constructor(prefix: string) {
+ this.prefix = prefix;
+ this.domain = 0xFF00;
+ }
+
+ debug(...args: string[]): void {
+ hilog.debug(this.domain, this.prefix, this.format, args);
+ }
+
+ info(...args: string[]): void {
+ hilog.info(this.domain, this.prefix, this.format, args);
+ }
+
+ warn(...args: string[]): void {
+ hilog.warn(this.domain, this.prefix, this.format, args);
+ }
+
+ error(...args: string[]): void {
+ hilog.error(this.domain, this.prefix, this.format, args);
+ }
+}
+
+export default new Logger(TAG);
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/ets/utils/RsaUtils.ets b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/utils/RsaUtils.ets
new file mode 100644
index 0000000000000000000000000000000000000000..2e401048d959e5228cad45bb7b9e9cefd8e7ab84
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/utils/RsaUtils.ets
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { cryptoFramework } from "@kit.CryptoArchitectureKit";
+import Logger from "./Logger";
+import { fromHexString, stringToUint8Array, TAG, uint8ArrayToShowStr } from "./Utils";
+
+// RSA密钥对
+export interface RsaKey {
+ publicKey: string;
+ privateKey: string;
+}
+
+/**
+ * 生成RSA密钥对
+ * @returns RSA密钥对
+ */
+export async function generateRsaKey(): Promise {
+ try {
+ // 创建非对称密钥生成器
+ let rsaKeyGenerator: cryptoFramework.AsyKeyGenerator = cryptoFramework.createAsyKeyGenerator('RSA1024');
+ // 通过密钥生成器随机生成非对称密钥
+ let keyPair: cryptoFramework.KeyPair = await rsaKeyGenerator.generateKeyPair();
+ // 获取非对称密钥的二进制数据
+ let encodedPriKey: cryptoFramework.DataBlob = keyPair.priKey.getEncoded();
+ // 私钥
+ let priKeyData: Uint8Array = encodedPriKey.data;
+ let encodedPubKey: cryptoFramework.DataBlob = keyPair.pubKey.getEncoded();
+ // 公钥
+ let pubKeyData: Uint8Array = encodedPubKey.data;
+ let rsaKey: RsaKey = {
+ privateKey: uint8ArrayToShowStr(priKeyData),
+ publicKey: uint8ArrayToShowStr(pubKeyData)
+ };
+ return rsaKey;
+ } catch (error) {
+ Logger.error(TAG, 'RSA create failed');
+ return undefined;
+ }
+}
+
+/**
+ * RSA加密
+ * @param data 需要加密的数据
+ * @param publicKey 公钥
+ * @returns 完成加密的数据
+ */
+export async function rsaEncryption(data: string, publicKey: string): Promise {
+ try {
+ // 创建非对称密钥生成器实例
+ let rsaKeyGenerator: cryptoFramework.AsyKeyGenerator = cryptoFramework.createAsyKeyGenerator('RSA1024');
+ // 将RSA密钥字符串转换为密钥类型
+ let pubKeyBlob: cryptoFramework.DataBlob = { data: fromHexString(publicKey) };
+ let key: cryptoFramework.PubKey = (await rsaKeyGenerator.convertKey(pubKeyBlob, null)).pubKey;
+ // 指定算法名称(含密钥长度)、加密模式以及填充方法的组合
+ let cipherAlgName: string = 'RSA1024|PKCS1';
+ let cipher: cryptoFramework.Cipher = cryptoFramework.createCipher(cipherAlgName);
+ // 表示进行加密操作
+ let mode: cryptoFramework.CryptoMode = cryptoFramework.CryptoMode.ENCRYPT_MODE;
+ // Cipher中init、update、doFinal为三段式接口,需要成组使用。其中init和doFinal必选,update可选。
+ // 初始化加解密的cipher对象
+ await cipher.init(mode, key, null);
+ // 将明文转换为Uint8Array,用于后续加密操作
+ let plainText: cryptoFramework.DataBlob = { data: fromHexString(data) };
+ // 用于处理剩余数据和本次传入的数据,并最终结束加密或解密操作。
+ // 在RSA非对称加解密中,doFinal加解密本次传入的数据,通过注册回调函数获取加密或者解密数据。如果数据量较大,可以多次调用doFinal,拼接结果得到完整的明文/密文。
+ let encryptBlob: cryptoFramework.DataBlob = await cipher.doFinal(plainText);
+ return uint8ArrayToShowStr(encryptBlob.data);
+ } catch (err) {
+ Logger.error(TAG, `RSA encryption failed, ${err.code}, ${err.message}`);
+ }
+ return "";
+}
+
+
+/**
+ * RSA解密
+ * @param encryptedData 经过加密的数据
+ * @param privateKey 私钥
+ * @returns 解密后的数据
+ */
+export async function rsaDecryption(encryptedData: string, privateKey: string): Promise {
+ try {
+ // 创建非对称密钥生成器实例
+ let rsaKeyGenerator: cryptoFramework.AsyKeyGenerator = cryptoFramework.createAsyKeyGenerator('RSA1024');
+ // 将RSA密钥字符串转换为密钥类型
+ let priKeyBlob: cryptoFramework.DataBlob = { data: fromHexString(privateKey) };
+ let key: cryptoFramework.PriKey = (await rsaKeyGenerator.convertKey(null, priKeyBlob)).priKey;
+ let cipherAlgName: string = 'RSA1024|PKCS1';
+ let cipher: cryptoFramework.Cipher = cryptoFramework.createCipher(cipherAlgName);
+ // 表示进行解密操作
+ let mode: cryptoFramework.CryptoMode = cryptoFramework.CryptoMode.DECRYPT_MODE;
+ // Cipher中init、update、doFinal为三段式接口,需要成组使用。其中init和doFinal必选,update可选。
+ // 初始化加解密的cipher对象
+ await cipher.init(mode, key, null);
+ // 将秘文转换为Uint8Array,用于后续解密操作
+ let plainText: cryptoFramework.DataBlob = { data: fromHexString(encryptedData) };
+ let decryptBlob: cryptoFramework.DataBlob = await cipher.doFinal(plainText);
+ return uint8ArrayToShowStr(decryptBlob.data);
+ } catch (err) {
+ Logger.error(TAG, `RSA decryption failed, ${err.code}, ${err.message}`);
+ }
+ return "";
+}
+
+/**
+ * 验证签名
+ * @param encryptedData 待验证的数据
+ * @param singedData 签名信息
+ * @param publicKey 公钥
+ * @returns 签名验证是否通过
+ */
+export async function verify(encryptedData: string, singedData: string, publicKey: string): Promise {
+ try {
+ let verifyer = cryptoFramework.createVerify('RSA1024|PKCS1|SHA256');
+ // 创建非对称密钥生成器实例
+ let rsaKeyGenerator: cryptoFramework.AsyKeyGenerator = cryptoFramework.createAsyKeyGenerator('RSA1024');
+ // 将RSA密钥字符串转换为密钥类型
+ let pubKeyBlob: cryptoFramework.DataBlob = { data: fromHexString(publicKey) };
+ let key: cryptoFramework.PubKey = (await rsaKeyGenerator.convertKey(pubKeyBlob, null)).pubKey;
+ let encryptedBlob: Uint8Array = stringToUint8Array(encryptedData);
+ let signedBlob: Uint8Array = fromHexString(singedData);
+ await verifyer.init(key);
+ let result: boolean = await verifyer.verify({ data: encryptedBlob }, { data: signedBlob });
+ return result;
+ } catch (err) {
+ Logger.error(TAG, `RSA verify failed, ${err.code}, ${err.message}`);
+ }
+ return false;
+}
+
+/**
+ * 签名
+ * @param data 需要签名的数据
+ * @param privateKey 私钥
+ * @returns 签名信息
+ */
+export async function sign(data: string, privateKey: string): Promise {
+ try {
+ let signer = cryptoFramework.createSign('RSA1024|PKCS1|SHA256');
+ // 创建非对称密钥生成器实例
+ let rsaKeyGenerator: cryptoFramework.AsyKeyGenerator = cryptoFramework.createAsyKeyGenerator('RSA1024');
+ // 将RSA密钥字符串转换为密钥类型
+ let priKeyBlob: cryptoFramework.DataBlob = { data: fromHexString(privateKey) };
+ let key: cryptoFramework.PriKey = (await rsaKeyGenerator.convertKey(null, priKeyBlob)).priKey;
+ await signer.init(key);
+ let signBlob = stringToUint8Array(data);
+ let signedBlob = await signer.sign({ data: signBlob });
+ let tmpArr: Uint8Array = signedBlob.data;
+ let rsaSignedBlobString = uint8ArrayToShowStr(tmpArr);
+ return rsaSignedBlobString;
+ } catch (error) {
+ Logger.error(TAG, `RSA sign failed, ${error.code}, ${error.message}`);
+ return "";
+ }
+}
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/ets/utils/Utils.ets b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/utils/Utils.ets
new file mode 100644
index 0000000000000000000000000000000000000000..134c8b392ea7d23e4baa5571ae63661966e095cd
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/ets/utils/Utils.ets
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { buffer, util } from '@kit.ArkTS';
+import { emitter } from '@kit.BasicServicesKit';
+
+export const TAG: string = "KeyManager";
+
+export enum MessagePosition {
+ Left, Right
+}
+
+export class Message {
+ message: string = "";
+ position: MessagePosition = MessagePosition.Right;
+}
+
+export const messageEvent: emitter.InnerEvent = {
+ eventId: 1
+}
+
+export function sendProcessMessage(message: string, position: MessagePosition) {
+ emitter.emit(messageEvent, {
+ data: {
+ message: message,
+ position: position
+ }
+ })
+}
+
+// 字节流以16进制字符串输出
+export function uint8ArrayToShowStr(uint8Array: Uint8Array): string {
+ let hexString: string = '';
+ for (let i = 0; i < uint8Array.length; i++) {
+ let char = ('00' + uint8Array[i].toString(16)).slice(-2);
+ hexString += char;
+ }
+ return hexString;
+}
+
+// 16进制字符串转字节流
+export function fromHexString(hexString: string): Uint8Array {
+ return new Uint8Array(buffer.from(hexString, 'hex').buffer);
+}
+
+
+// 字节流转字符串
+export function arrayBufferToString(uint8Array: Uint8Array): string {
+ let textDecoderOptions: util.TextDecoderOptions = {
+ fatal: false,
+ ignoreBOM: true
+ }
+ let decodeToStringOptions: util.DecodeToStringOptions = {
+ stream: false
+ }
+ let textDecoder = util.TextDecoder.create('utf-8', textDecoderOptions);
+ let retStr = textDecoder.decodeToString(new Uint8Array(uint8Array), decodeToStringOptions);
+ console.info('字节流转成可理解的字符串:' + retStr);
+ return retStr;
+}
+
+// 可理解的字符串转成字节流
+export function stringToUint8Array(str: string): Uint8Array {
+ console.info('字符串转成字节流:' + new Uint8Array(buffer.from(str, 'utf-8').buffer));
+ return new Uint8Array(buffer.from(str, 'utf-8').buffer);
+}
+
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/module.json5 b/code/BasicFeature/Security/KeyManager/entry/src/main/module.json5
new file mode 100644
index 0000000000000000000000000000000000000000..4144486d1af4c03b0d767cce1cda86fc0d697f91
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/module.json5
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ "module": {
+ "name": "entry",
+ "type": "entry",
+ "description": "$string:module_desc",
+ "mainElement": "EntryAbility",
+ "deviceTypes": [
+ "default",
+ "tablet"
+ ],
+ "deliveryWithInstall": true,
+ "installationFree": false,
+ "pages": "$profile:main_pages",
+ "abilities": [
+ {
+ "name": "EntryAbility",
+ "srcEntry": "./ets/entryability/EntryAbility.ets",
+ "description": "$string:EntryAbility_desc",
+ "icon": "$media:layered_image",
+ "label": "$string:EntryAbility_label",
+ "startWindowIcon": "$media:startIcon",
+ "startWindowBackground": "$color:start_window_background",
+ "exported": true,
+ "skills": [
+ {
+ "entities": [
+ "entity.system.home"
+ ],
+ "actions": [
+ "action.system.home"
+ ]
+ }
+ ]
+ }
+ ],
+ "extensionAbilities": [
+ {
+ "name": "EntryBackupAbility",
+ "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
+ "type": "backup",
+ "exported": false,
+ "metadata": [
+ {
+ "name": "ohos.extension.backup",
+ "resource": "$profile:backup_config"
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/element/color.json b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/element/color.json
new file mode 100644
index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/element/color.json
@@ -0,0 +1,8 @@
+{
+ "color": [
+ {
+ "name": "start_window_background",
+ "value": "#FFFFFF"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/element/float.json b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/element/float.json
new file mode 100644
index 0000000000000000000000000000000000000000..33ea22304f9b1485b5f22d811023701b5d4e35b6
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/element/float.json
@@ -0,0 +1,8 @@
+{
+ "float": [
+ {
+ "name": "page_text_font_size",
+ "value": "50fp"
+ }
+ ]
+}
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/element/string.json b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/element/string.json
new file mode 100644
index 0000000000000000000000000000000000000000..cc45e90d0cd1673ca6fe01eefed69651bb57bd43
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/element/string.json
@@ -0,0 +1,24 @@
+{
+ "string": [
+ {
+ "name": "module_desc",
+ "value": "通用密钥管理"
+ },
+ {
+ "name": "EntryAbility_desc",
+ "value": "通用密钥管理"
+ },
+ {
+ "name": "EntryAbility_label",
+ "value": "通用密钥管理"
+ },
+ {
+ "name": "please_input",
+ "value": "通用密钥管理"
+ },
+ {
+ "name": "send",
+ "value": "发送"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/media/background.png b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/media/background.png
new file mode 100644
index 0000000000000000000000000000000000000000..923f2b3f27e915d6871871deea0420eb45ce102f
Binary files /dev/null and b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/media/background.png differ
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/media/foreground.png b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/media/foreground.png
new file mode 100644
index 0000000000000000000000000000000000000000..97014d3e10e5ff511409c378cd4255713aecd85f
Binary files /dev/null and b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/media/foreground.png differ
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/media/layered_image.json b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/media/layered_image.json
new file mode 100644
index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/media/layered_image.json
@@ -0,0 +1,7 @@
+{
+ "layered-image":
+ {
+ "background" : "$media:background",
+ "foreground" : "$media:foreground"
+ }
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/media/startIcon.png b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/media/startIcon.png
new file mode 100644
index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b
Binary files /dev/null and b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/media/startIcon.png differ
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/profile/backup_config.json b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/profile/backup_config.json
new file mode 100644
index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/profile/backup_config.json
@@ -0,0 +1,3 @@
+{
+ "allowToBackupRestore": true
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/profile/main_pages.json b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/profile/main_pages.json
new file mode 100644
index 0000000000000000000000000000000000000000..189fe9b252ee8128beb79ca720b44d95c088f249
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/base/profile/main_pages.json
@@ -0,0 +1,5 @@
+{
+ "src": [
+ "pages/KeyManager"
+ ]
+}
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/main/resources/dark/element/color.json b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/dark/element/color.json
new file mode 100644
index 0000000000000000000000000000000000000000..79b11c2747aec33e710fd3a7b2b3c94dd9965499
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/main/resources/dark/element/color.json
@@ -0,0 +1,8 @@
+{
+ "color": [
+ {
+ "name": "start_window_background",
+ "value": "#000000"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/ohosTest/ets/test/Ability.test.ets b/code/BasicFeature/Security/KeyManager/entry/src/ohosTest/ets/test/Ability.test.ets
new file mode 100644
index 0000000000000000000000000000000000000000..1e32ab19f4ebe2a8aee4bffd470098787cd99c1e
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/ohosTest/ets/test/Ability.test.ets
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
+import { beforeAll, describe, it } from '@ohos/hypium';
+import Logger from '../utils/Logger';
+import { Driver, ON } from '@kit.TestKit';
+
+const TAG = '[Sample_KeyManager]';
+const DELAY_MS_500: number = 500; // 指定500ms的延迟
+const DELAY_MS_2000: number = 2000; // 指定2000ms的延迟
+
+export default function abilityTest() {
+ const driver: Driver = Driver.create();
+ describe('ActsAbilityTest', () => {
+ beforeAll(async (done: Function) => {
+ Logger.info(TAG, 'beforeAll begin');
+ let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
+ try {
+ await abilityDelegator.startAbility({
+ bundleName: 'com.samples.keymanager',
+ abilityName: 'EntryAbility'
+ });
+ } catch (err) {
+ Logger.error(TAG, `beforeAll exception = ${JSON.stringify(err)}`);
+ }
+ await driver.delayMs(DELAY_MS_2000);
+ Logger.info(TAG, 'beforeAll end');
+ done();
+ });
+
+ // 查看页面显示是否正常,是否可以正常进行页面切换
+ it('通用密钥管理', 0, async (done: Function) => {
+ await driver.assertComponentExist(ON.id('input'));
+ await driver.assertComponentExist(ON.id('button'));
+ await driver.assertComponentExist(ON.id('list'));
+
+ let textInput = await driver.findComponent(ON.id('input'));
+ await driver.delayMs(DELAY_MS_500);
+ await textInput.inputText("测试数据");
+ await driver.delayMs(DELAY_MS_500);
+ let button = await driver.findComponent(ON.id('button'));
+ await driver.delayMs(DELAY_MS_500);
+ await button.click();
+ await driver.delayMs(DELAY_MS_500);
+ let list = await driver.findComponent(ON.id('list'));
+ await driver.delayMs(DELAY_MS_500);
+ await list.scrollToBottom();
+ await driver.delayMs(DELAY_MS_500);
+ await textInput.inputText("测试数据");
+ await driver.delayMs(DELAY_MS_500);
+ await button.click();
+ await driver.delayMs(DELAY_MS_500);
+ await list.scrollToBottom();
+ done();
+ })
+ })
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/ohosTest/ets/test/List.test.ets b/code/BasicFeature/Security/KeyManager/entry/src/ohosTest/ets/test/List.test.ets
new file mode 100644
index 0000000000000000000000000000000000000000..1eac52fcebe8958e19a7b8fed2e8f39c520a3e42
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/ohosTest/ets/test/List.test.ets
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import abilityTest from './Ability.test';
+
+export default function testsuite() {
+ abilityTest();
+}
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/ohosTest/ets/utils/Logger.ets b/code/BasicFeature/Security/KeyManager/entry/src/ohosTest/ets/utils/Logger.ets
new file mode 100644
index 0000000000000000000000000000000000000000..eaac4acd2b2fc38673f82120eddbe99e7a22c38c
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/ohosTest/ets/utils/Logger.ets
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import hilog from '@ohos.hilog';
+
+class Logger {
+ private domain: number = 0xF811;
+ private prefix: string = '';
+ private format: string = '%{public}s, %{public}s';
+
+ constructor(prefix: string) {
+ this.prefix = prefix;
+ this.domain = 0xF811;
+ }
+
+ debug(...args: string[]): void {
+ hilog.debug(this.domain, this.prefix, this.format, args);
+ }
+
+ info(...args: string[]): void {
+ hilog.info(this.domain, this.prefix, this.format, args);
+ }
+
+ warn(...args: string[]): void {
+ hilog.warn(this.domain, this.prefix, this.format, args);
+ }
+
+ error(...args: string[]): void {
+ hilog.error(this.domain, this.prefix, this.format, args);
+ }
+}
+
+export default new Logger('[Sample_KeyManager]');
\ No newline at end of file
diff --git a/code/BasicFeature/Security/KeyManager/entry/src/ohosTest/module.json5 b/code/BasicFeature/Security/KeyManager/entry/src/ohosTest/module.json5
new file mode 100644
index 0000000000000000000000000000000000000000..c3fd9dda3040d888d9d8b0b62bcb5d3b6fbeb614
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/entry/src/ohosTest/module.json5
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ "module": {
+ "name": "entry_test",
+ "type": "feature",
+ "deviceTypes": [
+ "default",
+ "tablet"
+ ],
+ "deliveryWithInstall": true,
+ "installationFree": false
+ }
+}
diff --git a/code/BasicFeature/Security/KeyManager/hvigor/hvigor-config.json5 b/code/BasicFeature/Security/KeyManager/hvigor/hvigor-config.json5
new file mode 100644
index 0000000000000000000000000000000000000000..a63d34ae5ce5833b3874807e2b8d472687c6c5bf
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/hvigor/hvigor-config.json5
@@ -0,0 +1,22 @@
+{
+ "modelVersion": "5.0.4",
+ "dependencies": {
+ },
+ "execution": {
+ // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */
+ // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */
+ // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */
+ // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */
+ // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */
+ },
+ "logging": {
+ // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */
+ },
+ "debugging": {
+ // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */
+ },
+ "nodeOptions": {
+ // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/
+ // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/
+ }
+}
diff --git a/code/BasicFeature/Security/KeyManager/hvigorfile.ts b/code/BasicFeature/Security/KeyManager/hvigorfile.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/hvigorfile.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { appTasks } from '@ohos/hvigor-ohos-plugin';
+
+export default {
+ system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
+ plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
+}
diff --git a/code/BasicFeature/Security/KeyManager/oh-package.json5 b/code/BasicFeature/Security/KeyManager/oh-package.json5
new file mode 100644
index 0000000000000000000000000000000000000000..feee3e2fc0ddc7df19a7498269e29d53fdd944e7
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/oh-package.json5
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+{
+ "modelVersion": "5.0.4",
+ "description": "Please describe the basic information.",
+ "dependencies": {
+ },
+ "devDependencies": {
+ "@ohos/hypium": "1.0.21",
+ "@ohos/hamock": "1.0.0"
+ }
+}
diff --git a/code/BasicFeature/Security/KeyManager/ohosTest.md b/code/BasicFeature/Security/KeyManager/ohosTest.md
new file mode 100644
index 0000000000000000000000000000000000000000..9cfdbc6529354b86bce4922ed55a7ab7b8887e81
--- /dev/null
+++ b/code/BasicFeature/Security/KeyManager/ohosTest.md
@@ -0,0 +1,12 @@
+# 通用密钥管理 测试用例归档
+
+## 用例表
+
+| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 |
+|-----------------|-----------------|---------------------|--------------------| -------- |---------|
+| 首次加密先生成密钥再进行加解密 | 成功拉起应用,进入案例页面 | 点击输入框,随意输入内容,点击发送按钮 | 页面显示从生成密钥到加解密的完整流程 | 是 | 是 |
+| 第二次加密只有加解密流程 | 在案例页面,并且已经加密过一次 | 点击输入框,随意输入内容,点击发送按钮 | 页面显示加解密的完整流程 | 是 | 是 |
+
+
+
+