HarmonyOS开发:系统签名应用——特权应用开发
HarmonyOS开发:系统签名应用——特权应用开发
核心要点:系统签名应用是鸿蒙生态中的"特权阶层",拥有普通应用无法触及的系统能力,掌握系统签名机制是做系统级开发的第一步。
背景与动机
你有没有遇到过这种情况——明明API文档里写着一个方法,你调用的时候却直接报"Permission denied"?查了半天发现,这个API只对系统签名应用开放。
普通应用能做的事,系统签名应用都能做;系统签名应用能做的事,普通应用大部分做不了。这就是鸿蒙里最根本的能力分界线。
做设备厂商定制、做系统工具、做预装应用,你绕不开系统签名。不理解这套机制,你连入口都找不到。
那系统签名应用到底跟普通应用有啥区别?怎么获取系统签名?拿到签名之后又能干啥?这篇就把这些事掰扯清楚。
核心原理
特权应用 vs 普通应用
鸿蒙的应用体系分三个层级:
| 层级 | 签名类型 | 能力范围 | 典型场景 |
|---|---|---|---|
| 普通应用 | 普通签名 | 沙箱内受限API | 第三方应用商店App |
| 特权应用 | 系统签名 | 系统级API、跨沙箱访问 | 设置、系统管家 |
| 核心系统应用 | 平台签名 | 全部系统能力 | 桌面、系统UI |
关键区别在哪?三个字——权限域。
普通应用的权限声明在module.json5里写再多,也只是在"普通权限域"里打转。系统签名应用一签名,直接进入"系统权限域",能申请那些标注了system或system_core级别的权限。
flowchart TD
A[应用安装] --> B{签名类型判断}
B -->|普通签名| C[普通权限域]
B -->|系统签名| D[系统权限域]
B -->|平台签名| E[核心权限域]
C --> C1[基础API访问]
C --> C2[受限文件系统]
C --> C3[沙箱隔离]
D --> D1[系统API访问]
D --> D2[跨沙箱数据访问]
D --> D3[系统属性读写]
D --> D4[系统服务调用]
E --> E1[全部系统能力]
E --> E2[内核级操作]
E --> E3[系统分区写入]
classDef normal fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
classDef system fill:#fff3e0,stroke:#ff9800,color:#e65100
classDef core fill:#fce4ec,stroke:#f44336,color:#b71c1c
class C,C1,C2,C3 normal
class D,D1,D2,D3,D4 system
class E,E1,E2,E3 core
系统签名的本质
系统签名不是什么神秘的东西。本质上就是用平台密钥对你的HAP包做二次签名。
鸿蒙的签名体系分两层:
- 应用签名:开发者自己用调试证书或发布证书签名,证明"这个包是我打的"
- 系统签名:用设备厂商的平台密钥再签一次,证明"这个应用受系统信任"
拿到系统签名后,应用在安装时会被标记为SystemApp,这个标记决定了后续权限校验的走向。
系统签名的获取方式
获取系统签名有三条路:
方式一:源码编译集成
把你的应用源码放到OpenHarmony源码树的applications/目录下,跟着系统一起编译。编译脚本会自动用平台密钥签名。这是最正规的方式,但需要你有完整的源码编译环境。
方式二:手动签名
拿到平台密钥文件(.p12或.pem),用hapsigner工具手动签名。适合调试阶段快速验证。
方式三:开发者模式
在开发者选项中开启"系统应用调试模式",设备会临时授予系统签名能力。仅限调试,不能用于生产。
flowchart LR
A[获取系统签名] --> B{选择方式}
B -->|源码编译| C[放入源码树]
C --> C1[gn/ninja编译]
C1 --> C2[自动平台签名]
B -->|手动签名| D[获取平台密钥]
D --> D1[hapsigner工具]
D1 --> D2[手动二次签名]
B -->|开发者模式| E[开启调试模式]
E --> E1[临时系统权限]
E1 --> E2[仅限调试]
classDef method fill:#e3f2fd,stroke:#2196f3,color:#0d47a1
classDef step fill:#f3e5f5,stroke:#9c27b0,color:#4a148c
classDef result fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
class B method
class C,C1,D,D1,E,E1 step
class C2,D2,E2 result
代码实战
基础用法:系统签名应用配置
先看一个系统签名应用的基本配置。关键在于module.json5里的声明:
// src/main/module.json5
{
"module": {
"name": "SystemTool",
"type": "entry",
"deviceTypes": ["default"],
"distro": {
"deliveryWithInstall": true,
"moduleName": "SystemTool",
"moduleType": "entry",
"installationFree": false
},
// 关键:声明系统应用标识
"systemApp": true,
// 关键:声明特权权限
"requestPermissions": {
"name": "ohos.permission.SET_TIME",
"reason": "$string:time_permission_reason",
"usedScene": {
"abilities": ["SystemToolAbility"],
"when": "inuse"
}
}
}
}
注意systemApp: true这一行。这一行是告诉系统:“我是系统应用,请给我系统权限域的待遇”。但光声明没用,还得有系统签名配合才行。没有系统签名就声明systemApp: true,安装的时候直接给你报错。
进阶用法:特权API调用
拿到系统签名后,你能调用的API范围一下子就宽了。比如修改系统时间、安装应用、访问系统属性——这些普通应用想都别想。
// SystemAbilityManager.ets - 系统能力管理工具
import systemDateTime from '@ohos.systemDateTime';
import installer from '@ohos.bundle.installer';
import systemParameter from '@ohos.systemParameter';
class SystemAbilityManager {
private tag: string = 'SystemAbilityManager';
// 设置系统时间 - 需要ohos.permission.SET_TIME(系统权限)
async setSystemTime(timestamp: number): Promise<boolean> {
try {
await systemDateTime.setTime(timestamp);
console.info(this.tag, `系统时间设置成功: ${timestamp}`);
return true;
} catch (err) {
console.error(this.tag, `设置系统时间失败: ${JSON.stringify(err)}`);
return false;
}
}
// 静默安装应用 - 需要ohos.permission.INSTALL_BUNDLE(系统核心权限)
async silentInstallApp(hapPath: string): Promise<boolean> {
try {
const bundleInstaller = installer.getBundleInstaller();
const installParam: installer.InstallParam = {
userId: 100,
installFlag: installer.InstallFlag.NORMAL,
isKeepData: false
};
await new Promise<void>((resolve, reject) => {
bundleInstaller.install([hapPath], installParam, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
console.info(this.tag, '应用安装成功');
return true;
} catch (err) {
console.error(this.tag, `应用安装失败: ${JSON.stringify(err)}`);
return false;
}
}
// 读取系统属性 - 需要系统签名
getSystemProperty(key: string): string {
try {
const value = systemParameter.get(key);
console.info(this.tag, `系统属性 ${key} = ${value}`);
return value;
} catch (err) {
console.error(this.tag, `读取系统属性失败: ${JSON.stringify(err)}`);
return '';
}
}
}
export default new SystemAbilityManager();
上面这三个操作,没有系统签名一个都跑不通。SET_TIME是系统级权限,INSTALL_BUNDLE是系统核心权限,systemParameter.get某些key也需要系统签名才能读。
完整示例:系统签名应用入口
把上面的能力串起来,写一个完整的系统签名应用入口页面:
// pages/Index.ets - 系统签名应用主页面
import SystemAbilityManager from '../common/SystemAbilityManager';
import promptAction from '@ohos.promptAction';
@Entry
@Component
struct IndexPage {
@State currentTime: string = '';
@State deviceModel: string = '';
@State installStatus: string = '等待操作';
@State isSystemApp: boolean = false;
aboutToAppear() {
this.checkSystemAppStatus();
this.loadSystemInfo();
}
// 检查当前应用是否为系统应用
checkSystemAppStatus() {
try {
// 尝试读取系统属性来验证系统签名是否生效
const bootMode = SystemAbilityManager.getSystemProperty('ohos.boot.mode');
this.isSystemApp = bootMode.length > 0;
} catch (err) {
this.isSystemApp = false;
}
}
// 加载系统信息
loadSystemInfo() {
this.deviceModel = SystemAbilityManager.getSystemProperty('ro.product.model');
this.updateTimeDisplay();
}
updateTimeDisplay() {
const now = new Date();
this.currentTime = `${now.getFullYear()}-${this.padZero(now.getMonth() + 1)}-${this.padZero(now.getDate())} ` +
`${this.padZero(now.getHours())}:${this.padZero(now.getMinutes())}:${this.padZero(now.getSeconds())}`;
}
padZero(num: number): string {
return num < 10 ? `0${num}` : `${num}`;
}
build() {
Column() {
// 状态指示
Row() {
Circle({ width: 12, height: 12 })
.fill(this.isSystemApp ? '#4caf50' : '#f44336')
Text(this.isSystemApp ? '系统签名已生效' : '系统签名未生效')
.fontSize(14)
.fontColor(this.isSystemApp ? '#4caf50' : '#f44336')
.margin({ left: 8 })
}
.margin({ bottom: 24 })
// 系统信息卡片
Column() {
Text('系统信息')
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 16 })
this.InfoRow('设备型号', this.deviceModel || '未知')
this.InfoRow('当前时间', this.currentTime)
this.InfoRow('签名状态', this.isSystemApp ? '已签名' : '未签名')
}
.width('100%')
.padding(20)
.backgroundColor('#ffffff')
.borderRadius(12)
.margin({ bottom: 16 })
// 操作按钮
Button('同步网络时间')
.width('100%')
.height(48)
.backgroundColor('#1976d2')
.borderRadius(8)
.enabled(this.isSystemApp)
.onClick(async () => {
// 模拟网络时间同步
const networkTime = Date.now();
const success = await SystemAbilityManager.setSystemTime(networkTime);
if (success) {
this.updateTimeDisplay();
promptAction.showToast({ message: '时间同步成功' });
} else {
promptAction.showToast({ message: '时间同步失败,请检查权限' });
}
})
Text(this.installStatus)
.fontSize(14)
.fontColor('#666666')
.margin({ top: 16 })
}
.width('100%')
.height('100%')
.padding(24)
.backgroundColor('#f5f5f5')
}
@Builder
InfoRow(label: string, value: string) {
Row() {
Text(label)
.fontSize(14)
.fontColor('#999999')
.width(80)
Text(value)
.fontSize(14)
.fontColor('#333333')
.layoutWeight(1)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ bottom: 12 })
}
}
这个页面做了三件事:检测系统签名是否生效、展示系统信息、提供时间同步功能。每个操作都依赖系统签名,没有签名就灰掉按钮——别让用户点了再报错,那体验太差。
踩坑与注意事项
坑一:声明了systemApp但没有系统签名
这是新手最容易踩的坑。在module.json5里写了"systemApp": true,结果安装直接失败,报错信息类似:
Error: The system app is not signed with system certificate.
记住:systemApp: true只是一个声明,真正的"通行证"是系统签名。声明和签名必须同时具备,缺一不可。
坑二:系统签名和调试签名混用
你在DevEco Studio里调试的时候用的是调试证书,这个跟系统签名是两码事。调试证书能让你在开发板上跑起来,但不代表你有了系统签名。
正确做法:先用调试证书开发调试,功能验证完之后,再用平台密钥做系统签名,然后推到设备上验证特权API。
坑三:权限声明不完整
有些开发者以为有了系统签名就天下无敌了,啥权限都不用声明。错!系统签名只是让你"有资格"申请系统级权限,你还得老老实实在module.json5里把需要的权限声明出来。
// 错误示范:以为有系统签名就不用声明权限
{
"module": {
"systemApp": true
// 缺少requestPermissions,调用特权API照样报错
}
}
// 正确做法:系统签名 + 权限声明缺一不可
{
"module": {
"systemApp": true,
"requestPermissions": [
{ "name": "ohos.permission.SET_TIME" },
{ "name": "ohos.permission.INSTALL_BUNDLE" }
]
}
}
坑四:系统签名密钥泄露
平台密钥是设备厂商的核心资产,泄露了意味着任何人都能做系统签名应用,安全体系直接崩塌。所以:
- 密钥文件不要放在代码仓库里
- 构建服务器做好访问控制
- 发布流程要审计
- 不同产品线用不同密钥
坑五:多HAP场景的签名问题
一个应用如果有多个HAP(比如一个Entry + 多个Feature),每个HAP都需要系统签名。只签Entry不签Feature,Feature模块里的特权API照样调不通。
用hapsigner批量签名的脚本:
# 批量签名脚本示例
for hap in ./build/outputs/*.hap; do
java -jar hapsigner.jar sign-app \
-keyAlias "platform_key" \
-keyPwd "your_password" \
-keystoreFile "./platform.p12" \
-keystorePwd "your_keystore_password" \
-appCertFile "./certs/app.cer" \
-profileFile "./certs/profile.p7b" \
-inFile "$hap" \
-outFile "$hap" \
-signAlg SHA256withECDSA \
-mode localSign
done
HarmonyOS 6适配说明
HarmonyOS 6在系统签名方面有几个重要变化:
-
签名工具升级:
hapsigner升级到2.0版本,支持更细粒度的权限声明。旧版本的签名文件需要重新生成。 -
权限分级更严格:部分原本属于
system级别的权限提升到了system_core,意味着即使有系统签名也不够了,需要平台签名。比如INSTALL_BUNDLE权限,在HarmonyOS 6中升级为system_core级别。 -
新增签名校验:安装时增加了签名链完整性校验。之前有些场景下自签名+系统签名混用能过,现在不行了,签名链必须完整。
-
系统应用沙箱增强:即使有系统签名,系统应用也不再拥有完全的文件系统访问权限。需要通过
FileShare或FilePicker来访问其他应用的数据。
适配建议:
// HarmonyOS 6 的 module.json5 新增字段
{
"module": {
"systemApp": true,
// 新增:声明应用类型
"appType": "system_app",
// 新增:声明需要的权限域
"permissionDomains": ["system"],
"requestPermissions": [
{
"name": "ohos.permission.SET_TIME",
// 新增:权限使用场景声明
"usedScene": {
"abilities": ["MainAbility"],
"when": "inuse"
}
}
]
}
}
总结
系统签名应用是鸿蒙系统开发的基础门槛。没有系统签名,你连系统级API的门都摸不到;有了系统签名,你还得正确声明权限、理解权限分级、注意签名链完整性。
核心要点回顾:
- 系统签名 = 平台密钥签名,不是调试签名
systemApp: true只是声明,必须配合系统签名才生效- 系统权限域的权限也需要显式声明
- 多HAP场景每个模块都要签名
- HarmonyOS 6权限分级更严格,注意适配
| 维度 | 评价 |
|---|---|
| 学习难度 | ⭐⭐⭐⭐ 涉及签名体系、权限分级、构建流程,概念较多 |
| 使用频率 | ⭐⭐⭐⭐ 做系统级开发必用,普通应用开发用不到 |
| 重要程度 | ⭐⭐⭐⭐⭐ 系统开发的入场券,不会这个就别做系统应用 |
- 点赞
- 收藏
- 关注作者
评论(0)