Amazon S3 Signature Does Not Match - AWS SDK Java

Viewed 51998

I have a play application that needs to upload files to S3. We are developing in scala and using the Java AWS SDK.

I'm having trouble trying to upload files, I keep getting 403 SignatureDoesNotMatch when using presigned urls. The url is being genereated using AWS Java SDK by the following code:

def generatePresignedPutRequest(filename: String) = {
    val expiration = new java.util.Date();
    var msec = expiration.getTime() + 1000 * 60 * 60; // Add 1 hour.
    expiration.setTime(msec);

    s3 match {
      case Some(s3) => s3.generatePresignedUrl(bucketname, filename, expiration, HttpMethod.PUT).toString
      case None => {
        Logger.warn("S3 is not availiable. Cannot generate PUT request.")
        "URL not availiable"
      }
    }
  }

For the frontend code we followed ioncannon article.

The js function that uploads the file (the same as the one used in the article)

 function uploadToS3(file, url)
     {
       var xhr = createCORSRequest('PUT', url);
       if (!xhr) 
       {
         setProgress(0, 'CORS not supported');
       }
       else
       {
         xhr.onload = function() 
         {
           if(xhr.status == 200)
           {
             setProgress(100, 'Upload completed.');
           }
           else
           {
             setProgress(0, 'Upload error: ' + xhr.status);
           }
         };

         xhr.onerror = function() 
         {
           setProgress(0, 'XHR error.');
         };

         xhr.upload.onprogress = function(e) 
         {
           if (e.lengthComputable) 
           {
             var percentLoaded = Math.round((e.loaded / e.total) * 100);
             setProgress(percentLoaded, percentLoaded == 100 ? 'Finalizing.' : 'Uploading.');
           }
         };

         xhr.setRequestHeader('Content-Type', 'image/png');
         xhr.setRequestHeader('x-amz-acl', 'authenticated-read');

         xhr.send(file);
       }
     }

The server's response is

<?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>
<StringToSignBytes>50 55 bla bla bla...</StringToSignBytes>
<RequestId>F7A8F1659DE5909C</RequestId>
<HostId>q+r+2T5K6mWHLKTZw0R9/jm22LyIfZFBTY8GEDznfmJwRxvaVJwPiu/hzUfuJWbW</HostId>
<StringToSign>PUT

    image/png
    1387565829
    x-amz-acl:authenticated-read
    /mybucketname/icons/f5430c16-32da-4315-837f-39a6cf9f47a1</StringToSign>
<AWSAccessKeyId>myaccesskey</AWSAccessKeyId></Error>

I have configured CORS, double checked aws credentials and tried changing request headers. I always get the same result. Why is Amazon telling me that signatures dont match?

8 Answers

Same problem for me but a different cause. I was using POST instead of PUT

I had the same issue, but removing content-type works fine. Hereby sharing the complete code.

public class GeneratePresignedUrlAndUploadObject {
    private static final String BUCKET_NAME = "<YOUR_AWS_BUCKET_NAME>"; 
    private static final String OBJECT_KEY  = "<YOUR_AWS_KEY>";
    private static final String AWS_ACCESS_KEY = "<YOUR_AWS_ACCESS_KEY>";
    private static final String AWS_SECRET_KEY = "<YOUR_AWS_SECRET_KEY>";

    public static void main(String[] args) throws IOException {
        BasicAWSCredentials awsCreds = new BasicAWSCredentials(AWS_ACCESS_KEY, AWS_SECRET_KEY);

        AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withRegion(Regions.US_EAST_1)
                .withCredentials(new AWSStaticCredentialsProvider(awsCreds)).build();

        try {
            System.out.println("Generating pre-signed URL.");
            java.util.Date expiration = new java.util.Date();
            long milliSeconds = expiration.getTime();
            milliSeconds += 1000 * 60 * 60;
            expiration.setTime(milliSeconds);

            GeneratePresignedUrlRequest generatePresignedUrlRequest = 
                    new GeneratePresignedUrlRequest(BUCKET_NAME, OBJECT_KEY);
            generatePresignedUrlRequest.setMethod(HttpMethod.PUT); 
            generatePresignedUrlRequest.setExpiration(expiration);
            URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest); 

            UploadObject(url);

            System.out.println("Pre-Signed URL = " + url.toString());
        } catch (AmazonServiceException exception) {
            System.out.println("Caught an AmazonServiceException, " +
                    "which means your request made it " +
                    "to Amazon S3, but was rejected with an error response " +
            "for some reason.");
            System.out.println("Error Message: " + exception.getMessage());
            System.out.println("HTTP  Code: "    + exception.getStatusCode());
            System.out.println("AWS Error Code:" + exception.getErrorCode());
            System.out.println("Error Type:    " + exception.getErrorType());
            System.out.println("Request ID:    " + exception.getRequestId());
        } catch (AmazonClientException ace) {
            System.out.println("Caught an AmazonClientException, " +
                    "which means the client encountered " +
                    "an internal error while trying to communicate" +
                    " with S3, " +
            "such as not being able to access the network.");
            System.out.println("Error Message: " + ace.getMessage());
        }
    }

    public static void UploadObject(URL url) throws IOException
    {
        HttpURLConnection connection=(HttpURLConnection) url.openConnection();
        connection.setDoOutput(true);
        connection.setRequestMethod("PUT");
        OutputStreamWriter out = new OutputStreamWriter(
                connection.getOutputStream());
        out.write("This text uploaded as object.");
        out.close();
        int responseCode = connection.getResponseCode();
        System.out.println("Service returned response code " + responseCode);

    }
}

I faced with SignatureDoesNotMatch error using the Java AWS SDK. In my case, SignatureDoesNotMatch error occurred after upgraded maven dependencies without changes in my code (so credentials are correct and were not changed). After upgrading dependency org.apache.httpcomponents:httpclient from version 4.5.6 to 4.5.7 (actually it was upgrade of Spring Boot from 2.1.2 to 2.1.3, and there bom has specified httpclient version), code became throw exceptions while doing some AWS SDK S3 requests like AmazonS3.getObject.

After digging into the root cause, I found that httpclient library did breaking changes with normalized URI, that affected Java AWS SDK S3. Please take a look for opened GitHub ticket org.apache.httpcomponents:httpclient:4.5.7 breaks fetching S3 objects for more details.

If your access keys and secret keys are good but it is saying "SignatureDoesNotMAtch", check your secret key, it probably has any of some special charaters, e.g +/ - / *

Go to aws and generate another access key, where the the secret key does not have those. Then try again :)

Related