在 Node.js 中实现 HKDF-Expand 密钥扩展函数

在 node.js 中实现 hkdf-expand 密钥扩展函数

本文详细介绍了在 Node.js 环境下实现 HMAC-based Extract-and-Expand Key Derivation Function (HKDF) 的扩展(Expand)阶段。针对 Node.js crypto 模块不直接提供 HKDFExpand 的挑战,文章通过分析其算法原理,提供了一个基于 Bitwarden 源码的自定义实现方案,并结合 PBKDF2HMAC 演示了密钥派生的完整流程,旨在帮助开发者在 Node.js 中实现与 Python 等其他语言兼容的密钥扩展功能。

1. 理解 HKDF 与密钥派生

密钥派生函数 (Key Derivation Function, KDF) 的主要目的是从一个主密钥(或密码)派生出多个安全强度高、用途不同的子密钥。HKDF (HMAC-based Extract-and-Expand Key Derivation Function) 是一种广泛使用的 KDF,它由两个主要阶段组成:

Extract(提取):从一个弱熵输入密钥(IKM, Input Key Material)和一个可选的盐值(salt)中提取出一个固定长度的伪随机密钥(PRK, Pseudo-Random Key)。这个阶段通常使用 HMAC-SHA256 等算法。Expand(扩展):从 PRK 派生出所需长度的输出密钥材料(OKM, Output Key Material)。这个阶段允许派生出多个不同用途的子密钥,每个子密钥都可以通过一个“信息”(info)字符串进行上下文绑定,确保其唯一性。

在 Bitwarden 等密码管理器中,通常会先使用 PBKDF2HMAC 等算法从用户密码派生出一个主密钥,然后将此主密钥作为 HKDF 的 PRK,通过 HKDF-Expand 派生出用于加密、解密等操作的实际工作密钥。

2. Node.js 中实现 HKDF-Expand 的挑战

Node.js 的 crypto 模块提供了 HMAC、PBKDF2 等核心加密原语,但并没有直接提供一个与 cryptography.hazmat.primitives.kdf.hkdf.HKDFExpand 功能完全对应的 API。这导致在尝试从 Python 等其他语言移植使用 HKDF-Expand 的密钥派生逻辑时,开发者可能会遇到不一致的结果。

例如,直接使用 crypto.createHmac 尝试模拟 HKDF-Expand 是不正确的,因为 HKDF-Expand 包含了一个迭代过程和特定的数据拼接逻辑。

不正确的尝试示例:

import crypto from 'crypto';const salt = 'salt';const iterations = 100000;const password = '123';const info = 'enc'; // 对应 HKDF-Expand 的 info 参数// 1. PBKDF2HMAC 派生主密钥 (masterKey)const masterKey = crypto.pbkdf2Sync(password, salt, iterations, 32, 'sha256');console.log('Node.js Master Key:', masterKey.toString('hex')); // 与 Python 结果一致// 2. 尝试使用 createHmac 模拟 HKDF-Expand// 这种方式是错误的,因为它不符合 HKDF-Expand 的算法规范const stretchedKeyAttempt = crypto.createHmac('sha256', info).update(masterKey).digest();console.log('Node.js Stretched Key (Incorrect Attempt):', stretchedKeyAttempt.toString('hex'));

与 Python cryptography 库的正确 HKDF-Expand 结果进行对比,可以发现 stretchedKeyAttempt 的结果是不同的,这验证了直接使用 createHmac 的方法是错误的。

3. HKDF-Expand 算法原理

HKDF-Expand 的核心在于迭代地计算 HMAC,并拼接上下文信息和计数器。其算法步骤如下:

给定:

PRK (Pseudo-Random Key):伪随机密钥,长度至少为哈希输出长度。info:可选的上下文信息字符串。L:所需输出密钥材料的字节长度。Hash:HMAC 使用的哈希函数(例如 SHA256),其输出长度为 hashLen。

步骤:

计算 N = ceil(L / hashLen),即需要进行 HMAC 迭代的次数。初始化 T(0) 为空字节序列。对于 i 从 1 到 N:T(i) = HMAC(PRK, T(i-1) || info || L_byte)|| 表示字节序列拼接。L_byte 是一个单字节,表示当前的迭代计数 i (例如,0x01, 0x02, …)。最终的 OKM 是 T(1) || T(2) || … || T(N) 的前 L 个字节。

4. 在 Node.js 中实现自定义 HKDF-Expand 函数

根据 HKDF-Expand 的算法原理,我们可以实现一个兼容的自定义函数。以下是基于 Bitwarden 源码的实现,它利用 Node.js crypto 模块的 createHmac 功能来执行 HMAC 运算。

import crypto from 'crypto';/** * 内部 HMAC 辅助函数 * @param data 要进行 HMAC 计算的数据 * @param key HMAC 密钥 * @param algorithm 哈希算法(例如 'sha256') * @returns HMAC 结果 Buffer */function hmac(data: Uint8Array, key: Buffer, algorithm: string): Buffer {    return crypto.createHmac(algorithm, key).update(data).digest();}/** * 实现 HKDF-Expand 密钥扩展函数 *  * @param prk 伪随机密钥 (Pseudo-Random Key),Buffer 类型 * @param info 上下文信息字符串 * @param outputByteSize 所需输出密钥的字节长度 * @returns 派生出的输出密钥材料 (Output Key Material) Buffer */function hkdfExpand(    prk: Buffer,    info: string,    outputByteSize: number): Buffer {    const algorithm = "sha256"; // 使用 SHA256 哈希算法    const hashLen = 32;         // SHA256 的输出长度为 32 字节    // 确保 PRK 长度足够    const prkArr = new Uint8Array(prk);    if (prkArr.length < hashLen) {        throw new Error("prk is too small.");    }    // 将 info 字符串转换为 Uint8Array    const infoBuf = Buffer.from(info);    const infoArr = new Uint8Array(infoBuf);    let runningOkmLength = 0; // 当前已生成的 OKM 长度    let previousT = new Uint8Array(0); // T(i-1)    // 计算需要迭代的次数 N    const n = Math.ceil(outputByteSize / hashLen);    // 预分配 OKM 存储空间    const okm = new Uint8Array(n * hashLen);    for (let i = 0; i = outputByteSize) {            break;        }    }    // 返回 OKM 的前 outputByteSize 字节    return Buffer.from(okm.slice(0, outputByteSize).buffer);}

5. 完整示例:结合 PBKDF2HMAC 和 HKDF-Expand

现在,我们可以将 PBKDF2HMAC 密钥派生与自定义的 hkdfExpand 函数结合起来,实现完整的密钥派生过程。

import crypto from 'crypto';// 假设上述 hkdfExpand 和 hmac 函数已定义或导入// ... (hkdfExpand 和 hmac 函数代码如上所示) ...const salt = 'salt';const iterations = 100000;const password = '123';const info = 'enc'; // HKDF-Expand 的上下文信息const outputKeyLength = 32; // 期望派生出的密钥长度// 1. 使用 PBKDF2HMAC 从密码派生主密钥 (PRK)const masterKey = crypto.pbkdf2Sync(    password,     salt,     iterations,     outputKeyLength, // 这里通常是 32 字节,作为 HKDF 的 PRK    'sha256');console.log('Node.js Master Key (PBKDF2):', masterKey.toString('hex'));// 2. 使用自定义的 hkdfExpand 函数从 masterKey 派生最终的工作密钥const stretchedKey = hkdfExpand(masterKey, info, outputKeyLength);console.log('Node.js Stretched Key (HKDF-Expand):', stretchedKey.toString('hex'));// Python 对应结果 (仅供参考,应与上述 Node.js 结果一致)// master_key.hex() # 5bb4...5990// stretched.hex()  # 5bf9...473b

通过运行上述代码,你会发现 Node.js 输出的 stretchedKey 与 Python 中 HKDFExpand 派生出的密钥完全一致。这表明自定义的 hkdfExpand 函数成功地模拟了 HKDF-Expand 的行为。

6. 注意事项

Buffer 与 Uint8Array 的转换:在 Node.js 中,Buffer 和 Uint8Array 都是处理二进制数据的重要类型。在加密操作中,经常需要在这两者之间进行转换。Buffer.from(uint8array) 和 new Uint8Array(buffer) 是常见的转换方式。本例中,为了与 crypto 模块的 Buffer 接口兼容,并在内部处理中方便地进行字节拼接,使用了两者结合的方式。安全性盐值 (Salt):在实际应用中,盐值必须是随机生成且足够长的,并且对于每个用户或每个密钥都应该是唯一的,不能使用固定的字符串如 ‘salt’。迭代次数 (Iterations):PBKDF2 的迭代次数应足够大,以抵御暴力破解攻击。随着计算能力提升,推荐的迭代次数也在增加。密钥长度:派生出的密钥长度应根据加密算法的要求选择,通常为 16 字节(AES-128)、24 字节(AES-192)或 32 字节(AES-256)。上下文信息 (Info):HKDF-Expand 的 info 参数非常重要,它用于将派生出的密钥与特定的上下文或用途绑定。不同的 info 值将派生出不同的密钥,即使 PRK 相同。这有助于防止密钥重用,并提高安全性。性能:对于性能敏感的应用,尤其是需要大量密钥派生操作的场景,应评估自定义实现的性能。Node.js 的 crypto 模块底层是 C++ 实现,性能通常较高,但手动循环和内存操作仍需注意。库的替代方案:虽然 Node.js crypto 模块没有直接提供 HKDF-Expand,但社区中可能存在封装好的第三方库,它们可能提供了更简洁的 API 或更全面的功能。在生产环境中,优先考虑使用经过充分审计和测试的第三方库,而不是自行实现复杂加密算法。

7. 总结

在 Node.js 中实现 HKDF-Expand 密钥扩展功能,虽然 crypto 模块未直接提供 API,但通过深入理解 HKDF 的算法原理,并利用 crypto.createHmac 等基本原语,我们可以构建一个功能完整且与其他语言实现兼容的自定义函数。本文提供的实现方案,源自对实际应用(如 Bitwarden)的分析,展示了如何从 PBKDF2HMAC 派生主密钥,再通过自定义的 HKDF-Expand 派生出最终的工作密钥。在实际应用中,务必关注安全性最佳实践,确保密钥派生过程的健壮性和安全性。

以上就是在 Node.js 中实现 HKDF-Expand 密钥扩展函数的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1521722.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
什么是JavaScript的生成器协程,以及它如何模拟多线程并发处理异步任务?
上一篇 2025年12月20日 14:25:22
JavaScript开发:工具是否必需?初学者指南
下一篇 2025年12月20日 14:25:33

相关推荐

  • 红队战术-躲避日志检查

    红队战术-躲避日志检查红队战术-躲避日志检查红队战术-躲避日志检查红队战术-躲避日志检查

    前言 Windows事件日志概述:Windows系统日志记录系统中硬件、软件和系统问题的信息,并监控系统中发生的事件。用户可以利用这些日志检查错误原因或追踪攻击者留下的痕迹。 禁用Windows事件日志记录是红队常用策略之一,旨在减少安全人员可用于检测和审核的数据量,提升红队活动的隐蔽性。通常,安全…

    2026年9月4日 用户投稿
    000
  • 12305官方网站快速登录_12305投诉平台入口指引

    12305官方网站快速登录入口为https://sswz.spb.gov.cn/portal/home,用户可点击页面右上角“立即登录”进入,支持手机号、密码或短信验证码登录。 12305官方网站快速登录入口在哪里?这是不少网友关心的问题,接下来由PHP小编为大家带来12305投诉平台入口指引,有需…

    2026年9月4日
    000
  • 渗透测试安全检测漏洞

    最近由于渗透测试任务繁重,笔者一直未能抽出时间撰写文章。今天,我们sine安全的渗透主管将为大家普及java安全测试的基础知识。许多客户希望了解具体的javascript调用漏洞、提交payload的过程,以及如何获取最高权限、绕过登录等执行命令漏洞的安全检测方法。 4.4.1.1. 显式原型和隐式…

    2026年9月4日
    000
  • 如何为iPhone13Pro获取固件包?最新固件下载攻略

    首先通过Apple官方iTunes或Finder下载iPhone 13 Pro固件,确保安全可靠;其次可从iStoreOS平台获取优化版固件;最后开发者可登录苹果开发者网站下载测试版固件。 如果您尝试为您的iPhone 13 Pro获取固件包,但无法找到可靠的来源,则可能是由于未访问官方或受信任的固…

    2026年9月4日
    100
  • C++中如何用Lambda函数实现私有函数仅供公有函数调用?

    C++中使用Lambda函数模拟私有函数,仅供公有函数调用 问题:如何在C++中实现类似于其他语言中“私有函数仅供公有函数调用”的特性? 解决方法:虽然C++没有直接的“私有函数”概念像Java或C#那样,但我们可以巧妙地利用Lambda表达式来模拟这种行为。Lambda表达式创建的匿名函数,其作用…

    2026年9月4日
    000
  • ​​无法定位程序输入点于动态链接库?错误修复指南​​

    重新安装或修复出错程序,确保安装完整且无残留;2. 运行sfc++ /scannow和dism命令修复系统文件,必要时手动替换受损dll;3. 更新或重装microsoft visual c++ redistributable和.net framework等运行库;4. 更新驱动程序和系统补丁,确保…

    2026年9月4日
    300
  • 怎么更换VSCode的语言_VSCode界面语言切换与本地化设置教程

    更改VSCode语言需安装对应语言包并配置显示语言,重启生效;2. 语言包安装失败可检查网络、清缓存或手动安装.vsix文件;3. 界面语言与文件编码无关,前者影响UI显示,后者决定字符存储解析;4. 扩展语言问题多因不支持多语言或设置不同步,可检查扩展设置或系统语言。 VSCode更改语言其实非常…

    2026年9月4日
    100
  • Karate框架中构建包含复杂嵌套参数的GET请求指南

    本教程详细介绍了如何在Karate API测试框架中正确处理包含复杂嵌套参数(如filters[key])的GET请求。当直接使用JSON对象定义此类参数时,Karate可能无法正确解析URL。文章将演示如何利用* param关键字逐一设置这些参数,确保生成的请求URL格式符合预期,并提供示例代码和…

    2026年9月4日
    200
  • 安全基线检查平台

    安全基线检查平台安全基线检查平台安全基线检查平台安全基线检查平台

    0x01 介绍 最近我在进行安全基线检查相关的工作,网络上的一些代码比较零散;也有一些比较完整的项目,比如OWASP中的安全基线检查项目,但需要付费;还有一些开源且完整的,比如Lynis,但这些都不符合我的需求。 我的需求如下: 最终的效果是什么呢?最好能够达到阿里云里的安全基线检查的样子,即使差一…

    2026年9月4日 用户投稿
    100
  • MAC的iMessage怎么同步手机上的所有短信_MAC iMessage同步手机短信教程

    首先确保Mac与iPhone登录同一Apple ID并开启iCloud信息同步,接着在iPhone上启用短信转发功能以实现跨设备接收,最后检查网络连接与账户状态,必要时通过重新登录账户触发同步。 如果您希望在MAC电脑上查看和管理iPhone上的所有短信,可以通过iMessage功能实现跨设备同步。…

    2026年9月4日
    100
  • 通过 Eloquent 关联模型获取分组数据:以餐厅订单为例

    本文旨在提供一种使用 Laravel Eloquent ORM 通过关联模型获取并分组数据的有效方法。我们将以餐厅、菜品和订单之间的关系为例,展示如何使用 with() 和 whereHas() 方法,避免使用循环,从而编写更简洁、更高效的代码。通过本文,你将学会如何根据订单 ID 对结果进行分组,…

    2026年9月4日
    000
  • 电脑突然蓝屏怎么办?

    很多用户在使用电脑的过程中可能会遭遇电脑突然蓝屏的情况,这种情况其实并不少见。今天,小编就给大家分享一些解决电脑蓝屏问题的方法,希望能帮到您。 电脑蓝屏是微软Windows操作系统在遇到严重错误且无法自行恢复时,为了保护用户的数据安全而强制显示的蓝色错误画面。许多用户都曾经历过电脑蓝屏的问题,下面我…

    2026年9月4日
    100
  • 已恢复!国内大量iPhone 17新机无法激活 网友:买个了砖头回来

    10月14日,微博、小红书等多个社交平台涌现大量用户反馈,新款iphone17在激活过程中遭遇普遍性故障,导致设备无法正常使用。 据多方信息显示,此次问题波及范围极广,无论是国行版、有锁机还是无锁机,只要是新购买的iPhone设备,不论型号新旧,均有用户报告出现激活失败的情况。 一位刚入手iPhon…

    2026年9月4日
    000
  • C#——获取鼠标当前坐标

    c#中坐标转换的详细说明与示例: 在C#编程中,坐标转换是常见的操作,尤其是在处理用户界面时。以下是关于坐标转换的一些关键方法及其使用说明: Control.PointToScreen:将控件上的点转换为屏幕坐标系中的点。例如,如果你有一个控件上的点,需要知道它在整个屏幕上的位置,可以使用此方法。 …

    2026年9月4日
    300
  • 为什么iPhoneSE2020屏幕失灵如何强制重启?按音量加减后长按电源键重启

    先尝试强制重启解决屏幕无响应问题。1、快速按放音量加键;2、快速按放音量减键;3、长按侧边电源键约10秒至出现苹果Logo。若无效,使用原装线连接电脑iTunes,强制重启进入恢复模式后选择“恢复”重装系统。最后检查充电反应、屏幕显示及机身是否进水或损坏,排除硬件故障。 如果您尝试操作iPhone …

    2026年9月4日
    100
  • 微软确认 Win11 24H2 系统移除“写字板”应用

    微软已明确表示,从 windows 11 vision 24h2 和 windows server 2025 起,后续系统更新将不再包含“写字板”(wordpad)这一经典应用。这款软件最早出现在 1995 年的 windows 95 系统中,并一直作为 windows 的默认程序之一,为用户提供基…

    2026年9月4日
    100
  • AI做音乐推荐怎么实现_Spotify推荐算法原理与应用

    AI做音乐推荐怎么实现_Spotify推荐算法原理与应用AI做音乐推荐怎么实现_Spotify推荐算法原理与应用AI做音乐推荐怎么实现_Spotify推荐算法原理与应用AI做音乐推荐怎么实现_Spotify推荐算法原理与应用

    答案:AI音乐推荐通过协同过滤、内容分析、混合系统和上下文感知等多维度算法,结合用户行为与歌曲特征,实现个性化推荐。Spotify利用隐式反馈、音频特征提取、深度学习和NLP技术,融合用户偏好与情境因素,解决冷启动问题,并在探索与利用间平衡,持续优化用户体验。 ☞☞☞AI 智能聊天, 问答助手, A…

    2026年9月4日 用户投稿
    000
  • Safari阻止跨网站跟踪怎么开_Safari防跟踪功能隐私保护设置

    开启“阻止跨站跟踪”可有效保护Safari隐私,防止广告商跨站追踪;在Mac上进入Safari偏好设置→隐私,勾选该选项并启用“要求网站不要跟踪我”;iPhone则在设置→Safari浏览器中开启“阻止跨网站跟踪”和“欺骗性网站警告”;通过地址栏护盾图标查看隐私报告,了解被拦截的跟踪器数量及来源,确…

    2026年9月4日
    000
  • Linux文件系统目录结构详解

    大家好,又见面了,我是你们的朋友全栈君。 引言 对于每一个Linux学习者来说,了解Linux文件系统的目录结构,是学好Linux的至关重要的一步.,深入了解#%#$#%@%@%$#%$#%#%#$%@_e206a54e97690c++e50cc872dd70ee896文件目录结构的标准和每个目录的…

    2026年9月4日
    200
  • MAC的隔空投送(AirDrop)搜不到设备怎么办_MAC AirDrop搜不到设备解决方法

    首先检查设备Wi-Fi和蓝牙是否开启,并确保隔空投送设置为“所有人”或“仅限联系人”;接着重启所有设备以排除临时故障;若问题仍存在,尝试退出并重新登录iCloud账户以重置网络与同步状态;为进一步排查,可启动Mac安全模式以排除第三方软件干扰;最后通过创建新管理员账户判断是否为用户配置文件损坏所致。…

    2026年9月4日
    200

发表回复

登录后才能评论
关注微信