Как правильно подписать AWS запросов с подписью версии 4 в Java? - PullRequest
0 голосов
/ 01 февраля 2020

Я хочу отправить запрос на работу в Elemental MediaConvert по номеру AWS, и я читал о том, как вам нужно запрашивать конечную точку специально для вашей учетной записи и региона, поэтому я сделал, и теперь у меня есть эта пользовательская конечная точка. Я прошел процесс подписания моего запроса к этой конечной точке, которая показана здесь и здесь , но я просто не могу пройти мимо - 403 Forbidden: [{"message":"The request signature we calculated does not match the signature you provided.....

Вот то, что у меня сейчас есть:

private String MEDIA_CONVERT_ENDPOINT = "https://abcd1234.mediaconvert.us-east-1.amazonaws.com/2017-08-29/jobs";
private String MEDIA_CONVERT_HOST = "abcd1234.mediaconvert.us-east-1.amazon.com";
private String AWS_ACCESS_KEY = "AKIAXXXXXXEXAMPLE";
private String AWS_SECRET_KEY = "2AXXXXXXXXXXXX/XXXXXXXXXXXXXXX";

static byte[] HmacSHA256(String data, byte[] key) throws NoSuchAlgorithmException, InvalidKeyException {
    String algorithm = "HmacSHA256";
    Mac mac = Mac.getInstance(algorithm);
    mac.init(new SecretKeySpec(key, algorithm));
    return mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
}

static String getSignature(String secretKey, String date, String region, String service, String stringToSign) {
    byte[] kSecret, kDate, kRegion, kService, kSigning, signature;
    try {
        kSecret = ("AWS4" + secretKey).getBytes(StandardCharsets.UTF_8);
        kDate = HmacSHA256(date, kSecret);
        kRegion = HmacSHA256(region, kDate);
        kService = HmacSHA256(service, kRegion);
        kSigning = HmacSHA256("aws4_request", kService);
        signature = HmacSHA256(stringToSign, kSigning);
    } catch (Exception e) {
        return "";
    }
    return DatatypeConverter.printHexBinary(signature).toLowerCase();
}

static String hash(String stringToEncrypt) {
    MessageDigest messageDigest;
    try {
        messageDigest = MessageDigest.getInstance("SHA-256");
    } catch (Exception e) {
        return "";
    }
    messageDigest.update(stringToEncrypt.getBytes(StandardCharsets.UTF_8));
    return DatatypeConverter.printHexBinary(messageDigest.digest()).toLowerCase();
}

private ExecutorService executor = Executors.newFixedThreadPool(5);

public Future<APIResponse> sendJob(String jobString) {
    TimeZone tz = TimeZone.getTimeZone("UTC");

    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
    sdf.setTimeZone(tz);

    DateFormat df = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
    df.setTimeZone(tz);
    String nowAsISO = df.format(new Date());

    StringBuilder canonicalRequestBuilder = new StringBuilder("POST");
    canonicalRequestBuilder.append("\n")
            .append("/2017-08-29/jobs/").append("\n")
            .append("\n")
            .append("content-type:application/json").append("\n")
            .append("host:").append(MEDIA_CONVERT_HOST).append("\n")
            .append("x-amz-content-sha256:").append(hash(jobString)).append("\n")
            .append("x-amz-date:").append(nowAsISO).append("\n")
            .append("\n")
            .append("content-type;host;x-amz-content-sha256;x-amz-date").append("\n")
            .append(hash(jobString));

    String hashedCanonicalRequest = hash(canonicalRequestBuilder.toString());

    StringBuilder stringToSignBuilder = new StringBuilder("AWS4-HMAC-SHA256");
    stringToSignBuilder.append("\n")
            .append(nowAsISO).append("\n")
            .append(sdf.format(date)).append("/us-east-1/mediaconvert/aws4_request").append("\n")
            .append(hashedCanonicalRequest);

    String signature = getSignature(
            "AWS4" + AWS_SECRET_KEY,
            sdf.format(date),
            "us-east-1",
            "mediaconvert",
            stringToSignBuilder.toString()
    );

    Callable<APIResponse> task = () -> {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "application/json");
        headers.set("X-Amz-Content-Sha256", hash(jobString));
        headers.set("X-Amz-Date", nowAsISO);
        headers.set(
                "Authorization",
                "AWS4-HMAC-SHA256 Credential=" +
                        AWS_ACCESS_KEY +
                        "/" + sdf.format(date) + "/us-east-1/mediaconvert/aws4_request, " +
                        "SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date, Signature=" + signature
        );
        HttpEntity<String> restRequest = new HttpEntity<>(jobString, headers);
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setReadTimeout(20000);
        requestFactory.setConnectTimeout(5000);
        restTemplate.setRequestFactory(requestFactory);
        try {
            ResponseEntity<APIResponse> response = restTemplate.exchange(MEDIA_CONVERT_ENDPOINT, HttpMethod.POST, restRequest, APIResponse.class);
            return response.getBody();
        } catch (Exception e) {
            return new APIResponse(APIResponse.STATUS_FAILED, e.getMessage());
        }
    };
    return executor.submit(task);
}

Я также попытался отправить запрос с помощью Postman, и это прекрасно работает, вот как выглядит CURL на Postman: enter image description here

Я работал над этим 18 часов подряд и просто не могу понять, где я испорчил подпись. Спасибо.

...