


























应用的安全性是一个应用能否成功的关键。
HarmonyOS提供了系统安全、IDE/工具安全以及应用安全生态等三个层面的安全能力。
下面我们讨论应用安全生态层面的敏感数据存储和用户隐私保护的实践。
HarmonyOS安全能力是以分级安全为架构思想的基础安全底座,面向业务场景构建HarmonyOS安全的应用生态。其中的分级分为两个维度:一是设备的安全等级划分,二是数据的安全等级划分。
根据设备安全能力,比如是否有TEE、是否有安全存储芯片等,将设备安全等级分为SL1、SL2、SL3、SL4、SL5五个等级。例如,智能穿戴通常为低安全的SL1设备,手机、平板通常为高安全的设备。

设备从SL1到SL5分级,在完整性保护、加密及数据保护、权限及访问控制、可信执行环境和漏洞防利用这几个维度对应的安全能力要求逐渐提高。
敏感数据分类可帮助应用开发者根据数据的敏感性、价值以及数据泄露时的潜在影响来识别和分类数据。开发者需要了解不同类型的数据及其使用方式,并对数据进行分类,针对分类制定适当的安全措施和控制措施来保护数据并确保遵守相关法规和标准。下表举例说明了个人数据的分类:
|
个人数据分类 |
数据分级 |
举例 | |
|---|---|---|---|
|
个人/敏感数据 |
关键资产数据 |
严重 |
口令、认证TOKEN、密保问题答案等 |
|
个人种族数据 |
种族血统 |
||
|
权威社会识别标识 |
高 |
身份证号码、军官证、社会/保险保障号码(社会号码)、驾照、护照号码、签证授权编号 |
|
|
负向名誉数据 |
犯罪记录(刑事、民事犯罪和诉讼记录),入狱记录、纪律处分 |
||
|
健康数据 |
健康数据(身高、体重、体脂、血压、血糖、心率等)、医疗记录、性生活 |
||
|
家居控制数据 |
家居控制、HICar反向控制、手机投屏后反向控制等 |
||
|
年龄生辰数据 |
中 |
年龄,出生日期 |
|
|
虚拟网络身份标识 |
华为帐号、宽带帐号、其他网络帐号(电话号码、邮箱) |
||
|
一般社会识别标识 |
姓名 |
||
|
个人多媒体数据 |
照片、视频、录音、文字、日历日程、备忘录文本等 |
||
|
一般注册信息 |
低 |
昵称、头像、性别、国籍、出生地、教育程度、专业背景等 |
|
|
正向名誉数据 |
专业成就 |
||
|
非个人数据 |
匿名化数据 |
公开 |
匿名化处理后的个人数据 |
|
非个人数据 |
系统、设备信息中公开发布的数据,如:TCB模块的版本信息、完整性度量值、访问控制策略数据的完整性度量值、策略数据本身等 |
||
除了基于内容对数据分类,开发者还需遵守相关的法律法规,如通用数据保护条例(GDPR)和个人信息保护法等,以保护用户的隐私权和数据安全。下图是基于法律法规的数据分类图:

按照数据分类分级规范要求,可将数据分为S1、S2、S3、S4四个安全等级。
|
风险等级 |
风险标准 |
定义 |
样例 |
|---|---|---|---|
|
严重 |
S4 |
业界法律法规定义的特殊数据类型,涉及个人的最私密领域的信息或一旦泄露、篡改、破坏、销毁可能会给个人或组织造成重大的不利影响的数据。 |
政治观点、宗教和哲学信仰、工会成员资格、基因数据、生物信息、健康和性生活状况,性取向等或设备认证鉴权、个人信用卡等财物信息等。 |
|
高 |
S3 |
数据的泄露、篡改、破坏、销毁可能会给个人或组织导致严峻的不利影响 |
个人实时精确定位信息、运动轨迹等。 |
|
中 |
S2 |
数据的泄露、篡改、破坏、销毁可能会给个人或组织导致严重的不利影响 |
个人的详细通信地址、姓名昵称等。 |
|
低 |
S1 |
数据的泄露、篡改、破坏、销毁可能会给个人或组织导致有限的不利影响 |
性别、国籍、用户申请记录等。 |
数据安全标签和设备安全等级越高,加密措施和访问控制措施越严格,数据安全性越高。
数据跨设备同步时,数据管理基于数据安全标签和设备安全等级进行访问控制。规则为,在本设备的数据安全标签不高于对端设备的设备安全等级时,数据才能从本设备同步到对端设备,否则不能同步。具体访问控制矩阵如下:
|
设备安全级别 |
可同步的数据安全标签 |
|---|---|
|
SL1 |
S1 |
|
SL2 |
S1~S2 |
|
SL3 |
S1~S3 |
|
SL4 |
S1~S4 |
|
SL5 |
S1~S4 |
例如,手表通常为低安全的SL1设备。若创建数据安全标签为S1的数据库,则此数据库数据可以在这些设备间同步;若创建的数据库标签为S2-S4,则不能在这些设备间同步。
不同的文件路径具备不同的属性和特征。分级数据使用应用沙箱作为保护机制,避免数据受到恶意路径穿越访问。在应用文件目录中,根据不同的文件加密类型,区分了不同的目录。
应用如无特殊需要,应将数据存放在el2加密目录下,以尽可能保证数据安全。但是对于某些场景,一些应用文件需要在用户解锁前就可被访问,例如时钟、闹铃、壁纸等,此时应用需要将这些文件存放到设备级加密区(el1)。切换应用文件加密类型目录的方法请参见获取和修改加密分区。
应用文件目录的详细介绍请参考应用文件目录。
分级数据文件路径使用应用通用文件路径,获取路径代码如下:
getEL2Path() { let context = getContext(this) as common.UIAbilityContext; context.area = contextConstant.AreaMode.EL2; let file_path = context.filesDir + '/health_data.txt'; this.message = file_path; }
如果需要获取el1的路径者需要修改AreaMode,代码如下:
getEl1Path() { let context = getContext(this) as common.UIAbilityContext; context.area = contextConstant.AreaMode.EL1; let file_path = context.filesDir + '/health_data.txt'; this.message = file_path; }
应用如无特殊需要,应将数据存放在el2加密目录下。
系统的文件级加密提供了4种加密类型实现文件保护,应用可以根据自己的述求,把文件保存到相应的数据目录。下表是各个分级加密区所对应的策略:
|
分级加密 |
策略 |
|---|---|
|
el4 |
用户锁定设备后不久(一般为 10 秒钟),解密的数据保护类密钥会被从内存丢弃,此类的所有数据都无法访问,除非用户再次输入密码或使用指纹或面容解锁设备。 |
|
el3 |
用户锁定设备后,如果文件已经被打开,则文件始终可以被继续访问,一旦文件关闭(锁屏),文件将不能被再次访问,除非用户再次输入密码或使用指纹或面容解锁设备 |
|
el2 |
用户开机后首次解锁设备后,即可对文件进行访问。这是未分配给数据保护类的所有第三方应用数据的默认数据保护类 |
|
el1 |
设备在直接启动模式下和用户解锁设备后均可对文件进行访问 |
通用密钥库系统(OpenHarmony Universal KeyStore,简称HUKS),向业务提供了平台级的密钥管理能力,包括密钥的生成、导入、销毁、证明、协商、派生、加密/解密、签名/验签及访问控制等功能。HUKS是系统的底座安全能力,业务无需自己实现复杂的密码学算法、密钥访问控制模型,即可安全、便捷地使用HUKS提供的密钥全生命周期管理能力,进而保证业务敏感数据的安全性。
使用数据加密算法实现数据加密请参考通用加密库。
健康数据是日常生活中常见的敏感数据,并且在HarmonyOS设备中广泛存在,如手机,手表等。下面介绍体检报表的存储和保护的案例。
根据数据分类规则,体检数据被归类为高风险级别的数据。因此,在程序设计过程中,除了采用分级数据加密的保护措施外,还需要进行二次加密以加强数据的安全性。具体而言,可以先对数据进行一次加密,然后再使用分级数据加密的方法对加密后的数据进行处理。这样做的目的是为了提高数据的保密性和完整性,有效地防止潜在的数据泄露和非法访问。通过这样多层次的加密措施,体检数据将更加安全可靠地存储和传输。
程序的场景主要由三个页面构成,分别为体检列表页、数据录入页和数据详情页,页面如下:
图1 场景设计图

在实际开发中,数据的加密与解密经常是同时存在的,本场景中也是一样,由两个子场景组成,数据录入和数据详情。下面分别介绍这两个子场景的具体实现方式:
参考分级数据保护中代码获取到数据路径后按正常的文件读写即可,以下是文件读写的代码:
function writeFile(file_path: string, data: string) { let file = fileIo.openSync(file_path, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); let writeLen = fileIo.writeSync(file.fd, data); hilog.info(0x0000, 'AppDataSecurity', 'The length of str is: ' + writeLen); fileIo.closeSync(file); } function readFile(file_path: string): object { let file = fileIo.openSync(file_path, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); let arrayBuffer = new ArrayBuffer(1024); class Option { public offset: number = 0; public length: number = 0; } let option = new Option(); option.length = arrayBuffer.byteLength; let readLen = fileIo.readSync(file.fd, arrayBuffer, option); let buf = buffer.from(arrayBuffer, 0, readLen); hilog.info(0x0000, 'AppDataSecurity', `The length of of file: ${readLen}`); fileIo.closeSync(file); return [readLen, buf.toString()] }
在使用huks之前要先配置加密算法的规格,本案例中选择的加解密算法规格为密钥长度为128的AES算法。在使用加解密算法计算前要先生成算法key,以下是算法配置和生成算法key代码:
在生成算法key的配置函数中需要指出算法类型HUKS_ALG_AES,算法key的大小以及密钥用途:
function GetAesGenerateProperties() { let properties: Array<huks.HuksParam> = [{ tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }, { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_128 }, { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT }]; return properties; }
加密函数配置的算法规格需要跟生成算法key的配置保持一致。
HUKS_TAG_PADDING填充模式有三种不同填充模式。
HUKS_TAG_BLOCK_MODE有7种分组模式,对于CBC、CTR、OFB、CFB模式,仅使用HUKS_TAG_IV(密钥初始化的向量)加解密参数;对于GCM、CCM需要设置HUKS_TAG_NONCE(密钥加解密的字段)、HUKS_TAG_ASSOCIATED_DATA(附加身份验证数据的Tag )。并且需要配置HUKS_TAG_IV密钥初始化向量选项。
function GetAesEncryptProperties() { let properties: Array<huks.HuksParam> = [{ tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }, { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_128 }, { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT }, { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_PKCS7 }, { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC }, { tag: huks.HuksTag.HUKS_TAG_IV, value: StringToUint8Array(IV) }]; return properties; }
解密函数配置的算法规格需要跟加密算法的配置保持一致,不同点是需要设置密钥用途为解密。
function GetAesDecryptProperties() { let properties: Array<huks.HuksParam> = [{ tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }, { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_128 }, { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT }, { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_PKCS7 }, { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC }, { tag: huks.HuksTag.HUKS_TAG_IV, value: StringToUint8Array(IV) }]; return properties; }
完成上述配置后,调用生成算法key函数。
async function GenerateAesKey() { let genProperties = GetAesGenerateProperties(); let options: huks.HuksOptions = { properties: genProperties }; await huks.generateKeyItem(aesKeyAlias, options) .then((data) => { hilog.info(0x0000, 'AppDataSecurity', `promise: generate AES Key success, data = ${JSON.stringify(data)}`); }).catch((error: Error) => { hilog.error(0x0000, 'AppDataSecurity', `promise: generate AES Key failed, ${JSON.stringify(error)}`); }) }
生成算法key后即可进行加解密,在加解密中采用同样的步骤,先获取相应的配置,然后调用initSession和finishSession实现加解密操作。
async function EncryptData() { let encryptProperties = GetAesEncryptProperties(); let options: huks.HuksOptions = { properties: encryptProperties, inData: StringToUint8Array(plainText) }; await huks.initSession(aesKeyAlias, options) .then((data) => { handle = data.handle; }).catch((error: Error) => { hilog.error(0x0000, 'AppDataSecurity', `promise: init EncryptData failed, ${JSON.stringify(error)}`); }) await huks.finishSession(handle, options) .then((data) => { hilog.info(0x0000, 'AppDataSecurity', `promise: encrypt data success, data is ` + Uint8ArrayToString(data.outData as Uint8Array)); cipherData = data.outData as Uint8Array; }).catch((error: Error) => { hilog.error(0x0000, 'AppDataSecurity', `promise: encrypt data failed, ${JSON.stringify(error)}`); }) } async function DecryptData() { let decryptOptions = GetAesDecryptProperties() let options: huks.HuksOptions = { properties: decryptOptions, inData: cipherData }; await huks.initSession(aesKeyAlias, options) .then((data) => { handle = data.handle; }).catch((error: Error) => { hilog.error(0x0000, 'AppDataSecurity', `promise: init DecryptData failed, ${JSON.stringify(error)}`); }) await huks.finishSession(handle, options) .then((data) => { hilog.info(0x0000, 'AppDataSecurity', `promise: decrypt data success, data is ` + Uint8ArrayToString(data.outData as Uint8Array)); }).catch((error: Error) => { hilog.error(0x0000, 'AppDataSecurity', `promise: decrypt data failed, ${JSON.stringify(error)}`); }) }
说明
在体检报表中有敏感数据,因此需要在对数据进行二次加密再进行分级加密,解密得过程顺序与加密相反,先读取分级加密的数据,再进行解密。在涉及的敏感的数据密级不同的时候,可以对不同的字符按密级分别封装序列化,避免在数据流动的时候可能把高密级的数据流动到低密级的设备中,造成数据泄露。
这里为了方便日志打印输出,对加密后的数据进行base64转码,在应用开发中此步骤可以省略。
随着信息技术的不断发展,数据的价值和重要性日益凸显,因此保护数据安全已成为应用开发中的重要课题。数据安全保护需要综合考虑分析评估和设计开发两个阶段的工作:
本文中,针对体检报表中的数据将其进行安全等级划分后,根据定义的的等级采取了对应的加密。
在实际应用开发中,开发者要重视数据安全工作,不断完善数据安全保护措施,为用户的数据安全和隐私保护提供更加可靠的保障。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。