diff options
author | Ahmad Samir <[email protected]> | 2025-04-30 17:30:42 +0300 |
---|---|---|
committer | Ahmad Samir <[email protected]> | 2025-05-14 04:41:13 +0300 |
commit | 2be51c692307f067bb5bdc011e5f3dca1dbe3ff8 (patch) | |
tree | 81367961331585b4ad02dd8773b467af929d67e5 | |
parent | 2875c4358beedd0997b60d21df4b95dbfec4f9a6 (diff) |
QSslCertificate: add fromFile() method
QSslCertificate::fromPath() does some extra work:
- matching wildcard glob or regular expression patterns
- checks if the string it's called on is a file or a dir
That extra work isn't needed when you already have the path to a
specific certificate file.
E.g. qtlsbackend_openssl.cpp:systemCaCertificates() used to call
fromPath() on *.pem/*.crt files that it got from iterating over system
certifcates dirs.
This also de-duplicates the code in fromPath().
[ChangeLog][QtNetwork][QSslCertificate] Added fromFile() method.
Change-Id: I92ab358e4711866dd4510da42c47905c7dae58b1
Reviewed-by: Ivan Solovev <[email protected]>
Reviewed-by: MÃ¥rten Nordheim <[email protected]>
-rw-r--r-- | src/network/ssl/qsslcertificate.cpp | 45 | ||||
-rw-r--r-- | src/network/ssl/qsslcertificate.h | 2 | ||||
-rw-r--r-- | src/plugins/tls/openssl/qtlsbackend_openssl.cpp | 2 | ||||
-rw-r--r-- | tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp | 52 |
4 files changed, 82 insertions, 19 deletions
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index af3e47f66f9..38fea369055 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -633,6 +633,9 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, if (path.isEmpty()) return {}; + if (syntax == PatternSyntax::FixedString && QFileInfo(path).isFile()) + return fromFile(path, format); + // $, (,), *, +, ., ?, [, ,], ^, {, | and }. // make sure to use the same path separators on Windows and Unix like systems. @@ -665,15 +668,8 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, pathPrefix = {}; } else { // Check if the path is a file. - if (QFileInfo(sourcePath).isFile()) { - QFile file(sourcePath); - QIODevice::OpenMode openMode = QIODevice::ReadOnly; - if (format == QSsl::Pem) - openMode |= QIODevice::Text; - if (file.open(openMode)) - return QSslCertificate::fromData(file.readAll(), format); - return QList<QSslCertificate>(); - } + if (QFileInfo(sourcePath).isFile()) + return fromFile(sourcePath, format); } // Special case - if the prefix ends up being nothing, use "." instead. @@ -710,12 +706,7 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path, continue; #endif - QFile file(filePath); - QIODevice::OpenMode openMode = QIODevice::ReadOnly; - if (format == QSsl::Pem) - openMode |= QIODevice::Text; - if (file.open(openMode)) - certs += QSslCertificate::fromData(file.readAll(), format); + certs += QSslCertificate::fromFile(filePath, format); } return certs; } @@ -760,6 +751,30 @@ QList<QSslCertificate> QSslCertificate::fromData(const QByteArray &data, QSsl::E return reader(data, -1); } +/*! + \since 6.10 + + Reads the data from the file \a filePath and parses all certificates + that are encoded in the specified \a format and returns a list of + QSslCertificate objects. + + If \a filePath isn't a regular file, this method will return an empty + list. + + \sa fromData(), fromPath() +*/ +QList<QSslCertificate> QSslCertificate::fromFile(const QString &filePath, + QSsl::EncodingFormat format) +{ + QFile file(filePath); + QIODevice::OpenMode openMode = QIODevice::ReadOnly; + if (format == QSsl::Pem) + openMode |= QIODevice::Text; + if (file.open(openMode)) + return QSslCertificate::fromData(file.readAll(), format); + return {}; +} + #ifndef QT_NO_SSL /*! Verifies a certificate chain. The chain to be verified is passed in the diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h index e34fa4d92a3..17f931c1268 100644 --- a/src/network/ssl/qsslcertificate.h +++ b/src/network/ssl/qsslcertificate.h @@ -107,6 +107,8 @@ public: QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem); static QList<QSslCertificate> fromData( const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem); + static QList<QSslCertificate> fromFile( + const QString &filePath, QSsl::EncodingFormat format = QSsl::Pem); #ifndef QT_NO_SSL static QList<QSslError> verify(const QList<QSslCertificate> &certificateChain, const QString &hostName = QString()); diff --git a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp index 6a55f275943..bce31734f94 100644 --- a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp +++ b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp @@ -410,7 +410,7 @@ QList<QSslCertificate> systemCaCertificates() } } for (const QString& file : std::as_const(certFiles)) - systemCerts.append(QSslCertificate::fromPath(file, QSsl::Pem)); + systemCerts.append(QSslCertificate::fromFile(file, QSsl::Pem)); } #endif // platform #ifdef QSSLSOCKET_DEBUG diff --git a/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp b/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp index b51053effde..7a9286b329c 100644 --- a/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp +++ b/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp @@ -20,6 +20,8 @@ #include <openssl/obj_mac.h> #endif +using namespace Qt::StringLiterals; + class tst_QSslCertificate : public QObject { Q_OBJECT @@ -97,6 +99,9 @@ private slots: // helper for verbose test failure messages QString toString(const QList<QSslError>&); +private: + void certInfo_helper(const char *methodName); + // ### add tests for certificate bundles (multiple certificates concatenated into a single // structure); both PEM and DER formatted #endif // QT_CONFIG(ssl) @@ -494,12 +499,23 @@ void tst_QSslCertificate::subjectIssuerDisplayName() QFETCH(const QString, certName); QFETCH(const QString, expectedName); + { const auto chain = QSslCertificate::fromPath(testDataDir + certName); QCOMPARE(chain.size(), 1); const auto cert = chain.at(0); QVERIFY(!cert.isNull()); QCOMPARE(cert.subjectDisplayName(), expectedName); QCOMPARE(cert.issuerDisplayName(), expectedName); + } + + { + const auto chain = QSslCertificate::fromFile(testDataDir + certName); + QCOMPARE(chain.size(), 1); + const auto cert = chain.at(0); + QVERIFY(!cert.isNull()); + QCOMPARE(cert.subjectDisplayName(), expectedName); + QCOMPARE(cert.issuerDisplayName(), expectedName); + } } void tst_QSslCertificate::utf8SubjectNames() @@ -701,9 +717,14 @@ void tst_QSslCertificate::fromPath_qregularexpression() pemencoding ? QSsl::Pem : QSsl::Der, QSslCertificate::PatternSyntax(syntax)).size(), numCerts); + + if (QSslCertificate::PatternSyntax(syntax) == QSslCertificate::PatternSyntax::FixedString) { + const auto list = QSslCertificate::fromFile(path, pemencoding ? QSsl::Pem : QSsl::Der); + QCOMPARE(list.size(), numCerts); + } } -void tst_QSslCertificate::certInfo() +void tst_QSslCertificate::certInfo_helper(const char *methodName) { // MD5 Fingerprint=B6:CF:57:34:DA:A9:73:21:82:F7:CF:4D:3D:85:31:88 // SHA1 Fingerprint=B6:D1:51:82:E0:29:CA:59:96:38:BD:B6:F9:40:05:91:6D:49:09:60 @@ -788,8 +809,14 @@ void tst_QSslCertificate::certInfo() "dc:c2:eb:b7:bb:50:18:05:ba:ad:af:08:49:fe:98:63" "55:ba:e7:fb:95:5d:91"; - QSslCertificate cert = QSslCertificate::fromPath(testDataDir + "certificates/cert.pem", QSsl::Pem, - QSslCertificate::PatternSyntax::FixedString).first(); + QSslCertificate cert; + if (methodName == "fromPath"_L1) { + cert = QSslCertificate::fromPath(testDataDir + "certificates/cert.pem", QSsl::Pem, + QSslCertificate::PatternSyntax::FixedString).first(); + } else if (methodName == "fromFile"_L1) { + cert = QSslCertificate::fromFile(testDataDir + "certificates/cert.pem", QSsl::Pem).first(); + } + QVERIFY(!cert.isNull()); QCOMPARE(cert.issuerInfo(QSslCertificate::Organization)[0], QString("CryptSoft Pty Ltd")); @@ -845,8 +872,15 @@ void tst_QSslCertificate::certInfo() QCOMPARE(cert, QSslCertificate(QByteArray::fromHex(der), QSsl::Der)); } +void tst_QSslCertificate::certInfo() +{ + certInfo_helper("fromPath"); + certInfo_helper("fromFile"); +} + void tst_QSslCertificate::certInfoQByteArray() { + { QSslCertificate cert = QSslCertificate::fromPath(testDataDir + "certificates/cert.pem", QSsl::Pem, QSslCertificate::PatternSyntax::FixedString).first(); QVERIFY(!cert.isNull()); @@ -855,6 +889,18 @@ void tst_QSslCertificate::certInfoQByteArray() // we fixed a bug we had with lazy initialization of the values. QCOMPARE(cert.issuerInfo("CN")[0], QString("Test CA (1024 bit)")); QCOMPARE(cert.subjectInfo("CN")[0], QString("name/with/slashes")); + } + + { + QSslCertificate cert = + QSslCertificate::fromFile(testDataDir + "certificates/cert.pem", QSsl::Pem).first(); + QVERIFY(!cert.isNull()); + + // in this test, check the bytearray variants before the enum variants to see if + // we fixed a bug we had with lazy initialization of the values. + QCOMPARE(cert.issuerInfo("CN")[0], QString("Test CA (1024 bit)")); + QCOMPARE(cert.subjectInfo("CN")[0], QString("name/with/slashes")); + } } void tst_QSslCertificate::task256066toPem() |