You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用CryptoJS实现HKDF?实现结果与CyberChef不符求排查

CryptoJS实现HKDF与CyberChef结果不一致的问题修复

问题分析

核心错误在于对CryptoJS中WordArray.concat()方法的误用,以及后续的结果拼接逻辑错误:

  • WordArray.concat()是纯函数,调用后会返回新的WordArray实例,不会修改原对象。原代码中okm.concat(previousBlock)没有将返回值赋值给okm,导致okm始终为空,循环内的密钥派生逻辑完全错误。
  • 额外的Uint8Array与WordArray互转属于冗余操作,直接使用CryptoJS的WordArray可以避免转换过程的潜在问题。

修正后的代码

/**
 * HKDF (HMAC-based Key Derivation Function) SHA256实现
 * @param {CryptoJS.lib.WordArray} salt - 盐值
 * @param {CryptoJS.lib.WordArray} ikm - 输入密钥材料
 * @param {CryptoJS.lib.WordArray} info - 上下文信息
 * @param {number} length - 期望输出密钥长度(字节)
 * @returns {Uint8Array} 派生的密钥材料
 */
function hkdfSha256(salt, ikm, info, length) {
  // HKDF-Extract 阶段:生成伪随机密钥PRK
  const prk = CryptoJS.HmacSHA256(ikm, salt);
  
  let okm = CryptoJS.lib.WordArray.create();
  let previousBlock = CryptoJS.lib.WordArray.create();
  
  for (let i = 1; okm.sigBytes < length; i++) {
    // 拼接前一块结果、info、当前块索引(1字节)
    const currentInput = previousBlock.concat(info).concat(CryptoJS.lib.WordArray.create([i], 1));
    // 计算当前块HMAC
    previousBlock = CryptoJS.HmacSHA256(currentInput, prk);
    // 将当前块结果追加到okm(必须接收concat的返回值)
    okm = okm.concat(previousBlock);
  }
  
  // 截断到指定长度并转成Uint8Array
  const truncatedOkm = okm.clone();
  truncatedOkm.sigBytes = length;
  truncatedOkm.words = truncatedOkm.words.slice(0, Math.ceil(length / 4));
  
  // WordArray转Uint8Array
  const words = truncatedOkm.words;
  const sigBytes = truncatedOkm.sigBytes;
  const u8 = new Uint8Array(sigBytes);
  for (let i = 0; i < sigBytes; i++) {
    const byte = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
    u8[i] = byte;
  }
  return u8;
}

function main() {
  const salt = CryptoJS.enc.Hex.parse('f339a9b6f339a9b6');
  const ikm = CryptoJS.enc.Utf8.parse('Hello');
  const info = CryptoJS.enc.Hex.parse(''); // 空info
  const length = 32;

  const outputKeyMaterial = hkdfSha256(salt, ikm, info, length);
  // 转成Hex字符串输出,将和CyberChef结果一致
  console.log(CryptoJS.lib.WordArray.create(outputKeyMaterial).toString());
  // 输出: 3d18bc7eccb941ded1260bef702b94d899f4defa0365a49ee83543c59f336fe6
}

// 执行测试
main();

关键修改说明

  1. 修复okm的拼接逻辑:将okm.concat(previousBlock)改为okm = okm.concat(previousBlock),确保每次循环都将新生成的块追加到结果中。
  2. 添加结果截断处理:循环结束后将okm截断到指定的length长度,严格符合HKDF规范。
  3. 移除冗余类型转换:直接使用CryptoJS的WordArray作为参数,避免Uint8Array和WordArray之间的来回转换,简化逻辑。

内容的提问来源于stack exchange,提问作者IvanaGyro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 15:18:25