首页 > 解决方案 > 无法签署 aws s3 发布请求

问题描述

我想创建一个带有发布策略的签名请求,用于在 s3 上上传对象。此处描述的签名计算https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html#sigv4-post-signature-calc

这是我的政策

{
  "expiration": "2020-08-02T21:24:00.000Z",
  "conditions": [
    {"x-amz-algorithm": "AWS4-HMAC-SHA256"},
    {"x-amz-credential": "my_aws_access_key_id/20200702/us_east_1/s3/aws4_request"},
    {"x-amz-date": "20200702T000000Z"},{"bucket": "my_bucket_name"},
    ["starts-with", "$key", "prefix/"]
  ]
}

这是我的代码,其中计算请求的帖子参数:

public void createParams() throws Exception {
    String secretKey = getAwsSecretKey();
    String region = getRegionName();

    String policy = "{" +
                "\"expiration\": \"2020-08-02T21:24:00.000Z\"," +
                "\"conditions\": [" +
                "{\"x-amz-algorithm\": \"AWS4-HMAC-SHA256\"}," +
                "{\"x-amz-credential\": \"my_aws_access_key_id/20200702/us_east_1/s3/aws4_request\"}," +
                "{\"x-amz-date\": \"20200702T000000Z\"},{\"bucket\": \"my_bucket_name\"}," +
                "[\"starts-with\", \"$key\", \"prefix/\"]" +
                "]" +
                "}";

    byte[] b64PolicyBytes = toBase64(policy.getBytes(StandardCharsets.UTF_8));
    String b64policy = new String(b64PolicyBytes);

    Map<String, String> params = new HashMap<>();
    params.put("policy", b64policy);
    params.put("x-amz-algorithm", "AWS4-HMAC-SHA256");
    params.put("x-amz-credential", "aws_access_key_id/20200702/us_east_1/s3/aws4_request");
    params.put("x-amz-date", "20200702T000000Z");
    
    byte[] key = createSignedKey(secretKey, "20200702", region);
    byte[] sign = hmac(b64policy.getBytes(StandardCharsets.UTF_8), key);

    params.put("x-amz-signature", toHex(sign));

    params.forEach((k, value) -> System.out.printf("%s: %s%n", k, value));
}

private byte[] createSignedKey(String secretKey, String date, String region) throws Exception {
    byte[] s1 = hmac("AWS4".concat(secretKey).getBytes(StandardCharsets.UTF_8), date.getBytes());
    byte[] s2 = hmac(s1, region.getBytes(StandardCharsets.UTF_8));
    byte[] s3 = hmac(s2, "s3".getBytes(StandardCharsets.UTF_8));
    return hmac(s3, "aws4_request".getBytes(StandardCharsets.UTF_8));
}

byte[] toBase64(byte[] policy) {
    return Base64.getEncoder().encode(policy);
}

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

static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();

static String toHex(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);
}

如果您调用 createParams() 方法后参数将被打印。

接下来我去邮递员并像这样构造表单数据请求:

POST https://my_bucket_name.s3.amazonaws.com
policy:policy...
x-amz-credential:aws_access_key_id/20200702/us-east-1/s3/aws4_request
x-amz-algorithm:AWS4-HMAC-SHA256
x-amz-date:20200701T000000Z
x-amz-signature:D90FEF721FD70812502A6D61CEC9CAD700EBEC810A2BB7C6439E3A088B27FFC7
key:ttt/object_name

和 aws 以错误响应:

<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>SignatureDoesNotMatch</Code>
    <Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
    <AWSAccessKeyId>aws_access_key_id</AWSAccessKeyId>
    <StringToSign>my policy base64 string</StringToSign>
    <SignatureProvided>...</SignatureProvided>
    <StringToSignBytes>...</StringToSignBytes>
    <RequestId>F292097E276C15C3</RequestId>
    <HostId>...</HostId>
</Error>

我做错了什么?

标签: javaamazon-web-servicesamazon-s3sign

解决方案


推荐阅读