summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/network/ssl/qsslcontext_openssl.cpp24
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp52
-rw-r--r--src/network/ssl/qsslsocket_openssl11_symbols_p.h13
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h3
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp25
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h45
-rw-r--r--tests/auto/network/ssl/qocsp/certs/alice.crt24
-rw-r--r--tests/auto/network/ssl/qocsp/certs/alice.key28
-rw-r--r--tests/auto/network/ssl/qocsp/certs/ca1.crt24
-rw-r--r--tests/auto/network/ssl/qocsp/certs/ca1.key28
-rw-r--r--tests/auto/network/ssl/qocsp/certs/infbob.key28
-rw-r--r--tests/auto/network/ssl/qocsp/certs/infbobchain.crt49
-rw-r--r--tests/auto/network/ssl/qocsp/certs/ss1-private.key28
-rw-r--r--tests/auto/network/ssl/qocsp/certs/ss1.crt25
-rw-r--r--tests/auto/network/ssl/qocsp/qocsp.pro15
-rw-r--r--tests/auto/network/ssl/qocsp/tst_qocsp.cpp823
-rw-r--r--tests/auto/network/ssl/ssl.pro2
17 files changed, 1216 insertions, 20 deletions
diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp
index 35cca9f01a..e81e5582f4 100644
--- a/src/network/ssl/qsslcontext_openssl.cpp
+++ b/src/network/ssl/qsslcontext_openssl.cpp
@@ -243,12 +243,28 @@ QString QSslContext::errorString() const
return errorStr;
}
+#if QT_CONFIG(ocsp)
+extern "C" int qt_OCSP_status_server_callback(SSL *ssl, void *); // Defined in qsslsocket_openssl.cpp.
+#endif // ocsp
// static
void QSslContext::applyBackendConfig(QSslContext *sslContext)
{
- if (sslContext->sslConfiguration.backendConfiguration().isEmpty())
+ const QMap<QByteArray, QVariant> &conf = sslContext->sslConfiguration.backendConfiguration();
+ if (conf.isEmpty())
return;
+#if QT_CONFIG(ocsp)
+ auto ocspResponsePos = conf.find("Qt-OCSP-response");
+ if (ocspResponsePos != conf.end()) {
+ // This is our private, undocumented configuration option, existing only for
+ // the purpose of testing OCSP status responses. We don't even check this
+ // callback was set. If no - the test must fail.
+ q_SSL_CTX_set_tlsext_status_cb(sslContext->ctx, qt_OCSP_status_server_callback);
+ if (conf.size() == 1)
+ return;
+ }
+#endif // ocsp
+
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
if (QSslSocket::sslLibraryVersionNumber() >= 0x10002000L) {
QSharedPointer<SSL_CONF_CTX> cctx(q_SSL_CONF_CTX_new(), &q_SSL_CONF_CTX_free);
@@ -256,8 +272,10 @@ void QSslContext::applyBackendConfig(QSslContext *sslContext)
q_SSL_CONF_CTX_set_ssl_ctx(cctx.data(), sslContext->ctx);
q_SSL_CONF_CTX_set_flags(cctx.data(), SSL_CONF_FLAG_FILE);
- const auto &backendConfig = sslContext->sslConfiguration.backendConfiguration();
- for (auto i = backendConfig.constBegin(); i != backendConfig.constEnd(); ++i) {
+ for (auto i = conf.constBegin(); i != conf.constEnd(); ++i) {
+ if (i.key() == "Qt-OCSP-response") // This never goes to SSL_CONF_cmd().
+ continue;
+
if (!i.value().canConvert(QMetaType::QByteArray)) {
sslContext->errorCode = QSslError::UnspecifiedError;
sslContext->errorStr = msgErrorSettingBackendConfig(
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 56764ebc7f..f6ee067c15 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -87,6 +87,8 @@
#include <openssl/ocsp.h>
#endif
+#include <algorithm>
+
#include <string.h>
QT_BEGIN_NAMESPACE
@@ -135,6 +137,37 @@ static unsigned int q_ssl_psk_server_callback(SSL *ssl,
return d->tlsPskServerCallback(identity, psk, max_psk_len);
}
#endif
+
+#if QT_CONFIG(ocsp)
+
+int qt_OCSP_status_server_callback(SSL *ssl, void *ocspRequest)
+{
+ Q_UNUSED(ocspRequest)
+ if (!ssl)
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ auto d = static_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData));
+ if (!d)
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ Q_ASSERT(d->mode == QSslSocket::SslServerMode);
+ const QByteArray &response = d->ocspResponseDer;
+ Q_ASSERT(response.size());
+
+ unsigned char *derCopy = static_cast<unsigned char *>(q_OPENSSL_malloc(size_t(response.size())));
+ if (!derCopy)
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ std::copy(response.data(), response.data() + response.size(), derCopy);
+ // We don't check the return value: internally OpenSSL simply assignes the
+ // pointer (it assumes it now owns this memory btw!) and the length.
+ q_SSL_set_tlsext_status_ocsp_resp(ssl, derCopy, response.size());
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+#endif // ocsp
+
} // extern "C"
QSslSocketBackendPrivate::QSslSocketBackendPrivate()
@@ -507,6 +540,25 @@ bool QSslSocketBackendPrivate::initSslContext()
return false;
}
}
+
+ ocspResponseDer.clear();
+ auto responsePos = configuration.backendConfig.find("Qt-OCSP-response");
+ if (responsePos != configuration.backendConfig.end()) {
+ // This is our private, undocumented 'API' we use for the auto-testing of
+ // OCSP-stapling. It must be a der-encoded OCSP response, presumably set
+ // by tst_QOcsp.
+ const QVariant data(responsePos.value());
+ if (data.canConvert<QByteArray>())
+ ocspResponseDer = data.toByteArray();
+ }
+
+ if (ocspResponseDer.size()) {
+ if (mode != QSslSocket::SslServerMode) {
+ setErrorAndEmit(QAbstractSocket::SslInvalidUserDataError,
+ QSslSocket::tr("Client-side sockets do not send OCSP responses"));
+ return false;
+ }
+ }
#endif // ocsp
return true;
diff --git a/src/network/ssl/qsslsocket_openssl11_symbols_p.h b/src/network/ssl/qsslsocket_openssl11_symbols_p.h
index b94b05b765..a44d00a830 100644
--- a/src/network/ssl/qsslsocket_openssl11_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl11_symbols_p.h
@@ -84,12 +84,12 @@ int q_DSA_bits(DSA *a);
int q_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c);
int q_EVP_PKEY_base_id(EVP_PKEY *a);
int q_RSA_bits(RSA *a);
-int q_OPENSSL_sk_num(OPENSSL_STACK *a);
-void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *));
-OPENSSL_STACK *q_OPENSSL_sk_new_null();
-void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data);
-void q_OPENSSL_sk_free(OPENSSL_STACK *a);
-void * q_OPENSSL_sk_value(OPENSSL_STACK *a, int b);
+Q_AUTOTEST_EXPORT int q_OPENSSL_sk_num(OPENSSL_STACK *a);
+Q_AUTOTEST_EXPORT void q_OPENSSL_sk_pop_free(OPENSSL_STACK *a, void (*b)(void *));
+Q_AUTOTEST_EXPORT OPENSSL_STACK *q_OPENSSL_sk_new_null();
+Q_AUTOTEST_EXPORT void q_OPENSSL_sk_push(OPENSSL_STACK *st, void *data);
+Q_AUTOTEST_EXPORT void q_OPENSSL_sk_free(OPENSSL_STACK *a);
+Q_AUTOTEST_EXPORT void * q_OPENSSL_sk_value(OPENSSL_STACK *a, int b);
int q_SSL_session_reused(SSL *a);
unsigned long q_SSL_CTX_set_options(SSL_CTX *ctx, unsigned long op);
int q_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings);
@@ -102,6 +102,7 @@ const SSL_METHOD *q_TLS_server_method();
ASN1_TIME *q_X509_getm_notBefore(X509 *a);
ASN1_TIME *q_X509_getm_notAfter(X509 *a);
+Q_AUTOTEST_EXPORT void q_X509_up_ref(X509 *a);
long q_X509_get_version(X509 *a);
EVP_PKEY *q_X509_get_pubkey(X509 *a);
void q_X509_STORE_set_verify_cb(X509_STORE *ctx, X509_STORE_CTX_verify_cb verify_cb);
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
index 6396b44808..c23234e291 100644
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -157,12 +157,13 @@ public:
#if QT_CONFIG(ocsp)
bool checkOcspStatus();
+#endif
// This decription will go to setErrorAndEmit(SslHandshakeError, ocspErrorDescription)
QString ocspErrorDescription;
// These will go to sslErrors()
QVector<QSslError> ocspErrors;
-#endif
+ QByteArray ocspResponseDer;
Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions);
static QSslCipher QSslCipher_from_SSL_CIPHER(const SSL_CIPHER *cipher);
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index 3688e8ffd3..f9e8f01222 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -172,6 +172,7 @@ DEFINEFUNC2(unsigned long, SSL_set_options, SSL *ssl, ssl, unsigned long op, op,
DEFINEFUNC(const SSL_METHOD *, TLS_method, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(const SSL_METHOD *, TLS_client_method, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(const SSL_METHOD *, TLS_server_method, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, X509_up_ref, X509 *a, a, return, DUMMYARG)
DEFINEFUNC(ASN1_TIME *, X509_getm_notBefore, X509 *a, a, return nullptr, return)
DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return nullptr, return)
DEFINEFUNC(long, X509_get_version, X509 *a, a, return -1, return)
@@ -217,8 +218,15 @@ DEFINEFUNC(void, OCSP_CERTID_free, OCSP_CERTID *cid, cid, return, DUMMYARG)
DEFINEFUNC5(int, OCSP_id_get0_info, ASN1_OCTET_STRING **piNameHash, piNameHash, ASN1_OBJECT **pmd, pmd,
ASN1_OCTET_STRING **piKeyHash, piKeyHash, ASN1_INTEGER **pserial, pserial, OCSP_CERTID *cid, cid,
return 0, return)
+DEFINEFUNC2(OCSP_RESPONSE *, OCSP_response_create, int status, status, OCSP_BASICRESP *bs, bs, return nullptr, return)
DEFINEFUNC(const STACK_OF(X509) *, OCSP_resp_get0_certs, const OCSP_BASICRESP *bs, bs, return nullptr, return)
DEFINEFUNC2(int, OCSP_id_cmp, OCSP_CERTID *a, a, OCSP_CERTID *b, b, return -1, return)
+DEFINEFUNC7(OCSP_SINGLERESP *, OCSP_basic_add1_status, OCSP_BASICRESP *r, r, OCSP_CERTID *c, c, int s, s,
+ int re, re, ASN1_TIME *rt, rt, ASN1_TIME *t, t, ASN1_TIME *n, n, return nullptr, return)
+DEFINEFUNC(OCSP_BASICRESP *, OCSP_BASICRESP_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC2(int, i2d_OCSP_RESPONSE, OCSP_RESPONSE *r, r, unsigned char **ppout, ppout, return 0, return)
+DEFINEFUNC6(int, OCSP_basic_sign, OCSP_BASICRESP *br, br, X509 *signer, signer, EVP_PKEY *key, key,
+ const EVP_MD *dg, dg, STACK_OF(X509) *cs, cs, unsigned long flags, flags, return 0, return)
#endif // ocsp
DEFINEFUNC2(void, BIO_set_data, BIO *a, a, void *ptr, ptr, return, DUMMYARG)
@@ -462,6 +470,7 @@ DEFINEFUNC(SSL_CTX *, SSL_CTX_new, const SSL_METHOD *a, a, return nullptr, retur
DEFINEFUNC(SSL_CTX *, SSL_CTX_new, SSL_METHOD *a, a, return nullptr, return)
#endif
DEFINEFUNC2(int, SSL_CTX_set_cipher_list, SSL_CTX *a, a, const char *b, b, return -1, return)
+DEFINEFUNC3(long, SSL_CTX_callback_ctrl, SSL_CTX *ctx, ctx, int dst, dst, GenericCallbackType cb, cb, return 0, return)
DEFINEFUNC(int, SSL_CTX_set_default_verify_paths, SSL_CTX *a, a, return -1, return)
DEFINEFUNC3(void, SSL_CTX_set_verify, SSL_CTX *a, a, int b, b, int (*c)(int, X509_STORE_CTX *), c, return, DUMMYARG)
DEFINEFUNC2(void, SSL_CTX_set_verify_depth, SSL_CTX *a, a, int b, b, return, DUMMYARG)
@@ -527,6 +536,9 @@ DEFINEFUNC(X509 *, X509_dup, X509 *a, a, return nullptr, return)
DEFINEFUNC2(void, X509_print, BIO *a, a, X509 *b, b, return, DUMMYARG);
DEFINEFUNC(ASN1_OBJECT *, X509_EXTENSION_get_object, X509_EXTENSION *a, a, return nullptr, return)
DEFINEFUNC(void, X509_free, X509 *a, a, return, DUMMYARG)
+//Q_AUTOTEST_EXPORT ASN1_TIME *q_X509_gmtime_adj(ASN1_TIME *s, long adj);
+DEFINEFUNC2(ASN1_TIME *, X509_gmtime_adj, ASN1_TIME *s, s, long adj, adj, return nullptr, return)
+DEFINEFUNC(void, ASN1_TIME_free, ASN1_TIME *t, t, return, DUMMYARG)
DEFINEFUNC2(X509_EXTENSION *, X509_get_ext, X509 *a, a, int b, b, return nullptr, return)
DEFINEFUNC(int, X509_get_ext_count, X509 *a, a, return 0, return)
DEFINEFUNC4(void *, X509_get_ext_d2i, X509 *a, a, int b, b, int *c, c, int *d, d, return nullptr, return)
@@ -606,6 +618,7 @@ DEFINEFUNC2(void, BIO_clear_flags, BIO *b, b, int flags, flags, return, DUMMYARG
DEFINEFUNC2(void *, BIO_get_ex_data, BIO *b, b, int idx, idx, return nullptr, return)
DEFINEFUNC3(int, BIO_set_ex_data, BIO *b, b, int idx, idx, void *data, data, return -1, return)
+DEFINEFUNC3(void *, CRYPTO_malloc, size_t num, num, const char *file, file, int line, line, return nullptr, return)
DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return nullptr, return)
DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG)
DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return nullptr, return)
@@ -1010,6 +1023,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(TLS_method)
RESOLVEFUNC(TLS_client_method)
RESOLVEFUNC(TLS_server_method)
+ RESOLVEFUNC(X509_up_ref)
RESOLVEFUNC(X509_STORE_CTX_get0_chain)
RESOLVEFUNC(X509_getm_notBefore)
RESOLVEFUNC(X509_getm_notAfter)
@@ -1059,7 +1073,12 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(OCSP_check_validity)
RESOLVEFUNC(OCSP_cert_to_id)
RESOLVEFUNC(OCSP_id_get0_info)
- RESOLVEFUNC(OCSP_resp_get0_certs);
+ RESOLVEFUNC(OCSP_resp_get0_certs)
+ RESOLVEFUNC(OCSP_basic_sign)
+ RESOLVEFUNC(OCSP_response_create)
+ RESOLVEFUNC(i2d_OCSP_RESPONSE)
+ RESOLVEFUNC(OCSP_basic_add1_status)
+ RESOLVEFUNC(OCSP_BASICRESP_new)
RESOLVEFUNC(OCSP_CERTID_free)
RESOLVEFUNC(OCSP_cert_to_id)
RESOLVEFUNC(OCSP_id_cmp)
@@ -1281,6 +1300,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_CTX_free)
RESOLVEFUNC(SSL_CTX_new)
RESOLVEFUNC(SSL_CTX_set_cipher_list)
+ RESOLVEFUNC(SSL_CTX_callback_ctrl)
RESOLVEFUNC(SSL_CTX_set_default_verify_paths)
RESOLVEFUNC(SSL_CTX_set_verify)
RESOLVEFUNC(SSL_CTX_set_verify_depth)
@@ -1358,6 +1378,8 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(X509_digest)
RESOLVEFUNC(X509_EXTENSION_get_object)
RESOLVEFUNC(X509_free)
+ RESOLVEFUNC(X509_gmtime_adj)
+ RESOLVEFUNC(ASN1_TIME_free)
RESOLVEFUNC(X509_get_ext)
RESOLVEFUNC(X509_get_ext_count)
RESOLVEFUNC(X509_get_ext_d2i)
@@ -1395,6 +1417,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(DTLS_server_method)
RESOLVEFUNC(DTLS_client_method)
#endif // dtls
+ RESOLVEFUNC(CRYPTO_malloc)
RESOLVEFUNC(DH_new)
RESOLVEFUNC(DH_free)
RESOLVEFUNC(d2i_DHparams)
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
index b5aac1c2ae..27e317ab1f 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -281,7 +281,7 @@ const EVP_CIPHER *q_EVP_des_ede3_cbc();
#ifndef OPENSSL_NO_RC2
const EVP_CIPHER *q_EVP_rc2_cbc();
#endif
-const EVP_MD *q_EVP_sha1();
+Q_AUTOTEST_EXPORT const EVP_MD *q_EVP_sha1();
int q_EVP_PKEY_assign(EVP_PKEY *a, int b, char *c);
Q_AUTOTEST_EXPORT int q_EVP_PKEY_set1_RSA(EVP_PKEY *a, RSA *b);
int q_EVP_PKEY_set1_DSA(EVP_PKEY *a, DSA *b);
@@ -289,7 +289,7 @@ int q_EVP_PKEY_set1_DH(EVP_PKEY *a, DH *b);
#ifndef OPENSSL_NO_EC
int q_EVP_PKEY_set1_EC_KEY(EVP_PKEY *a, EC_KEY *b);
#endif
-void q_EVP_PKEY_free(EVP_PKEY *a);
+Q_AUTOTEST_EXPORT void q_EVP_PKEY_free(EVP_PKEY *a);
RSA *q_EVP_PKEY_get1_RSA(EVP_PKEY *a);
DSA *q_EVP_PKEY_get1_DSA(EVP_PKEY *a);
DH *q_EVP_PKEY_get1_DH(EVP_PKEY *a);
@@ -366,6 +366,10 @@ int q_SSL_CTX_set_cipher_list(SSL_CTX *a, const char *b);
int q_SSL_CTX_set_default_verify_paths(SSL_CTX *a);
void q_SSL_CTX_set_verify(SSL_CTX *a, int b, int (*c)(int, X509_STORE_CTX *));
void q_SSL_CTX_set_verify_depth(SSL_CTX *a, int b);
+extern "C" {
+typedef void (*GenericCallbackType)();
+}
+long q_SSL_CTX_callback_ctrl(SSL_CTX *, int, GenericCallbackType);
int q_SSL_CTX_use_certificate(SSL_CTX *a, X509 *b);
int q_SSL_CTX_use_certificate_file(SSL_CTX *a, const char *b, int c);
int q_SSL_CTX_use_PrivateKey(SSL_CTX *a, EVP_PKEY *b);
@@ -428,7 +432,9 @@ X509 *q_X509_dup(X509 *a);
void q_X509_print(BIO *a, X509*b);
int q_X509_digest(const X509 *x509, const EVP_MD *type, unsigned char *md, unsigned int *len);
ASN1_OBJECT *q_X509_EXTENSION_get_object(X509_EXTENSION *a);
-void q_X509_free(X509 *a);
+Q_AUTOTEST_EXPORT void q_X509_free(X509 *a);
+Q_AUTOTEST_EXPORT ASN1_TIME *q_X509_gmtime_adj(ASN1_TIME *s, long adj);
+Q_AUTOTEST_EXPORT void q_ASN1_TIME_free(ASN1_TIME *t);
X509_EXTENSION *q_X509_get_ext(X509 *a, int b);
int q_X509_get_ext_count(X509 *a);
void *q_X509_get_ext_d2i(X509 *a, int b, int *c, int *d);
@@ -587,17 +593,26 @@ QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime);
#ifndef OPENSSL_NO_TLSEXT
-#define q_SSL_set_tlsext_status_type(ssl, type) q_SSL_ctrl((ssl), SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE, (type), nullptr)
+#define q_SSL_set_tlsext_status_type(ssl, type) \
+ q_SSL_ctrl((ssl), SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE, (type), nullptr)
#endif // OPENSSL_NO_TLSEXT
#if QT_CONFIG(ocsp)
OCSP_RESPONSE *q_d2i_OCSP_RESPONSE(OCSP_RESPONSE **a, const unsigned char **in, long len);
-void q_OCSP_RESPONSE_free(OCSP_RESPONSE *rs);
+Q_AUTOTEST_EXPORT int q_i2d_OCSP_RESPONSE(OCSP_RESPONSE *r, unsigned char **ppout);
+Q_AUTOTEST_EXPORT OCSP_RESPONSE *q_OCSP_response_create(int status, OCSP_BASICRESP *bs);
+Q_AUTOTEST_EXPORT void q_OCSP_RESPONSE_free(OCSP_RESPONSE *rs);
int q_OCSP_response_status(OCSP_RESPONSE *resp);
OCSP_BASICRESP *q_OCSP_response_get1_basic(OCSP_RESPONSE *resp);
-void q_OCSP_BASICRESP_free(OCSP_BASICRESP *bs);
+Q_AUTOTEST_EXPORT OCSP_SINGLERESP *q_OCSP_basic_add1_status(OCSP_BASICRESP *rsp, OCSP_CERTID *cid,
+ int status, int reason, ASN1_TIME *revtime,
+ ASN1_TIME *thisupd, ASN1_TIME *nextupd);
+Q_AUTOTEST_EXPORT int q_OCSP_basic_sign(OCSP_BASICRESP *brsp, X509 *signer, EVP_PKEY *key, const EVP_MD *dgst,
+ STACK_OF(X509) *certs, unsigned long flags);
+Q_AUTOTEST_EXPORT OCSP_BASICRESP *q_OCSP_BASICRESP_new();
+Q_AUTOTEST_EXPORT void q_OCSP_BASICRESP_free(OCSP_BASICRESP *bs);
int q_OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, X509_STORE *st, unsigned long flags);
int q_OCSP_resp_count(OCSP_BASICRESP *bs);
OCSP_SINGLERESP *q_OCSP_resp_get0(OCSP_BASICRESP *bs, int idx);
@@ -606,15 +621,27 @@ int q_OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, ASN1_GENERAL
int q_OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long nsec, long maxsec);
int q_OCSP_id_get0_info(ASN1_OCTET_STRING **piNameHash, ASN1_OBJECT **pmd, ASN1_OCTET_STRING **pikeyHash,
ASN1_INTEGER **pserial, OCSP_CERTID *cid);
+
const STACK_OF(X509) *q_OCSP_resp_get0_certs(const OCSP_BASICRESP *bs);
-OCSP_CERTID *q_OCSP_cert_to_id(const EVP_MD *dgst, X509 *subject, X509 *issuer);
-void q_OCSP_CERTID_free(OCSP_CERTID *cid);
+Q_AUTOTEST_EXPORT OCSP_CERTID *q_OCSP_cert_to_id(const EVP_MD *dgst, X509 *subject, X509 *issuer);
+Q_AUTOTEST_EXPORT void q_OCSP_CERTID_free(OCSP_CERTID *cid);
int q_OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b);
-#define q_SSL_get_tlsext_status_ocsp_resp(ssl, arg) q_SSL_ctrl(ssl, SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP, 0, arg)
+#define q_SSL_get_tlsext_status_ocsp_resp(ssl, arg) \
+ q_SSL_ctrl(ssl, SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP, 0, arg)
+
+#define q_SSL_CTX_set_tlsext_status_cb(ssl, cb) \
+ q_SSL_CTX_callback_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB, GenericCallbackType(cb))
+
+# define q_SSL_set_tlsext_status_ocsp_resp(ssl, arg, arglen) \
+ q_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP, arglen, arg)
#endif // ocsp
+
+void *q_CRYPTO_malloc(size_t num, const char *file, int line);
+#define q_OPENSSL_malloc(num) q_CRYPTO_malloc(num, "", 0)
+
QT_END_NAMESPACE
#endif
diff --git a/tests/auto/network/ssl/qocsp/certs/alice.crt b/tests/auto/network/ssl/qocsp/certs/alice.crt
new file mode 100644
index 0000000000..02df86a517
--- /dev/null
+++ b/tests/auto/network/ssl/qocsp/certs/alice.crt
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEGDCCAwCgAwIBAgIBAjANBgkqhkiG9w0BAQsFADCBjjELMAkGA1UEBhMCTk8x
+DTALBgNVBAgMBE9zbG8xEjAQBgNVBAcMCU9zbG8gQ2l0eTETMBEGA1UECgwKVGhl
+IFF0IENBMTENMAsGA1UECwwEUVRDTjERMA8GA1UEAwwIY2ExcXQuaW8xJTAjBgkq
+hkiG9w0BCQEWFnRpbXVyLnBvY2hlcHRzb3ZAcXQuaW8wHhcNMTgxMTIyMTEwNjE4
+WhcNMjgxMTE5MTEwNjE4WjCBkjELMAkGA1UEBhMCTk8xDTALBgNVBAgMBE9zbG8x
+EjAQBgNVBAcMCU9zbG8gQ2l0eTEdMBsGA1UECgwUVGhlIEZhbW91cyBBbGljZSBM
+dGQxDTALBgNVBAsMBEdPQUExEjAQBgNVBAMMCWFsaWNlLm9yZzEeMBwGCSqGSIb3
+DQEJARYPYWxpY2VAYWxpY2Uub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAtuGDR9oIEkK57xlxq/xc3u7B1ni4pdoyhf9r+pkgmu591qp2kl3Xcq3W
+Ve5Z553orAUCAExPlKfFV+CYYAedSgsDYlKk8DN+f/n+hkG6Wl2qyFzHgl+mvPwa
+eDqdVMIcDHGhSljALi9AqsN4lbrUhSxiyuPhAwl82WB0EIucmBs1NxSSZgFPRBLG
+Uzy9WvtQFq1qtn795PVIUsNg68qZQ9BvRduOQAr3bg3anoYqytthWnzLWKri2QR4
+Z4Y0mvcbT/PZwhtcFZzDXG3Hvc7k3AroAbWoSghMEgok9TW9grKYkW2d5cpQTP+l
+ptkB6yZ06MY9/uCdYzhm8eu2RgVndwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCG
+SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E
+FgQUQz2PuE4VuqHtZYTvVLr4+0IuHTUwHwYDVR0jBBgwFoAUeBcnAkU7sTqm7i2Q
+vTxwgr0nQ0QwDQYJKoZIhvcNAQELBQADggEBABGGmo1vUAXKQm9kowvUtjDpEIIY
+TpT+KqiUBOgJg5fGn6a63vBn5GMA6eT948ywi9ZU2M9dIXJCM+bdqjXeOtt4bBPZ
+xz6DcBPW9CoTR4CV1muNa95WIXzAHatq3XYG041ddMf41WG7QIdQsojBYEG0IYlv
+PQx+B+m2cu7A04aI2tCS8aUh7Xc9wRilJ+h/FlYFFQzgyEKsd7CFgkyxG/sLyFNH
+skYYk/DLlmaWa+YScHYB5kAk8StoETeMI2LLs7rgJmchi8eAxjLroYDUhQclUjqz
+vlNM+4GvcF5RluyuEXFOZVdmQahkXcyu0Q3yxvsBbnDglmbb2YHPl/blB7w=
+-----END CERTIFICATE-----
diff --git a/tests/auto/network/ssl/qocsp/certs/alice.key b/tests/auto/network/ssl/qocsp/certs/alice.key
new file mode 100644
index 0000000000..6f2666ebde
--- /dev/null
+++ b/tests/auto/network/ssl/qocsp/certs/alice.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC24YNH2ggSQrnv
+GXGr/Fze7sHWeLil2jKF/2v6mSCa7n3WqnaSXddyrdZV7lnnneisBQIATE+Up8VX
+4JhgB51KCwNiUqTwM35/+f6GQbpaXarIXMeCX6a8/Bp4Op1UwhwMcaFKWMAuL0Cq
+w3iVutSFLGLK4+EDCXzZYHQQi5yYGzU3FJJmAU9EEsZTPL1a+1AWrWq2fv3k9UhS
+w2DryplD0G9F245ACvduDdqehirK22FafMtYquLZBHhnhjSa9xtP89nCG1wVnMNc
+bce9zuTcCugBtahKCEwSCiT1Nb2CspiRbZ3lylBM/6Wm2QHrJnToxj3+4J1jOGbx
+67ZGBWd3AgMBAAECggEADbzU+sHDF3QRuYdExbGYXFq9DtpUrIi+gNhWCSYVj+3Y
+YBa//3CzLXcngZ78++wdvUZHBzS0SatspJRHffc0doprP6iLoUuM9hoWZ4lqcT1W
+BeUKS53ZzZp2do+Yn/RQ3RJwFkCidxWvmuRCG6VEL5jM9wa1MWA2E7IuJcwHAFny
+WIByosje5Qrd7eXDuVoqr1hjJ2UxIjIJ8Zgg3EE9wVUyJE3PU1HLz2AefonYRwbL
+XlzNgnj0c9Ti9ejfyon+jTnpslLKtPal2kxyGoKPAngadAhCzqSaCWggACm7R8Ge
+pZ0Y0pV7QReEgjfFd4D3qOqLRQZVJOMDb3vJu2/rsQKBgQDfKjfSpUweBM9li7GS
+xXbDpC3Y8zQ2VY+2SvgYoYiYU/Y6YenxhKM1XDbWZxhxS8GVfCUAESFDOTZcMvdi
+QEbG1uEmuCn1ksvrC2y54rtd8WDppcS0vJxCrU4nZG0v9IjVKp67B8EpBpAQeNb1
+tR6ByT2fLJu5+WU2S7OxqX7uLwKBgQDRyfKvrCQgdJOQlJlHOv1y1hN8WY51A8P/
+JbDXoun3PCPd+JczvFXCUh3ZLXqUEAX2qDOBD1pBM62EN6/A9ukO4mcMd8uYIet6
+nR4nVqXUjWuzXe6eo913lTDQrIOGWpViTc8fnvFlwBPfwzxbZNx38HZbw0L0nT7d
+8TE/JxLROQKBgQC4Kzo4b8vadjPGZLueGbICkQp5IXR0ZrYcRdBrW1vEAn6Q/d84
+PzMFxV1IIXrNfSx8NiC+5mQh+yQ+gJ0iC1OdoxXag1+1V3lMN3h6C4B/bcWB7Rjh
+40m9yRJXdgyZ59/Is8ydIzAosE7SGTelPNy5VR+yrfiySPxbC6x3MR8cZwKBgQCq
+PVTg1bIjXDZ7NvsDYI1XaP07BXmi30FnhXByLFPsOzNn51jbtNNq8zQhjtRP3ojY
+VjolWw4EpykBiCbpUfRiDbtN1NC0TaJHR8S2a4v6ZiCl123R8mu/pKOOUtAQcOWU
+dkvD/zkpNqtqA4axK7H06n9Bi7yDwC7J7/Xkp5KPkQKBgFDprXrXg4zvIsxbXYZ3
+2bCaxyhBXNKcGwtWbbLfJcOwHJPns/abGkYIJ0NbMZX1LwTDfQWmC+8YKKvIlbKG
+S2uk5H4qzupR4XN6YJ7SCHlGv2z0vxVjV7aWc1TME2iZQoBuO1urxPZwHd/euruo
+kluWh1KV5XnWjBSYjZpiXxWl
+-----END PRIVATE KEY-----
diff --git a/tests/auto/network/ssl/qocsp/certs/ca1.crt b/tests/auto/network/ssl/qocsp/certs/ca1.crt
new file mode 100644
index 0000000000..b5ae194fab
--- /dev/null
+++ b/tests/auto/network/ssl/qocsp/certs/ca1.crt
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIID9DCCAtygAwIBAgIJAMQbE3657KDYMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD
+VQQGEwJOTzENMAsGA1UECAwET3NsbzESMBAGA1UEBwwJT3NsbyBDaXR5MRMwEQYD
+VQQKDApUaGUgUXQgQ0ExMQ0wCwYDVQQLDARRVENOMREwDwYDVQQDDAhjYTFxdC5p
+bzElMCMGCSqGSIb3DQEJARYWdGltdXIucG9jaGVwdHNvdkBxdC5pbzAeFw0xODEx
+MjIxMDIxMTNaFw0yODExMTkxMDIxMTNaMIGOMQswCQYDVQQGEwJOTzENMAsGA1UE
+CAwET3NsbzESMBAGA1UEBwwJT3NsbyBDaXR5MRMwEQYDVQQKDApUaGUgUXQgQ0Ex
+MQ0wCwYDVQQLDARRVENOMREwDwYDVQQDDAhjYTFxdC5pbzElMCMGCSqGSIb3DQEJ
+ARYWdGltdXIucG9jaGVwdHNvdkBxdC5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAOCs3AV7sDKHJUJcm7a0OqnShIvoB1qv6UcOmlBmUzGl5GzX90Jz
+7jYJoOPjxjNyRxMOsOReB1ZcSuIAjkdAEfFMaVe6j7qKTJ5ycTVY/fVoxyxsSNuI
+xOJ6RCEjLHcxONEbkN/xI8LMdVko3m4P10r5GxwrgyPvpa87Yq5+XJ1BPWJyKbD7
+Tqpn3dvZUj0/POsMUTT7Q7VXOfDlZj58XWAC6ECTqJauhGFMhiwgqOn2Qo1W0QjV
+DkGqRTdgIAM6Rv2cSRxgnflwW5QZ8kWUV81h/yx4cck/D9TcVxjr3Pvy6aJ/U41u
+d4XJQgwCj4LJi4msw1S0CvZWmz+2BKxcbRsCAwEAAaNTMFEwHQYDVR0OBBYEFHgX
+JwJFO7E6pu4tkL08cIK9J0NEMB8GA1UdIwQYMBaAFHgXJwJFO7E6pu4tkL08cIK9
+J0NEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADp1kqDRcyVG
+BdMge+Il10IjbpzzSjAoZiqiw69V99LiHW9ePbxG4AmliE6Za60GE5PCXOLjJh/5
+efgnIbybbyIOIT9iK4TXWLw2XW+rMY51c0RAxp2h/sc+5CZ0F0I811F5VUHXg2qR
+U7C2zbzqAimN8TBm6FRe7NFQfqLCrsuFJjSc3obrqKQcpvRwxMk6NpkdoemzqLmY
+lrBrTaeVbZ4ix3srVPvXRm9TdiC+JuuFmvulMfe+/wwnhb+dwT3JUC+EIq/Uf5Wb
+g8lvB4ntitL8NLQ2hFGqYuoFNIGs6tRN71ohk+/ONqe9wJhcI9QAruPOvsg+8J0H
+uGooX7PUNHg=
+-----END CERTIFICATE-----
diff --git a/tests/auto/network/ssl/qocsp/certs/ca1.key b/tests/auto/network/ssl/qocsp/certs/ca1.key
new file mode 100644
index 0000000000..4ee080f706
--- /dev/null
+++ b/tests/auto/network/ssl/qocsp/certs/ca1.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDgrNwFe7AyhyVC
+XJu2tDqp0oSL6Adar+lHDppQZlMxpeRs1/dCc+42CaDj48YzckcTDrDkXgdWXEri
+AI5HQBHxTGlXuo+6ikyecnE1WP31aMcsbEjbiMTiekQhIyx3MTjRG5Df8SPCzHVZ
+KN5uD9dK+RscK4Mj76WvO2KuflydQT1icimw+06qZ93b2VI9PzzrDFE0+0O1Vznw
+5WY+fF1gAuhAk6iWroRhTIYsIKjp9kKNVtEI1Q5BqkU3YCADOkb9nEkcYJ35cFuU
+GfJFlFfNYf8seHHJPw/U3FcY69z78umif1ONbneFyUIMAo+CyYuJrMNUtAr2Vps/
+tgSsXG0bAgMBAAECggEBAL1RCwjXw42gEUZM8KzQS0pD6IpXVrMU3ZWReXhb8Kg6
+KDOK+3+UXlpMXLUKfj1lgvxM+cNEdBxSIoszerARDc1s3KseufOui4dL2ZbhSQVc
+Z9BH4lCSe4x3CCeAEvzQjhatirMY51BCpnMdm+fUE07KfwyKobNLQSpZ+Pod4f5i
+oQbOiZYfRfU2quaWIsVb/a5IiUD0gG0KS9O5wX6VigVeFRpOPHT4YCQ1qds4HqQq
+PKQtkLq0mo6beXCfXWrJ5Nc0QOIFlgSAHkeRR7zLK8MlaerwZ5YdeJIWuPM9l9H+
+34FVkHle1rPN6dJf7EPwWxn1PceFe3QYn1GHoiMmXfkCgYEA/U6iQypbLEKLmLbt
+XTvhV1FVDQM42BX+ATNQ8Wro0ybdyzM+d4271uAUGTF1Zvxndv22p+JOmldWveAR
+0iVK4mvrs25ACg27Bz3LiUaQB2OyYrj9M7TLgQ47gYEhwgnsSniFyrMcptNyIfW5
+GoB1N00EKiCvHyWo5LK6kRZt5QcCgYEA4xBOC/Otc9lTp24iSVA8Y7XJ+nlypFtc
+pehf262jH11wEkWskmc9aP/kpxt9fUrDxf3YIOqITR4mMNn184P1WywHAQF7Adfd
+3r5YBMVaanuaMsSuAZOJGyvuk2BE6328IKdE+3emndzXuQdDf0X2TUznwdKe9AzZ
+qadCBLfUpk0CgYAgDKbzIJTQkMrg06RMu5rTVXMRZmr2zDGLLVb8dK5oqO4/G4i3
+z7MIiOmCFoPoN99PauKFc1jGpm5PL96RXC6RX14/IZ/wpbQYQnVSNR9cD/0uCIHg
+3OsytP5KcHA5ANBoy78B2o+xe+dg7JozBDXQfWodem0t37Hy3bpFSTU2WQKBgETY
+qcFn9hydNYcblpvCDz1wXjhq4H7DENlhFseF42LcMuHnbEbLtMwEYrDkXe1CYQ/E
+QubgFcnELXI8dB2M0jT9qXX9m+1YJXanIgr4R8zngz6HcfcaY8TwUhsvYlZAvmzs
+KrdQdR2CW4pHkIijjuWrPs3+7aEz0D9nblX94yU1AoGATVCfQOwmEMFHg33luMMt
+0lTOHsar6g1O5vz0ZPZ1NjJF9Qe3+T7B4n4gq9pLwfi7Ohoa4CDmt0nKmy56dBha
+5LM8mzw+PaH9a3pP93caS6k/X68TLOp7fwvnzP6HTjtis2sdYzVma6ghEF0zRdQr
+6nWMI6Kx2kFaNdzKSHzxP5A=
+-----END PRIVATE KEY-----
diff --git a/tests/auto/network/ssl/qocsp/certs/infbob.key b/tests/auto/network/ssl/qocsp/certs/infbob.key
new file mode 100644
index 0000000000..7878339151
--- /dev/null
+++ b/tests/auto/network/ssl/qocsp/certs/infbob.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDkF2BuPlkQa3Ox
+JxnbQ+gy1mAxaGGzfnmGocraxVSKSssX3zSkeIrWB1kW3diOtIZ1jovYftHGsqoB
+F5k6fN9dr37mFIZnilF6Bq0seYjNUl6I0d7Cf3NJSf7TG5+P+dAyNLN8aXILctY8
+krlSe4ysb++9xgawt2DYL3I0LBd+W27hd2BlfcmB4g2X3zOasjZM8F5HSBxF5e7f
+fVaUmCC8S4jdXUQEbUNFk6HufDwDhP1gMtoGKHusWOdI5O6cQAfUUmRLSfi/jWoe
+KLPafbj7KIcA8+YojOvub2guNpO42h9fc83/gCkkBJhwXNdIQjRUnz6Lu05kDTG9
+X28gbX9TAgMBAAECggEBAN9xCxVUXJmaOb6ciFblIi3TFm6wS62zw0chbgB8eQH0
+nRoonYBVWeSrVBnzf7bkoCe/Wb3fFo+o7KOfQ4spUwOK7SxlhPkfZgu9SJ4d/Obu
+vw8XUTqF8iEkrM6P6/L2DX9xYzcIcSFIARlbvtJPmBJAocHtoRYyvltpt13mp6kt
+/LVYR4qFAWRpPR6rnjlXEjfrE9taHWgIJEjwMj+IcTjnGiS1rX1T/JNhjRxsRJwU
+qxqhYmeenNymyUJxS2B806EcG3UAJu63dK61VXN1dtPhS3FqjeR//GRlmy14n7RW
+ZQAuT9dPB/WzUZVzEcOgTwe/+XsPTSfz7gpaoffgMJECgYEA/43xZRi48Dfp3tdq
+qwwLf6Ya9pB2zb3XE8MUQhxsrygzL5ngZhmr2m0LgGCf5fXa983G6wzA6sOdpvve
+jFAlBZYbWaYnvog9QIFcTj0S6PahGTBaR7PSNzK0UzoHYexYVCkyzQl1O2hktazd
+Cankh/6IlAFkKbUSDqAwc07jNCkCgYEA5H0tNXpcDN6JTgKNe8YHM0GjZB+qGEoL
+7YZbFlANjO9pOPY6JMQ3+DbOoruIH97CIyVYokuH0qRAfjm5LNiVYECFVFZRnGFb
+BNPOPAnPJPISDF66zjW0KLMYCykJAHQ4SpHUPcJ6JnfBr76s/xSbNI9qnhpy3QYI
+ATkqOrP25xsCgYEAy3pjmJGEv5BloM942VSv2yWRFn2UeuELXWrYuIMVbqndh6tH
+50PNeA+XNtK4vktx3Bl2pzTybnrvDkRBwQsXT0lj4Y/Q2X509uWJb6plYiTtxLah
+S7I8UUMIHbR4qFmdQvXCw0sikvjeJ2HKZaVml3ntmZs5+5N3GzolGcrYUXECgYEA
+pPsBnsCoIJ66s7pCIKIfZtI5QT1f20P0EuDVemn5Ls9bwcaAuzV3WGFymKwiISj+
+MtRviFhTTTROYRYa8Be+3A4ad4gQS4M8bmLlYhKPIJUtlQL9jZHXcR/H9578ofhJ
+AQcFIkb/XjFQiC58yX4+hxgbGufsEk2dkAyPwm1ZlQsCgYBTjnraJbYSz1v3MQKx
+fHm9eHki/ODR3lWiCYYnnW3AwRa7AXS4ZiSw78wzkUX2XTJbE6JlEUH4M9DMzr4y
+QBwKmx+3u+Im4WcZ889jo6XrF0X9mXRmY25+gr2ypTbKZjT8FCYcXIgiOxITLXZh
+Bmn7KZcsdaPxSFn05ASEanLNqA==
+-----END PRIVATE KEY-----
diff --git a/tests/auto/network/ssl/qocsp/certs/infbobchain.crt b/tests/auto/network/ssl/qocsp/certs/infbobchain.crt
new file mode 100644
index 0000000000..7ed13c2856
--- /dev/null
+++ b/tests/auto/network/ssl/qocsp/certs/infbobchain.crt
@@ -0,0 +1,49 @@
+-----BEGIN CERTIFICATE-----
+MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQsFADCBjjELMAkGA1UEBhMCTk8x
+DTALBgNVBAgMBE9zbG8xEjAQBgNVBAcMCU9zbG8gQ2l0eTETMBEGA1UECgwKVGhl
+IFF0IENBMTENMAsGA1UECwwEUVRDTjERMA8GA1UEAwwIY2ExcXQuaW8xJTAjBgkq
+hkiG9w0BCQEWFnRpbXVyLnBvY2hlcHRzb3ZAcXQuaW8wHhcNMTgxMTIyMTAyOTM3
+WhcNMjgxMTE5MTAyOTM3WjCBmDELMAkGA1UEBhMCTk8xDTALBgNVBAgMBE9zbG8x
+EjAQBgNVBAcMCU9zbG8gQ2l0eTEkMCIGA1UECgwbVGhlIEluZmFtb3VzIFNuZWFr
+eSBCb2IgTHRkMQwwCgYDVQQLDANCREExEzARBgNVBAMMCmluZmJvYi5jb20xHTAb
+BgkqhkiG9w0BCQEWDmJvYkBpbmZib2IuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEA5Bdgbj5ZEGtzsScZ20PoMtZgMWhhs355hqHK2sVUikrLF980
+pHiK1gdZFt3YjrSGdY6L2H7RxrKqAReZOnzfXa9+5hSGZ4pRegatLHmIzVJeiNHe
+wn9zSUn+0xufj/nQMjSzfGlyC3LWPJK5UnuMrG/vvcYGsLdg2C9yNCwXfltu4Xdg
+ZX3JgeINl98zmrI2TPBeR0gcReXu331WlJggvEuI3V1EBG1DRZOh7nw8A4T9YDLa
+Bih7rFjnSOTunEAH1FJkS0n4v41qHiiz2n24+yiHAPPmKIzr7m9oLjaTuNofX3PN
+/4ApJASYcFzXSEI0VJ8+i7tOZA0xvV9vIG1/UwIDAQABo3sweTAJBgNVHRMEAjAA
+MCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAd
+BgNVHQ4EFgQUloqk6Iihkkcxp85jAzeUPGVmapkwHwYDVR0jBBgwFoAUeBcnAkU7
+sTqm7i2QvTxwgr0nQ0QwDQYJKoZIhvcNAQELBQADggEBAGCdYFNskTzMilRtmw+v
+oJQM3mc6LdYYuADCuh8O/GKaqUnE7V2XnMBYWMN93eeN9VXmK2yAZaQU1J6ruP1S
+pLMzJ8hbQej+sm+XAHVxAtr34KmEC50gIn1cB/sRKxHMombbNl7EK44puFU7q58P
+zBz5lTXXTfA954D/ijEMMSDvIZ25me6vrGPMj1LX/wC6CWadSr9IxAO9HQVQQqwv
+AbbqrCvMSMv633/f1EYU8Q6jhUCTlnin4pXtriOnqi+6MZICaYRCUgV224Rs3OUS
+jmrbOeoaZUpmOVmuoYXWeexe229G2KGiEIgnSBEk5OLFHCeZ8++WJ5/SLHt8MBLc
+O0w=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID9DCCAtygAwIBAgIJAMQbE3657KDYMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD
+VQQGEwJOTzENMAsGA1UECAwET3NsbzESMBAGA1UEBwwJT3NsbyBDaXR5MRMwEQYD
+VQQKDApUaGUgUXQgQ0ExMQ0wCwYDVQQLDARRVENOMREwDwYDVQQDDAhjYTFxdC5p
+bzElMCMGCSqGSIb3DQEJARYWdGltdXIucG9jaGVwdHNvdkBxdC5pbzAeFw0xODEx
+MjIxMDIxMTNaFw0yODExMTkxMDIxMTNaMIGOMQswCQYDVQQGEwJOTzENMAsGA1UE
+CAwET3NsbzESMBAGA1UEBwwJT3NsbyBDaXR5MRMwEQYDVQQKDApUaGUgUXQgQ0Ex
+MQ0wCwYDVQQLDARRVENOMREwDwYDVQQDDAhjYTFxdC5pbzElMCMGCSqGSIb3DQEJ
+ARYWdGltdXIucG9jaGVwdHNvdkBxdC5pbzCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAOCs3AV7sDKHJUJcm7a0OqnShIvoB1qv6UcOmlBmUzGl5GzX90Jz
+7jYJoOPjxjNyRxMOsOReB1ZcSuIAjkdAEfFMaVe6j7qKTJ5ycTVY/fVoxyxsSNuI
+xOJ6RCEjLHcxONEbkN/xI8LMdVko3m4P10r5GxwrgyPvpa87Yq5+XJ1BPWJyKbD7
+Tqpn3dvZUj0/POsMUTT7Q7VXOfDlZj58XWAC6ECTqJauhGFMhiwgqOn2Qo1W0QjV
+DkGqRTdgIAM6Rv2cSRxgnflwW5QZ8kWUV81h/yx4cck/D9TcVxjr3Pvy6aJ/U41u
+d4XJQgwCj4LJi4msw1S0CvZWmz+2BKxcbRsCAwEAAaNTMFEwHQYDVR0OBBYEFHgX
+JwJFO7E6pu4tkL08cIK9J0NEMB8GA1UdIwQYMBaAFHgXJwJFO7E6pu4tkL08cIK9
+J0NEMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADp1kqDRcyVG
+BdMge+Il10IjbpzzSjAoZiqiw69V99LiHW9ePbxG4AmliE6Za60GE5PCXOLjJh/5
+efgnIbybbyIOIT9iK4TXWLw2XW+rMY51c0RAxp2h/sc+5CZ0F0I811F5VUHXg2qR
+U7C2zbzqAimN8TBm6FRe7NFQfqLCrsuFJjSc3obrqKQcpvRwxMk6NpkdoemzqLmY
+lrBrTaeVbZ4ix3srVPvXRm9TdiC+JuuFmvulMfe+/wwnhb+dwT3JUC+EIq/Uf5Wb
+g8lvB4ntitL8NLQ2hFGqYuoFNIGs6tRN71ohk+/ONqe9wJhcI9QAruPOvsg+8J0H
+uGooX7PUNHg=
+-----END CERTIFICATE-----
diff --git a/tests/auto/network/ssl/qocsp/certs/ss1-private.key b/tests/auto/network/ssl/qocsp/certs/ss1-private.key
new file mode 100644
index 0000000000..1c42daaf9f
--- /dev/null
+++ b/tests/auto/network/ssl/qocsp/certs/ss1-private.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC4bXcfIjweShLx
+6jBTKu/i5sXmlwTH9Z4PTzQ1VteKyEIDlnW5ocVWqRgBrvz3NlTFkDKkQXshkXyE
+JyVZFbAPCfGsroZVISpFhUmJbPMBNn3SyGEU+sxWIpOZOKmG5tel6B4Bt5TWsRHL
+mtU8Pv/APsz+i9JhgE25ksGhx16MqvdRv/xNGleF8qe+hDOeiNF3/lNv2hYb/MvD
+1F73FBoDVnqty/VXXOJFb7elLE4ArXsTN/hip42Lbl1guYvnqnTZFhCHwMzRu4qc
+3FTlemumfJpacpRnqVw2TURA5SdpTp9NYIxygEGNY201meNEjAEyg8GeFkAgu99R
+LPQT+rTNAgMBAAECggEAPQEIfCXo2OQLrDWY0onLW7SWFZYyoKngJJRAYrxdA60G
+GQW13zdhfS7ln/jv+B3ioI74EVkPj6T+GQCR3AvOdssFQ+dey93yi5hxIKIHJ4mM
+ySI66qOi34MEa5RQjyzgfCJxeoPtGa7sgfqvOgRkuISNbk11w4abLx0aK5c08TY0
+JdeoWWhATaFZXl782Aw2FwGPTwOIf7GB09BJS3qUqlMT9fowLmWO10jOKkNtvcnT
+2mAqT5cdZG1ffT5+f0JETPCbBPhhyE7VyYEVQfqTkRnEoz3hcZvjx91jD527+CSL
+Qhg7zZu2oakyJQvpHETZ6cgrs7uDEiol7ARANezwyQKBgQDmapxV/qIOd5WFDVXw
+lGt+dsELBBdMhvzr4A9eZdIZiXu48rdFG0XoECo5BKpXa1+ISr2od0U0YODrJrws
+OHxHhlxGjJFs8kFteUPHyEZv6/rvkbA+xc0Uw04NnDRHBLK8VvX7MBWfvTqLN4bK
+sZsMblscRBtEpFpN1fiJgnNASwKBgQDM56lBugtueBV9M4C/JF2is96d14ue3Y4i
+SgMnHY18D3ru+KDuxPYoIs5Yos2vDWK2k8754WZ+WNXokjRoYPiFbeBPpI9NudJs
+BUJz/sLJHjs3a4HrQs3hCuufczNxq9wQnALQHCMEqeBUTYCu/1+zYgwAu3Z/R8rJ
+jKgexl+gRwKBgGrLCNCWpze7VzKGvsk1kSjZE5nueHoAqqMMgzMGUD2DyjMrU6QV
+Au6O53Lr5aOE4Y9CzOqS9SFUsYprtpVsTLW94XDVX+W11ntN1At5mKPxJKn6xUwi
+022HI9sNBfHQjKLcTz/vxmX2B3dU8gVqEenOEC5mppjG8A/ZV0ssigxHAoGAfGsG
+OSSwoElGMxm8yVNZj9vMBufEnZhGH8f1FiE5seTsboKFpbXvCfvoc6WXYv2rvNUP
+TmdxBrMGYAu2ytJm1Q4cr/9qDHYSsQiYizpcKCa1KjebUbDktgsde1pGGHWUUHmK
+s7cCBGjqEAZnZtslzxRv2Vn639pF5hAEXXtywS0CgYAUIjhp43qgtbQdZMX7xbVR
+lT26aq7NguCtt7njpgkhqc0HThb3I8ImrhNSDcS0/T9dPU70vt0ceruyRXmwX5hA
+l28i5GzF5ufaRQdcsSR9u+P67nD5sTZBesbejXFySis5EC/97A4XZvkSfY4DQSZ+
+u8JJPZUlb2kGAHRpmxvpDA==
+-----END PRIVATE KEY-----
diff --git a/tests/auto/network/ssl/qocsp/certs/ss1.crt b/tests/auto/network/ssl/qocsp/certs/ss1.crt
new file mode 100644
index 0000000000..43ca8316c2
--- /dev/null
+++ b/tests/auto/network/ssl/qocsp/certs/ss1.crt
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEKzCCAxOgAwIBAgIJAOO/b5uLSmT8MA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD
+VQQGEwJOTzENMAsGA1UECAwET3NsbzENMAsGA1UEBwwET3NsbzESMBAGA1UECgwJ
+VGhlIFF0IENBMScwJQYDVQQLDB5SJkQgKGZha2UgY2VydGlmaWNhdGVzIGlzc3Vl
+cikxGTAXBgNVBAMMEFRpbXVyIFBvY2hlcHRzb3YxJTAjBgkqhkiG9w0BCQEWFnRp
+bXVyLnBvY2hlcHRzb3ZAcXQuaW8wIBcNMTgxMTE3MDUxNDA1WhgPMjExODEwMjQw
+NTE0MDVaMIGqMQswCQYDVQQGEwJOTzENMAsGA1UECAwET3NsbzENMAsGA1UEBwwE
+T3NsbzESMBAGA1UECgwJVGhlIFF0IENBMScwJQYDVQQLDB5SJkQgKGZha2UgY2Vy
+dGlmaWNhdGVzIGlzc3VlcikxGTAXBgNVBAMMEFRpbXVyIFBvY2hlcHRzb3YxJTAj
+BgkqhkiG9w0BCQEWFnRpbXVyLnBvY2hlcHRzb3ZAcXQuaW8wggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQC4bXcfIjweShLx6jBTKu/i5sXmlwTH9Z4PTzQ1
+VteKyEIDlnW5ocVWqRgBrvz3NlTFkDKkQXshkXyEJyVZFbAPCfGsroZVISpFhUmJ
+bPMBNn3SyGEU+sxWIpOZOKmG5tel6B4Bt5TWsRHLmtU8Pv/APsz+i9JhgE25ksGh
+x16MqvdRv/xNGleF8qe+hDOeiNF3/lNv2hYb/MvD1F73FBoDVnqty/VXXOJFb7el
+LE4ArXsTN/hip42Lbl1guYvnqnTZFhCHwMzRu4qc3FTlemumfJpacpRnqVw2TURA
+5SdpTp9NYIxygEGNY201meNEjAEyg8GeFkAgu99RLPQT+rTNAgMBAAGjUDBOMB0G
+A1UdDgQWBBSyHPlJr6BrpwMY7Sxg2R3CpQR7UzAfBgNVHSMEGDAWgBSyHPlJr6Br
+pwMY7Sxg2R3CpQR7UzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBD
+o86xp1WwvX6mYzF94ifZlkq1aDN6/njj2B9fvJCtygfqq6b9BrQJ0hNeqRh8OaIh
+v2YmjbdUaoYguHmUxL+SeS67Sp8QBoSwdU5x0i8ygrigBrbb3myNqN6hGvpGy9E0
+B8PnVDt9DaOCunaMyGNPMLNPVGYULmberGtxV9wilcH4Q6WZrk9IhuyfqeBZtBYM
+IcjV3OKdUv/ggu2IZSN7njKcgr+uyPt0Ymo9GozJSTdnN/E4hsRgzcgzCMf2fxzj
+nGcsDRQ4L1R8p1zDlduxmmk42zGCGz3duFX7dijAxJWirS8Zsea4aooLgDQYT/zI
+8hKd3KC3knLhPcxFKiUg
+-----END CERTIFICATE-----
diff --git a/tests/auto/network/ssl/qocsp/qocsp.pro b/tests/auto/network/ssl/qocsp/qocsp.pro
new file mode 100644
index 0000000000..f4e846f39b
--- /dev/null
+++ b/tests/auto/network/ssl/qocsp/qocsp.pro
@@ -0,0 +1,15 @@
+CONFIG += testcase
+
+SOURCES += tst_qocsp.cpp
+QT = core network network-private testlib
+
+TARGET = tst_qocsp
+
+win32 {
+ CONFIG(debug, debug|release) {
+ DESTDIR = debug
+ } else {
+ DESTDIR = release
+ }
+}
+
diff --git a/tests/auto/network/ssl/qocsp/tst_qocsp.cpp b/tests/auto/network/ssl/qocsp/tst_qocsp.cpp
new file mode 100644
index 0000000000..9716c04bbb
--- /dev/null
+++ b/tests/auto/network/ssl/qocsp/tst_qocsp.cpp
@@ -0,0 +1,823 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2018 The Qt Company Ltd.
+ ** Contact: https://www.qt.io/licensing/
+ **
+ ** This file is part of the test suite of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+ ** Commercial License Usage
+ ** Licensees holding valid commercial Qt licenses may use this file in
+ ** accordance with the commercial license agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and The Qt Company. For licensing terms
+ ** and conditions see https://www.qt.io/terms-conditions. For further
+ ** information use the contact form at https://www.qt.io/contact-us.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU
+ ** General Public License version 3 as published by the Free Software
+ ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+ ** included in the packaging of this file. Please review the following
+ ** information to ensure the GNU General Public License requirements will
+ ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/private/qsslsocket_openssl_symbols_p.h>
+#include <QtNetwork/private/qsslsocket_openssl_p.h>
+
+#include <QtNetwork/qsslcertificate.h>
+#include <QtNetwork/qtcpserver.h>
+#include <QtNetwork/qsslerror.h>
+#include <QtNetwork/qsslkey.h>
+#include <QtNetwork/qssl.h>
+
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qdir.h>
+
+#include <openssl/ocsp.h>
+
+#include <algorithm>
+#include <utility>
+
+// NOTE: the word 'subject' in the code below means the subject of a status request,
+// so in general it's our peer's certificate we are asking about.
+
+using SslError = QT_PREPEND_NAMESPACE(QSslError);
+using VectorOfErrors = QT_PREPEND_NAMESPACE(QVector<SslError>);
+using Latin1String = QT_PREPEND_NAMESPACE(QLatin1String);
+
+Q_DECLARE_METATYPE(SslError)
+Q_DECLARE_METATYPE(VectorOfErrors)
+Q_DECLARE_METATYPE(Latin1String)
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+using OcspResponse = QSharedPointer<OCSP_RESPONSE>;
+using BasicResponse = QSharedPointer<OCSP_BASICRESP>;
+using SingleResponse = QSharedPointer<OCSP_SINGLERESP>;
+using CertId = QSharedPointer<OCSP_CERTID>;
+using EvpKey = QSharedPointer<EVP_PKEY>;
+using Asn1Time = QSharedPointer<ASN1_TIME>;
+using CertificateChain = QList<QSslCertificate>;
+
+using NativeX509Ptr = X509 *;
+
+class X509Stack {
+public:
+ explicit X509Stack(const QList<QSslCertificate> &chain);
+
+ ~X509Stack();
+
+ int size() const;
+ X509 *operator[](int index) const;
+ operator STACK_OF(X509) *() const;
+
+private:
+ OPENSSL_STACK *stack = nullptr;
+
+ Q_DISABLE_COPY(X509Stack)
+};
+
+X509Stack::X509Stack(const QList<QSslCertificate> &chain)
+{
+ if (!chain.size())
+ return;
+
+ stack = q_OPENSSL_sk_new_null();
+ if (!stack)
+ return;
+
+ for (const QSslCertificate &cert : chain) {
+ X509 *nativeCert = NativeX509Ptr(cert.handle());
+ if (!nativeCert)
+ continue;
+ q_OPENSSL_sk_push(stack, nativeCert);
+ q_X509_up_ref(nativeCert);
+ }
+}
+
+X509Stack::~X509Stack()
+{
+ if (stack)
+ q_OPENSSL_sk_pop_free(stack, reinterpret_cast<void(*)(void*)>(q_X509_free));
+}
+
+int X509Stack::size() const
+{
+ if (stack)
+ return q_OPENSSL_sk_num(stack);
+ return 0;
+}
+
+X509 *X509Stack::operator[](int index) const
+{
+ return NativeX509Ptr(q_OPENSSL_sk_value(stack, index));
+}
+
+X509Stack::operator STACK_OF(X509) *() const
+{
+ return reinterpret_cast<STACK_OF(X509)*>(stack);
+}
+
+struct OcspTimeStamp
+{
+ OcspTimeStamp() = default;
+ OcspTimeStamp(long secondsBeforeNow, long secondsAfterNow);
+
+ static Asn1Time timeToAsn1Time(long adjustment);
+
+ Asn1Time thisUpdate;
+ Asn1Time nextUpdate;
+};
+
+OcspTimeStamp::OcspTimeStamp(long secondsBeforeNow, long secondsAfterNow)
+{
+ Asn1Time start = timeToAsn1Time(secondsBeforeNow);
+ Asn1Time end = timeToAsn1Time(secondsAfterNow);
+ if (start.data() && end.data()) {
+ thisUpdate.swap(start);
+ nextUpdate.swap(end);
+ }
+}
+
+Asn1Time OcspTimeStamp::timeToAsn1Time(long adjustment)
+{
+ if (ASN1_TIME *adjusted = q_X509_gmtime_adj(nullptr, adjustment))
+ return Asn1Time(adjusted, q_ASN1_TIME_free);
+ return Asn1Time{};
+}
+
+struct OcspResponder
+{
+ OcspResponder(const OcspTimeStamp &stamp, const CertificateChain &subjs,
+ const CertificateChain &respChain, const QSslKey &respPKey);
+
+ QByteArray buildResponse(int responseStatus, int certificateStatus) const;
+ static EvpKey privateKeyToEVP_PKEY(const QSslKey &privateKey);
+ static CertId certificateToCertId(X509 *subject, X509 *issuer);
+ static QByteArray responseToDer(OCSP_RESPONSE *response);
+
+ OcspTimeStamp timeStamp;
+ // Plural, we can send a 'wrong' BasicResponse containing more than
+ // 1 SingleResponse.
+ X509Stack subjects;
+ X509Stack responderChain;
+ QSslKey responderKey;
+};
+
+OcspResponder::OcspResponder(const OcspTimeStamp &stamp, const CertificateChain &subjs,
+ const CertificateChain &respChain, const QSslKey &respPKey)
+ : timeStamp(stamp),
+ subjects(subjs),
+ responderChain(respChain),
+ responderKey(respPKey)
+{
+}
+
+QByteArray OcspResponder::buildResponse(int responseStatus, int certificateStatus) const
+{
+ if (responseStatus != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ OCSP_RESPONSE *response = q_OCSP_response_create(responseStatus, nullptr);
+ if (!response)
+ return {};
+ const OcspResponse rGuard(response, q_OCSP_RESPONSE_free);
+ return responseToDer(response);
+ }
+
+ Q_ASSERT(subjects.size() && responderChain.size() && responderKey.handle());
+
+ const EvpKey nativeKey = privateKeyToEVP_PKEY(responderKey);
+ if (!nativeKey.data())
+ return {};
+
+ OCSP_BASICRESP *basicResponse = q_OCSP_BASICRESP_new();
+ if (!basicResponse)
+ return {};
+ const BasicResponse brGuard(basicResponse, q_OCSP_BASICRESP_free);
+
+ for (int i = 0, e = subjects.size(); i < e; ++i) {
+ X509 *subject = subjects[i];
+ Q_ASSERT(subject);
+ CertId certId = certificateToCertId(subject, responderChain[0]);
+ if (!certId.data())
+ return {};
+
+ // NOTE: we do not own this 'singleResponse':
+ ASN1_TIME *revisionTime = certificateStatus == V_OCSP_CERTSTATUS_REVOKED ?
+ timeStamp.thisUpdate.data() : nullptr;
+
+ if (!q_OCSP_basic_add1_status(basicResponse, certId.data(), certificateStatus, 0, revisionTime,
+ timeStamp.thisUpdate.data(), timeStamp.nextUpdate.data())) {
+ return {};
+ }
+ }
+
+ if (q_OCSP_basic_sign(basicResponse, responderChain[0], nativeKey.data(), q_EVP_sha1(),
+ responderChain, 0) != 1) {
+ return {};
+ }
+
+ OCSP_RESPONSE *ocspResponse = q_OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, basicResponse);
+ if (!ocspResponse)
+ return {};
+ const OcspResponse rGuard(ocspResponse, q_OCSP_RESPONSE_free);
+ return responseToDer(ocspResponse);
+}
+
+EvpKey OcspResponder::privateKeyToEVP_PKEY(const QSslKey &privateKey)
+{
+ const EvpKey nullKey;
+ if (privateKey.isNull() || privateKey.algorithm() != QSsl::Rsa) {
+ // We use only RSA keys in this auto-test, since we test OCSP only,
+ // not handshake/TLS in general.
+ return nullKey;
+ }
+
+ EVP_PKEY *nativeKey = q_EVP_PKEY_new();
+ if (!nativeKey)
+ return nullKey;
+
+ const EvpKey keyGuard(nativeKey, q_EVP_PKEY_free);
+ if (!q_EVP_PKEY_set1_RSA(nativeKey, reinterpret_cast<RSA *>(privateKey.handle())))
+ return nullKey;
+
+ return keyGuard;
+}
+
+CertId OcspResponder::certificateToCertId(X509 *subject, X509 *issuer)
+{
+ const CertId nullId;
+ if (!subject || !issuer)
+ return nullId;
+
+ const EVP_MD *digest = q_EVP_sha1();
+ if (!digest)
+ return nullId;
+
+ OCSP_CERTID *certId = q_OCSP_cert_to_id(digest, subject, issuer);
+ if (!certId)
+ return nullId;
+
+ return CertId(certId, q_OCSP_CERTID_free);
+}
+
+QByteArray OcspResponder::responseToDer(OCSP_RESPONSE *response)
+{
+ if (!response)
+ return {};
+
+ const int derSize = q_i2d_OCSP_RESPONSE(response, nullptr);
+ if (derSize <= 0)
+ return {};
+
+ QByteArray derData(derSize, Qt::Uninitialized);
+ unsigned char *pData = reinterpret_cast<unsigned char *>(derData.data());
+ const int serializedSize = q_i2d_OCSP_RESPONSE(response, &pData);
+ if (serializedSize != derSize)
+ return {};
+
+ return derData;
+}
+
+// The QTcpServer capable of sending OCSP status responses.
+class OcspServer : public QTcpServer
+{
+ Q_OBJECT
+
+public:
+ OcspServer(const CertificateChain &serverChain, const QSslKey &privateKey);
+
+ void configureResponse(const QByteArray &responseDer);
+ QString hostName() const;
+ QString peerVerifyName() const;
+
+Q_SIGNALS:
+ void internalServerError();
+
+private:
+ void incomingConnection(qintptr descriptor) override;
+
+public:
+ QSslConfiguration serverConfig;
+ QSslSocket serverSocket;
+};
+
+OcspServer::OcspServer(const CertificateChain &serverChain, const QSslKey &privateKey)
+{
+ Q_ASSERT(serverChain.size());
+ Q_ASSERT(!privateKey.isNull());
+
+ serverConfig = QSslConfiguration::defaultConfiguration();
+ serverConfig.setLocalCertificateChain(serverChain);
+ serverConfig.setPrivateKey(privateKey);
+}
+
+void OcspServer::configureResponse(const QByteArray &responseDer)
+{
+ serverConfig.setBackendConfigurationOption("Qt-OCSP-response", responseDer);
+}
+
+QString OcspServer::hostName() const
+{
+ // It's 'name' and not 'address' to be consistent with QSslSocket's naming style,
+ // where it's connectToHostEncrypted(hostName, ...)
+ const QHostAddress &addr = serverAddress();
+ if (addr == QHostAddress::Any || addr == QHostAddress::AnyIPv4)
+ return QStringLiteral("127.0.0.1");
+ if (addr == QHostAddress::AnyIPv6)
+ return QStringLiteral("::1");
+ return addr.toString();
+}
+
+QString OcspServer::peerVerifyName() const
+{
+ const CertificateChain &localChain = serverConfig.localCertificateChain();
+ if (localChain.isEmpty())
+ return {};
+ const auto cert = localChain.first();
+ if (cert.isNull())
+ return {};
+
+ const QStringList &names = cert.subjectInfo(QSslCertificate::CommonName);
+ return names.isEmpty() ? QString{} : names.first();
+}
+
+void OcspServer::incomingConnection(qintptr socketDescriptor)
+{
+ close();
+
+ if (!serverSocket.setSocketDescriptor(socketDescriptor)) {
+ emit internalServerError();
+ return;
+ }
+
+ serverSocket.setSslConfiguration(serverConfig);
+ // Since we test a client, not a server, we don't care about any
+ // possible errors on the server (QAbstractSocket or QSslSocket-related).
+ // Thus, we don't connect to any error signal.
+ serverSocket.startServerEncryption();
+}
+
+} // unnamed namespace
+
+class tst_QOcsp : public QObject
+{
+ Q_OBJECT
+
+public slots:
+ void initTestCase();
+
+private slots:
+ void connectSelfSigned();
+ void badStatus_data();
+ void badStatus();
+ void multipleSingleResponses();
+ void malformedResponse();
+ void expiredResponse_data();
+ void expiredResponse();
+ void noNextUpdate();
+ void wrongCertificateInResponse_data();
+ void wrongCertificateInResponse();
+ void untrustedResponder();
+
+ // OCSPTODO: more tests in future ...
+
+private:
+ void setupOcspClient(QSslSocket &clientSocket, const CertificateChain &trustedCAs,
+ const QString &peerName);
+ bool containsOcspErrors(const QList<QSslError> &errorsFound) const;
+ static bool containsError(const QList<QSslError> &errors, QSslError::SslError code);
+ static QByteArray goodResponse(const CertificateChain &subject, const CertificateChain &responder,
+ const QSslKey &privateKey, long beforeNow = -1000, long afterNow = 1000);
+ static bool loadPrivateKey(const QString &keyName, QSslKey &key);
+ static CertificateChain issuerToChain(const CertificateChain &chain);
+ static CertificateChain subjectToChain(const CertificateChain &chain);
+
+ static QString certDirPath;
+
+ void (QSslSocket::*socketErrorSignal)(QAbstractSocket::SocketError) = &QAbstractSocket::error;
+ void (QSslSocket::*tlsErrorsSignal)(const QList<QSslError> &) = &QSslSocket::sslErrors;
+ void (QTestEventLoop::*exitLoopSlot)() = &QTestEventLoop::exitLoop;
+
+ const int handshakeTimeoutMS = 500;
+ QTestEventLoop loop;
+
+ std::vector<QSslError::SslError> ocspErrorCodes = {QSslError::OcspNoResponseFound,
+ QSslError::OcspMalformedRequest,
+ QSslError::OcspMalformedResponse,
+ QSslError::OcspInternalError,
+ QSslError::OcspTryLater,
+ QSslError::OcspSigRequred,
+ QSslError::OcspUnauthorized,
+ QSslError::OcspResponseCannotBeTrusted,
+ QSslError::OcspResponseCertIdUnknown,
+ QSslError::OcspResponseExpired,
+ QSslError::OcspStatusUnknown};
+};
+
+#define QCOMPARE_SINGLE_ERROR(sslSocket, expectedError) \
+ const auto &tlsErrors = sslSocket.sslErrors(); \
+ QCOMPARE(tlsErrors.size(), 1); \
+ QCOMPARE(tlsErrors[0].error(), expectedError)
+
+#define QVERIFY_HANDSHAKE_WITHOUT_ERRORS(sslSocket) \
+ QVERIFY(sslSocket.isEncrypted()); \
+ QCOMPARE(sslSocket.state(), QAbstractSocket::ConnectedState); \
+ QVERIFY(sslSocket.sslErrors().isEmpty())
+
+#define QDECLARE_CHAIN(object, chainFileName) \
+ CertificateChain object = QSslCertificate::fromPath(certDirPath + QLatin1String(chainFileName)); \
+ QVERIFY(object.size())
+
+#define QDECLARE_PRIVATE_KEY(key, keyFileName) \
+ QSslKey key; \
+ QVERIFY(loadPrivateKey(QLatin1String(keyFileName), key))
+
+QString tst_QOcsp::certDirPath;
+
+void tst_QOcsp::initTestCase()
+{
+ QVERIFY(QSslSocket::supportsSsl());
+
+ certDirPath = QFileInfo(QFINDTESTDATA("certs")).absolutePath();
+ QVERIFY(certDirPath.size() > 0);
+ certDirPath += QDir::separator() + QStringLiteral("certs") + QDir::separator();
+}
+
+void tst_QOcsp::connectSelfSigned()
+{
+ // This test may look a bit confusing, since we have essentially 1
+ // self-signed certificate, which we trust for the purpose of this test,
+ // but we also request its (the certificate's) status and then we sign
+ // the status response using the same certificate and the corresponding
+ // private key. Anyway, we test the very basic things here: we send
+ // an OCSP status request, we verify the response (if server has sent it),
+ // and detect errors (if any).
+ QDECLARE_CHAIN(subjectChain, "ss1.crt");
+ QDECLARE_CHAIN(responderChain, "ss1.crt");
+ QDECLARE_PRIVATE_KEY(privateKey, "ss1-private.key");
+ {
+ // This server ignores our status request:
+ const QSslError::SslError expectedError = QSslError::OcspNoResponseFound;
+
+ OcspServer server(subjectChain, privateKey);
+ QVERIFY(server.listen());
+ connect(&server, &OcspServer::internalServerError, &loop, exitLoopSlot);
+
+ QSslSocket clientSocket;
+ QSslConfiguration clientConfig = QSslConfiguration::defaultConfiguration();
+ auto roots = clientConfig.caCertificates();
+ setupOcspClient(clientSocket, issuerToChain(subjectChain), server.peerVerifyName());
+ clientSocket.connectToHostEncrypted(server.hostName(), server.serverPort());
+ loop.enterLoopMSecs(handshakeTimeoutMS);
+
+ QVERIFY(!clientSocket.isEncrypted());
+ QCOMPARE_SINGLE_ERROR(clientSocket, expectedError);
+ }
+ {
+ // Now the server will send a valid 'status: good' response.
+ OcspServer server(subjectChain, privateKey);
+ const QByteArray response(goodResponse(subjectChain, responderChain, privateKey));
+ QVERIFY(response.size());
+ server.configureResponse(response);
+ QVERIFY(server.listen());
+
+ QSslSocket clientSocket;
+ setupOcspClient(clientSocket, issuerToChain(subjectChain), server.peerVerifyName());
+ clientSocket.connectToHostEncrypted(server.hostName(), server.serverPort());
+ loop.enterLoopMSecs(handshakeTimeoutMS);
+
+ QVERIFY_HANDSHAKE_WITHOUT_ERRORS(clientSocket);
+ }
+}
+
+void tst_QOcsp::badStatus_data()
+{
+ QTest::addColumn<int>("responseStatus");
+ QTest::addColumn<int>("certificateStatus");
+ QTest::addColumn<QSslError>("expectedError");
+
+ QTest::addRow("malformed-request") << OCSP_RESPONSE_STATUS_MALFORMEDREQUEST << 1 << QSslError(QSslError::OcspMalformedRequest);
+ QTest::addRow("internal-error") << OCSP_RESPONSE_STATUS_INTERNALERROR << 2 << QSslError(QSslError::OcspInternalError);
+ QTest::addRow("try-later") << OCSP_RESPONSE_STATUS_TRYLATER << 3 << QSslError(QSslError::OcspTryLater);
+ QTest::addRow("signed-request-require") << OCSP_RESPONSE_STATUS_SIGREQUIRED << 2 << QSslError(QSslError::OcspSigRequred);
+ QTest::addRow("unauthorized-request") << OCSP_RESPONSE_STATUS_UNAUTHORIZED << 1 <<QSslError(QSslError::OcspUnauthorized);
+
+ QTest::addRow("certificate-revoked") << OCSP_RESPONSE_STATUS_SUCCESSFUL << V_OCSP_CERTSTATUS_REVOKED
+ << QSslError(QSslError::CertificateRevoked);
+ QTest::addRow("status-unknown") << OCSP_RESPONSE_STATUS_SUCCESSFUL << V_OCSP_CERTSTATUS_UNKNOWN
+ << QSslError(QSslError::OcspStatusUnknown);
+}
+
+void tst_QOcsp::badStatus()
+{
+ // This test works with two types of 'bad' responses:
+ // 1. 'Error messages' (the response's status is anything but SUCCESSFUL,
+ // no information about the certificate itself, no signature);
+ // 2. 'REVOKED' or 'UNKNOWN' status for a certificate in question.
+ QFETCH(const int, responseStatus);
+ QFETCH(const int, certificateStatus);
+ QFETCH(const QSslError, expectedError);
+
+ QDECLARE_CHAIN(subjectChain, "infbobchain.crt");
+ QCOMPARE(subjectChain.size(), 2);
+ QDECLARE_CHAIN(responderChain, "ca1.crt");
+ QDECLARE_PRIVATE_KEY(subjPrivateKey, "infbob.key");
+ QDECLARE_PRIVATE_KEY(respPrivateKey, "ca1.key");
+
+ OcspServer server(subjectChain, subjPrivateKey);
+ const OcspTimeStamp stamp(-1000, 1000);
+ OcspResponder builder(stamp, subjectToChain(subjectChain), responderChain, respPrivateKey);
+ const QByteArray response(builder.buildResponse(responseStatus, certificateStatus));
+ QVERIFY(response.size());
+ server.configureResponse(response);
+ QVERIFY(server.listen());
+ connect(&server, &OcspServer::internalServerError, &loop, exitLoopSlot);
+
+ QSslSocket clientSocket;
+ setupOcspClient(clientSocket, issuerToChain(subjectChain), server.peerVerifyName());
+ clientSocket.connectToHostEncrypted(server.hostName(), server.serverPort());
+ loop.enterLoopMSecs(handshakeTimeoutMS);
+
+ QVERIFY(!clientSocket.isEncrypted());
+ QCOMPARE_SINGLE_ERROR(clientSocket, expectedError.error());
+}
+
+void tst_QOcsp::multipleSingleResponses()
+{
+ // We handle a response with more than one SingleResponse as malformed:
+ const QSslError::SslError expectedError = QSslError::OcspMalformedResponse;
+
+ // Here we use subjectChain only to generate a response, the server
+ // is configured with the responder chain (it's the same cert after all).
+ QDECLARE_CHAIN(subjectChain, "ss1.crt");
+ QDECLARE_CHAIN(responderChain, "ss1.crt");
+ QDECLARE_PRIVATE_KEY(privateKey, "ss1-private.key");
+
+ // Let's have more than 1 certificate in a chain:
+ subjectChain.append(subjectChain[0]);
+
+ OcspServer server(responderChain, privateKey);
+ // Generate a BasicOCSPResponse containing 2 SingleResponses:
+ const QByteArray response(goodResponse(subjectChain, responderChain, privateKey));
+ QVERIFY(response.size());
+ server.configureResponse(response);
+ QVERIFY(server.listen());
+ connect(&server, &OcspServer::internalServerError, &loop, exitLoopSlot);
+
+ QSslSocket clientSocket;
+ setupOcspClient(clientSocket, issuerToChain(responderChain), server.peerVerifyName());
+ clientSocket.connectToHostEncrypted(server.hostName(), server.serverPort());
+ loop.enterLoopMSecs(handshakeTimeoutMS);
+
+ QVERIFY(!clientSocket.isEncrypted());
+ QCOMPARE_SINGLE_ERROR(clientSocket, expectedError);
+}
+
+void tst_QOcsp::malformedResponse()
+{
+ QDECLARE_CHAIN(serverChain, "ss1.crt");
+ QDECLARE_PRIVATE_KEY(privateKey, "ss1-private.key");
+
+ OcspServer server(serverChain, privateKey);
+ // Let's send some arbitrary bytes instead of DER and see what happens next:
+ server.configureResponse("Sure, you can trust me, this cert was not revoked (I don't say it was issued at all)!");
+ QVERIFY(server.listen());
+ connect(&server, &OcspServer::internalServerError, &loop, exitLoopSlot);
+
+ QSslSocket clientSocket;
+ setupOcspClient(clientSocket, issuerToChain(serverChain), server.peerVerifyName());
+ clientSocket.connectToHostEncrypted(server.hostName(), server.serverPort());
+ loop.enterLoopMSecs(handshakeTimeoutMS);
+
+ QVERIFY(!clientSocket.isEncrypted());
+ QCOMPARE(clientSocket.error(), QAbstractSocket::SslHandshakeFailedError);
+}
+
+void tst_QOcsp::expiredResponse_data()
+{
+ QTest::addColumn<long>("beforeNow");
+ QTest::addColumn<long>("afterNow");
+
+ QTest::addRow("expired") << -2000L << -1000L;
+ QTest::addRow("not-valid-yet") << 5000L << 10000L;
+ QTest::addRow("next-before-this") << -1000L << -2000L;
+}
+
+void tst_QOcsp::expiredResponse()
+{
+ // We report different kinds of problems with [thisUpdate, nextUpdate]
+ // as 'expired' (to keep it simple):
+ const QSslError::SslError expectedError = QSslError::OcspResponseExpired;
+
+ QFETCH(const long, beforeNow);
+ QFETCH(const long, afterNow);
+
+ QDECLARE_CHAIN(subjectChain, "ss1.crt");
+ QDECLARE_CHAIN(responderChain, "ss1.crt");
+ QDECLARE_PRIVATE_KEY(privateKey, "ss1-private.key");
+
+ OcspServer server(subjectChain, privateKey);
+ const QByteArray response(goodResponse(subjectChain, responderChain, privateKey, beforeNow, afterNow));
+ QVERIFY(response.size());
+ server.configureResponse(response);
+ QVERIFY(server.listen());
+ connect(&server, &OcspServer::internalServerError, &loop, exitLoopSlot);
+
+ QSslSocket clientSocket;
+ setupOcspClient(clientSocket, issuerToChain(subjectChain), server.peerVerifyName());
+ clientSocket.connectToHostEncrypted(server.hostName(), server.serverPort());
+ loop.enterLoopMSecs(handshakeTimeoutMS);
+
+ QVERIFY(!clientSocket.isEncrypted());
+ QCOMPARE_SINGLE_ERROR(clientSocket, expectedError);
+}
+
+void tst_QOcsp::noNextUpdate()
+{
+ // RFC2560, 2.4:
+ // "If nextUpdate is not set, the responder is indicating that newer
+ // revocation information is available all the time."
+ //
+ // This test is just to verify that we correctly handle such responses.
+ QDECLARE_CHAIN(subjectChain, "ss1.crt");
+ QDECLARE_CHAIN(responderChain, "ss1.crt");
+ QDECLARE_PRIVATE_KEY(privateKey, "ss1-private.key");
+
+ OcspServer server(subjectChain, privateKey);
+ OcspTimeStamp openRange(-1000, 0);
+ openRange.nextUpdate.clear();
+ const OcspResponder responder(openRange, subjectChain, responderChain, privateKey);
+ const QByteArray response(responder.buildResponse(OCSP_RESPONSE_STATUS_SUCCESSFUL,
+ V_OCSP_CERTSTATUS_GOOD));
+ QVERIFY(response.size());
+ server.configureResponse(response);
+ QVERIFY(server.listen());
+ connect(&server, &OcspServer::internalServerError, &loop, exitLoopSlot);
+
+ QSslSocket clientSocket;
+ setupOcspClient(clientSocket, issuerToChain(subjectChain), server.peerVerifyName());
+ clientSocket.connectToHostEncrypted(server.hostName(), server.serverPort());
+ loop.enterLoopMSecs(handshakeTimeoutMS);
+
+ QVERIFY_HANDSHAKE_WITHOUT_ERRORS(clientSocket);
+}
+
+void tst_QOcsp::wrongCertificateInResponse_data()
+{
+ QTest::addColumn<QLatin1String>("respChainName");
+ QTest::addColumn<QLatin1String>("respKeyName");
+ QTest::addColumn<QLatin1String>("wrongChainName");
+
+ QTest::addRow("same-CA-wrong-subject") << QLatin1String("ca1.crt") << QLatin1String("ca1.key")
+ << QLatin1String("alice.crt");
+ QTest::addRow("wrong-CA-same-subject") << QLatin1String("ss1.crt") << QLatin1String("ss1-private.key")
+ << QLatin1String("alice.crt");
+ QTest::addRow("wrong-CA-wrong-subject") << QLatin1String("ss1.crt") << QLatin1String("ss1-private.key")
+ << QLatin1String("ss1.crt");
+}
+
+void tst_QOcsp::wrongCertificateInResponse()
+{
+ QFETCH(const QLatin1String, respChainName);
+ QFETCH(const QLatin1String, respKeyName);
+ QFETCH(const QLatin1String, wrongChainName);
+ // In this test, the server will send a valid response (correctly signed
+ // by a trusted key/cert) but for a wrong certificate (not the one the
+ // server presented to the client in the server's 'Certificate' message).
+ const QSslError::SslError expectedError = QSslError::OcspResponseCertIdUnknown;
+
+ QDECLARE_CHAIN(subjectChain, "infbobchain.crt");
+ QDECLARE_PRIVATE_KEY(subjectKey, "infbob.key");
+ QDECLARE_CHAIN(responderChain, respChainName);
+ QDECLARE_PRIVATE_KEY(responderKey, respKeyName);
+
+ QDECLARE_CHAIN(wrongChain, wrongChainName);
+
+ OcspServer server(subjectToChain(subjectChain), subjectKey);
+ const QByteArray wrongResponse(goodResponse(wrongChain, responderChain, responderKey));
+ QVERIFY(wrongResponse.size());
+ server.configureResponse(wrongResponse);
+ QVERIFY(server.listen());
+ connect(&server, &OcspServer::internalServerError, &loop, exitLoopSlot);
+
+ QSslSocket clientSocket;
+ setupOcspClient(clientSocket, issuerToChain(subjectChain), server.peerVerifyName());
+ clientSocket.connectToHostEncrypted(server.hostName(), server.serverPort());
+ loop.enterLoopMSecs(handshakeTimeoutMS);
+
+ QVERIFY(!clientSocket.isEncrypted());
+ QVERIFY(containsError(clientSocket.sslErrors(), expectedError));
+}
+
+void tst_QOcsp::untrustedResponder()
+{
+ const QSslError::SslError expectedError = QSslError::OcspResponseCannotBeTrusted;
+
+ QDECLARE_CHAIN(subjectChain, "infbobchain.crt");
+ QDECLARE_PRIVATE_KEY(subjectKey, "infbob.key");
+ QDECLARE_CHAIN(responderChain, "ca1.crt");
+ QDECLARE_PRIVATE_KEY(responderKey, "ca1.key");
+
+ OcspServer server(subjectChain, subjectKey);
+ const QByteArray response(goodResponse(subjectToChain(subjectChain), responderChain, responderKey));
+ QVERIFY(response.size());
+ server.configureResponse(response);
+ QVERIFY(server.listen());
+ connect(&server, &OcspServer::internalServerError, &loop, exitLoopSlot);
+
+ QSslSocket clientSocket;
+ setupOcspClient(clientSocket, {}, server.peerVerifyName());
+ clientSocket.connectToHostEncrypted(server.hostName(), server.serverPort());
+ loop.enterLoopMSecs(handshakeTimeoutMS);
+
+ QVERIFY(!clientSocket.isEncrypted());
+ QVERIFY(containsError(clientSocket.sslErrors(), expectedError));
+}
+
+void tst_QOcsp::setupOcspClient(QSslSocket &clientSocket, const CertificateChain &caCerts, const QString &name)
+{
+ QSslConfiguration clientConfig = QSslConfiguration::defaultConfiguration();
+ clientConfig.setOcspStaplingEnabled(true);
+
+ if (caCerts.size()) {
+ auto roots = clientConfig.caCertificates();
+ roots.append(caCerts);
+ clientConfig.setCaCertificates(roots);
+ }
+
+ clientSocket.setSslConfiguration(clientConfig);
+ clientSocket.setPeerVerifyName(name);
+
+ connect(&clientSocket, socketErrorSignal, &loop, exitLoopSlot);
+ connect(&clientSocket, tlsErrorsSignal, &loop, exitLoopSlot);
+ connect(&clientSocket, &QSslSocket::encrypted, &loop, exitLoopSlot);
+}
+
+bool tst_QOcsp::containsOcspErrors(const QList<QSslError> &errorsFound) const
+{
+ for (auto code : ocspErrorCodes) {
+ if (containsError(errorsFound, code))
+ return true;
+ }
+ return false;
+}
+
+bool tst_QOcsp::containsError(const QList<QSslError> &errors, QSslError::SslError code)
+{
+ const auto it = std::find_if(errors.begin(), errors.end(),
+ [&code](const QSslError &other){return other.error() == code;});
+ return it != errors.end();
+}
+
+QByteArray tst_QOcsp::goodResponse(const CertificateChain &subject, const CertificateChain &responder,
+ const QSslKey &privateKey, long beforeNow, long afterNow)
+{
+ const OcspResponder builder(OcspTimeStamp(beforeNow, afterNow), subject, responder, privateKey);
+ return builder.buildResponse(OCSP_RESPONSE_STATUS_SUCCESSFUL, V_OCSP_CERTSTATUS_GOOD);
+}
+
+bool tst_QOcsp::loadPrivateKey(const QString &keyFileName, QSslKey &key)
+{
+ QFile keyFile(certDirPath + keyFileName);
+ if (!keyFile.open(QIODevice::ReadOnly))
+ return false;
+ key = QSslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
+ return !key.isNull();
+}
+
+CertificateChain tst_QOcsp::issuerToChain(const CertificateChain &chain)
+{
+ // Here we presume that, if the chain isn't a single self-signed certificate, its second
+ // entry is the issuer.
+ const int length = chain.size();
+ Q_ASSERT(length > 0);
+ return CertificateChain() << chain[length > 1 ? 1 : 0];
+}
+
+CertificateChain tst_QOcsp::subjectToChain(const CertificateChain &chain)
+{
+ Q_ASSERT(chain.size());
+ return CertificateChain() << chain[0];
+}
+
+QT_END_NAMESPACE
+
+QTEST_MAIN(tst_QOcsp)
+
+#include "tst_qocsp.moc"
diff --git a/tests/auto/network/ssl/ssl.pro b/tests/auto/network/ssl/ssl.pro
index e89443ef4e..169e9bce83 100644
--- a/tests/auto/network/ssl/ssl.pro
+++ b/tests/auto/network/ssl/ssl.pro
@@ -21,6 +21,8 @@ qtConfig(ssl) {
qdtlscookie \
qdtls
}
+
+ qtConfig(ocsp): SUBDIRS += qocsp
}
}