From 451f3b3785ea5d08f5092978b6ebe17f25ef7a88 Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Sat, 18 Jun 2011 15:53:53 +0100 Subject: [PATCH] Add the ability to verify a chain of certificates Currently it is only possible to verify a certificate chain when connecting to a server. This change makes it possible to verify a chain at any time. Change-Id: Ib70ad7b81418f880e995f391b82ce59561ededb8 Merge-request: 11 Reviewed-by: Peter Hartmann Reviewed-on: http://codereview.qt.nokia.com/1509 --- src/network/ssl/qsslcertificate.cpp | 12 ++ src/network/ssl/qsslcertificate.h | 3 + src/network/ssl/qsslsocket_openssl.cpp | 181 ++++++++++++++++++--- src/network/ssl/qsslsocket_openssl_p.h | 2 + src/network/ssl/qsslsocket_openssl_symbols.cpp | 6 + src/network/ssl/qsslsocket_openssl_symbols_p.h | 4 + tests/auto/qsslcertificate/tst_qsslcertificate.cpp | 66 ++++++++ tests/auto/qsslcertificate/verify-certs/README | 2 + tests/auto/qsslcertificate/verify-certs/cacert.pem | 23 +++ .../verify-certs/test-addons-mozilla-org-cert.pem | 34 ++++ .../verify-certs/test-intermediate-ca-cert.pem | 66 ++++++++ .../verify-certs/test-intermediate-is-ca-cert.pem | 53 ++++++ .../verify-certs/test-intermediate-not-ca-cert.pem | 54 ++++++ .../verify-certs/test-ocsp-good-cert.pem | 67 ++++++++ 14 files changed, 547 insertions(+), 26 deletions(-) create mode 100644 tests/auto/qsslcertificate/verify-certs/README create mode 100644 tests/auto/qsslcertificate/verify-certs/cacert.pem create mode 100644 tests/auto/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem create mode 100644 tests/auto/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem create mode 100644 tests/auto/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem create mode 100644 tests/auto/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem create mode 100644 tests/auto/qsslcertificate/verify-certs/test-ocsp-good-cert.pem diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index 839a253..41e54b2 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -121,6 +121,7 @@ #include #include #include +#include #include #include #include @@ -666,6 +667,17 @@ QList QSslCertificate::fromData(const QByteArray &data, QSsl::E : QSslCertificatePrivate::certificatesFromDer(data); } +/*! + Verifies a certificate chain. If a hostName is specified then the certificate is + also checked to see if it is valid for the specified hostName. + Note that the first certificate in the list should be the leaf certificate of + the chain to be verified. + */ +QList QSslCertificate::verify(QList certificateChain, const QString &hostName) +{ + return QSslSocketBackendPrivate::verify(certificateChain, hostName); +} + void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format) { if (!data.isEmpty()) { diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h index 4de84dd..a057d7a 100644 --- a/src/network/ssl/qsslcertificate.h +++ b/src/network/ssl/qsslcertificate.h @@ -62,6 +62,7 @@ QT_MODULE(Network) class QDateTime; class QIODevice; +class QSslError; class QSslKey; class QStringList; template class QMultiMap; @@ -122,6 +123,8 @@ public: static QList fromData( const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem); + static QList verify(QList certificateChain, const QString &hostName = QString()); + Qt::HANDLE handle() const; private: diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index c7e938a..84e5200 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1283,33 +1283,14 @@ bool QSslSocketBackendPrivate::startHandshake() // if we're the server, don't check CN if (mode == QSslSocket::SslClientMode) { QString peerName = (verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName); - QStringList commonNameList = configuration.peerCertificate.subjectInfo(QSslCertificate::CommonName); - bool matched = false; - foreach (const QString &commonName, commonNameList) { - if (isMatchingHostname(commonName.toLower(), peerName.toLower())) { - matched = true; - break; - } - } - - if (!matched) { - foreach (const QString &altName, configuration.peerCertificate - .alternateSubjectNames().values(QSsl::DnsEntry)) { - if (isMatchingHostname(altName.toLower(), peerName.toLower())) { - matched = true; - break; - } - } - } - - if (!matched) { - // No matches in common names or alternate names. - QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate); - errors << error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; + if (!isMatchingHostname(configuration.peerCertificate, peerName)) { + // No matches in common names or alternate names. + QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate); + errors << error; + emit q->peerVerifyError(error); + if (q->state() != QAbstractSocket::ConnectedState) + return false; } } } else { @@ -1444,6 +1425,25 @@ QString QSslSocketBackendPrivate::getErrorsFromOpenSsl() return errorString; } +bool QSslSocketBackendPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName) +{ + QStringList commonNameList = cert.subjectInfo(QSslCertificate::CommonName); + + foreach (const QString &commonName, commonNameList) { + if (isMatchingHostname(commonName.toLower(), peerName.toLower())) { + return true; + } + } + + foreach (const QString &altName, cert.alternateSubjectNames().values(QSsl::DnsEntry)) { + if (isMatchingHostname(altName.toLower(), peerName.toLower())) { + return true; + } + } + + return false; +} + bool QSslSocketBackendPrivate::isMatchingHostname(const QString &cn, const QString &hostname) { int wildcard = cn.indexOf(QLatin1Char('*')); @@ -1484,4 +1484,133 @@ bool QSslSocketBackendPrivate::isMatchingHostname(const QString &cn, const QStri return true; } +QList QSslSocketBackendPrivate::verify(QList certificateChain, const QString &hostName) +{ + QList errors; + if (certificateChain.count() <= 0) { + errors << QSslError(QSslError::UnspecifiedError); + return errors; + } + + // Setup the store with the default CA certificates + X509_STORE *certStore = q_X509_STORE_new(); + if (!certStore) { + qWarning() << "Unable to create certificate store"; + errors << QSslError(QSslError::UnspecifiedError); + return errors; + } + + QList expiredCerts; + + foreach (const QSslCertificate &caCertificate, QSslSocket::defaultCaCertificates()) { + // add expired certs later, so that the + // valid ones are used before the expired ones + if (!caCertificate.isValid()) { + expiredCerts.append(caCertificate); + } else { + q_X509_STORE_add_cert(certStore, (X509 *)caCertificate.handle()); + } + } + + bool addExpiredCerts = true; +#if defined(Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5) + //On Leopard SSL does not work if we add the expired certificates. + if (QSysInfo::MacintoshVersion == QSysInfo::MV_10_5) + addExpiredCerts = false; +#endif + // now add the expired certs + if (addExpiredCerts) { + foreach (const QSslCertificate &caCertificate, expiredCerts) { + q_X509_STORE_add_cert(certStore, (X509 *)caCertificate.handle()); + } + } + + _q_sslErrorList()->mutex.lock(); + + // Register a custom callback to get all verification errors. + X509_STORE_set_verify_cb_func(certStore, q_X509Callback); + + // Build the chain of intermediate certificates + STACK_OF(X509) *intermediates = 0; + if (certificateChain.length() > 1) { + intermediates = (STACK_OF(X509) *) q_sk_new_null(); + + if (!intermediates) { + q_X509_STORE_free(certStore); + errors << QSslError(QSslError::UnspecifiedError); + return errors; + } + + bool first = true; + foreach (const QSslCertificate &cert, certificateChain) { + if (first) { + first = false; + continue; + } +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + q_sk_push( (_STACK *)intermediates, (X509 *)cert.handle()); +#else + q_sk_push( (STACK *)intermediates, (X509 *)cert.handle()); +#endif + } + } + + X509_STORE_CTX *storeContext = q_X509_STORE_CTX_new(); + if (!storeContext) { + q_X509_STORE_free(certStore); + errors << QSslError(QSslError::UnspecifiedError); + return errors; + } + + if (!q_X509_STORE_CTX_init(storeContext, certStore, (X509 *)certificateChain[0].handle(), intermediates)) { + q_X509_STORE_CTX_free(storeContext); + q_X509_STORE_free(certStore); + errors << QSslError(QSslError::UnspecifiedError); + return errors; + } + + // Now we can actually perform the verification of the chain we have built. + // We ignore the result of this function since we process errors via the + // callback. + (void) q_X509_verify_cert(storeContext); + + q_X509_STORE_CTX_free(storeContext); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + q_sk_free( (_STACK *) intermediates); +#else + q_sk_free( (STACK *) intermediates); +#endif + + // Now process the errors + const QList > errorList = _q_sslErrorList()->errors; + _q_sslErrorList()->errors.clear(); + + _q_sslErrorList()->mutex.unlock(); + + // Translate the errors + if (QSslCertificatePrivate::isBlacklisted(certificateChain[0])) { + QSslError error(QSslError::CertificateBlacklisted, certificateChain[0]); + errors << error; + } + + // Check the certificate name against the hostname if one was specified + if ((!hostName.isEmpty()) && (!isMatchingHostname(certificateChain[0], hostName))) { + // No matches in common names or alternate names. + QSslError error(QSslError::HostNameMismatch, certificateChain[0]); + errors << error; + } + + // Translate errors from the error list into QSslErrors. + for (int i = 0; i < errorList.size(); ++i) { + const QPair &errorAndDepth = errorList.at(i); + int err = errorAndDepth.first; + int depth = errorAndDepth.second; + errors << _q_OpenSSL_to_QSslError(err, certificateChain.value(depth)); + } + + q_X509_STORE_free(certStore); + + return errors; +} + QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index 7e489a4..080ad00 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -120,7 +120,9 @@ public: static QSslCipher QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher); static QList STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); + static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName); Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname); + static QList verify(QList certificateChain, const QString &hostName); static QString getErrorsFromOpenSsl(); }; diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 31afab0..be8da0e 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -157,9 +157,13 @@ DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG) DEFINEFUNC(int, sk_num, STACK *a, a, return -1, return) DEFINEFUNC2(void, sk_pop_free, STACK *a, a, void (*b)(void*), b, return, DUMMYARG) #if OPENSSL_VERSION_NUMBER >= 0x10000000L +DEFINEFUNC(_STACK *, sk_new_null, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC2(void, sk_push, _STACK *a, a, void *b, b, return, DUMMYARG) DEFINEFUNC(void, sk_free, _STACK *a, a, return, DUMMYARG) DEFINEFUNC2(void *, sk_value, STACK *a, a, int b, b, return 0, return) #else +DEFINEFUNC(STACK *, sk_new_null, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC2(void, sk_push, STACK *a, a, void *b, b, return, DUMMYARG) DEFINEFUNC(void, sk_free, STACK *a, a, return, DUMMYARG) DEFINEFUNC2(char *, sk_value, STACK *a, a, int b, b, return 0, return) #endif @@ -710,6 +714,8 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(RAND_seed) RESOLVEFUNC(RAND_status) RESOLVEFUNC(RSA_free) + RESOLVEFUNC(sk_new_null) + RESOLVEFUNC(sk_push) RESOLVEFUNC(sk_free) RESOLVEFUNC(sk_num) RESOLVEFUNC(sk_pop_free) diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index cd3aa07..a1db6d9 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -263,9 +263,13 @@ void q_RSA_free(RSA *a); int q_sk_num(STACK *a); void q_sk_pop_free(STACK *a, void (*b)(void *)); #if OPENSSL_VERSION_NUMBER >= 0x10000000L +_STACK *q_sk_new_null(); +void q_sk_push(_STACK *st, void *data); void q_sk_free(_STACK *a); void * q_sk_value(STACK *a, int b); #else +STACK *q_sk_new_null(); +void q_sk_push(STACK *st, void *data); void q_sk_free(STACK *a); char * q_sk_value(STACK *a, int b); #endif diff --git a/tests/auto/qsslcertificate/tst_qsslcertificate.cpp b/tests/auto/qsslcertificate/tst_qsslcertificate.cpp index f12af02..451465d 100644 --- a/tests/auto/qsslcertificate/tst_qsslcertificate.cpp +++ b/tests/auto/qsslcertificate/tst_qsslcertificate.cpp @@ -116,6 +116,7 @@ private slots: void toText(); void multipleCommonNames(); void subjectAndIssuerAttributes(); + void verify(); // ### add tests for certificate bundles (multiple certificates concatenated into a single // structure); both PEM and DER formatted @@ -901,6 +902,71 @@ void tst_QSslCertificate::subjectAndIssuerAttributes() QVERIFY(attributes.contains(QByteArray("1.3.6.1.4.1.311.60.2.1.3"))); } +void tst_QSslCertificate::verify() +{ + QList errors; + QList toVerify; + + // Empty chain is unspecified error + errors = QSslCertificate::verify(toVerify); + QVERIFY(errors.count() == 1); + QVERIFY(errors[0] == QSslError(QSslError::UnspecifiedError)); + errors.clear(); + + // Verify a valid cert signed by a CA + QList caCerts = QSslCertificate::fromPath(SRCDIR "verify-certs/cacert.pem"); + QSslSocket::addDefaultCaCertificate(caCerts.first()); + + toVerify = QSslCertificate::fromPath(SRCDIR "verify-certs/test-ocsp-good-cert.pem"); + + errors = QSslCertificate::verify(toVerify); + QVERIFY(errors.count() == 0); + errors.clear(); + + // Test a blacklisted certificate + toVerify = QSslCertificate::fromPath(SRCDIR "verify-certs/test-addons-mozilla-org-cert.pem"); + errors = QSslCertificate::verify(toVerify); + bool foundBlack = false; + foreach (const QSslError &error, errors) { + if (error.error() == QSslError::CertificateBlacklisted) { + foundBlack = true; + break; + } + } + QVERIFY(foundBlack); + errors.clear(); + + // This one is expired and untrusted + toVerify = QSslCertificate::fromPath(SRCDIR "more-certificates/cert-large-serial-number.pem"); + errors = QSslCertificate::verify(toVerify); + QVERIFY(errors.contains(QSslError(QSslError::SelfSignedCertificate, toVerify[0]))); + QVERIFY(errors.contains(QSslError(QSslError::CertificateExpired, toVerify[0]))); + errors.clear(); + toVerify.clear(); + + // This one is signed by a valid cert, but the signer is not a valid CA + toVerify << QSslCertificate::fromPath(SRCDIR "verify-certs/test-intermediate-not-ca-cert.pem").first(); + toVerify << QSslCertificate::fromPath(SRCDIR "verify-certs/test-ocsp-good-cert.pem").first(); + errors = QSslCertificate::verify(toVerify); + QVERIFY(errors.contains(QSslError(QSslError::InvalidCaCertificate, toVerify[1]))); + toVerify.clear(); + + // This one is signed by a valid cert, and the signer is a valid CA + toVerify << QSslCertificate::fromPath(SRCDIR "verify-certs/test-intermediate-is-ca-cert.pem").first(); + toVerify << QSslCertificate::fromPath(SRCDIR "verify-certs/test-intermediate-ca-cert.pem").first(); + errors = QSslCertificate::verify(toVerify); + QVERIFY(errors.length() == 0); + + // Recheck the above with hostname validation + errors = QSslCertificate::verify(toVerify, QLatin1String("example.com")); + QVERIFY(errors.length() == 0); + + // Recheck the above with a bad hostname + errors = QSslCertificate::verify(toVerify, QLatin1String("fail.example.com")); + QVERIFY(errors.contains(QSslError(QSslError::HostNameMismatch, toVerify[0]))); + toVerify.clear(); +} + #endif // QT_NO_OPENSSL QTEST_MAIN(tst_QSslCertificate) diff --git a/tests/auto/qsslcertificate/verify-certs/README b/tests/auto/qsslcertificate/verify-certs/README new file mode 100644 index 0000000..87cb293 --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/README @@ -0,0 +1,2 @@ +openssl verify -CAfile cacert.pem -untrusted test-intermediate-ca-cert.pem test-intermediate-is-ca-cert.pem +openssl verify -CAfile cacert.pem -untrusted test-ocsp-good-cert.pem test-intermediate-not-ca-cert.pem diff --git a/tests/auto/qsslcertificate/verify-certs/cacert.pem b/tests/auto/qsslcertificate/verify-certs/cacert.pem new file mode 100644 index 0000000..0e06285 --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/cacert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID6zCCAtOgAwIBAgIJALIOhqebAhS9MA0GCSqGSIb3DQEBBQUAMIGrMSYwJAYD +VQQDEx1XZXN0cG9pbnQgQ2VydGlmaWNhdGUgVGVzdCBDQTETMBEGA1UECBMKTGFu +Y2FzaGlyZTELMAkGA1UEBhMCVUsxHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUu +Y29tMUAwPgYDVQQKEzdXZXN0cG9pbnQgQ2VydGlmaWNhdGUgVGVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTExMDYyNjE5MzYzOVoXDTExMDcyNjE5 +MzYzOVowgasxJjAkBgNVBAMTHVdlc3Rwb2ludCBDZXJ0aWZpY2F0ZSBUZXN0IENB +MRMwEQYDVQQIEwpMYW5jYXNoaXJlMQswCQYDVQQGEwJVSzEdMBsGCSqGSIb3DQEJ +ARYOY2FAZXhhbXBsZS5jb20xQDA+BgNVBAoTN1dlc3Rwb2ludCBDZXJ0aWZpY2F0 +ZSBUZXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCoP3znXuxcIAWkyu51aPWwXYX0kSPnBO1gcJj6 +xQa4ycOHv9Cs9XTVTGvpj4aoc6lP+6/jUe14cVCR7018zHRe7u5g4ozO1aZqISqS +Y4hdWkTSFPmFoiyXkACl0ZGwcfv6QdFhNnK4COBrff4D6lndfQUZu8CnRYxlKGuR +1vGiUcJ88t0dDmMEFEdYNtlDnYlxXHbTS4VdRb2u3EGFzV24ENJwgqYuFrBAG/+N +TRXahWMsdfP0whCYJOsaNBwXaoeoxGlYz35gMU8A8AFmYOJLohsWqHcHmMV3X6hn +aKGnL3nOa8zlNKNr948Dwenucaggf5KquDCHVf2Ms+ROxlfTAgMBAAGjEDAOMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGmY4+AeMyx+FkpPxeNY3DAH +Jys0WfndLZFABARYfKdJE1dqQi3uXaRkKMkV7npb46cw92jmwFT+v9rHd88UmgMs +KGJNWjARD6Ai1dzenMNYBJz9GFkDJ0Pr4Gqj2tR9JuzEOdxss+nZ4r6vhC+/yeAB +4jGT4QMuYU+14Rfsv5Aw0HjbcH955zTy1pJ6ck9OWWyzET8ALxz+RTFOok/4r4++ +yhE5Hh8+2aE52AcZqKa4hKXdVBCb9oewl93h3rmYcA/Yz36w+GRkSnOPZUgDDL5D +HKnICDidtf9ZZBZ4iJxaBg4iCraeuei20V+0g+9/1aoIWJ5TrelVYVCx8O0x+E0= +-----END CERTIFICATE----- diff --git a/tests/auto/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem b/tests/auto/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem new file mode 100644 index 0000000..07123e8 --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF+DCCBOCgAwIBAgIRAJI51TSPQNFpWnRUcOHyP0MwDQYJKoZIhvcNAQEFBQAw +gZcxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtl +IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMY +aHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR8wHQYDVQQDExZVVE4tVVNFUkZpcnN0 +LUhhcmR3YXJlMB4XDTExMDMxNTAwMDAwMFoXDTE0MDMxNDIzNTk1OVowgeIxCzAJ +BgNVBAYTAlVTMQ4wDAYDVQQREwUzODQ3NzEQMA4GA1UECBMHRmxvcmlkYTEQMA4G +A1UEBxMHRW5nbGlzaDEXMBUGA1UECRMOU2VhIFZpbGxhZ2UgMTAxFDASBgNVBAoT +C0dvb2dsZSBMdGQuMRMwEQYDVQQLEwpUZWNoIERlcHQuMSgwJgYDVQQLEx9Ib3N0 +ZWQgYnkgR1RJIEdyb3VwIENvcnBvcmF0aW9uMRQwEgYDVQQLEwtQbGF0aW51bVNT +TDEbMBkGA1UEAxMSYWRkb25zLm1vemlsbGEub3JnMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAq8ZtNvMVc3iDc850hdWu7LLw4CQfE4O4IKy7mv6Iu6uh +HQsfRQCqSbc1Nwxq70dMudG+41cSBI2Sx7bsAby22seBOCCtcoXmDvyBbAetaHY4 +xUTXzMZKxZc+ZPRR5vB+suxW9yWCTUmYyxaY3SPxiZHRF5dAmSbW4qIrXt+9ifIb +GlMtzFBBetA9KgxVcBQB6VhJEHoLk4KL4R7tOoAQgs6WijTwzNfTubRQh1VUCbid +QihVAOWMNVS/3SWRRrcN5V2DqOWL+4TkPK522sRDK1t0C/i+XWjxeFu1zn3xXZlA +2sruOIFQvpihbLgkrfOvjA/XESgshBhMfbXZjzC1GwIDAQABo4IB8DCCAewwHwYD +VR0jBBgwFoAUoXJfJhsomEOVXQc31YWWnUvSw0UwHQYDVR0OBBYEFN2A0lQ990xw +yqOw3TR6MuToO1o7MA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1Ud +JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBGBgNVHSAEPzA9MDsGDCsGAQQBsjEB +AgEDBDArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5jb21vZG8uY29tL0NQ +UzB7BgNVHR8EdDByMDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9VVE4t +VVNFUkZpcnN0LUhhcmR3YXJlLmNybDA2oDSgMoYwaHR0cDovL2NybC5jb21vZG8u +bmV0L1VUTi1VU0VSRmlyc3QtSGFyZHdhcmUuY3JsMHEGCCsGAQUFBwEBBGUwYzA7 +BggrBgEFBQcwAoYvaHR0cDovL2NydC5jb21vZG9jYS5jb20vVVROQWRkVHJ1c3RT +ZXJ2ZXJDQS5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNv +bTA1BgNVHREELjAsghJhZGRvbnMubW96aWxsYS5vcmeCFnd3dy5hZGRvbnMubW96 +aWxsYS5vcmcwDQYJKoZIhvcNAQEFBQADggEBADM7YxX8sewULJPddZTegVrZTpm+ ++0qkOVVNoUB63hMqh6k3z+jV+63Re21vjCCHglTmV0m8ICiEzdYB2ZOLF24jZuWE +yIA/xqFwgOTsTR35/JFac2IpmvcgHGHgizmfyrx+jd282bHjn57fFVORIVIL2Roj +D2Y226yTlkqjpSLPKfeimaj2ttlArtl+tvZYLpusNspkj2VS3IacgqtuUEvaX/oF +AIgwDt6NVr+BR409BuKyYpJnj57ImrLlBrhwJLh3fCMKOMN5CNixUZ2slRHHQBee +oxyP8hGnaCfaSQWEGHxYLQFnXOWfoSm7SjlFL78Rqnmi7bTUtWVDt5NGitM= +-----END CERTIFICATE----- diff --git a/tests/auto/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem b/tests/auto/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem new file mode 100644 index 0000000..ab4c2da --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem @@ -0,0 +1,66 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 28 (0x1c) + Signature Algorithm: sha1WithRSAEncryption + Issuer: CN=Westpoint Certificate Test CA, ST=Lancashire, C=UK/emailAddress=ca@example.com, O=Westpoint Certificate Test Root Certification Authority + Validity + Not Before: Jun 26 19:36:42 2011 GMT + Not After : Jun 23 19:36:42 2021 GMT + Subject: ST=Lancashire, C=UK/emailAddress=test@example.com, O=Test intermediate CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:a7:ed:3f:86:ee:ad:f8:b7:60:44:63:fc:1b:2c: + 79:3c:90:8b:47:b8:51:b5:3e:96:6d:5d:f1:97:b3: + de:90:b2:9a:aa:9e:54:7e:2b:b1:6d:91:ae:20:29: + b6:69:7b:8a:e5:6c:41:10:b6:d3:73:4c:20:30:38: + 57:c9:c8:ef:67:7e:76:39:c4:1c:df:6b:73:4e:2b: + 21:2d:79:5a:c5:60:5e:85:11:52:3b:8e:ef:b0:e5: + 2f:0c:e8:a3:fc:05:27:91:08:64:ea:2e:5a:f2:82: + 0c:08:48:bc:bc:ca:60:02:1c:6a:38:eb:c8:02:a6: + f7:e8:c8:31:20:29:e6:e4:8d + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 94:1a:91:6a:05:41:0c:40:f9:46:35:e9:46:15:f1:3e:e4:d9: + 50:d6:0e:14:d8:1f:85:9e:a6:98:a2:db:db:ee:ff:56:55:6c: + 46:fd:0e:b0:03:79:a6:96:0f:c1:85:c3:3e:68:e0:17:10:a6: + 9c:10:34:88:96:f7:c8:ef:32:31:24:f4:3c:2d:eb:51:08:d6: + 87:83:f7:db:68:43:ed:d9:af:46:a2:48:74:d2:9d:c9:af:0f: + 29:42:ad:a2:cf:1e:ab:50:6f:ef:33:18:d3:07:ef:13:13:10: + 50:db:a9:56:db:f4:38:c1:db:05:fa:5a:67:92:72:69:fb:7a: + 5b:ec:d4:dd:fd:a2:21:06:59:b0:0d:48:5f:53:c1:65:94:aa: + d4:4e:1c:e8:9f:b4:7d:9b:10:85:4a:b6:be:df:d8:33:b5:72: + b0:ac:46:a9:67:55:1e:3e:58:a5:52:ed:b6:4a:cb:e9:d2:e5: + f8:fe:56:b4:2c:5e:9f:3c:d1:7f:b4:eb:05:8d:46:1f:47:32: + 2b:4f:2e:ac:8d:c3:3c:eb:f1:0c:2f:3a:e0:fa:46:0b:e4:c5: + f2:03:24:70:fc:ef:1a:fd:7b:a9:9c:d9:b6:4f:0e:74:07:52: + 23:eb:cd:66:61:67:a1:91:7f:76:a2:9d:42:54:d4:c6:5e:10: + 26:eb:37:e7 +-----BEGIN CERTIFICATE----- +MIIDUDCCAjigAwIBAgIBHDANBgkqhkiG9w0BAQUFADCBqzEmMCQGA1UEAxMdV2Vz +dHBvaW50IENlcnRpZmljYXRlIFRlc3QgQ0ExEzARBgNVBAgTCkxhbmNhc2hpcmUx +CzAJBgNVBAYTAlVLMR0wGwYJKoZIhvcNAQkBFg5jYUBleGFtcGxlLmNvbTFAMD4G +A1UEChM3V2VzdHBvaW50IENlcnRpZmljYXRlIFRlc3QgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xMTA2MjYxOTM2NDJaFw0yMTA2MjMxOTM2NDJaMGIx +EzARBgNVBAgTCkxhbmNhc2hpcmUxCzAJBgNVBAYTAlVLMR8wHQYJKoZIhvcNAQkB +FhB0ZXN0QGV4YW1wbGUuY29tMR0wGwYDVQQKExRUZXN0IGludGVybWVkaWF0ZSBD +QTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAp+0/hu6t+LdgRGP8Gyx5PJCL +R7hRtT6WbV3xl7PekLKaqp5UfiuxbZGuICm2aXuK5WxBELbTc0wgMDhXycjvZ352 +OcQc32tzTishLXlaxWBehRFSO47vsOUvDOij/AUnkQhk6i5a8oIMCEi8vMpgAhxq +OOvIAqb36MgxICnm5I0CAwEAAaNLMEkwOQYIKwYBBQUHAQEELTArMCkGCCsGAQUF +BzABhh1odHRwOi8vb2NzcC5leGFtcGxlLmNvbTo4ODg4LzAMBgNVHRMEBTADAQH/ +MA0GCSqGSIb3DQEBBQUAA4IBAQCUGpFqBUEMQPlGNelGFfE+5NlQ1g4U2B+FnqaY +otvb7v9WVWxG/Q6wA3mmlg/BhcM+aOAXEKacEDSIlvfI7zIxJPQ8LetRCNaHg/fb +aEPt2a9Gokh00p3Jrw8pQq2izx6rUG/vMxjTB+8TExBQ26lW2/Q4wdsF+lpnknJp ++3pb7NTd/aIhBlmwDUhfU8FllKrUThzon7R9mxCFSra+39gztXKwrEapZ1UePlil +Uu22Ssvp0uX4/la0LF6fPNF/tOsFjUYfRzIrTy6sjcM86/EMLzrg+kYL5MXyAyRw +/O8a/XupnNm2Tw50B1Ij681mYWehkX92op1CVNTGXhAm6zfn +-----END CERTIFICATE----- diff --git a/tests/auto/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem b/tests/auto/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem new file mode 100644 index 0000000..2794585 --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem @@ -0,0 +1,53 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 29 (0x1d) + Signature Algorithm: sha1WithRSAEncryption + Issuer: ST=Lancashire, C=UK/emailAddress=test@example.com, O=Test intermediate CA + Validity + Not Before: Jun 26 19:36:42 2011 GMT + Not After : Jun 23 19:36:42 2021 GMT + Subject: CN=example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:ba:f2:dc:f9:77:b0:ad:fd:9d:42:4f:22:15:6d: + 87:40:ed:30:8a:04:ad:ac:7a:0b:d4:7a:a4:a7:ef: + e0:e7:9b:f2:5e:62:56:24:ed:88:bd:bd:e3:64:d2: + d4:b4:01:39:b8:9e:6f:c7:b0:fc:b9:fd:a8:4d:46: + c8:9e:6a:43:82:ca:56:83:d4:4b:ea:63:d5:56:d1: + 99:46:4f:8b:28:d0:2f:db:bf:04:65:64:82:c2:61: + aa:66:50:27:e5:7a:57:e3:72:e3:ae:22:8d:92:7e: + 25:90:a2:7c:0c:04:79:c5:ab:64:58:a9:83:79:67: + 7f:72:33:cc:5f:5b:cd:74:bb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + Signature Algorithm: sha1WithRSAEncryption + 19:e8:ce:9d:4d:ef:cc:4f:6c:e5:ab:df:aa:e2:3c:d8:6b:a8: + dd:b8:fa:50:e4:b6:04:fc:66:92:fb:e8:11:73:81:60:1a:88: + b2:18:0b:8c:89:05:47:16:50:09:be:bc:a6:3c:fe:2e:45:01: + 00:e3:27:30:72:f6:93:49:7f:d0:3b:a8:0e:cb:e7:01:95:28: + 8b:40:95:f7:b1:5b:c9:ff:26:ff:ad:4a:c4:e4:99:f7:65:fc: + e4:5e:d2:56:ea:98:42:dc:93:62:46:1a:33:53:0d:43:9d:ef: + 14:03:35:a7:13:fa:27:24:92:2f:9a:f9:0a:62:99:cc:c0:80: + 79:10 +-----BEGIN CERTIFICATE----- +MIICNjCCAZ+gAwIBAgIBHTANBgkqhkiG9w0BAQUFADBiMRMwEQYDVQQIEwpMYW5j +YXNoaXJlMQswCQYDVQQGEwJVSzEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl +LmNvbTEdMBsGA1UEChMUVGVzdCBpbnRlcm1lZGlhdGUgQ0EwHhcNMTEwNjI2MTkz +NjQyWhcNMjEwNjIzMTkzNjQyWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCBnzAN +BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuvLc+Xewrf2dQk8iFW2HQO0wigStrHoL +1Hqkp+/g55vyXmJWJO2Ivb3jZNLUtAE5uJ5vx7D8uf2oTUbInmpDgspWg9RL6mPV +VtGZRk+LKNAv278EZWSCwmGqZlAn5XpX43LjriKNkn4lkKJ8DAR5xatkWKmDeWd/ +cjPMX1vNdLsCAwEAAaNIMEYwCQYDVR0TBAIwADA5BggrBgEFBQcBAQQtMCswKQYI +KwYBBQUHMAGGHWh0dHA6Ly9vY3NwLmV4YW1wbGUuY29tOjg4ODgvMA0GCSqGSIb3 +DQEBBQUAA4GBABnozp1N78xPbOWr36riPNhrqN24+lDktgT8ZpL76BFzgWAaiLIY +C4yJBUcWUAm+vKY8/i5FAQDjJzBy9pNJf9A7qA7L5wGVKItAlfexW8n/Jv+tSsTk +mfdl/ORe0lbqmELck2JGGjNTDUOd7xQDNacT+ickki+a+QpimczAgHkQ +-----END CERTIFICATE----- diff --git a/tests/auto/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem b/tests/auto/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem new file mode 100644 index 0000000..704346c --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem @@ -0,0 +1,54 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 27 (0x1b) + Signature Algorithm: sha1WithRSAEncryption + Issuer: CN=example.com, ST=Lancashire, C=UK/emailAddress=test@example.com, O=Some organisation + Validity + Not Before: Jun 26 19:36:42 2011 GMT + Not After : Jun 23 19:36:42 2021 GMT + Subject: CN=example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:d7:b6:a8:07:83:27:b8:72:da:85:ad:50:bf:c2: + 3f:cc:d4:e7:97:55:b5:45:aa:d2:cb:df:b6:34:6f: + c2:8f:86:e2:15:8a:cd:3e:af:f5:c0:f9:2e:61:80: + 70:17:d3:db:0f:8a:e6:2c:a8:e3:12:2d:92:e1:8d: + 10:e0:e7:30:94:98:ec:b0:21:c3:86:f7:ff:29:58: + 2b:ab:b1:23:e4:ca:66:66:6a:18:b5:73:dc:c7:44: + 04:30:55:bf:f9:3b:74:f6:de:bd:d9:ef:46:b0:15: + 56:3b:43:cc:55:c2:cc:2e:5d:17:f8:04:dc:3d:bf: + 1b:cc:0a:41:61:c8:35:02:1b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + Signature Algorithm: sha1WithRSAEncryption + 18:e2:06:f3:4b:42:46:b6:29:d1:8d:50:ef:5b:8b:e5:33:24: + f3:f1:3e:58:4d:7f:3e:51:e9:c4:a2:cb:64:5f:d1:51:ce:8d: + b6:15:63:23:30:4b:7c:70:30:61:ce:1f:70:bb:99:63:5b:15: + d9:ce:aa:da:65:87:66:ab:ad:64:e8:09:37:ea:79:d0:3a:a2: + e0:cf:0b:1b:a7:35:3d:f8:45:4c:4b:96:15:ec:fe:64:9f:e0: + 1d:04:52:35:a1:b4:7e:31:34:84:7e:e6:e0:58:1e:14:02:df: + 68:f6:b6:eb:dc:10:eb:da:fe:8e:06:ab:52:b7:ca:15:c3:8d: + 5a:8a +-----BEGIN CERTIFICATE----- +MIICSTCCAbKgAwIBAgIBGzANBgkqhkiG9w0BAQUFADB1MRQwEgYDVQQDEwtleGFt +cGxlLmNvbTETMBEGA1UECBMKTGFuY2FzaGlyZTELMAkGA1UEBhMCVUsxHzAdBgkq +hkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20xGjAYBgNVBAoTEVNvbWUgb3JnYW5p +c2F0aW9uMB4XDTExMDYyNjE5MzY0MloXDTIxMDYyMzE5MzY0MlowFjEUMBIGA1UE +AxMLZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANe2qAeD +J7hy2oWtUL/CP8zU55dVtUWq0svftjRvwo+G4hWKzT6v9cD5LmGAcBfT2w+K5iyo +4xItkuGNEODnMJSY7LAhw4b3/ylYK6uxI+TKZmZqGLVz3MdEBDBVv/k7dPbevdnv +RrAVVjtDzFXCzC5dF/gE3D2/G8wKQWHINQIbAgMBAAGjSDBGMAkGA1UdEwQCMAAw +OQYIKwYBBQUHAQEELTArMCkGCCsGAQUFBzABhh1odHRwOi8vb2NzcC5leGFtcGxl +LmNvbTo4ODg4LzANBgkqhkiG9w0BAQUFAAOBgQAY4gbzS0JGtinRjVDvW4vlMyTz +8T5YTX8+UenEostkX9FRzo22FWMjMEt8cDBhzh9wu5ljWxXZzqraZYdmq61k6Ak3 +6nnQOqLgzwsbpzU9+EVMS5YV7P5kn+AdBFI1obR+MTSEfubgWB4UAt9o9rbr3BDr +2v6OBqtSt8oVw41aig== +-----END CERTIFICATE----- diff --git a/tests/auto/qsslcertificate/verify-certs/test-ocsp-good-cert.pem b/tests/auto/qsslcertificate/verify-certs/test-ocsp-good-cert.pem new file mode 100644 index 0000000..1e138ce --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/test-ocsp-good-cert.pem @@ -0,0 +1,67 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha1WithRSAEncryption + Issuer: CN=Westpoint Certificate Test CA, ST=Lancashire, C=UK/emailAddress=ca@example.com, O=Westpoint Certificate Test Root Certification Authority + Validity + Not Before: Jun 26 19:36:39 2011 GMT + Not After : Jun 23 19:36:39 2021 GMT + Subject: CN=example.com, ST=Lancashire, C=UK/emailAddress=test@example.com, O=Some organisation + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:c7:d8:d4:a7:c0:a0:e7:82:b4:ec:67:52:bf:50: + bf:b9:a6:f2:10:19:67:53:8c:99:5e:c8:fc:03:07: + 71:24:a3:a9:c4:49:f8:15:34:4a:45:ee:92:81:aa: + 3c:5a:1a:42:2b:db:d7:30:9e:85:e6:b8:74:bc:ae: + f0:ae:7d:05:4e:c9:0f:00:33:b2:86:ba:b6:49:9a: + 07:18:92:f9:35:69:d2:ac:39:b9:85:ac:78:99:81: + 06:f5:fa:2e:5b:18:f7:6a:16:d0:e6:f9:71:0f:b0: + 05:c4:f0:5f:ed:90:81:3c:96:f5:e3:45:73:72:5f: + ce:dc:ce:0b:56:2e:be:d2:eb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + Signature Algorithm: sha1WithRSAEncryption + 76:84:40:52:b8:1d:6c:64:58:f1:10:30:03:db:16:91:da:b1: + 70:28:54:6f:0f:32:1c:84:65:b3:14:99:c6:59:f9:ec:f9:3f: + 46:1f:12:77:4e:f1:26:ad:46:4b:ee:48:78:bd:bf:2a:11:01: + 5b:02:cd:d2:6e:2d:8c:08:ea:a7:5a:18:16:71:e8:5d:c8:e7: + 7d:f2:4f:23:6c:59:3c:17:93:02:60:c0:d0:62:09:d2:a3:7d: + 90:77:6c:f7:0c:b2:e4:9e:73:d2:f8:dd:a3:0c:70:36:2f:5a: + 58:2b:2d:3e:0e:71:43:b2:14:00:e2:eb:2d:0e:09:91:47:83: + e8:02:d4:7d:5c:1f:ce:d8:f5:ad:1f:ee:82:4f:23:47:db:f4: + 71:48:1b:e1:82:f1:d1:86:db:0f:b6:bb:3a:8f:40:05:48:b7: + f2:a8:c7:c9:46:e1:ea:28:b2:02:00:90:04:00:19:32:6f:8d: + 3e:c2:67:ca:b0:80:3a:32:e3:35:92:18:a2:62:30:9d:7a:f5: + 13:3b:c7:00:3f:4e:17:a9:01:5d:a1:2c:71:76:d7:37:c8:16: + 92:f8:82:69:15:5f:7d:5e:b0:ba:0b:9f:bd:53:ad:e5:a6:b3: + bc:6b:e4:1a:79:29:31:ff:ff:a1:2d:0b:30:46:d3:a5:2d:62: + e6:be:68:df +-----BEGIN CERTIFICATE----- +MIIDYDCCAkigAwIBAgIBATANBgkqhkiG9w0BAQUFADCBqzEmMCQGA1UEAxMdV2Vz +dHBvaW50IENlcnRpZmljYXRlIFRlc3QgQ0ExEzARBgNVBAgTCkxhbmNhc2hpcmUx +CzAJBgNVBAYTAlVLMR0wGwYJKoZIhvcNAQkBFg5jYUBleGFtcGxlLmNvbTFAMD4G +A1UEChM3V2VzdHBvaW50IENlcnRpZmljYXRlIFRlc3QgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xMTA2MjYxOTM2MzlaFw0yMTA2MjMxOTM2MzlaMHUx +FDASBgNVBAMTC2V4YW1wbGUuY29tMRMwEQYDVQQIEwpMYW5jYXNoaXJlMQswCQYD +VQQGEwJVSzEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTEaMBgGA1UE +ChMRU29tZSBvcmdhbmlzYXRpb24wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +AMfY1KfAoOeCtOxnUr9Qv7mm8hAZZ1OMmV7I/AMHcSSjqcRJ+BU0SkXukoGqPFoa +Qivb1zCehea4dLyu8K59BU7JDwAzsoa6tkmaBxiS+TVp0qw5uYWseJmBBvX6LlsY +92oW0Ob5cQ+wBcTwX+2QgTyW9eNFc3JfztzOC1YuvtLrAgMBAAGjSDBGMAkGA1Ud +EwQCMAAwOQYIKwYBBQUHAQEELTArMCkGCCsGAQUFBzABhh1odHRwOi8vb2NzcC5l +eGFtcGxlLmNvbTo4ODg4LzANBgkqhkiG9w0BAQUFAAOCAQEAdoRAUrgdbGRY8RAw +A9sWkdqxcChUbw8yHIRlsxSZxln57Pk/Rh8Sd07xJq1GS+5IeL2/KhEBWwLN0m4t +jAjqp1oYFnHoXcjnffJPI2xZPBeTAmDA0GIJ0qN9kHds9wyy5J5z0vjdowxwNi9a +WCstPg5xQ7IUAOLrLQ4JkUeD6ALUfVwfztj1rR/ugk8jR9v0cUgb4YLx0YbbD7a7 +Oo9ABUi38qjHyUbh6iiyAgCQBAAZMm+NPsJnyrCAOjLjNZIYomIwnXr1EzvHAD9O +F6kBXaEscXbXN8gWkviCaRVffV6wugufvVOt5aazvGvkGnkpMf//oS0LMEbTpS1i +5r5o3w== +-----END CERTIFICATE----- -- 2.7.4