Converting cURL command and openSSL signing to Qt
-
I currently use an HTTP PUT request using cURL and openSSL to upload files on S3 Bucket.
Being new to Qt and not being familiar with Windows environment too much, I am pretty much stuck trying to figure out the best way to go about this.
I have contemplated on installing cURL, openSSL, date, base64 etc. on Windows environment and using Qt to invoke command line, but that involves a lot of dependencies with my application. It would be really great to use Qt framework to be able to request this successfully.Here is the shell script I had been using on the Linux Environment:
#!/bin/bash fileName=$1 AccessKey="EXAMPLEACCESSKEY" SecretKey="mysecretkey" RFCDate=`date -R` S3Bucket="my-bucket" S3Path="/${S3Bucket}/${fileName}" ContentType="application/octet-stream" StringToSign="PUT\\n\\n${ContentType}\\n${RFCDate}\\n${S3Path}" SignString=`echo -en ${StringToSign} | openssl sha1 -hmac ${SecretKey} -binary | base64` curl -X PUT -T "${fileName}" \ -H "Host: ${S3Bucket}.s3.amazonaws.com" \ -H "Date: ${RFCDate}" \ -H "Content-Type: ${ContentType}" \ -H "Authorization: AWS ${AccessKey}:${SignString}" \ http://${S3Bucket}.s3.amazonaws.com/${fileName}
Please advise on how to go about converting this to Qt framework.
Thanks!
-
@PrateekKhatri you may want to take a look at QNetworkAccessManager::put() method.
-
Hi,
You'll need at least OpenSSL for the encryption part. The rest you can do with
QNetworkAccessManager
as @Pablo-J-Rogina suggests. Base64 is already supported byQByteArray
. -
void LogMgr::startLogUpload() { QNetworkAccessManager networkAccessManager; QByteArray RFCDate = QDateTime::currentDateTime().toString(Qt::RFC2822Date).toUtf8(); QByteArray S3Path = "/" + m_S3Bucket + "/" + m_logFileName.toUtf8(); QByteArray StringToSign = "PUT\n\n" + m_ContentType + "\n" + RFCDate + "\n" + S3Path; QByteArray SignString = QMessageAuthenticationCode::hash(StringToSign, m_SecretKey, QCryptographicHash::Sha1).toBase64(); QNetworkRequest request(QUrl(m_S3Bucket + ".s3.amazonaws.com/" + m_logFileName)); request.setRawHeader("Host", m_S3Bucket + ".s3.amazonaws.com"); request.setRawHeader("Date", RFCDate); request.setHeader(QNetworkRequest::ContentTypeHeader, m_ContentType); request.setRawHeader("Authorization", "AWS " + m_AccessKey + ":" + SignString); QFile file(m_logFileName); qDebug() << "Sending Request"; m_networkReply = networkAccessManager.put(request, &file); connect(m_networkReply, &QNetworkReply::finished, this, &LogMgr::fileUploadResponseCheck); qDebug() << m_networkReply->errorString(); qDebug() << m_networkReply->error(); } void LogMgr::fileUploadResponseCheck() { disconnect(m_networkReply, &QNetworkReply::finished, this, &LogMgr::fileUploadResponseCheck); qDebug() << "Are we ever here??"; QList<QByteArray> headerList = m_networkReply->rawHeaderList(); for (quint8 i = 0; i < headerList.length(); ++i) { qDebug() << m_networkReply->rawHeader(headerList[i]); } }
This is what I could currently have. However, I am seeing
Unknown Error
as output forerrorString()
andQNetworkReply::NetworkError(NoError)
as output forerror()
.
Also, the slot forfinished()
signal is never being called.How do I exactly get OpenSSL to work with Qt, and how would I use it in this case?
-
Update:
After a lot of reading and trial and error, I've reached to a point where I do get a response from the S3 server and the finished signal emits.
Here is the Code I have now:
void LogMgr::startLogUpload() { QNetworkAccessManager * networkAccessManager; networkAccessManager = new QNetworkAccessManager(this); connect(networkAccessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileUploadResponseCheck(QNetworkReply*))); QByteArray RFCDate = QDateTime::currentDateTime().toString(Qt::RFC2822Date).toUtf8(); qDebug() << RFCDate; QByteArray S3Path = "/" + m_S3Bucket + "/" + m_logFileName.toUtf8(); QByteArray StringToSign = "PUT\n\n" + m_ContentType + "\n" + RFCDate + "\n" + S3Path; QByteArray SignString = QMessageAuthenticationCode::hash(StringToSign, m_SecretKey, QCryptographicHash::Sha1).toBase64(); QNetworkRequest request(QUrl("http://" + m_S3Bucket + ".s3.amazonaws.com/" + m_logFileName)); request.setRawHeader("Host", m_S3Bucket + ".s3.amazonaws.com"); request.setRawHeader("Date", RFCDate); request.setHeader(QNetworkRequest::ContentTypeHeader, m_ContentType); request.setRawHeader("Authorization", "AWS " + m_AccessKey + ":" + SignString); QIODevice * fileData = new QFile(m_logFileName, this); if (fileData->open(QIODevice::ReadOnly)) { qDebug() << "Sending Request"; networkAccessManager->put(request, fileData); } } void LogMgr::fileUploadResponseCheck(QNetworkReply * reply) { qDebug() << "File Upload Response Check"; if (reply == nullptr) { qDebug() << "Network reply ptr empty"; return; } qDebug() << "Response " << reply->readAll(); }
I have never used AWS S3 before so I am unaware of this response error:
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>AccessDenied</Code><Message>AWS authentication requires a valid Date or x-amz-date header</Message><RequestId>C9EDED0BEC1D699F</RequestId><HostId>ivgJ79t3rydmrW9Eeh8j1osjPlNKXgVHRIQeZ40iivrq4yZTb/u0c2yETZ0YGhGsDTW/0w2HZv0=</HostId></Error>"
AWS Authentication requires a valid Date or x-amz-date header
Surely this has something to do with the RFCDate header!Any advise on this would be really helpful.
Edit:
I found that the shell script returns date:Mon, 30 Apr 2018 18:35:18 -0700
Qt RFC2822Date is returned as:"30 Apr 2018 18:28:23 -0700
Going to try fixing this and will try again.
Thanks,
Prateek