Я пишу прокси-сервер ssl с использованием Qt. Вот пример кода:
# header
class SslProxyServer : public QTcpServer
{
Q_OBJECT
public:
explicit SslProxyServer(quint16 port, QObject *parent = 0);
private slots:
void onEncrypted();
void onReadyRead();
void onSslErrors(QList<QSslError> sslErrors);
void onModeChanged(QSslSocket::SslMode sslMode);
void onStateChanged(QAbstractSocket::SocketState socketState);
void onError(QAbstractSocket::SocketError socketError);
protected:
void incomingConnection(qintptr socketDescriptor);
};
# source
SslProxyServer::SslProxyServer(quint16 port, QObject *parent) : QTcpServer(parent)
{
if (!listen(QHostAddress::Any, port)) {
qDebug() << "Unable to start tcp server";
return;
}
if (m_tcpServer->isListening()) {
qDebug() << "Listening port" << m_tcpServer->serverPort();
} else {
qDebug() << "Not listening";
}
}
void SslProxyServer::incomingConnection(qintptr socketDescriptor)
{
qDebug() << "incomingConnection";
QSslSocket *serverSocket = new QSslSocket(this);
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
connect(serverSocket, SIGNAL(encrypted()), this, SLOT(onEncrypted()));
connect(serverSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(onSslErrors(QList<QSslError>)));
connect(serverSocket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(onModeChanged(QSslSocket::SslMode)));
connect(serverSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState)));
connect(serverSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError(QAbstractSocket::SocketError)));
QSslConfiguration sslConfiguration = serverSocket->sslConfiguration();
// ...
QSslCertificate cert(&certFile, QSsl::Pem);
QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem);
sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
sslConfiguration.setLocalCertificate(cert); // set domain cert
sslConfiguration.setPrivateKey(key); // set domain key
sslConfiguration.setProtocol(QSsl::AnyProtocol);
// ...
QSslCertificate caCert(&caCertFile, QSsl::Pem);
sslConfiguration.setCaCertificates(QList<QSslCertificate>() << caCert); // add ca cert
serverSocket->setSslConfiguration(sslConfiguration);
serverSocket->startServerEncryption();
} else {
qDebug() << "Cannot set socket descriptor";
delete serverSocket;
}
}
void SslProxyServer::onEncrypted()
{
qDebug() << "onEncrypted";
}
void SslProxyServer::onReadyRead()
{
qDebug() << "onReadyRead";
}
void SslProxyServer::onSslErrors(QList<QSslError> sslErrors)
{
qDebug() << "onSslErrors";
}
void SslProxyServer::onModeChanged(QSslSocket::SslMode sslMode)
{
qDebug() << "onModeChanged(" << (int) sslMode << ")";
}
void SslProxyServer::onStateChanged(QAbstractSocket::SocketState socketState)
{
qDebug() << "onStateChanged(" << (int) socketState << ")";
}
void SslProxyServer::onError(QAbstractSocket::SocketError socketError)
{
qDebug() << "onError(" << (int) socketError << ")";
QSslSocket *serverSocket = qobject_cast<QSslSocket *>(sender());
qDebug() << serverSocket->errorString();
}
Я создал самозаверяющий сертификат CA с закрытым ключом и еще один сертификат для конкретного домена, который я подписал своим сертификатом CA. После того, как я скопирую сертификат CA в /usr/local/share/ca-certificates
и беги sudo update-ca-certificates
, Но когда я пытаюсь подключиться к моему прокси-серверу с помощью стороннего приложения, где мой сервер используется в качестве https-прокси, я получаю ошибку QAbstractSocket :: SslHandshakeFailedError со следующим выводом:
Listening port 8888
incomingConnection
onModeChanged( 2 )
onError( 13 )
"Error during SSL handshake: error:1407609B:SSL routines:SSL23_GET_CLIENT_HELLO:https proxy request"onStateChanged( 0 )
Так что это даже не входит в onReadyRead
слот.
Когда я пытаюсь проверить свой сервер с помощью команды openssl: openssl s_client -connect 127.0.0.1:8888 -debug
— он успешно подключен к моему серверу. Вывод содержит следующие строки:
verify error:num=19:self signed certificate in certificate chain
verify return:0
No client certificate CA names sent
---
SSL handshake has read 2667 bytes and written 439 bytes
Verify return code: 19 (self signed certificate in certificate chain)
---
но я могу отправить данные на свой сервер и увидеть их необработанное значение в моем onReadyRead
слот.
Некоторая информация о моем env:
ОС: Ubuntu 12.04 x86_64
Qt: 5.2.1 (GCC 4.6.1, 64 бита)
Заранее спасибо,
… когда я пытаюсь подключиться к своему прокси-серверу с помощью стороннего приложения, где мой сервер используется в качестве https-прокси
… Когда я пытаюсь протестировать мой сервер с помощью команды openssl: openssl s_client -connect 127.0.0.1:8888 -debug — он успешно подключен к моему серверу.
Это разные вещи. С помощью команды openssl вы устанавливаете TCP-соединение, которое вы немедленно обновляете до SSL. Но прокси-сервер https работает по-другому: сначала он устанавливает TCP-соединение, затем выдает команду HTTP CONNECT и только после получения успешного ответа от прокси-сервера обновляет соединение до SSL, например,
- client to server
> CONNECT ip:port HTTP/1.0\r\n
> \r\n
- followed by server to client
< HTTP/1.0 200 connection established\r\n
< \r\n
... SSL handshake ...
И поскольку клиент отправляет запрос прокси-сервера https так, как он должен, но вы ожидаете немедленного запуска SSL-рукопожатия (то есть вы ожидаете сообщение ClientHello), это завершается неудачно с:
«Ошибка во время рукопожатия SSL: ошибка: 1407609B: процедуры SSL: SSL23_GET_CLIENT_HELLO: запрос прокси https»