QNetworkRequest с локальным сертификатом ssl

Мне нужно обмениваться данными с сервером, для которого требуется локальный сертификат (файл .crt). Я пробую это:

loginRequest = QNetworkRequest(QUrl("https://somesite.com/login"));

QSslConfiguration sslConf = loginRequest.sslConfiguration();
QList<QSslCertificate> certs = QSslCertificate::fromPath(Preferences::certificatePath());
qDebug() << certs.first().issuerInfo(QSslCertificate::Organization); // prints name
sslConf.setLocalCertificate(certs.first());
qDebug() << "is valid " << sslConf.localCertificate().isValid(); // true
qDebug() << "is null " << sslConf.localCertificate().isNull(); // false
qDebug() << "protocol " << sslConf.protocol(); // 0
sslConf.setProtocol(QSsl::SslV3); // i also tried Qssl::AnyProtocol
qDebug() << "protocol " << sslConf.protocol(); // 0

// if i uncomment these i expect everithing to work
//QSslConfiguration::setDefaultConfiguration(sslConf);
//QSslSocket::addDefaultCaCertificate(certs.first());
//loginRequest.setSslConfiguration(sslConf);

QObject::connect(connectionManager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(printSslErrors2(QNetworkReply*,QList<QSslError>)));

m_reply = connectionManager->get(loginRequest);
QObject::connect(m_reply, SIGNAL(readyRead()), this, SLOT(getCookie()));
QObject::connect(m_reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(printSslErrors(QList<QSslError>)));

Когда этот код выполняется, у меня есть следующие сообщения в WireShark (фильтр: tcp && ssl && ip.addr == my_addr):

Client Hello
ServerHello, Certificate
Server Key Exchange, Certificate request, Server Hello Done
Alert (level: Warning, Description: no certificate), client key exchange, change cipher spec, encrypted handshake message
Alert (level: Fatal, Description: Handshake failure)

Это ожидаемо - код для применения сертификата закомментирован, но странная вещь - я не получаю никаких ошибок ssl от моего QNetworkAccessManager и QNetworkReply (слоты printSslErrors и printSslErrors2).

Если я раскомментирую любую из этих 3 строк:

 //QSslConfiguration::setDefaultConfiguration(sslConf);
 //QSslSocket::addDefaultCaCertificate(certs.first());
 //loginRequest.setSslConfiguration(sslConf);

Я НИЧЕГО не получаю в wireshark (мало сообщений SYN, ACK и FIN tcp, но нет трафика http или ssl). Также до сих пор нет ошибок от QNetworkAccessManager и QNetworkReply, поэтому я понятия не имею, что происходит не так.

Есть ли шанс заставить Qt принять мой локальный сертификат или может быть какая-то сторонняя библиотека, ориентированная на qt, чтобы помочь мне?

P.S.: Кстати, несколько дней назад ssl и https работали нормально, до того, как сервер был изменен, чтобы требовать сертификаты на стороне клиента.

PPS: сертификат является самоподписанным, если это имеет значение. Также я попытался «установить» его (файл p12) в систему, и Chrome и IE7 могут использовать его и взаимодействовать с сервером.


person Aleksei Poliakov    schedule 21.09.2011    source источник


Ответы (2)


Полный выстрел в темноте и исходит из предположения, что Qt на самом деле может сообщать об ошибке, но вы не получаете сигнал.

Вы подключаете сигналы от connectionManager к this. Вы включили макрос Q_OBJECT в заголовок для this?

Также проверьте вывод при запуске приложения, поскольку Qt может сообщать о проблемах с подключением сигналов/слотов, если это действительно так.

person Matthew    schedule 21.09.2011
comment
1. это QMainWindow, он получает сигнал от кнопки входа в систему (она запускает последовательность входа в систему), поэтому я не понимаю, почему другие сигналы могут быть проигнорированы 2. Нет ошибок с сигналами/слотами. Tbh единственная известная мне ошибка (о которой сообщается в окне вывода приложения) связана с тем, что сигнал и слот не существуют или имеют неправильные параметры. - person Aleksei Poliakov; 22.09.2011
comment
Это хорошо, если вы получаете другие сигналы в свой класс QMainWindow. Проверка макроса Q_OBJECT — это первое, что я делаю, когда у меня возникают проблемы. Надеюсь, вы отследите реальную проблему. - person Matthew; 22.09.2011

РЕШЕНИЕ, часть I: я в основном решил эту проблему (отсутствие связи), было 2 причины:

1-й - серверу apache на самом деле требуется закрытый ключ (по какой-то неизвестной причине, нашел его [здесь] [1]), как добавить закрытый ключ:

QFile x(Preferences::certificateKeyPath()); 
x.open(QIODevice::ReadOnly);
pKey = QSslKey(x.readAll(),QSsl::Rsa);
QSslError error1(QSslError::SelfSignedCertificate, certs.first());
QSslError error2(QSslError::CertificateUntrusted, certs.first());
QList<QSslError> expectedSslErrors;
expectedSslErrors.append(error1);
expectedSslErrors.append(error2);

2d - сертификат, который у меня был, был не очень "хорошим". Я не знаю, что это на самом деле означает и почему это не работает, но когда я получил новый сертификат от администратора сервера и добавил закрытый ключ, рукопожатие прошло успешно.

Я до сих пор не знаю, как ловить sslErrors (например, чтобы показать пользователю, что его сертификат не работает), но это хорошее начало.

РЕШЕНИЕ, часть II:

Решил последнюю часть вопроса (kina a woraround). Кажется, что QNetworkReply, не выдающий SslErrors, является ошибкой (или, по крайней мере, она не работает постоянно или для всех веб-сайтов), нашел ее [в системе отслеживания ошибок Qt] [2]. И обходной путь тоже оттуда: так как мы не можем получить SslErrors, мы должны попытаться получить что-то еще - [ошибка][3], например. Он не дает подробной информации о том, что на самом деле произошло, но это лучше, чем ничего. Для меня код ошибки 6 — «сбой рукопожатия SSL / TLS, и зашифрованный канал не может быть установлен. Должен был быть отправлен сигнал sslErrors ()». идеально (остальное меня не волнует):

 QObject::connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleSslErrors(QNetworkReply::NetworkError)));

Важная часть: если у пользователя неправильный сертификат и/или ключ - выдается сигнал. Но он также выдается, если сертификат и ключ верны. Кажется, авторизация все еще может быть не идеальной, но вы можете легко отключить ее с помощью

QObject::connect(m_reply, SIGNAL(sslErrors(QList<QSslError>)), 
                  this, SLOT(printSslErrors(QList<QSslError>)));

Заключение кажется, они исправили много ошибок SSL в Qt 4.8, поэтому я надеюсь, что релиз будет скоро

person Aleksei Poliakov    schedule 18.12.2013