Я нахожусь в процессе написания нового бота Betfair на C ++ с использованием Qt, но я падаю на первое препятствие с процессом входа в систему. После долгого чтения я решил использовать QNetworkAccessManager
и вот что у меня сейчас есть:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
BetfairManager bet_man;
QNetworkAccessManager *manager = new QNetworkAccessManager();
QObject::connect(manager, SIGNAL(finished(QNetworkReply*)),
&bet_man,SLOT(replyFinished(QNetworkReply*)));
QObject::connect(manager,SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
&bet_man,SLOT(replySSLErrors(QNetworkReply*,QList<QSslError>)));
QObject::connect(manager,SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
&bet_man,SLOT(replyAuthenticationRequired(QNetworkReply*,QAuthenticator*)));
QObject::connect(manager,SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&,QAuthenticator*)),
&bet_man,SLOT(replyProxyAuthenticationRequired(const QNetworkProxy&,QAuthenticator*)));
QNetworkRequest request;QByteArray payload("username=myusername&password=mypassword");
QString url("https://identitysso.betfair.com/api/certlogin");
QSslConfiguration config;
QString pemfile("../../client-2048.pem");
QFile file(pemfile);
if(!file.exists()) {
qWarning("Filename %s doesn't exists.", qPrintable(pemfile));
}
if(!file.open(QIODevice::ReadOnly)) {
qWarning("Cannot open filename %s.", qPrintable(pemfile));
}
QByteArray data = file.readAll();
QSslCertificate sslcert(data, QSsl::Pem);
if(sslcert.isNull()) {
qWarning("The certificate has no content.");
}
config.setLocalCertificate(sslcert);
request.setSslConfiguration(config);
request.setUrl(url);
request.setRawHeader("X-Application","mykey");
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
manager->post(request,payload);
return a.exec();
}
BetfairManager
класс это просто фиктивный класс, который я создал, чтобы иметь слоты для QNetworkAccessManager
сигналы — они просто выводят некоторую отладочную информацию на экран, чтобы увидеть, какие сигналы были выпущены.
Когда я запускаю вышеизложенное, я вижу, что QNetworkAccessManager::finished()
сигнал сработал, но у меня пустой ответ — нет даже никаких заголовков HTTP.
Когда я прекращаю устанавливать сертификат, комментируя строку
request.setSslConfiguration(config);
Я получаю ответ
"{"loginStatus":"CERT_AUTH_REQUIRED"}"
что неудивительно, но показывает, что я передаю правильные параметры в своем запросе, поэтому я убежден, что проблема заключается в моем механизме передачи / создания сертификатов с классами Qt.
Я создал файл .pem в соответствии с документацией betfair (в которой говорится, что его нужно создавать, добавляя сгенерированные файлы .crt и .key), и я знаю, что они в порядке, так как у меня также есть бот Python, который отлично работает с теми же файлами ,
Я новичок в Qt и не эксперт по SSL, и у меня складывается впечатление, что мне здесь не хватает чего-то фундаментального.
В случае, если это полезно, вот мой эквивалентный код на Python, который отлично работает, используя urllib2
пакет
un = "myusername";
pw = "mypassword";
app_key = 'mykey'
payload = 'username=' + un + '&password=' + pw
login_headers = {'X-Application': app_key, 'Content-Type': 'application/x-www-form-urlencoded'}
resp = requests.post('https://identitysso.betfair.com/api/certlogin', data=payload, cert=('client-2048.crt', 'client-2048.key'), headers=login_headers)
Я разрабатываю это с Qt 5.2.1 на Ubuntu 14.04
Если вы хотите войти через соединение SSL с использованием сертификата клиента, вам необходимо дополнительно указать закрытый ключ клиента. Сертификат — это в основном только открытый ключ, который кто-то проверял. Доступ предоставляется, когда вы можете доказать серверу, что у вас также есть закрытый ключ к сертификату.
Вы можете сделать это, используя следующие методы:
// get certData and keyData from files
QSslConfiguration config;
QSslCertificate sslcert(certData, QSsl::Der);
config.setLocalCertificate(sslcert);
QSslKey privkey(keyData, QSsl::Rsa, QSsl::Der, QSsl::PrivateKey, "myKeyPassword")
config.setPrivateKey(privkey);
Когда оба ваших оригинальных файла client-2048.crt
а также client-2048.key
являются двоичными, вероятно, они хранятся в формате DER. В этом случае вы можете указать формат ввода, как указано выше, и вам не нужно ничего преобразовывать.