0

0

正确计算Google Apps Script中带十六进制密钥的HMAC

霞舞

霞舞

发布时间:2025-09-10 17:31:42

|

771人浏览过

|

来源于php中文网

原创

正确计算Google Apps Script中带十六进制密钥的HMAC

本文深入探讨了在Google Apps Script中使用十六进制密钥正确计算HMAC的常见问题。通过对比PHP和Google Apps Script的实现差异,重点阐述了将十六进制密钥和消息字符串精确转换为字节数组的重要性,并提供了详细的示例代码和注意事项,确保HMAC计算结果的一致性。

引言

hmac(keyed-hash message authentication code)是一种使用密钥的哈希消息认证码,广泛应用于api认证和数据完整性验证。在跨平台或跨语言实现hmac时,开发者常会遇到计算结果不一致的问题,尤其是在处理非标准格式的密钥(如十六进制字符串)时。本文将以一个具体的案例为例,详细讲解如何在google apps script中正确处理带十六进制密钥的hmac计算,使其与php等其他语言的实现保持一致。

问题描述

在API认证场景中,我们可能需要使用HMAC对请求进行签名。假设API提供了一个8字节的十六进制密钥,例如 C77C96EEF6F6995B。当使用PHP脚本按照厂商说明成功生成HMAC并完成认证后,尝试在Google Apps Script中以类似方式实现时,却得到了不同的哈希值,导致认证失败。

PHP工作示例:

上述PHP脚本生成的哈希值为 9b5f9bb41ac30b9c55b10f3cecdd6e12e852f274,该值经验证可用于API认证。

Google Apps Script尝试实现(失败):

function bytesToHex(data) {
    return data.map(function(e) {
        var v = (e < 0 ? e + 256 : e).toString(16);
        return v.length == 1 ? "0" + v : v;
    }).join("");
}

function callNumbers() {
  // 尝试将十六进制转换为字符作为密钥
  var klucz = String.fromCharCode(parseInt("C7",16))+String.fromCharCode(parseInt("7C",16))+String.fromCharCode(parseInt("96",16))+String.fromCharCode(parseInt("EE",16))+String.fromCharCode(parseInt("F6",16))+String.fromCharCode(parseInt("F6",16))+String.fromCharCode(parseInt("99",16))+String.fromCharCode(parseInt("5B",16)); 

  var user = "user@example.com"; // 实际应用中请替换为真实邮箱
  var url = "https://www.ifirma.pl/iapi/abonent/miesiacksiegowy.json";
  var wiadomosc = url+user+"abonent";

  // 直接传入字符串和字符密钥
  var hmac = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_1, wiadomosc, klucz);

  hmac=bytesToHex(hmac)
  Logger.log(hmac);
}

这段Google Apps Script代码生成的哈希值为 6f50edae11b1f7f5b8488a99c4df3e797662c739,与PHP结果不符。

根源分析:字节数组的精确处理

导致HMAC计算不一致的根本原因在于Google Apps Script中的 Utilities.computeHmacSignature 函数对输入参数(消息和密钥)的期望类型和字节表示方式与PHP存在差异。

  1. 密钥的误解: PHP的 chr(hexdec('XX')) 将十六进制值转换为单个原始字节字符,这些字符串连接起来形成一个字节序列作为HMAC密钥。 Google Apps Script中的 String.fromCharCode(parseInt(hex, 16)) 会创建JavaScript字符串。虽然这些字符串看起来包含正确的字符,但当它们被 Utilities.computeHmacSignature 作为密钥处理时,可能不会被解释为期望的原始字节数组。Utilities.computeHmacSignature 函数的 key 参数期望的是 byte[] 类型,即一个Java风格的带符号8位整数数组。

  2. 消息的误解:Utilities.computeHmacSignature 的 value 参数同样期望 byte[] 类型。直接传入JavaScript字符串 wiadomosc 可能导致内部默认的字符串到字节数组转换与API期望的编码(通常是UTF-8)不完全一致,或者与PHP中字符串的字节表示方式不同。PHP中的字符串在哈希函数中通常直接按其字节内容处理。

    AIPAI
    AIPAI

    AI视频创作智能体

    下载

解决方案:正确的字节数组转换

要解决这个问题,我们需要确保将十六进制密钥和消息字符串都精确地转换为Google Apps Script Utilities.computeHmacSignature 函数所期望的 byte[] 类型。

1. 十六进制密钥转换为带符号字节数组

Google Apps Script的 byte[] 存储的是带符号的8位整数(范围从-128到127)。而十六进制值通常表示无符号字节(范围从0到255)。因此,我们需要将无符号十六进制值转换为其对应的带符号字节表示。

例如,十六进制 C7 对应的无符号十进制是 199。作为带符号字节,199 实际上是 -57(因为 199 - 256 = -57)。对于十六进制 7C,对应的无符号十进制是 124124,它在带符号字节范围内,因此保持 124124。

以下代码片段展示了如何将十六进制字符串 C77C96EEF6F6995B 转换为符合 byte[] 要求的带符号字节数组:

var hexKeyString = "C77C96EEF6F6995B";
var kluczByteArray = hexKeyString.match(/.{2}/g).map(e => {
  let byteValue = parseInt(e, 16); // 将每对十六进制字符转换为无符号整数
  // 如果无符号值大于127(即最高位为1),则转换为对应的带符号值
  return (parseInt(e[0], 16).toString(2).length == 4) ? byteValue - 256 : byteValue;
});
// 简化的条件判断:如果解析的十六进制值大于127,则减去256
// return (byteValue > 127) ? byteValue - 256 : byteValue;

解释上述 map 函数中的条件判断:parseInt(e[0], 16).toString(2).length == 4 这个条件检查的是十六进制对 e 的第一个字符 e[0] 对应的十进制值(例如,'C' 对应 12)转换成二进制后是否为4位。这个条件对于所有十六进制数字 8 到 F 都是 true,而对于 0 到 7 则是 false。 这意味着,当十六进制对的第一个字符是 8-F 时(即该字节的无符号值在 128-255 范围内),byteValue - 256 会被执行,将其转换为对应的负数带符号字节。如果第一个字符是 0-7(即该字节的无符号值在 0-127 范围内),则 byteValue 保持不变,因为其已在带符号字节范围内。这正是将无符号字节正确映射到带符号字节的方法。

2. 消息字符串转换为字节数组

对于消息字符串,我们需要使用 Utilities.newBlob(string).getBytes() 方法将其转换为UTF-8编码的字节数组。这确保了消息的字节表示与PHP中字符串的默认处理方式保持一致。

var wiadomoscString = url + user + "abonent";
var wiadomoscByteArray = Utilities.newBlob(wiadomoscString).getBytes();

3. 整合到Google Apps Script

将上述转换应用到Google Apps Script代码中,修正后的 callNumbers 函数如下:

function bytesToHex(data) {
    return data.map(function(e) {
        var v = (e < 0 ? e + 256 : e).toString(16);
        return v.length == 1 ? "0" + v : v;
    }).join("");
}

function callNumbers() {
  var hexKeyString = "C77C96EEF6F6995B";
  // 将十六进制密钥转换为带符号字节数组
  var kluczByteArray = hexKeyString.match(/.{2}/g).map(e => {
    let byteValue = parseInt(e, 16);
    // 根据十六进制值范围转换为带符号字节
    return (parseInt(e[0], 16).toString(2).length == 4) ? byteValue - 256 : byteValue;
  });

  var user = "user@example.com"; // 实际应用中请替换为真实邮箱
  var url = "https://www.ifirma.pl/iapi/abonent/miesiacksiegowy.json";
  var wiadomoscString = url + user + "abonent";

  // 将消息字符串转换为UTF-8编码的字节数组
  var wiadomoscByteArray = Utilities.newBlob(wiadomoscString).getBytes();

  // 使用转换后的字节数组进行HMAC计算
  var hmac = Utilities.computeHmacSignature(Utilities.MacAlgorithm.HMAC_SHA_1, wiadomoscByteArray, kluczByteArray);

  hmac = bytesToHex(hmac);
  Logger.log(hmac);
}

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

463

2023.08.02

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

319

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

212

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1502

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

624

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

653

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

609

2024.04.29

go语言字符串相关教程
go语言字符串相关教程

本专题整合了go语言字符串相关教程,阅读专题下面的文章了解更多详细内容。

172

2025.07.29

C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

8

2026.01.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 10.2万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 11.2万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号