如何在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
相关产品推荐
相关产品推荐

