SignatureDoesNotMatch error when uploading to s3 via a pre signed url using Ionic 2
Asked Answered
W

3

17

I am trying to upload a video to s3 and have a pre-signed PUT url. The following is the code to do so.

import {Component} from '@angular/core';
import {NavController} from 'ionic-angular';
import {MediaCapture} from 'ionic-native';
import {Http} from '@angular/http';
import { Transfer } from 'ionic-native';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})

export class HomePage {

    public base64Image: string;

    constructor(private navController: NavController, public http: Http) {
        this.base64Image = "https://placehold.it/150x150";
    }

    public takeVideo() {
        MediaCapture.captureVideo({limit:2}).then(function(videoData){
            var link = "https://mysamplebucket.s3.amazonaws.com/non-tp/esx.mov?AWSAccessKeyId=TEMP_KEYY&Expires=1482290587&Signature=JUIHHI%2FcnLkqSVg%3D&x-amz-security-token=FQoDYXDGRfTXk6hma0Rxew6yraAX%2FlYGaQmYLwkvsuuB3%2F%2FtPvGDVs3dIQG0Ty3MeMjn0p%%26djt5xhAMk73pndJbZP0tCYYlvPvlUAyL8x7O%%2B3AwEa%%2B9b43yarIuPLCvujmKLTDyi%%3D%3Di";

            var options: any;

            options = {
             fileKey: 'file',
             fileName: 'esx.mov',
             httpMethod: 'PUT',
             chunkedMode: false,
             mimeType: 'video/quicktime',
             encodeURI: false,
             headers: {
                'Content-Type': 'video/quicktime'
              }
            };

            var ft = new Transfer();
            ft.upload(videoData[0].fullPath, link, options, false)
                .then((result: any) => {
                    this.success(result);
                }).catch((error: any) => {
                    this.failed(error);
                }); 

        }, function(err){
            alert(err);
        });
    }


}

Here is the code that generates the pre-signed PUT url.

var params = {Bucket: s3_bucket, Key: filename, Expires: 900000};
var url = { 
    'url' : s3.getSignedUrl('putObject', params)
};

I get, SignatureDoesNotMatch error. The message says, The request signature we calculated does not match the signature you provided. Check your key and signing method. I am not sure what I am doing wrong here - I looked a few other SO and Ionic questions and tried what they recommended to no avail. Any ideas on what I and doing wrong?

Wink answered 10/12, 2016 at 17:46 Comment(3)
You say you "have" a pre-signed PUT URL, but you don't seem to show the code you are using to generate it. That's almost certainly where the problem is. Show this code, and specifically the parameters it is using to generate the canonical request? (Not the secret key, of course). Note that the content-type is one of the parameters that must be included.Hormone
@Michael-sqlbot I have server side code that generates it. There is an api end point that I call to get it. Right now since I am testing this code, I call the api endpoint using Postman and just put it in the link variable.Wink
Okay, but that seems to be the source of the problem -- assuming that the signing code works correctly, then the problem seems to be with the parameters you are passing to it, in order to get the link. We need more information in order to diagnose this.Hormone
H
28

Your upload PUT request will have a Content-Type: video/quicktime header.

When the Content-Type header is present in the request (not the response), its value is a non-optional component in the Signature V2 canonical request... which means you have to pass it to the code generating the signature.

var params = {Bucket: s3_bucket, Key: filename, Expires: 900000}; also needs this string (video/quicktime, in this case) passed to it as ContentType: ... for a PUT request (but not for a GET request, since this describes the content you are sending, and GET requests customarily send no actual content.

The SDK documentation doesn't seem to specifically mention this, but it is most definitely required by S3.

Hormone answered 11/12, 2016 at 20:25 Comment(4)
What you're saying makes sense but here's what's confusing me. I have a version of the app written in Swift, where I make a PUT request with Almaofire without specifying the Content-Type, to the url that I get from the above code and it works just fine. Also, since I don't specify Content-Type when generating the url, why does removing Content-Type from the PUT request not work?Wink
Adding, "ContentType: video/quicktime" to the code that generated the PUT url solved the problem!Wink
"why does removing Content-Type from the PUT request not work?" Because not specifying it may not exclude it -- or some other value -- from being added by the user agent. mimeType: 'video/quicktime' probably has the equivalent effect of setting Content-Type, since the two are loosely synonymous, or the user agent may default to a generic type, like application/octet-stream or application/binary. You'd have to observe the actual HTTP transaction to know for sure.Hormone
Thanks a lot!. I add ContentType and everything works fine.Averett
M
4

In case someone else is looking at this and is in a similar situation as me, I got a similar SignatureDoesNotMatchError when my s3 bucket's CORS Configuration did not contain <AllowedHeader>*</AllowedHeader>

I ran into this when moving from one bucket to another, copying all the settings except for the CORS Configuration.

Medusa answered 21/6, 2018 at 19:3 Comment(1)
I was using the aws go sdk to generate the presigned req url and as such needed to specifically add <AllowedHeader>Content-Type</AllowedHeader> for the bucket CORS settingRestaurateur
R
1

We faced this issue when we were downloading an already uploaded file. We were receiving the presigned url, but when tried to download the file with that presign url, it said "signature does not match". The solution we received when we reported a ticket with AWS because all the approaches failed. The scenario is we have our custom AWS KMS encryption enabled for S3 bucket, but we were trying to send "kms key" along with our request when using GeneratePresignedUrlRequest api. AWS said, we don't have to send KMS key, instead send without encrypting from client. When I say unencrypted, it is not exactly that, it is already coming in encrypted form and when we were using "AWSS3V4SignerType" to sign, we were sending kms id as an additional param that wasn't required to begin with. Hope this makes sense. The params AWS looks for in the header are:

  1. Algorithm
  2. Credential Scope
  3. Signed headers
  4. Date
  5. Expiration Date
  6. Signature
  7. KMS Key - we were passing this, which wasn't required.
Reformatory answered 15/12, 2021 at 17:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.