无法使用 C++、Qt 和 Betfair 使用 SSL 证书登录 API-NG

Unable to login with SSL certificate using C++, Qt and Betfair API-NG

我正在使用 Qt 在 C++ 中编写一个新的 Betfair 机器人,但我遇到了登录过程的第一个障碍。经过大量阅读后,我决定使用 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 class 只是我创建的一个虚拟 class 用于 QNetworkAccessManager 信号的插槽 - 它们只是将一些调试信息转储到屏幕上查看发出了哪些信号。

当我 运行 以上时,我看到 QNetworkAccessManager::finished() 信号已触发,但我的响应是空的 - 甚至没有任何 HTTP headers.

当我通过注释行停止设置证书时

request.setSslConfiguration(config);

我收到回复

"{"loginStatus":"CERT_AUTH_REQUIRED"}"

这并不奇怪,但表明我在我的请求中传递了正确的参数,所以我确信问题出在我的证书通过 mechanism/creation 与 Qt classes。

我已经根据 betfair 文档创建了 .pem 文件(它说通过附加生成的 .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)

我正在 Ubuntu 14.04

上用 Qt 5.2.1 开发这个

当您想使用客户端证书通过SSL 连接登录时,您需要另外指定客户端的私钥。证书基本上只是一个 public 密钥,由其他人检查。当您可以向服务器证明您也拥有证书的私钥时,就会授予访问权限。

您可以使用以下方法做到这一点:

// 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.crtclient-2048.key 都是二进制文件时,它们很可能以 DER 格式存储。在这种情况下,您可以像上面那样指定输入格式,不需要进行任何转换。