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

使用WSO2类中介调用AWS IAM API时遇Signature not yet current错误

AWS IAM API调用报错"Signature not yet current"排查与修复

问题背景

尝试通过WSO2微集成器调用AWS IAM API列出所有用户时,收到AWS返回的XML响应,提示**"Signature not yet current"**错误。已将部署的Docker Pod时区设置为UTC,且采用AWS官方推荐的Java实现自定义类中介生成签名密钥,以下是生成签名头(SIGNATURE_HEADER)的IamConfigMediator代码:

package test;

import java.util.Calendar;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.synapse.MessageContext;
import org.apache.synapse.mediators.AbstractMediator;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class IamConfigMediator extends AbstractMediator {

    private static final String ACCESS_KEY = "XXXX";
    private static final String SECRET_KEY = "XXXXXX";
    private static final String region = "us-east-1";
    private static final String serviceName = "iam";
    private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
    
    
    public boolean mediate(MessageContext context) {
        log.info("[IamConfigMediator] Generating SIGNATURE_HEADER and EXPIRE_TIME ##############################");
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.HOUR_OF_DAY, 24);
        long time = cal.getTimeInMillis();
        String expireTime = formatTimestamp(time);
    
        context.setProperty("EXPIRE_TIME", expireTime);
    
        try {
            byte[] signature = getSignatureKey(SECRET_KEY, expireTime, region, serviceName);
            String header = "AWS4-HMAC-SHA256 Credential=" + ACCESS_KEY + "/" + expireTime + "/" + region + "/" + serviceName + "/aws4_request, SignedHeaders=host;x-amz-date, Signature=" + bytesToHex(signature);
            context.setProperty("SIGNATURE_HEADER", header);
        } catch (Exception e) {
            e.printStackTrace();
        }
    
        return true;
    }
    
    static byte[] HmacSHA256(String data, byte[] key) throws Exception {
        String algorithm = "HmacSHA256";
        Mac mac = Mac.getInstance(algorithm);
        mac.init(new SecretKeySpec(key, algorithm));
        return mac.doFinal(data.getBytes("UTF-8"));
    }
    
    static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception {
        byte[] kSecret = ("AWS4" + key).getBytes("UTF-8");
        byte[] kDate = HmacSHA256(dateStamp, kSecret);
        byte[] kRegion = HmacSHA256(regionName, kDate);
        byte[] kService = HmacSHA256(serviceName, kRegion);
        byte[] kSigning = HmacSHA256("aws4_request", kService);
        return kSigning;
    }
    
    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
    public static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = HEX_ARRAY[v >>> 4];
            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
        }
        return new String(hexChars);
    }
    
    private static final DateTimeFormatter dateFormatter = DateTimeFormat
        .forPattern("yyyyMMdd").withZoneUTC();
    
    private static final DateTimeFormatter timeFormatter = DateTimeFormat
        .forPattern("yyyyMMdd'T'HHmmss'Z'").withZoneUTC();
    
    public static String formatTimestamp(long timeMilli) {
        return timeFormatter.print(timeMilli);
    }

}

问题根源分析

从代码和错误信息来看,核心问题出在签名生成的时间参数不符合AWS Signature Version 4规范:

  1. getSignatureKey的dateStamp参数格式错误:AWS要求该参数为仅日期格式(yyyyMMdd),但当前代码传入的是带完整时间戳的格式(yyyyMMdd'T'HHmmss'Z'),导致签名根密钥与AWS验证逻辑不匹配。
  2. Credential路径中的日期格式错误:签名头的Credential字段里的日期部分必须是yyyyMMdd格式,而非代码中使用的过期时间戳(带完整时间),违反了签名规范。
  3. 错误使用过期时间作为签名时间:AWS签名v4需要使用请求发起的当前UTC时间生成签名,而非未来的过期时间。当前代码将时间设置为当前+24小时,导致签名时间远晚于AWS服务器时间,触发"Signature not yet current"错误。

修复后的代码

package test;

import java.util.Calendar;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.synapse.MessageContext;
import org.apache.synapse.mediators.AbstractMediator;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class IamConfigMediator extends AbstractMediator {

    private static final String ACCESS_KEY = "XXXX";
    private static final String SECRET_KEY = "XXXXXX";
    private static final String region = "us-east-1";
    private static final String serviceName = "iam";
    
    // 仅日期格式,用于签名密钥生成和Credential路径
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormat.forPattern("yyyyMMdd").withZoneUTC();
    // 完整时间戳格式,用于x-amz-date请求头和签名验证
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormat.forPattern("yyyyMMdd'T'HHmmss'Z'").withZoneUTC();

    public boolean mediate(MessageContext context) {
        log.info("[IamConfigMediator] Generating SIGNATURE_HEADER and X-AMZ-DATE ##############################");
        
        // 获取当前UTC时间,避免使用未来时间
        long currentTimeMillis = Calendar.getInstance().getTimeInMillis();
        String currentDate = DATE_FORMATTER.print(currentTimeMillis);
        String currentTimeStamp = TIME_FORMATTER.print(currentTimeMillis);

        // 传递当前时间戳到上下文,用于设置x-amz-date请求头
        context.setProperty("X_AMZ_DATE", currentTimeStamp);

        try {
            // 传入仅日期格式的currentDate生成签名密钥
            byte[] signingKey = getSignatureKey(SECRET_KEY, currentDate, region, serviceName);
            
            // 构造符合规范的签名头,Credential路径使用仅日期格式
            String signatureHeader = "AWS4-HMAC-SHA256 Credential=" + ACCESS_KEY + "/" + currentDate + "/" + region + "/" + serviceName + "/aws4_request, SignedHeaders=host;x-amz-date, Signature=" + bytesToHex(signingKey);
            context.setProperty("SIGNATURE_HEADER", signatureHeader);
        } catch (Exception e) {
            log.error("[IamConfigMediator] Failed to generate signature header", e);
        }

        return true;
    }

    static byte[] HmacSHA256(String data, byte[] key) throws Exception {
        String algorithm = "HmacSHA256";
        Mac mac = Mac.getInstance(algorithm);
        mac.init(new SecretKeySpec(key, algorithm));
        return mac.doFinal(data.getBytes("UTF-8"));
    }

    static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception {
        byte[] kSecret = ("AWS4" + key).getBytes("UTF-8");
        byte[] kDate = HmacSHA256(dateStamp, kSecret);
        byte[] kRegion = HmacSHA256(regionName, kDate);
        byte[] kService = HmacSHA256(serviceName, kRegion);
        byte[] kSigning = HmacSHA256("aws4_request", kService);
        return kSigning;
    }

    private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
    public static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = HEX_ARRAY[v >>> 4];
            hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
        }
        return new String(hexChars);
    }
}

额外注意事项

  • 必须同步x-amz-date请求头:生成签名用的currentTimeStamp必须和请求头中的x-amz-date值完全一致,否则AWS会验证失败。
  • 严格同步系统时间:即使Pod时区设置为UTC,若系统时间与UTC偏差超过5分钟,仍会触发签名时间相关错误,需确保时间同步。
  • 替换Calendar.getInstance():建议使用System.currentTimeMillis()直接获取当前时间,避免依赖系统时区可能带来的偏差。
  • 添加日志排查:输出currentDate、currentTimeStamp和生成的签名头,便于对比验证格式是否符合AWS要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 11:25:16