查看: 110|回复: 0

鸿蒙ASCF加密随机数实战:getRandomValues用法与踩坑

[复制链接]
发表于 半小时前 | 显示全部楼层 |阅读模式
在鸿蒙应用开发中,生成临时token、请求签名nonce或本地加密密钥时,很多开发者第一反应是顺手用Math.random()。但如果你所在的项目有安全评审,这个写法大概率会被打回——Math.random()基于伪随机算法(PRNG),输出序列存在统计规律,给定足够样本理论上可以推导后续值,完全不适合安全敏感场景。鸿蒙的ASCF(Application Security Configuration Framework,应用安全配置框架)提供了has.getRandomValues这个API,底层基于密码学安全伪随机数生成器(CSPRNG),生成的随机数不可预测,才是做token、nonce、密钥、验证码的正确选择。

先说结论:非安全场景(UI动画、随机背景色、列表洗牌)用Math.random()完全够用;安全相关场景必须用has.getRandomValues。下面围绕这个API的用法、边界条件和实际项目中的适配细节展开。

一、API基本用法与返回值类型

has.getRandomValues的接口签名很简洁,只有一个必填参数length,表示要生成随机数的字节数,最大支持1048576字节(1MB)。成功回调里返回的randomValues是ArrayBuffer类型,而不是直接可用的字符串。
  1. has.getRandomValues({ length: 16, // 生成16字节随机数 success: function(res) { // res.randomValues是ArrayBuffer console.info('随机数长度:', res.randomValues.byteLength); // 16 }, fail: function(err) { console.error('生成失败:', err); } });
复制代码

这里有个容易踩的坑:ArrayBuffer不能直接toString或JSON.stringify。笔者一开始图省事直接调用res.randomValues.toString(),得到的结果完全不是预期内容;而如果直接把ArrayBuffer放进JSON.stringify,序列化出来的是一个空对象{}。往Storage里存随机数时尤其要注意——存进去是{},取出来自然也是空的,排查半天还以为是存储模块坏了。正确做法是先用工具方法转换成字符串再存储。

二、ArrayBuffer的两种常用转换方式

ASCF提供了现成的has.arrayBufferToBase64()方法,用于将ArrayBuffer转为Base64字符串。Base64格式紧凑,16字节随机数转出来约24个字符,适合放在URL参数或HTTP Header中。
  1. has.getRandomValues({ length: 16, success: function(res) { let base64 = has.arrayBufferToBase64(res.randomValues); console.info('Base64:', base64); // 类似 "k7Bx3mP9qR2vN5wY" 的字符串 } });
复制代码

如果需要十六进制表示(比如做数据库主键或日志追踪ID,Hex更直观),ASCF没有直接提供转换工具,需要自己遍历Uint8Array实现。注意每个字节转Hex后不足两位要补零:
  1. function arrayBufferToHex(buffer) { let uint8Array = new Uint8Array(buffer); let hex = ''; for (let i = 0; i < uint8Array.length; i++) { let byte = uint8Array[i].toString(16); if (byte.length < 2) { byte = '0' + byte; } hex += byte; } return hex; }
复制代码

三、length参数的单位、上限与边界行为

length单位是字节不是位。需要128位随机数时,应该传length: 16而不是128。这个单位问题容易搞混,写代码时建议多看一眼注释。

边界行为笔者实测如下:length传0不会报错,返回一个空的ArrayBuffer;传负数会走fail回调;传超过1048576(1MB)的值也会走fail回调,但fail回调里没有返回具体错误码,报的是参数错误,排查时只能靠猜。实际开发中建议在调用前自行做参数校验,避免依赖fail回调的错误信息定位问题。

四、实战:验证码、批量生成与错误处理

生成6位数字验证码时,4字节(32位)随机数已经足够覆盖1000000以内的数字空间。核心思路是用Uint32Array读取随机值,对1000000取模后补零到6位:
  1. function generateVerifyCode() { return new Promise(function(resolve, reject) { has.getRandomValues({ length: 4, success: function(res) { let view = new Uint32Array(res.randomValues); let code = (view[0] % 1000000).toString(); while (code.length < 6) { code = '0' + code; } resolve(code); }, fail: reject }); }); }
复制代码

批量生成多个token有两种思路。思路一是单次调用拿到大块随机数后按偏移量切割,只发起一次API调用,效率更高,但要注意总字节数不能超过1MB上限:
  1. function batchGenerateTokens(count, tokenLength) { let totalBytes = count * tokenLength; if (totalBytes > 1048576) { console.error('总字节数超出上限'); return; } has.getRandomValues({ length: totalBytes, success: function(res) { let uint8 = new Uint8Array(res.randomValues); let tokens = []; for (let i = 0; i < count; i++) { let start = i * tokenLength; let slice = uint8.slice(start, start + tokenLength); tokens.push(has.arrayBufferToBase64(slice.buffer)); } console.info('生成了', tokens.length, '个token'); } }); }
复制代码

思路二是循环调用getRandomValues逐个生成,代码更直观但调用次数多。实测随机数生成本身很快,两种方案体感差异不大,可根据场景选择。

在实际项目里,has.getRandomValues主要用在三个地方:一是生成请求签名nonce,配合timestamp防止重放攻击;二是生成本地加密存储的key,例如32字节(256位)密钥,转成Base64后存入存储模块;三是生成唯一ID,12字节(96位)随机数已足够降低碰撞概率,转Hex字符串作为日志追踪ID或数据库主键。

五、容易忽视的几个安全细节

不要用Date.now()当随机数种子。时间戳可预测,攻击者知道大致时间范围就能缩小搜索空间。getRandomValues内部已经处理好种子生成问题,直接使用即可。

不要觉得"随机数就是随机数"就随意混用。Math.random()和has.getRandomValues的底层实现机制完全不同,前者是PRNG,后者是CSPRNG。是否使用加密级随机数,取决于数据是否会被攻击者利用来预测后续值。token、nonce、密钥、验证码这类直接影响安全的场景,没有偷懒的余地。

总的来说,has.getRandomValues的API本身不复杂,一个参数一个回调,真正的价值在于"用对场景"。如果后续需要生成UUID,也可以用getRandomValues生成16字节随机数后按RFC 4122格式设置版本位和变体位,不过这属于更进阶的用法了。文中涉及的has.arrayBufferToBase64()工具方法比手写Uint8Array到Base64的转换省事不少,值得收藏。
  1. // 常见组合速查:16字节随机数转Base64做nonce
  2. function generateNonce() { return new Promise(function(resolve, reject) { has.getRandomValues({ length: 16, success: function(res) { resolve(has.arrayBufferToBase64(res.randomValues)); }, fail: reject }); }); }
复制代码
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-8-26 14:37 , Processed in 0.019819 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部