Fix self-signed certificate handling (#1649)

This commit is contained in:
LordGrey 2023-10-29 21:13:34 +01:00 committed by GitHub
parent b73e9f4996
commit 27027b224c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 7 deletions

View File

@ -70,6 +70,7 @@ Note: The wizard will configure an APIv2 capable bridge always with Entertainmen
- Changed default build from Stretch to Buster
- Support Qt 6.7, Update to Protobuf 23.4.0, Update mbedTLS to v3.4.0, Update flatbuffers to v23.5.26
- Use C++17 standard as default
- Added Pull Request (PR) installation script, allowing users to test development builds savely on Linux
- Fixed missing include limits in QJsonSchemaChecker - Thanks @Portisch
- Fixed dependencies for deb packages in Debian Bookworm (#1579) - Thanks @hg42, @Psirus
- Fixed git version identification when run in docker and local code

View File

@ -584,11 +584,7 @@ int LedDevicePhilipsHueBridge::close()
bool LedDevicePhilipsHueBridge::configureSsl()
{
_restApi->setAlternateServerIdentity(_deviceBridgeId);
if (_isDiyHue)
{
_restApi->acceptSelfSignedCertificates(true);
}
_restApi->acceptSelfSignedCertificates(true);
bool success = _restApi->setCaCertificate(API_SSL_CA_CERTIFICATE_RESSOURCE);
if (!success)

View File

@ -11,6 +11,8 @@
#include <QList>
#include <QHash>
#include <QFile>
#include <QDir>
#include <QStandardPaths>
#include <QSslSocket>
@ -451,6 +453,63 @@ bool ProviderRestApi::checkServerIdentity(const QSslConfiguration& sslConfig) co
return isServerIdentified;
}
bool ProviderRestApi::matchesPinnedCertificate(const QSslCertificate& certificate)
{
bool isMatching {false};
QList certificateInfos = certificate.subjectInfo(QSslCertificate::CommonName);
if (certificateInfos.isEmpty())
{
return false;
}
QString identifier = certificateInfos.constFirst();
QString appDataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QString certDir = appDataDir + "/certificates";
QDir().mkpath(certDir);
QString filePath(certDir + "/" + identifier + ".pem");
QFile file(filePath);
if (file.open(QIODevice::ReadOnly))
{
QList certificates = QSslCertificate::fromDevice(&file, QSsl::Pem);
if (!certificates.isEmpty())
{
Debug (_log,"First used certificate loaded successfully");
QSslCertificate pinnedeCertificate = certificates.constFirst();
if (pinnedeCertificate == certificate)
{
isMatching = true;
}
}
else
{
Debug (_log,"Error reading first used certificate file: %s", QSTRING_CSTR(filePath));
}
file.close();
}
else
{
if (file.open(QIODevice::WriteOnly))
{
QByteArray pemData = certificate.toPem();
qint64 bytesWritten = file.write(pemData);
if (bytesWritten == pemData.size())
{
Debug (_log,"First used certificate saved to file: %s", QSTRING_CSTR(filePath));
isMatching = true;
}
else
{
Debug (_log,"Error writing first used certificate file: %s", QSTRING_CSTR(filePath));
}
file.close();
}
}
return isMatching;
}
void ProviderRestApi::onSslErrors(QNetworkReply* reply, const QList<QSslError>& errors)
{
int ignoredErrorCount {0};
@ -466,11 +525,21 @@ void ProviderRestApi::onSslErrors(QNetworkReply* reply, const QList<QSslError>&
}
break;
case QSslError::SelfSignedCertificate :
if (_isSeflSignedCertificateAccpeted)
if (_isSeflSignedCertificateAccpeted)
{
// Get the peer certificate associated with the error
QSslCertificate certificate = error.certificate();
if (matchesPinnedCertificate(certificate))
{
Debug (_log,"'Trust on first use' - Certificate received matches pinned certificate");
ignoreSslError = true;
}
break;
else
{
Error (_log,"'Trust on first use' - Certificate received does not match pinned certificate");
}
}
break;
default:
break;
}

View File

@ -444,6 +444,8 @@ private:
bool checkServerIdentity(const QSslConfiguration& sslConfig) const;
bool matchesPinnedCertificate(const QSslCertificate& certificate);
Logger* _log;
/// QNetworkAccessManager object for sending REST-requests.