使用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规范:
getSignatureKey的dateStamp参数格式错误:AWS要求该参数为仅日期格式(yyyyMMdd),但当前代码传入的是带完整时间戳的格式(yyyyMMdd'T'HHmmss'Z'),导致签名根密钥与AWS验证逻辑不匹配。- Credential路径中的日期格式错误:签名头的Credential字段里的日期部分必须是
yyyyMMdd格式,而非代码中使用的过期时间戳(带完整时间),违反了签名规范。 - 错误使用过期时间作为签名时间: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
相关产品推荐
相关产品推荐

