鸿蒙 PC 底层开发技术详解(四):代码签名机制对我们的影响
在鸿蒙 PC 的 HiShell 环境中运行二进制文件时,系统会强制校验该文件及其加载的所有 `.so` 库的代码签名。若签名无效,系统将拦截运行并报错 `permission denied`。
代码签名机制介绍
在鸿蒙 PC 的 HiShell 环境中运行二进制文件时,系统会强制校验该文件及其加载的所有 .so 库的代码签名。若签名无效,系统将拦截运行并报错 permission denied。
这一机制是 HarmonyOS 特有的安全特性。OpenHarmony 默认并不开启此项校验,甚至在目前的系统源码中,尚未完整实现相应的二进制验签逻辑。
两种签名类型
与 macOS(Apple Silicon 版本)类似,HarmonyOS 的代码签名有两种类型:
- 证书签名: 类似于 macOS 的正式证书签名,具备申请调试、系统级 API 等高危权限的资格。
- 自签名: 类似于 macOS 的 Ad Hoc 签名,能通过验签使程序运行,但无法获取受限的高级权限。
两种签名方法
给二进制进行代码签名的方法有两种,均基于 OpenHarmony SDK 进行操作:
- 使用二进制签名工具签名:支持证书签名和自签名。
- 使用链接器签名:仅支持自签名。
尽管 OpenHarmony 系统本身尚未实现完整的验签能力,但它的工具链 OpenHarmony SDK 却实现了完整的签名能力,它可以直接支持 HarmonyOS 环境下的签名需求。这种“上游工具链反向适配下游系统”的设计,对开发者而言非常友好:它确保了工具链的统一,用户无需为了适配鸿蒙 PC 而专门下载一套 HarmonyOS SDK。
使用二进制签名工具签名
这是最成熟的签名方式,它类似于 macOS 里面的 codesign 工具。
开发者可以在 ohos-sdk 中找到这个签名工具,路径是 toolchains/lib/binary-sign-tool。它既支持做证书签名,也支持做自签名。不过,虽然它支持证书签名,但却很少有人用得到这个能力。原因是 申请二进制证书 的门槛较高,普通开发者获取不到证书。大部分情况下我们做的都是自签名。
这里基于鸿蒙 PC HiShell 环境进行演示:
# 准备 ohos-sdk
curl -fL -o ohos-sdk-public_ohos.tar.gz https://cidownload.openharmony.cn/version/Master_Version/ohos-sdk-public_ohos/20260330_020501/version-Master_Version-ohos-sdk-public_ohos-20260330_020501-ohos-sdk-public_ohos.tar.gz
mkdir -p ~/ohos-sdk
tar -zxf ohos-sdk-public_ohos.tar.gz -C ~/ohos-sdk
cd ~/ohos-sdk/ohos
unzip -uq native-*.zip
unzip -uq toolchains-*.zip
cd -
export PATH=~/ohos-sdk/ohos/native/llvm/bin:~/ohos-sdk/ohos/toolchains/lib:$PATH
# 编译一个程序
clang my_program.c -o my_program
# 运行程序,报错 permission denied
./my_program
# 对它进行自签名
binary-sign-tool sign -selfSign 1 -inFile my_program -outFile my_program
# 运行程序,成功
./my_program
# 查看签名信息,结果显示有签名
binary-sign-tool display-sign -inFile my_program
使用链接器签名
这个机制与 macOS 上的那套链接器签名机制是类似的。
当我们在 macOS 上面用 LLVM 工具链编译程序,lld 链接器会在最后生成二进制文件的时候给它打上 Ad Hoc 签名。这也是为什么很多用户对 macOS 上的代码签名机制完全无感的原因——在 macOS 上,我们用 LLVM 编出来的程序都已经默认带有签名了,不需要再自己手动去签一遍。甚至不仅是 lld,在 macOS 上还有其他的一些链接器也会做这件事,比如 Go 语言自己实现的链接器也会做自动签名。
现在 OpenHarmony 社区的人也往 ohos-sdk 的 lld 链接器里面添加了类似的能力(PR 链接)。只不过他们做得没有 macOS 那么彻底。macOS 工具链里面的 lld 是默认开启了这个能力的,而 ohos-sdk 虽实现了这个能力但并未默认开启,需要用户传入特定的链接器参数(--code-sign)才能开启。
这个 ohos-sdk 的链接器签名,有两种用法:
用法 1:让 clang 驱动器帮你完成链接,让它把 --code-sign 参数透传给 lld 链接器
这里基于鸿蒙 PC HiShell 环境进行演示:
# 准备 ohos-sdk
curl -fL -o ohos-sdk-public_ohos.tar.gz https://cidownload.openharmony.cn/version/Master_Version/ohos-sdk-public_ohos/20260330_020501/version-Master_Version-ohos-sdk-public_ohos-20260330_020501-ohos-sdk-public_ohos.tar.gz
mkdir -p ~/ohos-sdk
tar -zxf ohos-sdk-public_ohos.tar.gz -C ~/ohos-sdk
cd ~/ohos-sdk/ohos
unzip -uq native-*.zip
unzip -uq toolchains-*.zip
cd -
export PATH=~/ohos-sdk/ohos/native/llvm/bin:$PATH
# 编译一个程序,用 `-Wl` 透传链接器参数
clang -Wl,--code-sign my_program.c -o my_program
# 运行程序,成功
./my_program
# 检查 ELF 文件的段信息,可以看到最末尾有 .codesign 段
llvm-readelf -S my_program
用法 2:自己调用 lld 链接器进行链接,把 --code-sign 参数直接传给 lld 链接器
这里基于鸿蒙 PC HiShell 环境进行演示:
# 准备 ohos-sdk
curl -fL -o ohos-sdk-public_ohos.tar.gz https://cidownload.openharmony.cn/version/Master_Version/ohos-sdk-public_ohos/20260330_020501/version-Master_Version-ohos-sdk-public_ohos-20260330_020501-ohos-sdk-public_ohos.tar.gz
mkdir -p ~/ohos-sdk
tar -zxf ohos-sdk-public_ohos.tar.gz -C ~/ohos-sdk
cd ~/ohos-sdk/ohos
unzip -uq native-*.zip
unzip -uq toolchains-*.zip
cd -
export PATH=~/ohos-sdk/ohos/native/llvm/bin:$PATH
# 定义 sysroot 路径以简化命令
SYSROOT="$HOME/ohos-sdk/ohos/native/llvm/bin/../../sysroot"
RT_DIR="$HOME/ohos-sdk/ohos/native/llvm/lib/clang/15.0.4/lib/aarch64-linux-ohos"
# 编译一个程序,只编译不链接
clang -c my_program.c -o my_program.o
# 手动调用链接器链接,这里加了 --code-sign 参数
ld.lld --code-sign -o my_program \
-dynamic-linker /lib/ld-musl-aarch64.so.1 \
-L$SYSROOT/usr/lib/aarch64-linux-ohos \
$SYSROOT/usr/lib/aarch64-linux-ohos/Scrt1.o \
$SYSROOT/usr/lib/aarch64-linux-ohos/crti.o \
my_program.o \
-lc \
$RT_DIR/libclang_rt.builtins.a \
$SYSROOT/usr/lib/aarch64-linux-ohos/crtn.o
# 运行程序,成功
./my_program
# 检查 ELF 文件的段信息,可以看到最末尾有 .codesign 段
llvm-readelf -S my_program
对链接器签名进行体验优化
在链接器签名这种用法下,开发者需要每次编译时手动传入特定的链接器参数,体验较差。
为了得到与 macOS 平台上相同的编译体验,这里提供一个技巧,可以让编译工具链在编东西的时候直接自动对产物打上代码签名,省去人工操作的繁琐——虽然 LLVM 默认没启用这个参数,但我们可以自己想办法让它变成“默认启用”。
这里基于鸿蒙 PC HiShell 环境进行演示:
# 准备 ohos-sdk
curl -fL -o ohos-sdk-public_ohos.tar.gz https://cidownload.openharmony.cn/version/Master_Version/ohos-sdk-public_ohos/20260330_020501/version-Master_Version-ohos-sdk-public_ohos-20260330_020501-ohos-sdk-public_ohos.tar.gz
mkdir -p ~/ohos-sdk
tar -zxf ohos-sdk-public_ohos.tar.gz -C ~/ohos-sdk
cd ~/ohos-sdk/ohos
unzip -uq native-*.zip
unzip -uq toolchains-*.zip
cd -
# 把 ld.lld 这个软链接删掉,然后写一个同名的封装脚本
cd ~/ohos-sdk/ohos/native/llvm/bin
rm ld.lld
lld_absolute_path=$(realpath lld)
printf '#!/bin/sh\nexec -a "$0" %s --code-sign "$@"\n' "$lld_absolute_path" > ld.lld
chmod 0755 ld.lld
cd -
从软链接改成脚本之后,clang 在进行链接的时候就会调用到这个脚本,再通过这个脚本去调用 lld,这样就每次都会把 --code-sign 参数带上,不需要我们再手动加上这个参数。
export PATH=~/ohos-sdk/ohos/native/llvm/bin:$PATH
# 编译一个程序,无需 `-Wl,--code-sign` 参数
clang my_program.c -o my_program
# 运行程序,成功
./my_program
# 检查 ELF 文件的段信息,可以看到最末尾有 .codesign 段
llvm-readelf -S my_program
更多推荐



所有评论(0)