Syn
Syn

Reputation: 776

Upload file to S3 Bucket using HTTP POST

I'm developing an application that will make use of S3 storage from Amazon. After following the tutorials and examples provided by Amazon I still find myself unable to get the the upload to work. This is the message I keep getting: The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256. The bucket is named testbucket-10.09.2017 and it is located in the Frankfurt (eu-central-1) region. I even found an application claiming to do exactly what I need but the error message is the same. The bulk of the code below has been taken and adapted from the documentation and tutorials provided by AWS. Any help would be greatly appreciated.

Here is the code I am using:

My HTML form:

<html> 
  <head>
    <title>S3 POST Form</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>

  <body> 
    <form action="https://testbucket-10.09.2017.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
      <input type="hidden" name="key" value="uploads/${filename}">
      <input type="hidden" name="AWSAccessKeyId" value="REMOVED FOR SAFETY"> 
      <input type="hidden" name="acl" value="private"> 
      <input type="hidden" name="success_action_redirect" value="http://localhost/">
      <input type="hidden" name="policy" value="ZXlKbGVIQnBjbUYwYVc5dUlqb2dJakl3TVRndE1ERXRNREZVTURBNk1EQTZNREJhSWl3S0lDQWlZMjl1WkdsMGFXOXVjeUk2SUZzZ0NpQWdJQ0I3SW1KMVkydGxkQ0k2SUNKMFpYTjBZblZqYTJWMExURXdMakE1TGpJd01UY2lmU3dnQ2lBZ0lDQmJJbk4wWVhKMGN5MTNhWFJvSWl3Z0lpUnJaWGtpTENBaWRYQnNiMkZrY3k4aVhTd0tJQ0FnSUhzaVlXTnNJam9nSW5CeWFYWmhkR1VpZlN3S0lDQWdJSHNpYzNWalkyVnpjMTloWTNScGIyNWZjbVZrYVhKbFkzUWlPaUFpYUhSMGNEb3ZMMnh2WTJGc2FHOXpkQzhpZlN3S0lDQWdJRnNpYzNSaGNuUnpMWGRwZEdnaUxDQWlKRU52Ym5SbGJuUXRWSGx3WlNJc0lDSWlYU3dLSUNBZ0lGc2lZMjl1ZEdWdWRDMXNaVzVuZEdndGNtRnVaMlVpTENBd0xDQXhNRFE0TlRjMlhRb2dJRjBLZlE9PQ==">
      <input type="hidden" name="signature" value="REMOVED FOR SAFETY">
      <input type="hidden" name="Content-Type" value="image/jpeg">
      <!-- Include any additional input fields here -->

      File to upload to S3: 
      <input name="file" type="file"> 
      <br> 
      <input type="submit" value="Upload File to S3"> 
    </form> 
  </body>
</html>

My Java code generating the policy and signature:

public static void myAttempt() throws Exception {

        String policy_document = constructPolicy();
        String aws_secret_key="REMOVED FOR SAFETY";

        String policy = (new BASE64Encoder()).encode(
                policy_document.getBytes("UTF-8")).replaceAll("\n","").replaceAll("\r","");

        String dateStamp ="20170912";
        String region = "eu-central-1";
        String serviceName ="s3";
        System.out.println("NEW SIGNATURE: "+getSignature(getSignatureKey(aws_secret_key,dateStamp,region,serviceName)));

        System.out.println("ENCODED POLICY: "+policy);        
    }

private static String constructPolicy() throws UnsupportedEncodingException {

        String policy_document="{\"expiration\": \"2018-01-01T00:00:00Z\",\n" +
                "  \"conditions\": [ \n" +
                "    {\"bucket\": \"testbucket-10.09.2017\"}, \n" +
                "    [\"starts-with\", \"$key\", \"uploads/\"],\n" +
                "    {\"acl\": \"private\"},\n" +
                "    {\"success_action_redirect\": \"http://localhost/\"},\n" +
                "    [\"starts-with\", \"$Content-Type\", \"\"],\n" +
                "    [\"content-length-range\", 0, 1048576]\n" +
                "  ]\n" +
                "}";

        String policy = (new BASE64Encoder()).encode(
                policy_document.getBytes("UTF-8")).replaceAll("\n","").replaceAll("\r","");
        return policy;
    }

private 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("UTF8"));
    }

private static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception  {
        byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
        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 String getSignature(byte[] key) throws Exception{

        return base16().lowerCase().encode(HmacSHA256(constructPolicy(), key));
    }

Upvotes: 3

Views: 10885

Answers (1)

Syn
Syn

Reputation: 776

Turns out that for some reason outdated AWS documentation and examples are among the first results when doing a search. A few google results pages later, a more up to date example came up. Basically the form I was using was wrong. The correct one is as follows:

<html>
  <head>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

  </head>
  <body>

  <form action="http://sigv4examplebucket.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
    Key to upload: 
    <input type="input"  name="key" value="user/user1/${filename}" /><br />
    <input type="hidden" name="acl" value="public-read" />
    <input type="hidden" name="success_action_redirect" value="http://sigv4examplebucket.s3.amazonaws.com/successful_upload.html" />
    Content-Type: 
    <input type="input"  name="Content-Type" value="image/jpeg" /><br />
    <input type="hidden" name="x-amz-meta-uuid" value="14365123651274" /> 
    <input type="hidden" name="x-amz-server-side-encryption" value="AES256" /> 
    <input type="text"   name="X-Amz-Credential" value="AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request" />
    <input type="text"   name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" />
    <input type="text"   name="X-Amz-Date" value="20151229T000000Z" />

    Tags for File: 
    <input type="input"  name="x-amz-meta-tag" value="" /><br />
    <input type="hidden" name="Policy" value='<Base64-encoded policy string>' />
    <input type="hidden" name="X-Amz-Signature" value="<signature-value>" />
    File: 
    <input type="file"   name="file" /> <br />
    <!-- The elements after this will be ignored -->
    <input type="submit" name="submit" value="Upload to Amazon S3" />
  </form>

</html>

Here is the link from where I got the form and where additional examples can be found.

Upvotes: 3

Related Questions