title: How to properly sign AWS Requests with Signature Version 4 in Java?
body: <p>I am looking to send a job request over to Elemental MediaConvert on AWS and I was reading up on how you need to request an endpoint specifically for your account and region, so I did and now I have that custom endpoint. I went through the process of signing my request to this endpoint that is shown <a href="https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html" rel="nofollow noreferrer">here</a> and <a href="https://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-java" rel="nofollow noreferrer">here</a>, but I just can't seem to get past - <code>403 Forbidden: [{"message":"The request signature we calculated does not match the signature you provided.....</code></p>
  
  <p>Here is what I currently have:</p>
  
  <pre><code>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&lt;APIResponse&gt; 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&lt;APIResponse&gt; task = () -&gt; {
          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&lt;String&gt; restRequest = new HttpEntity&lt;&gt;(jobString, headers);
          SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
          requestFactory.setReadTimeout(20000);
          requestFactory.setConnectTimeout(5000);
          restTemplate.setRequestFactory(requestFactory);
          try {
              ResponseEntity&lt;APIResponse&gt; 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);
  }
  </code></pre>
  
  <p>I have also tried sending a request using Postman and that works just fine, here is what the CURL looks like on Postman:
  <a href="https://i.stack.imgur.com/H8UQQ.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/H8UQQ.png" alt="enter image description here"></a></p>
  
  <p>Been working on this for 18 hours straight and just can't seem to find where I am messing up the signature. Thanks.</p>