第4章 非对称加密:RSA与ECC在鸿蒙中的密钥管理

说到非对称加密,很多开发者第一反应就是「慢」。确实,跟AES这种对称加密比起来,RSA和ECC的计算开销要大得多。但没办法,密钥分发这个老大难问题,还真得靠它来解决。

我在鸿蒙项目里做过一个物联网网关,设备端和云端要建立安全通道。如果用对称加密,密钥怎么安全地传给设备?每次预置?那设备量大了根本管不过来。最后还是老老实实上了非对称加密。

4.1 RSA:老牌选手,但要注意用法

RSA的原理我就不展开讲了,什么大素数分解、欧拉函数,教科书上都有。咱们直接说鸿蒙里怎么用。

鸿蒙的RSA密钥生成,我建议用RSA-2048。为什么不是1024?因为1024在业界已经被认为不够安全了。4096呢?性能开销太大,对移动设备不友好。2048是个平衡点。

核心要点:鸿蒙中RSA密钥对生成后,私钥必须存储在安全环境中,绝对不能明文落盘。

来看一段鸿蒙里生成RSA密钥对的代码:

// 鸿蒙RSA密钥生成示例
HksParamSet *paramSet = nullptr;
HksInitParamSet(¶mSet);

// 设置密钥算法为RSA
struct HksParam algParam = {
    .tag = HKS_TAG_ALGORITHM,
    .uint32Param = HKS_ALG_RSA
};
HksAddParams(paramSet, &algParam, 1);

// 密钥长度2048位
struct HksParam keySizeParam = {
    .tag = HKS_TAG_KEY_SIZE,
    .uint32Param = HKS_RSA_KEY_SIZE_2048
};
HksAddParams(paramSet, &keySizeParam, 1);

// 指定用途:加密+解密
struct HksParam purposeParam = {
    .tag = HKS_TAG_PURPOSE,
    .uint32Param = HKS_KEY_PURPOSE_ENCRYPT | HKS_KEY_PURPOSE_DECRYPT
};
HksAddParams(paramSet, &purposeParam, 1);

HksBuildParamSet(¶mSet);

// 生成密钥,密钥别名"rsa_key_001"
uint8_t keyAlias[] = "rsa_key_001";
HksGenerateKey(keyAlias, paramSet, nullptr);

HksFreeParamSet(¶mSet);

嗯,这里有个坑。密钥别名一定要有业务含义,别用什么"key1"、"mykey"这种。我在项目里见过一个同事,所有设备都用同一个别名,结果密钥冲突,调试了一整天。

注意:RSA加密有长度限制。2048位的RSA,一次最多加密245字节的数据。超过这个长度,要么分段加密,要么改用混合加密方案。

4.2 ECC:新一代宠儿,鸿蒙的偏爱

ECC(椭圆曲线密码学)在鸿蒙里地位很高。为什么?因为鸿蒙主打物联网场景,设备资源受限,ECC的密钥更短、计算更快。

举个例子:同样安全强度下,RSA-2048的密钥长度是2048位,而ECC只需要256位。你想想看,在内存只有几百KB的IoT设备上,这个差距有多大。

鸿蒙默认推荐的是ECC P-256曲线,也就是NIST P-256。我个人习惯用这个,兼容性好,主流平台都支持。

// 鸿蒙ECC密钥生成示例
HksParamSet *paramSet = nullptr;
HksInitParamSet(¶mSet);

// 算法设为ECC
struct HksParam algParam = {
    .tag = HKS_TAG_ALGORITHM,
    .uint32Param = HKS_ALG_ECC
};
HksAddParams(paramSet, &algParam, 1);

// 使用P-256曲线
struct HksParam curveParam = {
    .tag = HKS_TAG_KEY_SIZE,
    .uint32Param = HKS_ECC_KEY_SIZE_256
};
HksAddParams(paramSet, &curveParam, 1);

// 用途:签名+验签
struct HksParam purposeParam = {
    .tag = HKS_TAG_PURPOSE,
    .uint32Param = HKS_KEY_PURPOSE_SIGN | HKS_KEY_PURPOSE_VERIFY
};
HksAddParams(paramSet, &purposeParam, 1);

HksBuildParamSet(¶mSet);

uint8_t keyAlias[] = "ecc_sign_key_001";
HksGenerateKey(keyAlias, paramSet, nullptr);

HksFreeParamSet(¶mSet);

ECC在鸿蒙里主要干两件事:密钥交换和数字签名。密钥交换用ECDH,签名用ECDSA。这两个场景,ECC都比RSA有优势。

小技巧:如果你做的是设备间的安全通信,优先考虑ECDH密钥协商。双方各自生成临时ECC密钥对,然后交换公钥,就能算出相同的会话密钥。整个过程不需要传输私钥,安全得很。

4.3 密钥管理:别让私钥裸奔

密钥生成只是第一步,怎么管好这些密钥才是大头。鸿蒙提供了HUKS(Harmony Universal KeyStore),专门干这个的。

HUKS的核心思想:密钥一旦生成,私钥就永远不离开安全环境。你只能通过句柄或别名来引用它,根本拿不到私钥的明文。

我曾经在项目里犯过一个错误:为了调试方便,把私钥导出到了日志里。结果测试阶段就被安全审计发现了,被狠批了一顿。后来我学乖了,所有私钥操作都走HUKS接口。

密钥类型 存储位置 导出权限 推荐用途
RSA私钥 HUKS安全存储区 禁止导出 解密、签名
RSA公钥 可导出 允许导出 加密、验签
ECC私钥 HUKS安全存储区 禁止导出 签名、密钥协商
ECC公钥 可导出 允许导出 验签、密钥协商

这里有个关键点:公钥虽然可以导出,但一定要验证其合法性。我在项目中遇到过中间人攻击,攻击者替换了设备的公钥,结果所有通信都被监听了。后来我们加了一道工序:公钥导出时带上哈希校验,接收方先验哈希再使用。

4.4 混合加密:取长补短

实际项目中,我很少单独用RSA或ECC加密大量数据。太慢了。更常见的做法是混合加密:

  1. 用ECC或RSA加密一个临时生成的对称密钥
  2. 用这个对称密钥(比如AES-256)加密实际数据
  3. 传输时,把加密后的对称密钥和加密数据一起发过去
  4. 接收方先用私钥解密出对称密钥,再用它解密数据

说白了,非对称加密只保护那个「钥匙」,真正的「箱子」还是对称加密来管。这样既解决了密钥分发问题,又保证了加解密性能。

实战建议:在鸿蒙上实现混合加密时,临时对称密钥建议用HksGenerateRandom生成,别自己写随机数生成器。系统提供的随机源更可靠。

4.5 避坑指南

最后分享几个我踩过的坑:

  • 密钥过期问题:我曾经给设备预置了RSA密钥对,结果设备运行了三年,密钥到期了,所有安全通信全部中断。后来我加了密钥更新机制,每年自动轮换一次。
  • 性能陷阱:ECC签名在低端芯片上也可能耗时几百毫秒。如果设备需要频繁签名,建议用异步方式,别阻塞主线程。
  • 曲线选择:别为了追求「更安全」随便换曲线。鸿蒙默认的P-256经过了充分验证,换成其他曲线可能引入未知漏洞。

嗯,非对称加密这块内容不少,但核心就一句话:公钥加密、私钥解密,私钥永远别出安全区。把这个原则守住了,大部分安全问题都能避免。