文章目录前言一、数据编码格式差异与严格的类型转换二、加解密算法规格的底层差异与参数校验三、SM2 国密算法的序列化格式差异四、AES 算法的密钥与偏移量精度控制五、跨平台加解密的标准化工程方案总结前言真正到了鸿蒙应用和服务端、Web 端或者旧系统对接的时候问题往往不是接口不会调而是两端对同一份数据的理解并不一致。服务端明明加密成功鸿蒙侧却无法解密。鸿蒙生成的密文发到其他平台之后对方又无法识别。这类问题看上去像普通代码错误实际更常见的原因是数据编码、算法规格和序列化结构没有对齐。鸿蒙 6 的 API 20 在加解密能力上给出了更完整的系统支持cryptoFramework也提供了比较清晰的安全工具链。麻烦的地方不在接口够不够用而在跨平台场景里输入给算法的到底是不是正确字节双方用的是不是同一组参数密钥和密文是不是同一种结构。把这些问题理清联调才会顺。理不清后面的排查只会越来越乱。一、数据编码格式差异与严格的类型转换跨平台加解密最先出问题的地方通常不是算法本身而是数据在进入算法之前就已经变了形。服务端习惯传 Base64 或十六进制字符串这样方便网络传输和日志记录。鸿蒙侧的加解密 API 却不按字符串工作它要求的是Uint8Array字节流。两者不是写法不同而是底层语义不同。一个是文本表示一个才是算法真正处理的原始字节。这也是很多解密失败从一开始就注定了。服务端返回的是 Base64 编码后的密文鸿蒙侧如果不先做 Base64 解码而是直接把字符串送进解密流程算法看到的就不是原始密文字节而是一段文本编码后的内容。结果不对并不奇怪。鸿蒙这套 API 的设计思路很明确所有密钥和待处理数据都要求显式转换为Uint8Array。好处是边界清楚坏处是开发者必须自己把转换链路维护好。UTF-8 编码、Base64 编解码、十六进制解析这些都不能混着写。服务端如果用 Base64 传密文鸿蒙侧就必须先把它还原成原始字节再交给解密函数。鸿蒙侧如果生成的是Uint8Array密文后续要通过 HTTP 传输出去也必须重新编码成字符串格式。这里真正该记住的不是某个工具类怎么调用而是传输层格式和密码学输入格式必须严格分开。下面这段代码就是最典型的处理方式。它保留了原始文章里的核心流程也最能说明这个问题。import{util}fromkit.ArkTS;// 将Base64字符串解码为字节流functionbase64ToUint8Array(base64Str:string):Uint8Array{letbase64Helpernewutil.Base64Helper();returnbase64Helper.decodeSync(base64Str);}// 将字节流编码为Base64字符串functionuint8ArrayToBase64(bytes:Uint8Array):string{letbase64Helpernewutil.Base64Helper();returnbase64Helper.encodeToStringSync(bytes);}// 解密来自服务端的Base64密文示例流程asyncfunctiondecryptServiceData(base64Ciphertext:string,key:Uint8Array):Promisestring{// 步骤一 执行Base64解码获取原始字节constciphertextBytesbase64ToUint8Array(base64Ciphertext);// 步骤二 调用cryptoFramework执行解密逻辑// 解密核心代码略 重点说明格式转换的前置必要性console.info(待解密字节长度${ciphertextBytes.length});// 步骤三 将最终的解密结果或新密文重新编码constresultBytesnewUint8Array([0x01,0x02,0x03]);returnuint8ArrayToBase64(resultBytes);}代码里用到的Base64Helper是鸿蒙提供的原生工具类。中小规模数据用decodeSync基本够用。到了大文件或者大块数据场景还是要考虑流式异步处理不然内存占用会很快变得难看。二、加解密算法规格的底层差异与参数校验数据格式对上了还不代表两端一定能互通。很多联调问题真正卡住的地方是算法名字看起来一样底层规格却不是一回事。不同平台即便都写着 AES实际用到的模式、填充、初始向量和认证参数也可能完全不同。鸿蒙的cryptoFramework在这里要求开发者显式传入算法规格字符串这其实是件好事因为它逼着我们把算法环境写清楚。问题在于很多人会沿用别的平台习惯直接把默认规则搬过来。AES-GCM 就是最常见的坑。原先不少开发者会写成AES128|GCM|PKCS7看起来像是顺手带上了填充模式实际上 GCM 属于基于 CTR 的流加密模式不需要也不支持块填充。鸿蒙侧正确的规格应该是AES128|GCM|NoPadding。参数长度同样不能模糊处理。GCM 模式下IV 长度在不同平台和不同框架里经常存在默认差异。业界常见推荐值是 12 字节但有些旧系统会把长度写死成别的值。AAD、认证标签这些附加参数也一样只要有一项不一致完整性校验就会失败。这个阶段真正有效的做法不是反复试几次而是把两端的算法环境逐项展开对照检查。你要确认的不是都用了 AES而是都用了同一组 AES 运行条件。三、SM2 国密算法的序列化格式差异SM2 在鸿蒙平台上有原生支持但它跨平台对接最麻烦的地方经常不是算法计算而是密钥和密文的结构不统一。很多开发者一看到两端都支持 SM2就会默认应该可以直接互通。真到联调时才发现双方拿的根本不是同一种数据结构。鸿蒙底层的 SM2 实现更偏向标准化序列化也就是符合 ASN.1 规则的结构。很多旧服务端或者第三方密码库却喜欢直接传裸字节。公钥可能就是 04 前缀再拼上 64 字节坐标私钥可能就是一个 32 字节整数。从服务端角度看这种表示很直接。到了鸿蒙侧系统接口却未必能直接识别因为它期待的可能是带有完整结构信息的标准封装。密文顺序也是另一个高频坑。旧版国密标准采用C1C2C3拼接顺序新国标和鸿蒙底层更偏向C1C3C2而且要求使用 ASN.1 编码。服务端如果输出的是旧顺序裸密文鸿蒙侧就不能直接照读而是要先把字节流拆开再按系统能识别的结构重新组装。四、AES 算法的密钥与偏移量精度控制AES 跨平台对接里还有一类问题很常见看起来不复杂实际上很容易把人拖进反复排查那就是密钥长度和偏移量精度没有控制住。鸿蒙的规格字符串对密钥长度有明确约束。AES128对应的就是 16 字节物理长度不是 16 个字符也不是大概差不多就行。项目里常见的错误是把字符串直接当作密钥来源再按字符长度去截取。问题一碰到 UTF-8 编码就出来了。中文字符不是单字节长度一算就偏。你以为取到了合适的密钥底层看到的却是一个字节数不合法的输入。更稳妥的处理方式是通过PBKDF2或HKDF这类标准密钥派生函数把任意长度的口令转换成符合规范的固定长度字节数组。这样做不只是为了通过长度校验更是为了让密钥生成过程可复现、可对齐。IV 也不能当成可有可无的附加参数。CBC 模式依赖 16 字节偏移量GCM 模式除了 IV还涉及附加认证数据和认证标签。跨平台通讯时报文里哪一段是 IV哪一段是密文哪一段是标签顺序和长度都要明确。只要偏移一个字节后面的解密就可能直接失败。五、跨平台加解密的标准化工程方案前面这些问题放在一起看你会发现跨平台兼容真正难的不是某一条接口规则而是项目有没有建立一套固定的处理协议。今天服务端回 Base64明天改十六进制。这个接口传 ASN.1另一个接口又传裸字节。AES-GCM 这次用一套参数下次因为框架升级又换了默认值。只要项目没有统一规则联调就永远是在补洞。更稳妥的做法是从一开始就把边界收紧。网络传输层统一使用 Base64避免字符集干扰。客户端收到数据后第一时间转换成Uint8Array后续内部流转保持字节流格式直到输出。算法规格、IV 长度、标签长度、SM2 密钥格式、密文顺序这些都要写进协议而不是依赖某个平台的默认行为。异常处理也不能太粗。解密失败背后可能是密钥不对、报文截断、参数错位或者结构解析失败。业务层如果能做更细一点的错误分级排障路径会短很多。工程里真正好用的经验往往不是多写几层封装而是先把可变因素尽量变少。总结鸿蒙 6 API 20 的加解密能力本身已经比较完整真正决定跨平台能不能稳定互通的不是接口会不会调而是两端有没有在处理同一种字节流、同一组算法参数和同一种序列化结构。数据格式没还原算法输入就已经错了。AES 规格没对齐结果就不会一致。SM2 结构没统一接口再正确也读不出同一层语义。
鸿蒙 HarmonyOS 6 | 加解密 API 跨平台数据兼容性挑战
文章目录前言一、数据编码格式差异与严格的类型转换二、加解密算法规格的底层差异与参数校验三、SM2 国密算法的序列化格式差异四、AES 算法的密钥与偏移量精度控制五、跨平台加解密的标准化工程方案总结前言真正到了鸿蒙应用和服务端、Web 端或者旧系统对接的时候问题往往不是接口不会调而是两端对同一份数据的理解并不一致。服务端明明加密成功鸿蒙侧却无法解密。鸿蒙生成的密文发到其他平台之后对方又无法识别。这类问题看上去像普通代码错误实际更常见的原因是数据编码、算法规格和序列化结构没有对齐。鸿蒙 6 的 API 20 在加解密能力上给出了更完整的系统支持cryptoFramework也提供了比较清晰的安全工具链。麻烦的地方不在接口够不够用而在跨平台场景里输入给算法的到底是不是正确字节双方用的是不是同一组参数密钥和密文是不是同一种结构。把这些问题理清联调才会顺。理不清后面的排查只会越来越乱。一、数据编码格式差异与严格的类型转换跨平台加解密最先出问题的地方通常不是算法本身而是数据在进入算法之前就已经变了形。服务端习惯传 Base64 或十六进制字符串这样方便网络传输和日志记录。鸿蒙侧的加解密 API 却不按字符串工作它要求的是Uint8Array字节流。两者不是写法不同而是底层语义不同。一个是文本表示一个才是算法真正处理的原始字节。这也是很多解密失败从一开始就注定了。服务端返回的是 Base64 编码后的密文鸿蒙侧如果不先做 Base64 解码而是直接把字符串送进解密流程算法看到的就不是原始密文字节而是一段文本编码后的内容。结果不对并不奇怪。鸿蒙这套 API 的设计思路很明确所有密钥和待处理数据都要求显式转换为Uint8Array。好处是边界清楚坏处是开发者必须自己把转换链路维护好。UTF-8 编码、Base64 编解码、十六进制解析这些都不能混着写。服务端如果用 Base64 传密文鸿蒙侧就必须先把它还原成原始字节再交给解密函数。鸿蒙侧如果生成的是Uint8Array密文后续要通过 HTTP 传输出去也必须重新编码成字符串格式。这里真正该记住的不是某个工具类怎么调用而是传输层格式和密码学输入格式必须严格分开。下面这段代码就是最典型的处理方式。它保留了原始文章里的核心流程也最能说明这个问题。import{util}fromkit.ArkTS;// 将Base64字符串解码为字节流functionbase64ToUint8Array(base64Str:string):Uint8Array{letbase64Helpernewutil.Base64Helper();returnbase64Helper.decodeSync(base64Str);}// 将字节流编码为Base64字符串functionuint8ArrayToBase64(bytes:Uint8Array):string{letbase64Helpernewutil.Base64Helper();returnbase64Helper.encodeToStringSync(bytes);}// 解密来自服务端的Base64密文示例流程asyncfunctiondecryptServiceData(base64Ciphertext:string,key:Uint8Array):Promisestring{// 步骤一 执行Base64解码获取原始字节constciphertextBytesbase64ToUint8Array(base64Ciphertext);// 步骤二 调用cryptoFramework执行解密逻辑// 解密核心代码略 重点说明格式转换的前置必要性console.info(待解密字节长度${ciphertextBytes.length});// 步骤三 将最终的解密结果或新密文重新编码constresultBytesnewUint8Array([0x01,0x02,0x03]);returnuint8ArrayToBase64(resultBytes);}代码里用到的Base64Helper是鸿蒙提供的原生工具类。中小规模数据用decodeSync基本够用。到了大文件或者大块数据场景还是要考虑流式异步处理不然内存占用会很快变得难看。二、加解密算法规格的底层差异与参数校验数据格式对上了还不代表两端一定能互通。很多联调问题真正卡住的地方是算法名字看起来一样底层规格却不是一回事。不同平台即便都写着 AES实际用到的模式、填充、初始向量和认证参数也可能完全不同。鸿蒙的cryptoFramework在这里要求开发者显式传入算法规格字符串这其实是件好事因为它逼着我们把算法环境写清楚。问题在于很多人会沿用别的平台习惯直接把默认规则搬过来。AES-GCM 就是最常见的坑。原先不少开发者会写成AES128|GCM|PKCS7看起来像是顺手带上了填充模式实际上 GCM 属于基于 CTR 的流加密模式不需要也不支持块填充。鸿蒙侧正确的规格应该是AES128|GCM|NoPadding。参数长度同样不能模糊处理。GCM 模式下IV 长度在不同平台和不同框架里经常存在默认差异。业界常见推荐值是 12 字节但有些旧系统会把长度写死成别的值。AAD、认证标签这些附加参数也一样只要有一项不一致完整性校验就会失败。这个阶段真正有效的做法不是反复试几次而是把两端的算法环境逐项展开对照检查。你要确认的不是都用了 AES而是都用了同一组 AES 运行条件。三、SM2 国密算法的序列化格式差异SM2 在鸿蒙平台上有原生支持但它跨平台对接最麻烦的地方经常不是算法计算而是密钥和密文的结构不统一。很多开发者一看到两端都支持 SM2就会默认应该可以直接互通。真到联调时才发现双方拿的根本不是同一种数据结构。鸿蒙底层的 SM2 实现更偏向标准化序列化也就是符合 ASN.1 规则的结构。很多旧服务端或者第三方密码库却喜欢直接传裸字节。公钥可能就是 04 前缀再拼上 64 字节坐标私钥可能就是一个 32 字节整数。从服务端角度看这种表示很直接。到了鸿蒙侧系统接口却未必能直接识别因为它期待的可能是带有完整结构信息的标准封装。密文顺序也是另一个高频坑。旧版国密标准采用C1C2C3拼接顺序新国标和鸿蒙底层更偏向C1C3C2而且要求使用 ASN.1 编码。服务端如果输出的是旧顺序裸密文鸿蒙侧就不能直接照读而是要先把字节流拆开再按系统能识别的结构重新组装。四、AES 算法的密钥与偏移量精度控制AES 跨平台对接里还有一类问题很常见看起来不复杂实际上很容易把人拖进反复排查那就是密钥长度和偏移量精度没有控制住。鸿蒙的规格字符串对密钥长度有明确约束。AES128对应的就是 16 字节物理长度不是 16 个字符也不是大概差不多就行。项目里常见的错误是把字符串直接当作密钥来源再按字符长度去截取。问题一碰到 UTF-8 编码就出来了。中文字符不是单字节长度一算就偏。你以为取到了合适的密钥底层看到的却是一个字节数不合法的输入。更稳妥的处理方式是通过PBKDF2或HKDF这类标准密钥派生函数把任意长度的口令转换成符合规范的固定长度字节数组。这样做不只是为了通过长度校验更是为了让密钥生成过程可复现、可对齐。IV 也不能当成可有可无的附加参数。CBC 模式依赖 16 字节偏移量GCM 模式除了 IV还涉及附加认证数据和认证标签。跨平台通讯时报文里哪一段是 IV哪一段是密文哪一段是标签顺序和长度都要明确。只要偏移一个字节后面的解密就可能直接失败。五、跨平台加解密的标准化工程方案前面这些问题放在一起看你会发现跨平台兼容真正难的不是某一条接口规则而是项目有没有建立一套固定的处理协议。今天服务端回 Base64明天改十六进制。这个接口传 ASN.1另一个接口又传裸字节。AES-GCM 这次用一套参数下次因为框架升级又换了默认值。只要项目没有统一规则联调就永远是在补洞。更稳妥的做法是从一开始就把边界收紧。网络传输层统一使用 Base64避免字符集干扰。客户端收到数据后第一时间转换成Uint8Array后续内部流转保持字节流格式直到输出。算法规格、IV 长度、标签长度、SM2 密钥格式、密文顺序这些都要写进协议而不是依赖某个平台的默认行为。异常处理也不能太粗。解密失败背后可能是密钥不对、报文截断、参数错位或者结构解析失败。业务层如果能做更细一点的错误分级排障路径会短很多。工程里真正好用的经验往往不是多写几层封装而是先把可变因素尽量变少。总结鸿蒙 6 API 20 的加解密能力本身已经比较完整真正决定跨平台能不能稳定互通的不是接口会不会调而是两端有没有在处理同一种字节流、同一组算法参数和同一种序列化结构。数据格式没还原算法输入就已经错了。AES 规格没对齐结果就不会一致。SM2 结构没统一接口再正确也读不出同一层语义。