Posting images to AWS S3 with QNetworkAccessManager PUT (Android)
I am trying to get my android app (qt5 c++) to upload taken pictures to an amazon bucket using PUT method but keep getting errors about calculated signature not matching the one provided.
here's my code:
Code:
{
QByteArray RFCDate
= DayOfWeek
+ ", " + QDateTime::currentDateTime().
toString(Qt
::RFC2822Date).
toUtf8();
QByteArray StringToSign
= "PUT\\n\\n" + ContentType
+ "\\n" + RFCDate
+ "\\n" + "/" + S3Bucket
+ "/" + fileInfo.
fileName().
toUtf8();
QByteArray SignString
= QMessageAuthenticationCode
::hash(StringToSign, SecretKey, QCryptographicHash
::Sha1).
toBase64();
QNetworkAccessManager * networkAccessManager = new QNetworkAccessManager(this);
QNetworkRequest request
(QUrl("https://" + S3Bucket
+ ".s3." + S3region
+ ".amazonaws.com/" + fileInfo.
fileName()));
request.setHeader(QNetworkRequest::ContentTypeHeader, ContentType);
request.setHeader(QNetworkRequest::ContentLengthHeader, fileInfo.size());
request.setRawHeader("Host", S3Bucket + ".s3." + S3region + ".amazonaws.com");
request.setRawHeader("Date", RFCDate);
request.setRawHeader("Authorization", "AWS " + AccessKey + ":" + SignString);
m_reply = networkAccessManager->put(request, fileData);
connect(m_reply, SIGNAL(finished()), this, SLOT(mySlot()));
}
here's the error msg I am getting when reading m_reply->readAll():
<Code>SignatureDoesNotMatch</?Code>
<Message>The request signature we calculated does not match the signature you provided.? Check your key and signing method.?</?Message>
any help is much appreciated!
Re: Posting images to AWS S3 with QNetworkAccessManager PUT (Android)
Do you have access to the signature stored with AWS? Is it in fact different from the one you are sending? Have you carefully checked your access code and secret keys to make sure there aren't any typos?
Are there byte order (endian) differences between android architecture and the order expected by AWS (presumably internet byte order, big endian)? Take a look at QtEndian if so.
Re: Posting images to AWS S3 with QNetworkAccessManager PUT (Android)
the error message provides <StringToSign> and <SignatureProvided> fields, I believe <StringToSign> is auto calculated based on the header parameters I send and it matches the one I build (StringToSign in the code), however when I use online hmac calculators (like https://dinochiesa.github.io/hmachash/index.html for example) and enter that string, my secret key and select sha1 function the base64 output doesn't match my signed string for some reason. does it mean that QMessageAuthenticationCode::hash(StringToSign, SecretKey, QCryptographicHash::Sha1).toBase64() doesn't work as expected?
Re: Posting images to AWS S3 with QNetworkAccessManager PUT (Android)
Network byte order is big-endian. Android is little-endian, so it is possible that your hash() call is generating a little-endian QByteArray. So try this:
Code:
#include <QtEndian>
QByteArray AndroidTemp
= QMessageAuthenticationCode
::hash(StringToSign, SecretKey, QCryptographicHash
::Sha1);
QByteArray AndroidString
= AndroidTemp.
toBase64();
// for comparison purposes
QByteArray SignTemp
( AndroidString
);
// copy so the two arrays are the same size qToBigEndian( &AndroidTemp, AndroidTemp.size(), &SignTemp );
Are SignString and AndroidString the same? If so, then hash() is generating a big-endian array. If not, then it is probably little-endian.
Try sending this version of SignString to AWS
Re: Posting images to AWS S3 with QNetworkAccessManager PUT (Android)
Quote:
Originally Posted by
d_stranz
Network byte order is big-endian. Android is little-endian, so it is possible that your hash() call is generating a little-endian QByteArray. So try this:
Code:
#include <QtEndian>
QByteArray AndroidTemp
= QMessageAuthenticationCode
::hash(StringToSign, SecretKey, QCryptographicHash
::Sha1);
QByteArray AndroidString
= AndroidTemp.
toBase64();
// for comparison purposes
QByteArray SignTemp
( AndroidString
);
// copy so the two arrays are the same size qToBigEndian( &AndroidTemp, AndroidTemp.size(), &SignTemp );
Are SignString and AndroidString the same? If so, then hash() is generating a big-endian array. If not, then it is probably little-endian.
Try sending this version of SignString to AWS
thank you for your reply - turned out my issue with signature was caused by the double slashes in StringToSign so replacing \\n with \n solved it - I am not getting any more errors but m_reply->readAll() now returns an empty string, no errors are reported by errorOccurred signal and nothing gets posted to s3. so not sure what is wrong now
Re: Posting images to AWS S3 with QNetworkAccessManager PUT (Android)
Quote:
I am not getting any more errors but m_reply->readAll() now returns an empty string
From what I read in the docs about QNetworkReply, the finished() signal is emitted after the put() request is completely done. So by the time you see that, all of the reply has been sent and there is nothing more to read.
I think what you need to do is the handle the QIODevice::readyRead() signal and in your slot retrieve all of the pending data and put it onto the end of a buffer. Keep doing that until you receive the finished() signal, at which point you know you have received everything the server is going to send.