Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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.


  • Lifetime Qt Champion

    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 by QByteArray.



  • @SGaist

    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 for errorString() and QNetworkReply::NetworkError(NoError) as output for error().
    Also, the slot for finished() 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


Log in to reply