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

如何在Google Apps Script中生成AWS签名版本4的签名密钥

AWS Lambda调用403错误排查:凭证日期不匹配问题

我编写了以下Google Apps Script代码用于调用AWS Lambda,目的是生成AWS签名版本4(Signature Version 4)的签名密钥,但执行时返回403错误,错误信息为:

凭证范围中的日期与HTTP请求中ISO-8601格式日期的YYYYMMDD部分不匹配

原代码:

function invokeLambda() {
  // Credenciales de AWS
  var accessKey = 'KEY_TEST';
  var secretKey = 'KEY_TEST';
  var region = 'us-east-1';
  var service = 'lambda';

  // Fecha y hora actual en formato ISO 8601
  var now = new Date();
  var amzDate = now.toISOString().split('.')[0] + 'Z';

  // Payload de la solicitud
  var payload = {
    foo: 'bar'
  };

  // Convertir el payload a una cadena JSON
  var payloadString = JSON.stringify(payload);

  // Calcular el hash SHA-256 del payload
  var sha256 = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, payloadString);
  var payloadHash = Utilities.base64Encode(sha256);

  // Crear la cadena de autenticación
  var canonicalRequest = 'POST\n/2015-03-31/functions/your-function-name/invocations\n\ncontent-type:application/json\nhost:lambda.us-east-1.amazonaws.com\nx-amz-date:' + amzDate + '\n\ncontent-type;host;x-amz-date\n' + payloadHash;
  var canonicalRequestHash = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, canonicalRequest);
  var canonicalRequestHashString = Utilities.base64Encode(canonicalRequestHash);
  var stringToSign = 'AWS4-HMAC-SHA256\n' + amzDate + '\n' + now.toISOString().substr(0, 8) + '/' + region + '/' + service + '/aws4_request\n' + canonicalRequestHashString;

  // Convertir la clave secreta y los datos en arreglos de bytes
  var secretBytes = Utilities.newBlob(secretKey).getBytes();
  var dateBytes = Utilities.newBlob(now.toISOString().substr(0, 8)).getBytes();

  // Firmar la región utilizando la clave secreta y la fecha
  var dateRegionKey = Utilities.computeHmacSha256Signature(dateBytes, secretBytes);

  var serviceBytes = Utilities.newBlob(service).getBytes();
  var dateRegionServiceKey = Utilities.computeHmacSha256Signature(serviceBytes, dateRegionKey);

  // Convertir la cadena en un arreglo de bytes
  var stringToSignBytes = stringToBytes(stringToSign);

  // Firmar la cadena
  var signingKey = Utilities.computeHmacSha256Signature(stringToSignBytes, dateRegionServiceKey);

  // Convertir la cadena en un arreglo de bytes
  var stringToSignBytes = Utilities.newBlob(stringToSign).getBytes();
  var signingKeyBytes = Utilities.newBlob(signingKey).getBytes();

  // Crear una instancia de la clase Utilities.DigestHmacSha256
  var hmacSha256 = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, signingKeyBytes);

  // Firmar la cadena
  var signatureBytes = Utilities.computeHmacSha256Signature(stringToSignBytes, signingKeyBytes);

  // Convertir la firma en una cadena hexadecimal
  var signature = Utilities.base64Encode(signatureBytes);

  // Encabezados de la solicitud
  var headers = {
    'Content-Type': 'application/json',
    'X-Amz-Date': amzDate,
    'X-Amz-Security-Token': '',
    'Authorization': 'AWS4-HMAC-SHA256 Credential=' + accessKey + '/' + now.toISOString().substr(0, 8) + '/' + region + '/' + service + '/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=' + signature
  };

  // Opciones de la solicitud
  var options = {
    'method': 'post',
    'headers': headers,
    'payload': payloadString
  };

  // URL de la función Lambda
  var url = 'https://lambda-url.us-east-1.on.aws/';

  // Realizar la solicitud HTTP
  var response = UrlFetchApp.fetch(url, options);
  var responseText = response.getContentText();
  Logger.log(responseText);
}

function stringToBytes(s) {
  var bytes = [];
  for (var i = 0; i < s.length; i++) {
    bytes.push(s.charCodeAt(i));
  }
  return bytes;
}

核心问题与修复步骤

1. 日期格式错误(403直接诱因)

原代码用now.toISOString().substr(0, 8)提取日期,而toISOString()返回格式为YYYY-MM-DDTHH:MM:SS.sssZ,substr(0,8)得到的是YYYY-MM-,完全不符合AWS要求的YYYYMMDD格式。必须统一提取正确的日期戳:

var dateStamp = now.toISOString().slice(0, 10).replace(/-/g, ''); // 提取YYYY-MM-DD后替换横杠,得到YYYYMMDD

2. 哈希与签名编码错误

AWS要求SHA-256哈希和最终签名都使用十六进制小写字符串,而非Base64编码。需添加字节数组转十六进制的工具函数:

function bytesToHex(bytes) {
  return bytes.map(function(byte) {
    var hex = (byte < 0 ? byte + 256 : byte).toString(16);
    return hex.length === 1 ? '0' + hex : hex;
  }).join('').toLowerCase();
}

3. 签名密钥生成逻辑错误

  • 初始签名密钥必须以AWS4为前缀,和秘钥拼接后再转字节
  • computeHmacSha256Signature参数顺序为「待签名内容字节」在前,「密钥字节」在后,且每次HMAC结果需作为下一次的密钥

4. Host头不匹配

原代码中canonical request的host是lambda.us-east-1.amazonaws.com,但实际请求URL的主机是lambda-url.us-east-1.on.aws,二者必须保持一致。

5. 冗余代码清理

删除重复定义的stringToSignBytes变量,以及无意义的hmacSha256变量定义。

修复后的完整代码

function invokeLambda() {
  // AWS凭证
  var accessKey = 'KEY_TEST';
  var secretKey = 'KEY_TEST';
  var region = 'us-east-1';
  var service = 'lambda';

  // 统一处理日期
  var now = new Date();
  var amzDate = now.toISOString().split('.')[0] + 'Z'; // ISO8601格式:YYYYMMDD'T'HHMMSS'Z'
  var dateStamp = now.toISOString().slice(0, 10).replace(/-/g, ''); // YYYYMMDD格式日期戳

  // 请求Payload
  var payload = { foo: 'bar' };
  var payloadString = JSON.stringify(payload);

  // 计算Payload的SHA-256哈希(十六进制)
  var payloadSha256 = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, payloadString);
  var payloadHash = bytesToHex(payloadSha256);

  // 构造Canonical Request
  var host = 'lambda-url.us-east-1.on.aws';
  var canonicalHeaders = 'content-type:application/json\n' +
                         'host:' + host + '\n' +
                         'x-amz-date:' + amzDate + '\n';
  var signedHeaders = 'content-type;host;x-amz-date';
  var canonicalRequest = 'POST\n' +
                         '/\n' + // Lambda URL的路径是根路径
                         '\n' + // 查询字符串为空
                         canonicalHeaders + '\n' +
                         signedHeaders + '\n' +
                         payloadHash;

  // 计算Canonical Request的SHA-256哈希
  var canonicalRequestSha256 = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, canonicalRequest);
  var canonicalRequestHash = bytesToHex(canonicalRequestSha256);

  // 构造String to Sign
  var credentialScope = dateStamp + '/' + region + '/' + service + '/aws4_request';
  var stringToSign = 'AWS4-HMAC-SHA256\n' +
                     amzDate + '\n' +
                     credentialScope + '\n' +
                     canonicalRequestHash;

  // 生成签名密钥
  var kSecret = Utilities.newBlob('AWS4' + secretKey).getBytes();
  var kDate = Utilities.computeHmacSha256Signature(dateStamp, kSecret);
  var kRegion = Utilities.computeHmacSha256Signature(region, kDate);
  var kService = Utilities.computeHmacSha256Signature(service, kRegion);
  var kSigning = Utilities.computeHmacSha256Signature('aws4_request', kService);

  // 生成最终签名(十六进制)
  var signatureBytes = Utilities.computeHmacSha256Signature(stringToSign, kSigning);
  var signature = bytesToHex(signatureBytes);

  // 构造请求头
  var headers = {
    'Content-Type': 'application/json',
    'X-Amz-Date': amzDate,
    'Authorization': 'AWS4-HMAC-SHA256 Credential=' + accessKey + '/' + credentialScope + ', ' +
                     'SignedHeaders=' + signedHeaders + ', ' +
                     'Signature=' + signature
  };

  // 请求选项
  var options = {
    method: 'post',
    headers: headers,
    payload: payloadString
  };

  // Lambda URL
  var url = 'https://lambda-url.us-east-1.on.aws/';

  // 发送请求
  try {
    var response = UrlFetchApp.fetch(url, options);
    Logger.log(response.getContentText());
  } catch (e) {
    Logger.log('请求错误: ' + e.toString());
  }
}

// 工具函数:字节数组转十六进制字符串
function bytesToHex(bytes) {
  return bytes.map(function(byte) {
    var hex = (byte < 0 ? byte + 256 : byte).toString(16);
    return hex.length === 1 ? '0' + hex : hex;
  }).join('').toLowerCase();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 14:45:54