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

如何用JavaScript实现Go语言嵌套HMAC实例的创建?

解决方案:手动实现嵌套HMAC逻辑模拟Go的行为

Go中嵌套调用hmac.New的核心是将内层HMAC实例作为外层HMAC的底层哈希函数,这要求外层HMAC在计算时,用内层HMAC完成标准HMAC流程中的两次哈希操作。由于JavaScript的crypto模块不支持直接传入自定义哈希函数,我们可以手动实现HMAC的通用逻辑,从而支持嵌套的哈希函数(包括HMAC实例)。

核心思路

标准HMAC的计算流程是:

  1. 处理密钥,若长度超过哈希块大小则先哈希密钥
  2. 生成ipad(密钥与0x36异或)和opad(密钥与0x5c异或)
  3. 计算Hash(ipad || 数据)得到内层结果
  4. 计算Hash(opad || 内层结果)得到最终HMAC值

当Hash是另一个HMAC时,只需将上述流程中的Hash替换为内层HMAC的运算即可。

实现代码

1. 基础版(一次性处理数据)

适用于不需要多次调用update的场景:

const crypto = require('crypto');

// 基础SHA256哈希函数
function sha256Hash(data) {
  return crypto.createHash('sha256').update(data).digest();
}

// 通用HMAC实现,支持传入自定义哈希函数
function customHmac(customHash, key, data) {
  const blockSize = 64; // SHA256的块大小为64字节
  let keyBuf = Buffer.from(key);
  
  // 密钥过长时先哈希
  if (keyBuf.length > blockSize) {
    keyBuf = customHash(keyBuf);
  }

  const ipad = Buffer.alloc(blockSize, 0x36);
  const opad = Buffer.alloc(blockSize, 0x5c);
  
  // 生成ipad和opad
  for (let i = 0; i < keyBuf.length; i++) {
    ipad[i] ^= keyBuf[i];
    opad[i] ^= keyBuf[i];
  }

  // 计算内层和外层哈希
  const innerHash = customHash(Buffer.concat([ipad, data]));
  return customHash(Buffer.concat([opad, innerHash]));
}

// 构建嵌套HMAC链
const SALT = 'your-salt-here';
const path = ['path0', 'path1', 'path2'];

// 逐层定义嵌套HMAC函数
const innerMostHmac = (data) => customHmac(sha256Hash, SALT, data);
const innerHmac = (data) => customHmac(innerMostHmac, path[0], data);
const midHmac = (data) => customHmac(innerHmac, path[1], data);
const outerHmac = (data) => customHmac(midHmac, path[2], data);

// 使用示例
const testData = Buffer.from('test input');
const result = outerHmac(testData);
console.log(result.toString('hex'));

2. 完整版(支持多次update,贴近Go接口)

如果需要模拟Go中hash.Hash的Write(对应JS的update)和Sum(对应digest)接口,可以用类和工厂函数实现:

const crypto = require('crypto');

// 创建嵌套HMAC实例的工厂函数,返回符合Hash接口的对象
function createNestedHmac(hashFactory, key) {
  const blockSize = 64;
  let keyBuf = Buffer.from(key);
  
  // 处理过长密钥:用底层哈希工厂哈希密钥
  if (keyBuf.length > blockSize) {
    const hash = hashFactory();
    keyBuf = hash.update(keyBuf).digest();
  }

  const ipad = Buffer.alloc(blockSize, 0x36);
  const opad = Buffer.alloc(blockSize, 0x5c);
  
  for (let i = 0; i < keyBuf.length; i++) {
    ipad[i] ^= keyBuf[i];
    opad[i] ^= keyBuf[i];
  }

  // 返回支持update和digest的实例
  return {
    _buffers: [],
    update(data) {
      this._buffers.push(Buffer.from(data));
      return this;
    },
    digest() {
      const combinedData = Buffer.concat(this._buffers);
      // 计算内层:底层哈希处理ipad+数据
      const innerHash = hashFactory().update(Buffer.concat([ipad, combinedData])).digest();
      // 计算外层:底层哈希处理opad+内层结果
      return hashFactory().update(Buffer.concat([opad, innerHash])).digest();
    }
  };
}

// 模拟Go的嵌套创建方式
const SALT = 'your-salt-here';
const path = ['path0', 'path1', 'path2'];

// 逐层构建哈希工厂
const innerMostFactory = () => createNestedHmac(() => crypto.createHash('sha256'), SALT);
const innerFactory = () => createNestedHmac(innerMostFactory, path[0]);
const midFactory = () => createNestedHmac(innerFactory, path[1]);
const hmacf = createNestedHmac(midFactory, path[2]);

// 使用示例:多次调用update
hmacf.update('first part').update('second part');
const result = hmacf.digest();
console.log(result.toString('hex'));

验证一致性

上述代码完全遵循Go中HMAC嵌套的逻辑,无论是一次性数据还是多次更新,输出结果都会和Go版本完全一致。你可以用相同的测试数据对比两者的十六进制输出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 04:09:52