鸿蒙应用签名实操及机制探究

原创
01/14 10:14
阅读数 6.4K

本文对鸿蒙公开资料进行了深入分析和解读,梳理了鸿蒙单框架应用的签名机制,拆解每一步的实操过程和背后的实现原理,并对源码分析整理签名的校验机制。从中管中窥豹,探究鸿蒙系统的安全设计思路,希望能给从事鸿蒙研发的同学提供一些借鉴。

  1. 背景


华为鸿蒙单框架操作系统 HarmonyOS NEXT 已于 2024 年 10 月 23 日正式发布 Release 版。HarmonyOS NEXT 仅支持鸿蒙原生应用,不再兼容安卓。本文对鸿蒙公开资料进行了深入分析和解读,梳理了鸿蒙单框架应用的签名机制,拆解每一步的实操过程和背后的实现原理,并对源码分析整理签名的校验机制。从中管中窥豹,探究鸿蒙系统的安全设计思路,希望能给从事鸿蒙研发的同学提供一些借鉴。

成文过程中特别参考 OpenHarmony 5.0.0-Release 版的文档和源码,详见 openharmony

  1. 签名机制


签名相关的代码在 developtools_hapsigner 仓库里,签名流程梳理如下:

签名步骤可按如下分组:

  1. 生成开发者签名证书,包括①、②、③。

  2. 生成 Profile 文件,包括④、⑤。

  3. 生成签名的 App,包括⑥、⑦。

2.1 生成开发者签名证书

① 生成开发者公私钥

通过华为的 DevEco-Studio 工具可以直接生成包含开发者公私钥的 p12 文件(详见具体操作步骤。)

笔者示例生成的 p12 文件(保存为 my.p12)是标准的 PKCS#12 格式(定义在 RFC 7292),用来存储一组或多组公钥证书(里面包含公钥)和其对应的私钥(用 localKeyID 字段进行匹配公私钥的匹配),使用 ASN.1 来定义其数据结构,并采用 DER 编码规则将这些结构编码为二进制形式。

可以通过 openssl 命令解析其结构,或者直接查看公钥证书和私钥信息:

openssl asn1parse -in my.p12 -inform DER  //解码DER和解析ASN.1
openssl pkcs12 -info -in my.p12  //查看公钥证书和私钥信息

笔者用于示例生成的 p12 文件里包含的公钥证书如下:

-----BEGIN CERTIFICATE-----
MIIBqTCCAU+gAwIBAgIIKG2ih6j2GSswCgYIKoZIzj0EAwIwSTEJMAcGA1UEBhMA
MQkwBwYDVQQIEwAxCTAHBgNVBAcTADEJMAcGA1UEChMAMQkwBwYDVQQLEwAxEDAO
BgNVBAMTB3Rlc3RzY3IwHhcNMjQwOTIzMTI1NjM3WhcNNDkwOTE3MTI1NjM3WjBJ
MQkwBwYDVQQGEwAxCTAHBgNVBAgTADEJMAcGA1UEBxMAMQkwBwYDVQQKEwAxCTAH
BgNVBAsTADEQMA4GA1UEAxMHdGVzdHNjcjBZMBMGByqGSM49AgEGCCqGSM49AwEH
A0IABD28s78rF8+X1JWgkQcfHB2Gy20MCT51Oue6eG5ZbPsUKlZrPx0aRX0einL2
E5WsE3st0zI4yvj0KzhdEwksCWCjITAfMB0GA1UdDgQWBBRtCEWMjEr+bnXoAqSC
fjmk1btJQDAKBggqhkjOPQQDAgNIADBFAiAAiMtQXgCMUxrKtaPKvGqllswi1FRU
h1brCAbJ1t81FgIhAMXbzmeJlA7/zxZDULLRW0rCY6CU3KMDHr8N38EmuDug
-----END CERTIFICATE-----

公钥证书的表示是遵循 Privacy Enhanced Mail(PEM)协议(定义在 RFC 7468)的文本文件,其格式如下:

PEM 文件的 label 用于指示文件的内容类型。以下是一些常见的 PEM header 和 footer(后面会陆续见到):

解析具体公钥证书信息可以采用如下命令(将公钥证书以文本的形式保存为 my.pem 文件):

openssl x509 -in my.pem -text -noout

解析得到如下输出(重要部分加了注释):

Certificate:
    Data:
        Version: 3 (0x2) //证书的版本号
        Serial Number: 2913163237517564203 (0x286da287a8f6192b) //证书的序列号,用于唯一标识证书
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = , ST = , L = , O = , OU = , CN = testscr //证书颁发者的信息
        Validity
            Not Before: Sep 23 12:56:37 2024 GMT //证书的开始有效期
            Not After : Sep 17 12:56:37 2049 GMT //证书的结束有效期
        Subject: C = , ST = , L = , O = , OU = , CN = testscr //证书持有者的信息
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey //公钥算法,这里是椭圆曲线
                Public-Key: (256 bit) //公钥的位数,这里是256
                pub:// 证书持有者的公钥值,以十六进制表示
                    04:3d:bc:b3:bf:2b:17:cf:97:d4:95:a0:91:07:1f:
                    1c:1d:86:cb:6d:0c:09:3e:75:3a:e7:ba:78:6e:59:
                    6c:fb:14:2a:56:6b:3f:1d:1a:45:7d:1e:8a:72:f6:
                    13:95:ac:13:7b:2d:d3:32:38:ca:f8:f4:2b:38:5d:
                    13:09:2c:09:60
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Subject Key Identifier: //证书持有者的标识
                6D:08:45:8C:8C:4A:FE:6E:75:E8:02:A4:82:7E:39:A4:D5:BB:49:40
    Signature Algorithm: ecdsa-with-SHA256
    Signature Value: //证书的数字签名值
        30:45:02:20:00:88:cb:50:5e:00:8c:53:1a:ca:b5:a3:ca:bc:
        6a:a5:96:cc:22:d4:54:54:87:56:eb:08:06:c9:d6:df:35:16:
        02:21:00:c5:db:ce:67:89:94:0e:ff:cf:16:43:50:b2:d1:5b:
        4a:c2:63:a0:94:dc:a3:03:1e:bf:0d:df:c1:26:b8:3b:a0

公钥信息(包括公钥算法、公钥位数、公钥值等)属于结构化数据并且较长,不利于识别和比较,所以需要用一个简短的字符串来标识公钥唯一性。常用做法是使用公钥指纹(Public Key Pin,也叫公钥 Pin),计算方式是对 DER 编码的公钥进行 SHA-256 计算并进行 Base64 编码。

这里需要注意的是,在 X509 扩展字段里包括了 Subject Key Identifier(SKID)字段,也是证书持有者的标识。那标识公钥的唯一性为什么不直接使用证书里自带的 SKID,还要自己算一遍呢,根据 RFC 3280-4.2.1.2 章节中对 SKID 的定义:

For CA certificates, subject key identifiers SHOULD be derived from the public key or a method that generates unique values. Two common methods for generating key identifiers from the public key are: (1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the value of the BIT STRING subjectPublicKey (excluding the tag, length, and number of unused bits). (2) The keyIdentifier is composed of a four bit type field with the value 0100 followed by the least significant 60 bits of the SHA-1 hash of the value of the BIT STRING subjectPublicKey (excluding the tag, length, and number of unused bit string bits).

SKID 的计算可以通过公钥得到,但计算方式并不唯一,也可以通过任意的算法得到,只要保证唯一性就可以了。定义里推荐了两种基于 SHA-1 的算法,openssl 采用的算法(详见 v3_skid.c 的 ossl_x509_pubkey_hash 函数)是对 DER 编码的公钥进行 SHA-1 计算。

如果采用 SKID 来进行公钥的唯一性校验,那么攻击者可以伪造一个证书,里面的 SKID 和你的一样(SHA-1 碰撞,或者直接照抄一下也行),这样的证书也是合法的,就可以轻易绕过对公钥的校验。所以 SKID 一般只用于在证书链中寻找父子关系,并不用于公钥的唯一性标识。另外,还有 Authority Key Identifier(AKID)字段用于标识证书的颁发者。当验证一个证书链时,验证程序会检查每个证书的 AKID 和上一个证书的 SKID 是否匹配,确保它们形成一个连续的信任链。

使用如下命令可以从 PEM 协议的公钥证书中提取 PEM 协议的公钥:

openssl x509 -in my.pem -pubkey -noout

输出如下:

-----BEGIN PUBLIC KEY----- //公钥标头
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE95zFs5cFHauzIYEuuw3g2R75a1ir
qEW0JWP9qAKkyVCizN0nnzcn/Fo5oeSZR1iPUnJvjlnpNvZL9BcQbLqa7g==
-----END PUBLIC KEY-----

使用如下命令可以继续转换成 DER 编码并计算 SHA-256 和 Base64 编码:

openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

所以结合使用如下命令可以直接从符合 PEM 协议的公钥证书文件中得到对应的公钥指纹:

openssl x509 -in my.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

最终笔者示例的公钥证书计算得到的公钥指纹为:

fzyRjPvTPElBAj0VlYlVA74M3RMtUh5ljKbOYf1NDA0=

② 生成证书签名请求

同样通过 DevEco-Studio 可以直接生成证书签名请求 Certificate Signing Request(CSR)文件(详见具体操作步骤)。得到的 CSR 内容示例如下:

-----BEGIN NEW CERTIFICATE REQUEST----- //CSR标头
MIIBMzCB2wIBADBJMQkwBwYDVQQGEwAxCTAHBgNVBAgTADEJMAcGA1UEBxMAMQkw
BwYDVQQKEwAxCTAHBgNVBAsTADEQMA4GA1UEAxMHdGVzdHNjcjBZMBMGByqGSM49
AgEGCCqGSM49AwEHA0IABD28s78rF8+X1JWgkQcfHB2Gy20MCT51Oue6eG5ZbPsU
KlZrPx0aRX0einL2E5WsE3st0zI4yvj0KzhdEwksCWCgMDAuBgkqhkiG9w0BCQ4x
ITAfMB0GA1UdDgQWBBRtCEWMjEr+bnXoAqSCfjmk1btJQDAKBggqhkjOPQQDAgNH
ADBEAiAlzkRf0AHKh59/deFGo/4JHQRSbw6P+Q7qsiiMMWHT7wIgGugWrCm7tFLh
mRjEEyJNOpen9kfhyOanSRrwtBlEFc0=
-----END NEW CERTIFICATE REQUEST-----

生成的 CSR 文件是标准的 PKCS#10 格式(定义在 RFC 2986),用于向证书颁发机构(CA)请求签发数字证书的文件,包含申请者的公钥和一些身份信息,这些信息将包含在颁发的证书中。可以看到 CSR 文件也是遵循 PEM 协议的,可以如下命令解析 CSR 文件的内容(保存为 my.csr 文件):

openssl req -text -noout -verify -in my.csr

输出示例(重要部分加了注释):

Certificate request self-signature verify OK //表明CSR的自签名已成功验证
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = , ST = , L = , O = , OU = , CN = testscr //证书申请者的信息
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub: //证书申请者的公钥值,和上面my.pem里的公钥值相同
                    04:3d:bc:b3:bf:2b:17:cf:97:d4:95:a0:91:07:1f:
                    1c:1d:86:cb:6d:0c:09:3e:75:3a:e7:ba:78:6e:59:
                    6c:fb:14:2a:56:6b:3f:1d:1a:45:7d:1e:8a:72:f6:
                    13:95:ac:13:7b:2d:d3:32:38:ca:f8:f4:2b:38:5d:
                    13:09:2c:09:60
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        Attributes:
            Requested Extensions:
                X509v3 Subject Key Identifier: //证书申请者的标识
                    6D:08:45:8C:8C:4A:FE:6E:75:E8:02:A4:82:7E:39:A4:D5:BB:49:40
    Signature Algorithm: ecdsa-with-SHA256
    Signature Value:
        30:44:02:20:25:ce:44:5f:d0:01:ca:87:9f:7f:75:e1:46:a3:
        fe:09:1d:04:52:6f:0e:8f:f9:0e:ea:b2:28:8c:31:61:d3:ef:
        02:20:1a:e8:16:ac:29:bb:b4:52:e1:99:18:c4:13:22:4d:3a:
        97:a7:f6:47:e1:c8:e6:a7:49:1a:f0:b4:19:44:15:cd

注意到其中证书申请者的公钥值和上面 p12 文件中的公钥值是一样的,说明 CSR 中包含了我们的公钥信息。使用如下命令也可以直接从 CSR 文件中得到公钥指纹:

openssl req -in my.csr -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

示例生成的 CSR 计算得到的公钥指纹为:

fzyRjPvTPElBAj0VlYlVA74M3RMtUh5ljKbOYf1NDA0=

和通过公钥证书计算得到的公钥指纹相同。

③ 生成开发者签名叶子证书

证书的作用可以抽象概括为:

颁发者(Issuer)说:持有者(Subject)的公钥是某某某。

证书一般分为三级:根证书(Root Certificate)、中间证书(Intermediate Certificate)、叶子证书(Leaf Certificate)。

  • 叶子证书由中间证书颁发(即叶子证书的 Issuer+AKID 和中间证书的 Subject+SKID 相同)

  • 中间证书由根证书颁发(即中间证书的 Issuer+AKID 和根证书的 Subject+SKID 相同)

  • 根证书由自己颁发(也就是自签名,根证书的 Issuer 和 Subject 相同)

我们需要的是用于给我们 App 签名的开发者签名叶子证书,这需要华为的开发者签名中间证书来帮我们颁发。叶子证书分为调试证书和发布证书,我们以发布证书为例(详见具体操作步骤):

需要上传我们的 CSR 文件,得到的证书文件内容示例如下:

-----BEGIN CERTIFICATE-----
MIICGjCCAaGgAwIBAgIIShhpn519jNAwCgYIKoZIzj0EAwMwUzELMAkGA1UEBhMC
Q04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEeMBwGA1UE
AwwVSHVhd2VpIENCRyBSb290IENBIEcyMB4XDTIwMDMxNjAzMDQzOVoXDTQ5MDMx
NjAzMDQzOVowUzELMAkGA1UEBhMCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UE
CwwKSHVhd2VpIENCRzEeMBwGA1UEAwwVSHVhd2VpIENCRyBSb290IENBIEcyMHYw
EAYHKoZIzj0CAQYFK4EEACIDYgAEWidkGnDSOw3/HE2y2GHl+fpWBIa5S+IlnNrs
GUvwC1I2QWvtqCHWmwFlFK95zKXiM8s9yV3VVXh7ivN8ZJO3SC5N1TCrvB2lpHMB
wcz4DA0kgHCMm/wDec6kOHx1xvCRo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUo45a9Vq8cYwqaiVyfkiS4pLcIAAwCgYIKoZI
zj0EAwMDZwAwZAIwMypeB7P0IbY7c6gpWcClhRznOJFj8uavrNu2PIoz9KIqr3jn
BlBHJs0myI7ntYpEAjBbm8eDMZY5zq5iMZUC6H7UzYSix4Uy1YlsLVV738PtKP9h
FTjgDHctXJlC5L7+ZDY=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDATCCAoigAwIBAgIIXmuDXbWpOB8wCgYIKoZIzj0EAwMwUzELMAkGA1UEBhMC
Q04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEeMBwGA1UE
AwwVSHVhd2VpIENCRyBSb290IENBIEcyMB4XDTIwMDcwOTAyMDQyNFoXDTMwMDcw
NzAyMDQyNFowYjELMAkGA1UEBgwCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UE
CwwKSHVhd2VpIENCRzEtMCsGA1UEAwwkSHVhd2VpIENCRyBEZXZlbG9wZXIgUmVs
YXRpb25zIENBIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE65LdoIZh1hlpZ2gP
bJ6gPhHsvYSRe22KETgdqeVeYnrbRHI9wsPT6RGYS+pU4mPl6wxzgDMqN6SY/BoZ
luhkE1PzaHoPoNIWIq0O33hpyKyyYwAacIUEjYurkw1E9r9no4IBGDCCARQwHwYD
VR0jBBgwFoAUo45a9Vq8cYwqaiVyfkiS4pLcIAAwHQYDVR0OBBYEFNtek7Ij6NDk
/nF6Zumkc0dbf/NeMEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEGCCsGAQUFBwIBFiVo
dHRwOi8vY3BraS1jYXdlYi5odWF3ZWkuY29tL2Nwa2kvY3BzMBIGA1UdEwEB/wQI
MAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMGYGA1UdHwRfMF0wW6BZoFeGVWh0dHA6
Ly9jcGtpLWNhd2ViLmh1YXdlaS5jb20vY3BraS9zZXJ2bGV0L2NybEZpbGVEb3du
LmNybD9jZXJ0eXBlPTEwJi9yb290X2cyX2NybC5jcmwwCgYIKoZIzj0EAwMDZwAw
ZAIwWO1X5q2MdfpR1Q237GpUHGbL1C13rGyFg2p3AYo44FpZ2/A9ss0wOHKM4KDl
ZPqdAjBLkf8NPZy7KVog98+iCTLq35DJ2ZVxkCxknA9YhiHVyXf4HPm4JlT7rW7o
Q+FzM3c=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICujCCAkGgAwIBAgIOY8ui/vvwxqFf+kFokYUwCgYIKoZIzj0EAwMwYjELMAkG
A1UEBgwCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEt
MCsGA1UEAwwkSHVhd2VpIENCRyBEZXZlbG9wZXIgUmVsYXRpb25zIENBIEcyMB4X
DTI0MDkyMzEyNTgwNFoXDTI3MDkyMzEyNTgwNFowazELMAkGA1UEBhMCQ04xDzAN
BgNVBAoMBuW8oOaZqDEcMBoGA1UECwwTMTI4OTY3Njc4NjA2NTQ5NDk3NzEtMCsG
A1UEAwwk5byg5pmoKDEyODk2NzY3ODYwNjU0OTQ5NzcpXCxSZWxlYXNlMFkwEwYH
KoZIzj0CAQYIKoZIzj0DAQcDQgAEPbyzvysXz5fUlaCRBx8cHYbLbQwJPnU657p4
blls+xQqVms/HRpFfR6KcvYTlawTey3TMjjK+PQrOF0TCSwJYKOB0TCBzjAMBgNV
HRMBAf8EAjAAMFkGA1UdHwRSMFAwTqBMoEqGSGh0dHA6Ly9oNWhvc3RpbmctZHJj
bi5kYmFua2Nkbi5jbi9jY2g1L2NybC9oZHJjYWcyL0h1YXdlaUNCR0hEUkcyY3Js
LmNybDAfBgNVHSMEGDAWgBTbXpOyI+jQ5P5xembppHNHW3/zXjAdBgNVHQ4EFgQU
bQhFjIxK/m516AKkgn45pNW7SUAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoG
CCsGAQUFBwMDMAoGCCqGSM49BAMDA2cAMGQCMFzNlsafNs7ad5xelZOzCebdRofE
VaQZJW0o5QAdTX0t9Ij1o/zUm0bXIf8ZZTJLYgIwKuuZu+LeLCLZJFEM7tYKDhIK
TegCiesP1THuMgiZhZYOYl1kIZBPVrEB8O1wtxEm
-----END CERTIFICATE-----

可以看到叶子证书文件里也包括了中间证书和根证书,分别解析证书信息如下:

根证书:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 5339133492510690512 (0x4a18699f9d7d8cd0)
        Signature Algorithm: ecdsa-with-SHA384
        Issuer: C = CN, O = Huawei, OU = Huawei CBG, CN = Huawei CBG Root CA G2
        Validity
            Not Before: Mar 16 03:04:39 2020 GMT
            Not After : Mar 16 03:04:39 2049 GMT
        Subject: C = CN, O = Huawei, OU = Huawei CBG, CN = Huawei CBG Root CA G2
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (384 bit)
                pub:
                    04:5a:27:64:1a:70:d2:3b:0d:ff:1c:4d:b2:d8:61:
                    e5:f9:fa:56:04:86:b9:4b:e2:25:9c:da:ec:19:4b:
                    f0:0b:52:36:41:6b:ed:a8:21:d6:9b:01:65:14:af:
                    79:cc:a5:e2:33:cb:3d:c9:5d:d5:55:78:7b:8a:f3:
                    7c:64:93:b7:48:2e:4d:d5:30:ab:bc:1d:a5:a4:73:
                    01:c1:cc:f8:0c:0d:24:80:70:8c:9b:fc:03:79:ce:
                    a4:38:7c:75:c6:f0:91
                ASN1 OID: secp384r1
                NIST CURVE: P-384
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Subject Key Identifier: 
                A3:8E:5A:F5:5A:BC:71:8C:2A:6A:25:72:7E:48:92:E2:92:DC:20:00
    Signature Algorithm: ecdsa-with-SHA384
    Signature Value:
        30:64:02:30:33:2a:5e:07:b3:f4:21:b6:3b:73:a8:29:59:c0:
        a5:85:1c:e7:38:91:63:f2:e6:af:ac:db:b6:3c:8a:33:f4:a2:
        2a:af:78:e7:06:50:47:26:cd:26:c8:8e:e7:b5:8a:44:02:30:
        5b:9b:c7:83:31:96:39:ce:ae:62:31:95:02:e8:7e:d4:cd:84:
        a2:c7:85:32:d5:89:6c:2d:55:7b:df:c3:ed:28:ff:61:15:38:
        e0:0c:77:2d:5c:99:42:e4:be:fe:64:36

中间证书:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 6803676100576229407 (0x5e6b835db5a9381f)
        Signature Algorithm: ecdsa-with-SHA384
        Issuer: C = CN, O = Huawei, OU = Huawei CBG, CN = Huawei CBG Root CA G2
        Validity
            Not Before: Jul  9 02:04:24 2020 GMT
            Not After : Jul  7 02:04:24 2030 GMT
        Subject: C = CN, O = Huawei, OU = Huawei CBG, CN = Huawei CBG Developer Relations CA G2
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (384 bit)
                pub:
                    04:eb:92:dd:a0:86:61:d6:19:69:67:68:0f:6c:9e:
                    a0:3e:11:ec:bd:84:91:7b:6d:8a:11:38:1d:a9:e5:
                    5e:62:7a:db:44:72:3d:c2:c3:d3:e9:11:98:4b:ea:
                    54:e2:63:e5:eb:0c:73:80:33:2a:37:a4:98:fc:1a:
                    19:96:e8:64:13:53:f3:68:7a:0f:a0:d2:16:22:ad:
                    0e:df:78:69:c8:ac:b2:63:00:1a:70:85:04:8d:8b:
                    ab:93:0d:44:f6:bf:67
                ASN1 OID: secp384r1
                NIST CURVE: P-384
        X509v3 extensions:
            X509v3 Authority Key Identifier: 
                A3:8E:5A:F5:5A:BC:71:8C:2A:6A:25:72:7E:48:92:E2:92:DC:20:00
            X509v3 Subject Key Identifier: 
                DB:5E:93:B2:23:E8:D0:E4:FE:71:7A:66:E9:A4:73:47:5B:7F:F3:5E
            X509v3 Certificate Policies: 
                Policy: X509v3 Any Policy
                  CPS: http://cpki-caweb.huawei.com/cpki/cps
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 CRL Distribution Points: 
                Full Name:
                  URI:http://cpki-caweb.huawei.com/cpki/servlet/crlFileDown.crl?certype=10&/root_g2_crl.crl
    Signature Algorithm: ecdsa-with-SHA384
    Signature Value:
        30:64:02:30:58:ed:57:e6:ad:8c:75:fa:51:d5:0d:b7:ec:6a:
        54:1c:66:cb:d4:2d:77:ac:6c:85:83:6a:77:01:8a:38:e0:5a:
        59:db:f0:3d:b2:cd:30:38:72:8c:e0:a0:e5:64:fa:9d:02:30:
        4b:91:ff:0d:3d:9c:bb:29:5a:20:f7:cf:a2:09:32:ea:df:90:
        c9:d9:95:71:90:2c:64:9c:0f:58:86:21:d5:c9:77:f8:1c:f9:
        b8:26:54:fb:ad:6e:e8:43:e1:73:33:77

叶子证书:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            63:cb:a2:fe:fb:f0:c6:a1:5f:fa:41:68:91:85
        Signature Algorithm: ecdsa-with-SHA384
        Issuer: C = CN, O = Huawei, OU = Huawei CBG, CN = Huawei CBG Developer Relations CA G2
        Validity
            Not Before: Sep 23 12:58:04 2024 GMT
            Not After : Sep 23 12:58:04 2027 GMT
        Subject: C = CN, O = \E5\BC\A0\E6\99\A8, OU = 1289676786065494977, CN = "\E5\BC\A0\E6\99\A8(1289676786065494977)\\,Release"
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:3d:bc:b3:bf:2b:17:cf:97:d4:95:a0:91:07:1f:
                    1c:1d:86:cb:6d:0c:09:3e:75:3a:e7:ba:78:6e:59:
                    6c:fb:14:2a:56:6b:3f:1d:1a:45:7d:1e:8a:72:f6:
                    13:95:ac:13:7b:2d:d3:32:38:ca:f8:f4:2b:38:5d:
                    13:09:2c:09:60
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 CRL Distribution Points: 
                Full Name:
                  URI:http://h5hosting-drcn.dbankcdn.cn/cch5/crl/hdrcag2/HuaweiCBGHDRG2crl.crl
            X509v3 Authority Key Identifier: 
                DB:5E:93:B2:23:E8:D0:E4:FE:71:7A:66:E9:A4:73:47:5B:7F:F3:5E
            X509v3 Subject Key Identifier: 
                6D:08:45:8C:8C:4A:FE:6E:75:E8:02:A4:82:7E:39:A4:D5:BB:49:40
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage: 
                Code Signing
    Signature Algorithm: ecdsa-with-SHA384
    Signature Value:
        30:64:02:30:5c:cd:96:c6:9f:36:ce:da:77:9c:5e:95:93:b3:
        09:e6:dd:46:87:c4:55:a4:19:25:6d:28:e5:00:1d:4d:7d:2d:
        f4:88:f5:a3:fc:d4:9b:46:d7:21:ff:19:65:32:4b:62:02:30:
        2a:eb:99:bb:e2:de:2c:22:d9:24:51:0c:ee:d6:0a:0e:12:0a:
        4d:e8:02:89:eb:0f:d5:31:ee:32:08:99:85:96:0e:62:5d:64:
        21:90:4f:56:b1:01:f0:ed:70:b7:11:26

注意到,颁发下来的叶子证书里 Subject 和我们申请时所使用的 CSR 里的 Subject 不同,叶子证书里是:

Subject: C = CN, O = \E5\BC\A0\E6\99\A8, OU = 1289676786065494977, CN = "\E5\BC\A0\E6\99\A8(1289676786065494977)\,Release"

CSR 里是:

Subject: C = , ST = , L = , O = , OU = , CN = testscr

说明华为在颁发叶子证书的时候,并没有使用我们 CSR 里的 Subject,而是根据我们在开发者平台网站上登陆的账号信息,对 Subject 进行了填充。其中 OU 字段跟账号信息有关(类似 iOS 的 Team ID,但在华为开发者网站上没有找到直接查看的地方)。叶子证书里的公钥信息还是和 CSR 保持一致,计算得到的公钥指纹也一样。

另外,华为目前给叶子证书的有效期是 3 年,3 年以后需要续期成新证书。当然,如果续期新证书的时候使用的 CSR 不变,那么新证书的公钥指纹也依然会保持不变。

2.2 生成 Profile 文件

④ 登记 App 信息

这里主要是在华为开发者平台上登记一下 App 的各项信息,跟着操作步骤来即可:

需要注意的是,包名填写的时候,平台会在线检查一下是否和已经存在的包名有重复(包括其他人申请的包名)。申请成功以后,会分配一个唯一的 APP ID:

这个 APP ID 用来在网站上标识 APP 的唯一性。后面文章中会出现类似的名称,为了消除歧义,相似的字段用括号内容区分,这里称为 APP ID(网站)。

⑤ 生成 Profile 文件

Profile 文件是描述 App 的包名、签名、申请的权限列表、安装包类型、可安装设备等信息的文件(类似 iOS 的 Provisioning Profile),签名后保存为 Cryptographic Message Syntax 格式(CMS,扩展的 PKCS#7 / 格式,CMS 定义在 RFC 5652,PKCS#7 定义在 RFC 2315。)

跟着操作步骤,选择之前的 APP ID(网站)和证书,可以得到对应的 Profile 文件:

Profile 也分为发布和调试两种,发布只能选择发布证书,调试只能选择调试证书。这里继续以发布类型为例,生成的 Profile 文件(保存为 my.p7b)被华为签名后, 通过如下命令可以查看里面的所有信息:

openssl pkcs7 -in my.p7b -print -inform DER

主要包括配置信息和签名信息两部分,也可以通过如下命令分别查看配置信息和签名信息:

openssl smime -verify -in my.p7b -inform DER -noverify //查看配置信息
openssl pkcs7 -in my.p7b -print_certs  -inform DER //查看证书信息

得到的示例配置信息如下(* 为手动打码):

{
    "version-name": "2.0.0",
    "version-code": 2,
    "app-distribution-type": "app_gallery",
    "uuid": "234e1d73-****-****-****-f81e2598d0ff",
    "validity": {
        "not-before": 1727096284,
        "not-after": 1821704284
    },
    "type": "release",
    "bundle-info": {
        "developer-id": "300**********7916",
        "distribution-certificate": "-----BEGIN CERTIFICATE-----\nMIICujCCAkGgAwIBAgIOY8ui/vvwxqFf+kFokYUwCgYIKoZIzj0EAwMwYjELMAkG\nA1UEBgwCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEt\nMCsGA1UEAwwkSHVhd2VpIENCRyBEZXZlbG9wZXIgUmVsYXRpb25zIENBIEcyMB4X\nDTI0MDkyMzEyNTgwNFoXDTI3MDkyMzEyNTgwNFowazELMAkGA1UEBhMCQ04xDzAN\nBgNVBAoMBuW8oOaZqDEcMBoGA1UECwwTMTI4OTY3Njc4NjA2NTQ5NDk3NzEtMCsG\nA1UEAwwk5byg5pmoKDEyODk2NzY3ODYwNjU0OTQ5NzcpXCxSZWxlYXNlMFkwEwYH\nKoZIzj0CAQYIKoZIzj0DAQcDQgAEPbyzvysXz5fUlaCRBx8cHYbLbQwJPnU657p4\nblls+xQqVms/HRpFfR6KcvYTlawTey3TMjjK+PQrOF0TCSwJYKOB0TCBzjAMBgNV\nHRMBAf8EAjAAMFkGA1UdHwRSMFAwTqBMoEqGSGh0dHA6Ly9oNWhvc3RpbmctZHJj\nbi5kYmFua2Nkbi5jbi9jY2g1L2NybC9oZHJjYWcyL0h1YXdlaUNCR0hEUkcyY3Js\nLmNybDAfBgNVHSMEGDAWgBTbXpOyI+jQ5P5xembppHNHW3/zXjAdBgNVHQ4EFgQU\nbQhFjIxK/m516AKkgn45pNW7SUAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoG\nCCsGAQUFBwMDMAoGCCqGSM49BAMDA2cAMGQCMFzNlsafNs7ad5xelZOzCebdRofE\nVaQZJW0o5QAdTX0t9Ij1o/zUm0bXIf8ZZTJLYgIwKuuZu+LeLCLZJFEM7tYKDhIK\nTegCiesP1THuMgiZhZYOYl1kIZBPVrEB8O1wtxEm\n-----END CERTIFICATE-----\n",
        "bundle-name": "com.***.test",
        "apl": "normal",
        "app-feature": "hos_normal_app",
        "app-identifier": "576************2509"
    },
    "baseapp-info": {},
    "permissions": {},
    "acls": {},
    "issuer": "app_gallery"
}

具体每个字段的含义可以参考官方文档源码中的定义,其中重点字段解析如下:

得到的示例证书内容如下:

subject=C = CN, O = Huawei, OU = Huawei CBG, CN = Huawei CBG Root CA G2
issuer=C = CN, O = Huawei, OU = Huawei CBG, CN = Huawei CBG Root CA G2
-----BEGIN CERTIFICATE-----
MIICGjCCAaGgAwIBAgIIShhpn519jNAwCgYIKoZIzj0EAwMwUzELMAkGA1UEBhMC
Q04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEeMBwGA1UE
AwwVSHVhd2VpIENCRyBSb290IENBIEcyMB4XDTIwMDMxNjAzMDQzOVoXDTQ5MDMx
NjAzMDQzOVowUzELMAkGA1UEBhMCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UE
CwwKSHVhd2VpIENCRzEeMBwGA1UEAwwVSHVhd2VpIENCRyBSb290IENBIEcyMHYw
EAYHKoZIzj0CAQYFK4EEACIDYgAEWidkGnDSOw3/HE2y2GHl+fpWBIa5S+IlnNrs
GUvwC1I2QWvtqCHWmwFlFK95zKXiM8s9yV3VVXh7ivN8ZJO3SC5N1TCrvB2lpHMB
wcz4DA0kgHCMm/wDec6kOHx1xvCRo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUo45a9Vq8cYwqaiVyfkiS4pLcIAAwCgYIKoZI
zj0EAwMDZwAwZAIwMypeB7P0IbY7c6gpWcClhRznOJFj8uavrNu2PIoz9KIqr3jn
BlBHJs0myI7ntYpEAjBbm8eDMZY5zq5iMZUC6H7UzYSix4Uy1YlsLVV738PtKP9h
FTjgDHctXJlC5L7+ZDY=
-----END CERTIFICATE-----

subject=C = CN, O = Huawei, OU = HOS AppGallery, CN = HOS Profile Management
issuer=C = CN, O = Huawei, OU = Huawei CBG, CN = Huawei CBG Software Signing Service CA
-----BEGIN CERTIFICATE-----
MIIC7TCCAnOgAwIBAgIIV5nKqt2oGmwwCgYIKoZIzj0EAwMwZDELMAkGA1UEBhMC
Q04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEvMC0GA1UE
AwwmSHVhd2VpIENCRyBTb2Z0d2FyZSBTaWduaW5nIFNlcnZpY2UgQ0EwHhcNMjMw
NDI0MDYyNjMxWhcNMjgwNDI0MDYyNjMxWjBYMQswCQYDVQQGDAJDTjEPMA0GA1UE
CgwGSHVhd2VpMRcwFQYDVQQLDA5IT1MgQXBwR2FsbGVyeTEfMB0GA1UEAwwWSE9T
IFByb2ZpbGUgTWFuYWdlbWVudDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDdY
3RoPqb6WD8UpXJiavZLN48iamektKUKZHFl9xwr1Siu77z3lI86cREa3Flw50uKc
xkMNKM4FWBRMd3CDhI+jggEZMIIBFTAfBgNVHSMEGDAWgBT69fe+IFZdXdTabfEU
FTwdCduyNDAdBgNVHQ4EFgQU0a99kztpYeCetotz0YIduJ2I2VcwRgYDVR0gBD8w
PTA7BgRVHSAAMDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly9wa2kuY29uc3VtZXIuaHVh
d2VpLmNvbS9jYS9jcHMwDgYDVR0PAQH/BAQDAgeAMEwGA1UdHwRFMEMwQaA/oD2G
O2h0dHA6Ly9wa2kuY29uc3VtZXIuaHVhd2VpLmNvbS9jYS9jcmwvc29mdF9zaWdu
X3Nydl9jcmwuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMDMBgGDCsGAQQBj1sCgngB
AwQIMAYCAQEKAQEwCgYIKoZIzj0EAwMDaAAwZQIwRYOlQ6Qq2SF8LHQ78xpNYh47
zMemerx5oG4F6Uq/3ARPfowvdrEu5Ss+njPMG0FFAjEA0s7YhO7Ktm60mkuHuxQS
46fqIHh/PAPJ2ozg1yDSD771bAGn7mDeGjaAFXEtKzU5
-----END CERTIFICATE-----

subject=C = CN, O = Huawei, OU = Huawei CBG, CN = Huawei CBG Software Signing Service CA
issuer=C = CN, O = Huawei, OU = Huawei CBG, CN = Huawei CBG Root CA G2
-----BEGIN CERTIFICATE-----
MIIDADCCAoegAwIBAgIIJGDixWQS3MkwCgYIKoZIzj0EAwMwUzELMAkGA1UEBhMC
Q04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEeMBwGA1UE
AwwVSHVhd2VpIENCRyBSb290IENBIEcyMB4XDTIwMDMxNjEyMzIzOVoXDTQwMDMx
NjEyMzIzOVowZDELMAkGA1UEBhMCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UE
CwwKSHVhd2VpIENCRzEvMC0GA1UEAwwmSHVhd2VpIENCRyBTb2Z0d2FyZSBTaWdu
aW5nIFNlcnZpY2UgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASsEz7cwYkzFh9b
xIwKfXx5qHGjl5WITy0teGnNWqv+jYCceeixHqErvK7YRn2hVPIqhRqKWeANHZUK
G0qxi+NIpmSmQS8/63CLz1QAcxfv2Xl3/V82dF0v9lm16ehMsN+jggEVMIIBETAf
BgNVHSMEGDAWgBSjjlr1WrxxjCpqJXJ+SJLiktwgADAdBgNVHQ4EFgQU+vX3viBW
XV3U2m3xFBU8HQnbsjQwDwYDVR0TAQH/BAUwAwEB/zBGBgNVHSAEPzA9MDsGBFUd
IAAwMzAxBggrBgEFBQcCARYlaHR0cDovL2Nwa2ktY2F3ZWIuaHVhd2VpLmNvbS9j
cGtpL2NwczAOBgNVHQ8BAf8EBAMCAQYwZgYDVR0fBF8wXTBboFmgV4ZVaHR0cDov
L2Nwa2ktY2F3ZWIuaHVhd2VpLmNvbS9jcGtpL3NlcnZsZXQvY3JsRmlsZURvd24u
Y3JsP2NlcnR5cGU9MTAmL3Jvb3RfZzJfY3JsLmNybDAKBggqhkjOPQQDAwNnADBk
AjBrAQQxUlNgqhYkcEm5eksnPxDkPJSY/qNd2BDgbvEydiLwPSvB7Z9lipxz8ikZ
EeUCMGppWcaV//SIG1y5tEwthLwWeEaF613vUILWQLir8+CA3RZGsRBqtE8xSqfz
yafLYQ==
-----END CERTIFICATE-----

这里依然是完整的三级证书链,注意,根证书和之前申请到的开发者签名证书的根证书一样,但中间证书和叶子证书均不一样,比较如下(红色底色表示内容相同):

2.3 生成签名的 App

⑥ 得到签名的 App 包

将生成的 Profile 文件、叶子证书文件等配置到项目的 signingConfigs 里,使用华为的 IDE 可以直接得到签名后的 App 包:

或者参考命令手动给未签名的 App 包进行签名。

得到的签名的 App 包只是用于提供给华为商店进行审核和后续的拆包,并不能直接安装到手机上运行。App 包实际上就是标准的 ZIP 格式,可以修改后缀为.zip 进行解压:

可以看到里面包括了.hap 包和描述 App 一些信息的 pack.info 文件。

那么对 App 包进行签名的内容以及 Profile 文件在哪里呢?根据对源码里 VerifyHap.java 类的 verifyHap 函数进行分析,发现鸿蒙上的签名机制类似 Android V3,签名信息和 Profile 文件存储在自定义的 HapSigningBlock 区,放到了 ZIP 格式 Central Directory 区的前面,其结构如下:

HapSigningBlock 区的魔数(转成 string 也就是):

    /**
     * The value of lower 8 bytes of magic word
     */
    public static final long HAP_SIG_BLOCK_MAGIC_LO_V3 = 0x676973207061683cL;

    /**
     * The value of higher 8 bytes of magic word
     */
    public static final long HAP_SIG_BLOCK_MAGIC_HI_V3 = 0x3e6b636f6c62206eL;

    /**
     * Size of hap signature block header
     */
    public static final int HAP_SIG_BLOCK_HEADER_SIZE = 32;

通过 hex 工具直接打开 App 包也可以在 Central Directory 区前面找到:

其中 SignatureSchemeBlock 区存放了 CMS 格式的 Hap 包签名信息,而 Profile 文件就存储在 SigningBlock 区,Type 是 0x20000002:

    /**
     * ID of profile block
     */
    public static final int HAP_PROFILE_BLOCK_ID = 0x20000002;

通过如下 hap-sign-tool.jar 的命令可以导出存储在 App 包或 Hap 包里的签名证书和 Profile 文件:

java -jar hap-sign-tool.jar verify-app -inFile my-signed.app -outCertChain my-signed.cer -outProfile my-signed.p7b

⑦ 签名校验、拆包、重签名

提供给华为应用市场审核的 App 包在经过签名校验,确认是开发者的应用以及应用的完整性以后,华为会取出 App 包 SigningBlock 区的 Profile 文件,解压出 Hap 包,把 Profile 文件或 Profile 文件内的配置(下一章节展开描述区别)重新放到 Hap 包的 SigningBlock 区里,并用 Hap 的签名叶子证书对 Hap 包进行重新签名,签名方式和给 App 包签名一样。最终真正通过应用市场下发到手机上的是经过重签名的 Hap 包(类似 iOS 的双层签名机制)。解析出签名证书如下:

CN=HOS AppGallery Application Release, OU=HOS AppGallery, O=Huawei, C=CN
-----BEGIN CERTIFICATE-----
MIIC+TCCAn+gAwIBAgIIWXsBFAJOQzIwCgYIKoZIzj0EAwMwZDELMAkGA1UEBhMC
Q04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEvMC0GA1UE
AwwmSHVhd2VpIENCRyBTb2Z0d2FyZSBTaWduaW5nIFNlcnZpY2UgQ0EwHhcNMjMw
NDI0MDYyMjA1WhcNMjgwNDI0MDYyMjA1WjBkMQswCQYDVQQGDAJDTjEPMA0GA1UE
CgwGSHVhd2VpMRcwFQYDVQQLDA5IT1MgQXBwR2FsbGVyeTErMCkGA1UEAwwiSE9T
IEFwcEdhbGxlcnkgQXBwbGljYXRpb24gUmVsZWFzZTBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABIokjn9tVRpgEC6b1AR9chiiejUGBiF83Lzm1giyZX9XKVzTPkHq
RRuML+zhRtT1JESEMOUggPyJbe9+rt3k9CijggEZMIIBFTAfBgNVHSMEGDAWgBT6
9fe+IFZdXdTabfEUFTwdCduyNDAdBgNVHQ4EFgQUFzRtDLYZ7zX/idRsHYmJZ734
vwgwRgYDVR0gBD8wPTA7BgRVHSAAMDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly9wa2ku
Y29uc3VtZXIuaHVhd2VpLmNvbS9jYS9jcHMwDgYDVR0PAQH/BAQDAgeAMEwGA1Ud
HwRFMEMwQaA/oD2GO2h0dHA6Ly9wa2kuY29uc3VtZXIuaHVhd2VpLmNvbS9jYS9j
cmwvc29mdF9zaWduX3Nydl9jcmwuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMDMBgG
DCsGAQQBj1sCgngBAwQIMAYCAQEKAQAwCgYIKoZIzj0EAwMDaAAwZQIxAJofyGQW
4ZVDW64qTeiVQVn5w7iRhejP6YFqYX9h/5mNXKMQ8ZuQCFT7EaqhVblWlQIwWIPB
xC+YhPz6JmDMSZDynZINnXi0T3k9UvbcCybbd2k2PWHYvYqQdKAuYGcNc2Ho
-----END CERTIFICATE-----
CN=Huawei CBG Software Signing Service CA, OU=Huawei CBG, O=Huawei, C=CN
-----BEGIN CERTIFICATE-----
MIIDADCCAoegAwIBAgIIJGDixWQS3MkwCgYIKoZIzj0EAwMwUzELMAkGA1UEBhMC
Q04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEeMBwGA1UE
AwwVSHVhd2VpIENCRyBSb290IENBIEcyMB4XDTIwMDMxNjEyMzIzOVoXDTQwMDMx
NjEyMzIzOVowZDELMAkGA1UEBhMCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UE
CwwKSHVhd2VpIENCRzEvMC0GA1UEAwwmSHVhd2VpIENCRyBTb2Z0d2FyZSBTaWdu
aW5nIFNlcnZpY2UgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASsEz7cwYkzFh9b
xIwKfXx5qHGjl5WITy0teGnNWqv+jYCceeixHqErvK7YRn2hVPIqhRqKWeANHZUK
G0qxi+NIpmSmQS8/63CLz1QAcxfv2Xl3/V82dF0v9lm16ehMsN+jggEVMIIBETAf
BgNVHSMEGDAWgBSjjlr1WrxxjCpqJXJ+SJLiktwgADAdBgNVHQ4EFgQU+vX3viBW
XV3U2m3xFBU8HQnbsjQwDwYDVR0TAQH/BAUwAwEB/zBGBgNVHSAEPzA9MDsGBFUd
IAAwMzAxBggrBgEFBQcCARYlaHR0cDovL2Nwa2ktY2F3ZWIuaHVhd2VpLmNvbS9j
cGtpL2NwczAOBgNVHQ8BAf8EBAMCAQYwZgYDVR0fBF8wXTBboFmgV4ZVaHR0cDov
L2Nwa2ktY2F3ZWIuaHVhd2VpLmNvbS9jcGtpL3NlcnZsZXQvY3JsRmlsZURvd24u
Y3JsP2NlcnR5cGU9MTAmL3Jvb3RfZzJfY3JsLmNybDAKBggqhkjOPQQDAwNnADBk
AjBrAQQxUlNgqhYkcEm5eksnPxDkPJSY/qNd2BDgbvEydiLwPSvB7Z9lipxz8ikZ
EeUCMGppWcaV//SIG1y5tEwthLwWeEaF613vUILWQLir8+CA3RZGsRBqtE8xSqfz
yafLYQ==
-----END CERTIFICATE-----

注意这里给 Hap 的签名证书和我们之前申请的开发者签名证书不一样,Hap 签名证书和对应的私钥都是华为的,跟我们的应用没有关系,而且签名证书链里不包含根证书的信息。

这里再和之前的证书对比一下(红色和黄色底色表示内容分别相同):

三者的根证书都一样,Profile 签名证书和 Hap 签名证书的中间证书一样,三者的叶子证书均不一样。

另外,如果在上架应用市场的时候,勾选了加密:

根据《鸿蒙生态应用安全技术白皮书》描述,只是对 Hap 包里的代码做加密,然后重新签名,所以证书和 Profile 文件的解析均不受影响。

  1. 校验机制


签名相关的代码在 security_appverify 仓库里,签名校验流程梳理如下(图上所有判断条件在失败情况下均会导致签名校验失败,为了直观不画出此流程):

签名校验步骤可以分成三组,分别是:

  1. SignatureSchemeBlock 区校验。

  2. Profile 校验和解析。

  3. Hap 包完整性校验。

3.1 SignatureSchemeBlock 区校验

校验 Hap 包时首先在 ZIP 的 Central Directory 区前 32 个字节寻找是否有 HapSigningBlock 区的 Header,找到以后定位到 SignatureSchemeBlock 区,解析其 CMS 格式,并校验 SignatureSchemeBlock 区证书链的完整性。证书链完整性校验流程如下:

校验叶子证书时,需要按证书指定算法重新计算证书的 hash,并使用上一级证书(中间证书)的公钥对叶子证书里的证书签名进行解密,与重新计算的 hash 比对是否相同,相同则认为证书可信。中间证书继续通过根证书的公钥校验自己的证书签名。根证书用自己的公钥校验自己。

上一章说到,SignatureSchemeBlock 区的证书链不包括根证书,所以这一步需要使用到根证书其实是内置在鸿蒙系统里的,存放地址是:

/system/etc/security/trusted_root_ca.json

具体内容如下:

{
    "C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Root CA G2":"-----BEGIN CERTIFICATE-----\nMIICGjCCAaGgAwIBAgIIShhpn519jNAwCgYIKoZIzj0EAwMwUzELMAkGA1UEBhMC\nQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEeMBwGA1UE\nAwwVSHVhd2VpIENCRyBSb290IENBIEcyMB4XDTIwMDMxNjAzMDQzOVoXDTQ5MDMx\nNjAzMDQzOVowUzELMAkGA1UEBhMCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UE\nCwwKSHVhd2VpIENCRzEeMBwGA1UEAwwVSHVhd2VpIENCRyBSb290IENBIEcyMHYw\nEAYHKoZIzj0CAQYFK4EEACIDYgAEWidkGnDSOw3/HE2y2GHl+fpWBIa5S+IlnNrs\nGUvwC1I2QWvtqCHWmwFlFK95zKXiM8s9yV3VVXh7ivN8ZJO3SC5N1TCrvB2lpHMB\nwcz4DA0kgHCMm/wDec6kOHx1xvCRo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T\nAQH/BAUwAwEB/zAdBgNVHQ4EFgQUo45a9Vq8cYwqaiVyfkiS4pLcIAAwCgYIKoZI\nzj0EAwMDZwAwZAIwMypeB7P0IbY7c6gpWcClhRznOJFj8uavrNu2PIoz9KIqr3jn\nBlBHJs0myI7ntYpEAjBbm8eDMZY5zq5iMZUC6H7UzYSix4Uy1YlsLVV738PtKP9h\nFTjgDHctXJlC5L7+ZDY=\n-----END CERTIFICATE-----\n"
}

可以看到这个根证书就是上一章解析出来的根证书,所以这里可以校验通过。

在确认证书链可信以后,根据叶子证书的 Subject 判断 Hap 包的安装来源,具体代码在 trusted_source_manager.cpp 的 MatchTrustedSource 函数里,也就是和内置的 Hap 签名证书列表进行比较,看匹配到哪一个。内置的 Hap 签名证书列表存放地址是:

/system/etc/security/trusted_apps_sources.json

具体内容如下:

{
    "version": "1.0.1",
    "release-time":"2021-06-03 10:06:00",
    "trust-app-source":[
        {
            "name":"huawei app gallery",
            "app-signing-cert":"C=CN, O=Huawei, OU=HOS AppGallery, CN=HOS AppGallery Application Release",
            "profile-signing-certificate":"C=CN, O=Huawei, OU=HOS AppGallery, CN=HOS Profile Management",
            "profile-debug-signing-certificate":"C=CN, O=Huawei, OU=HOS AppGallery, CN=HOS Profile Management Debug",
            "issuer-ca":"C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Software Signing Service CA",
            "root-ca": "C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Root CA G2",
            "max-certs-path":3,
            "critialcal-cert-extension":["keyusage","huawei-signing-capability"]
        },
        {
            "name":"huawei system apps",
            "app-signing-cert":"C=CN, O=Huawei CBG, OU=HOS Development Team, CN=HOS Application Provision Release",
            "profile-signing-certificate":"C=CN, O=Huawei CBG, OU=HOS Development Team, CN=HOS Application Provision Profile Release",
            "profile-debug-signing-certificate":"C=CN, O=Huawei CBG, OU=HOS Development Team, CN=HOS Application Provision Profile Release_Debug",
            "issuer-ca":"C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Software Signing Service CA",
            "root-ca": "C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Root CA G2",
            "max-certs-path":3,
            "critialcal-cert-extension":["keyusage","huawei-signing-capability"]
        },
        {
            "name":"third_party app preload",
            "app-signing-cert":"C=CN, O=Huawei, OU=HOS Open Platform, CN=HOS Preload Service",
            "profile-signing-certificate":"",
            "profile-debug-signing-certificate":"",
            "issuer-ca":"C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Software Signing Service CA",
            "root-ca": "C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Root CA G2",
            "max-certs-path":3,
            "critialcal-cert-extension":["keyusage","huawei-signing-capability"]
        }
   ]
}

这里有 Huawei App Gallery(应用市场)、Huawei System Apps(系统应用)、Third_party App Preload(三方预装)三组。每一组包括对应的 Hap 签名证书 Subject、Profile 签名证书 Subject 等信息。

我们走应用市场分发的 Hap 包会匹配到 Huawei App Gallery 这个证书。

3.2 Profile 解析和校验

接下来在 SigningBlock 区寻找 Profile,这里注意到不同 Hap 包签名方式会影响 Profile 的存储格式。对于走应用市场分发的 Hap 包,Profile 是直接把其配置以字符串的格式保存,而对于其他情况,则是用 CMS 的格式保存。那么应用市场分发的 Hap 包也就不需要 Profile 的文件签名校验了。

对于其他情况,首先会使用 Profile 里保存的叶子证书公钥校验 Profile 的文件签名,然后会校验叶子证书 Subject 是否和匹配的同组内 Profile 签名证书 Subject 相同。

校验通过后解析 Profile 里的配置信息,根据 type 不同走不同的校验逻辑:

都校验通过后,再继续看 Profile 文件签名证书和 Hap 的签名正式是否相同,并继续对 Profile 配置的字段规则合法性进行检测。走完这一步,就可以认为 Profile 是可信的。

随后会生成 App ID(公钥)和 Fingerprint 两个新的字段和验证结果一并返回。其中 APP ID(公钥)是根据 Profile 配置里开发者签名证书公钥生成的(详见 GenerateAppId 函数),fingerprint 是根据 Profile 配置里开发者签名证书直接计算整个证书的指纹(注意不是上一章的公钥指纹,详见 GenerateFingerprint 函数)。至此完成 Profile 的解析和校验。

另外,在鸿蒙 SignatureInfo API 中,会返回三个参数:

其中 API 返回的 appId 为了消除歧义,这里称为 APP ID(接口)。

根据包管理子系统 bundle_install_checker.cpp 的 ParseHapFiles 函数inner_bundle_info.h 的 SetProvisionId 函数这个 PR 来看:

		// bundle_install_checker.cpp
    newInfo.SetProvisionId(provisionInfo.appId);

		// inner_bundle_info.h
    void SetProvisionId(const std::string &provisionId)
    {
        baseBundleInfo_->appId = baseBundleInfo_->name + Constants::FILE_UNDERLINE + provisionId;
    }

APP ID(接口)的值实际上是 APP ID(公钥)加上了 {bundleName}_的前缀。

3.3 Hap 包完整性校验

这一步的过程和 Hap 包签名类似,将 ZIP 包中数据和 HapSigningBlock 区里非 SignatureSchemeBlock 的部分拼接,重新计算 hash,与使用 Hap 签名叶子证书公钥解密 SignatureSchemeBlock 区签名后的 hash 比较,相同则认为 Hap 包未被篡改。具体可以参考 hap_signing_block_utils.cpp 的 VerifyHapIntegrity 函数,这里就不展开了。

总结

从鸿蒙单框架应用的签名和校验机制的种种细节中可以看出,HarmonyOS NEXT 的安全设计非常务实,融合了 Anroid 和 iOS 双端的特性,有借鉴 Android 成熟的部分(签名格式),但更多的是参考了 iOS 的设计思路(双层签名机制),甚至更加严格。期待 HarmonyOS NEXT 给我们带来一个新的未来。

在这里,特别感谢华为同学对本文的大力支持。

*内容转载自美团技术团队

展开阅读全文
加载中
点击加入讨论🔥(27) 发布并加入讨论🔥
27 评论
25 收藏
27
分享
返回顶部
顶部