diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c13c13be..5d1f8425 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,6 @@ -list(APPEND srcs PwDialogMock.h importPEM.cpp main.cpp main.h newKey.cpp pem.cpp) +list(APPEND srcs PwDialogMock.h importPEM.cpp main.cpp main.h + newKey.cpp pem.cpp export.cpp) list(TRANSFORM srcs PREPEND ${PROJECT_SOURCE_DIR}/test/) diff --git a/test/PwDialogMock.h b/test/PwDialogMock.h index f3fba8e4..88071d15 100644 --- a/test/PwDialogMock.h +++ b/test/PwDialogMock.h @@ -5,6 +5,8 @@ * All rights reserved. */ +#ifndef __PWDIALOGMOCK_H +#define __PWDIALOGMOCK_H #include #include "lib/debug_info.h" @@ -42,17 +44,27 @@ class PwDialogMock: public PwDialogUI_i pwe->pi.setTitle(p->getTitle()); pwe->pi.setDescription(p->getDescription()); - qWarning() << "PwDialogMock" << p->getDescription() << expect_idx; + qWarning() << "PwDialogMock" << p->getDescription() << expect_idx + << "Password:" << pwe->pass_return; *passwd = pwe->pass_return; return pwe->result; } + + ~PwDialogMock() + { + qDeleteAll(pw_expectations); + } + public: - int expect_idx{}; - QList pw_expectations{}; - void setExpectations(const QList pwe) - { - qDeleteAll(pw_expectations); - pw_expectations = pwe; - expect_idx = 0; - } + + int expect_idx{}; + QList pw_expectations{}; + + void setExpectations(const QList pwe) + { + qDeleteAll(pw_expectations); + pw_expectations = pwe; + expect_idx = 0; + } }; +#endif diff --git a/test/export.cpp b/test/export.cpp new file mode 100644 index 00000000..eafb0954 --- /dev/null +++ b/test/export.cpp @@ -0,0 +1,305 @@ +/* vi: set sw=4 ts=4: + * + * Copyright (C) 2023 Christian Hohnstaedt. + * + * All rights reserved. + */ + +#include + +#include +#include +#include +#include + +#include "lib/pki_multi.h" +#include "lib/db_x509.h" +#include "lib/pki_x509.h" +#include "lib/xfile.h" +#include "lib/database_model.h" + +#include +#include "main.h" + +void check_pems(const QString &name, int n, QStringList matches = QStringList()) +{ + int begin = 0, end = 0; + qWarning() << "Expecting" << n << "PEMs in" << name; + +#if 0 + // This is an endless loop: open_read() succeeds, + // but isOpen returns false. Stop investigating, use POSIX open() + XFile F(name); + while (!F.isOpen()) { + qWarning() << "OPEN" << name; + F.close(); + Q_ASSERT(F.open_read()); + } + QByteArray all = F.readAll(); +#else + int fd = open(qPrintable(name), O_RDONLY); + Q_ASSERT(fd != -1); + char buf[65536]; + ssize_t ret = read(fd, buf, sizeof buf); + Q_ASSERT(ret != -1); + QByteArray all(buf, ret); + close(fd); +#endif + qWarning() << "ALL" << name << all.size(); + + foreach(QByteArray b, all.split('\n')) { + if (b.indexOf("-----BEGIN ") == 0) + begin++; + if (b.indexOf("-----END ") == 0) + end++; + + QMutableStringListIterator i(matches); + while (i.hasNext()) { + QByteArray match = i.next().toUtf8(); + if (b.indexOf(match) != -1) + i.remove(); + } + } + QCOMPARE(begin, n); + QCOMPARE(end, n); + foreach(QString m, matches) { + QWARN(qPrintable(QString("Pattern %1 not found in %2").arg(m).arg(name))); + } + QCOMPARE(matches.size(), 0); +} + +void verify_key(const QString &name, QList hashes, bool priv) +{ + pki_multi *pems = new pki_multi(); + QVERIFY(pems != nullptr); + pems->probeAnything(name); + QCOMPARE(pems->get().size(), hashes.size()); + foreach (pki_base *pki, pems->get()) { + unsigned hash = pki->hash(); + qWarning() << pki->getIntName() << hash; + QVERIFY2(hashes.contains(hash), + qPrintable(QString("%1 not expected in %2") + .arg(pki->getIntName()) + .arg(name) + ) + ); + pki_key *key = dynamic_cast(pki); + if (key) { + QCOMPARE(key->isPrivKey(), priv); + } + } +} + +void verify_file(const QString &name, QList hashes) +{ + verify_key(name, hashes, false); +} + +void export_by_id(int id, const QString &name, + QModelIndexList &list, db_base *db) +{ + const pki_export *xport = pki_export::by_id(id); + QVERIFY(xport != nullptr); + XFile F(name); + F.open_write(); + if (xport->match_all(F_PEM)) { + QString prefix = QString("%1\n").arg(xport->help); + foreach (QModelIndex idx, list) { + pki_base *pki = db_base::fromIndex(idx); + QVERIFY(pki != nullptr); + prefix += QString(" - %1[%2]\n") + .arg(pki->getIntName()) + .arg(pki->getTypeString()); + } + F.write(prefix.toUtf8()); + } + db->exportItems(list, xport, F); + F.close(); +} + +void test_main::exportFormat() +{ + int l=0; + QModelIndex idx; + QModelIndexList list; + + try { + + ign_openssl_error(); + openDB(); + dbstatus(); + pki_multi *pem = new pki_multi(); + QString all = pemdata["Inter CA 1"] + + pemdata["Inter CA 1 Key"] + + pemdata["Root CA"] + + pemdata["Endentity"]; + + pem->fromPEMbyteArray(all.toUtf8(), QString()); + QCOMPARE(pem->failed_files.count(), 0); + Database.insert(pem); + dbstatus(); + + db_base *certs = Database.model(); + QVERIFY(certs != nullptr); + + // Root CA as only item: No chain, no private key + idx = certs->index(certs->getByName("Root CA")); + list << idx; + QCOMPARE(certs->exportFlags(idx) , F_CHAIN | F_PRIVATE); + QCOMPARE(certs->exportFlags(list) , F_CHAIN | F_PRIVATE | F_MULTI); + + // Inter CA 1: All export options permitted + // Together with "Root CA" in "list": No chain, private or single + idx = certs->index(certs->getByName("Inter CA 1")); + list << idx; + QCOMPARE(certs->exportFlags(idx) , 0); + QCOMPARE(certs->exportFlags(list) , F_CHAIN | F_PRIVATE | F_SINGLE); + + // Endentity has no private key and id no CA + idx = certs->index(certs->getByName("Endentity")); + list << idx; + QVERIFY(idx.isValid()); + QCOMPARE(certs->exportFlags(idx) , F_PRIVATE | F_CA); + + pki_key *key = new pki_evp(); + key->fromPEMbyteArray(pemdata["Endentity Key"].toUtf8(), QString()); + openssl_error(); + Database.insert(key); + dbstatus(); + + // Endentity now has a private key, but is still no CA + QCOMPARE(certs->exportFlags(idx) , F_CA); + +#define ROOT_HASH 531145749 +#define INTER_HASH 376625776 +#define END_HASH 94304590 +#define ENDKEY_HASH 1121702347 + +#define xstr(s) str(s) +#define str(s) #s +#define AUTOFILE(type) "_" # type "_Line" xstr(__LINE__) ".data" ; + + const char *file = AUTOFILE(ALLCERT) + // Export All certs in one PEM File + export_by_id(3, file, list, certs); + verify_file(file, QList { ROOT_HASH, INTER_HASH, END_HASH }); + check_pems(file, 3); + // Export 2 cert Chain from Inter CA1 + file = AUTOFILE(CERTCHAIN) + list.clear(); + list << certs->index(certs->getByName("Inter CA 1")); + export_by_id(2, file, list, certs); + verify_file(file, QList { ROOT_HASH, INTER_HASH }); + check_pems(file, 2); + + // Export 3 cert Chain from Endentity + file = AUTOFILE(CERTCHAIN) + list.clear(); + list << certs->index(certs->getByName("Endentity")); + export_by_id(2, file, list, certs); + verify_file(file, QList { ROOT_HASH, INTER_HASH, END_HASH }); + check_pems(file, 3); + + // Export Endentity + corresponding key + file = AUTOFILE(CERTKEY) + export_by_id(6, file, list, certs); + verify_key(file, QList { END_HASH, ENDKEY_HASH }, true); + check_pems(file, 2, QStringList { " RSA PRIVATE KEY-", " CERTIFICATE-" }); + + // Export Endentity + corresponding PKCS#8 key + file = AUTOFILE(CERTPK8) + pwdialog->setExpectations(QList{ + new pw_expect("pass", pw_ok), + new pw_expect("pass", pw_ok), + }); + export_by_id(7, file, list, certs); + verify_key(file, QList { END_HASH, ENDKEY_HASH }, true); + check_pems(file, 2, QStringList { " ENCRYPTED PRIVATE KEY-", " CERTIFICATE-" }); + // Export Endentity as PKCS#7 + file = AUTOFILE(CERTP7) + export_by_id(8, file, list, certs); + verify_file(file, QList { END_HASH }); + check_pems(file, 0); + // Export Endentity as PKCS#7 chain + file = AUTOFILE(CERTP7) + export_by_id(12, file, list, certs); + verify_file(file, QList { ROOT_HASH, INTER_HASH, END_HASH }); + check_pems(file, 0); + // Export Endentity as DER certificate + export_by_id(13, file, list, certs); + verify_file(file, QList { END_HASH }); + check_pems(file, 0); + + // Export Endentity key + list.clear(); + key = dynamic_cast(certs->getByName("Endentity"))->getRefKey(); + db_base *keys = Database.model(); + list << keys->index(key); + + // Public Key + file = AUTOFILE(PUBKEY) + export_by_id(19, file, list, keys); + verify_key(file, QList { ENDKEY_HASH }, false); + check_pems(file, 1, QStringList{ "PUBLIC KEY" }); + + // Private Key + file = AUTOFILE(PRIVKEY) + export_by_id(20, file, list, keys); + verify_key(file, QList { ENDKEY_HASH }, true); + check_pems(file, 1, QStringList{ "RSA PRIVATE KEY" }); + + // Private Key Openssl Encrypted + file = AUTOFILE(PRIVKEY) + pwdialog->setExpectations(QList{ + new pw_expect("pass", pw_ok), + new pw_expect("pass", pw_ok), + }); + export_by_id(21, file, list, keys); + verify_key(file, QList { ENDKEY_HASH }, true); + check_pems(file, 1, QStringList { "DEK-Info: ", "Proc-Type: 4,ENCRYPTED", "BEGIN RSA PRIVATE KEY" }); + + // Private SSH Key + file = AUTOFILE(PRIVSSH) + export_by_id(22, file, list, keys); + verify_key(file, QList { ENDKEY_HASH }, true); + check_pems(file, 1, QStringList{ "RSA PRIVATE KEY" }); + + // Public SSH Key + file = AUTOFILE(PUBSSH) + export_by_id(23, file, list, keys); + verify_key(file, QList { ENDKEY_HASH }, false); + check_pems(file, 0, QStringList{ "ssh-rsa " }); + + // Public DER Key + file = AUTOFILE(PUBDER) + export_by_id(24, file, list, keys); + verify_key(file, QList { ENDKEY_HASH }, false); + check_pems(file, 0); + + // Private DER Key + file = AUTOFILE(PRIVDER) + export_by_id(25, file, list, keys); + verify_key(file, QList { ENDKEY_HASH }, true); + check_pems(file, 0); + + // Private PVK Key + file = AUTOFILE(PVK) + export_by_id(26, file, list, keys); + verify_key(file, QList { ENDKEY_HASH }, true); + check_pems(file, 0); + + // Private PVK Key encrypted + file = AUTOFILE(PVK) + pwdialog->setExpectations(QList{ + new pw_expect("pass", pw_ok), + new pw_expect("pass", pw_ok), + }); + export_by_id(27, file, list, keys); + verify_key(file, QList { ENDKEY_HASH }, true); + check_pems(file, 0); + + } catch (...) { + QString m = QString("Exception thrown L %1").arg(l); + QVERIFY2(false, m.toUtf8().constData()); + } +} diff --git a/test/importPEM.cpp b/test/importPEM.cpp index 49a29624..179b6697 100644 --- a/test/importPEM.cpp +++ b/test/importPEM.cpp @@ -14,39 +14,48 @@ #include "lib/pki_multi.h" -#include "PwDialogMock.h" #include "main.h" void test_main::importPEM() { try { - class PwDialogMock *pwdialog = new PwDialogMock(); - PwDialogCore::setGui(pwdialog); - xcaWarning::setGui(new xcaWarningCore()); ign_openssl_error(); openDB(); + dbstatus(); pki_multi *pem = new pki_multi(); - pem->fromPEMbyteArray(pemdata["interca1"].toUtf8(), QString()); + pem->fromPEMbyteArray(pemdata["Inter CA 1"].toUtf8(), QString()); + pem->fromPEMbyteArray(pemdata["Root CA"].toUtf8(), QString()); + + // Enter a wrong password and then abort pwdialog->setExpectations(QList{ - new pw_expect("Title", pw_ok), + new pw_expect("wrongPassword", pw_ok), }); QVERIFY_EXCEPTION_THROWN(pem->fromPEMbyteArray( - pemdata["interpk8"].toUtf8(), QString()), errorEx); + pemdata["Inter CA 1 EncKey"].toUtf8(), QString()), errorEx); + + // Enter a wrong password and then the correct one pwdialog->setExpectations(QList{ - new pw_expect("pass", pw_ok), + new pw_expect("BadPassword", pw_ok), + new pw_expect("pass", pw_ok), }); - pem->fromPEMbyteArray(pemdata["interpk8"].toUtf8(), QString()); + pem->fromPEMbyteArray(pemdata["Inter CA 1 Key"].toUtf8(), QString()); + QCOMPARE(pem->failed_files.count(), 0); ImportMulti *dlg = new ImportMulti(mainwin); dlg->addItem(pem); dlg->show(); Q_ASSERT(QTest::qWaitForWindowActive(dlg)); + dlg->on_butOk_clicked(); + delete dlg; + QList allitems = Store.getAll(); + QCOMPARE(allitems.count() , 3); } catch (...) { QVERIFY2(false, "Exception thrown"); } + dbstatus(); } diff --git a/test/main.cpp b/test/main.cpp index ed85971a..4b2bec1d 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -6,6 +6,14 @@ */ #include +#include + +#include +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) +#include +#else +#define OSSL_PROVIDER_try_load(a,b,c) do{}while(0) +#endif #include "widgets/MainWindow.h" #include "ui_MainWindow.h" @@ -20,6 +28,8 @@ char segv_data[1024]; void test_main::initTestCase() { + OSSL_PROVIDER_try_load(0, "legacy", 1); + debug_info::init(); entropy = new Entropy; @@ -29,23 +39,46 @@ void test_main::initTestCase() mainwin = new MainWindow(); mainwin->show(); + + pwdialog = new PwDialogMock(); + PwDialogCore::setGui(pwdialog); + + xcaWarning::setGui(new xcaWarningCore()); } void test_main::cleanupTestCase() { - Database.close(); + mainwin->close_database(); delete entropy; delete mainwin; pki_export::free_elements(); + QFile::remove("testdb.xdb"); +} + +void test_main::cleanup() +{ + mainwin->close_database(); + dbstatus(); + QFile::remove("testdb.xdb"); } void test_main::openDB() { - pki_evp::passwd = "pass"; - QString salt = Entropy::makeSalt(); - pki_evp::passHash = pki_evp::sha512passwT(pki_evp::passwd, salt); - Settings["pwhash"] = pki_evp::passHash; - Database.open("testdb.xdb"); + pwdialog->setExpectations(QList{ + new pw_expect("testdbpass", pw_ok), + }); + mainwin->init_database("testdb.xdb"); + dbstatus(); +} + +void test_main::dbstatus() +{ + QList allitems = Store.getAll(); + QStringList out; + foreach(pki_base *p, allitems) + out << QString("%1[%2]").arg(p->getIntName()).arg(p->getTypeString()); + qDebug("%s ALL: %lld %s", Database.isOpen() ? "OPEN" : "CLOSED", + allitems.size(), out.join(", ").toUtf8().constData()); } QTEST_MAIN(test_main) diff --git a/test/main.h b/test/main.h index 92a41e47..d3301f92 100644 --- a/test/main.h +++ b/test/main.h @@ -11,19 +11,25 @@ #include "lib/entropy.h" #include "lib/pki_evp.h" +#include "PwDialogMock.h" + class test_main: public QObject { Q_OBJECT Entropy *entropy {}; + PwDialogMock *pwdialog{}; void openDB(); + void dbstatus(); static const QMap pemdata; private slots: void initTestCase(); void cleanupTestCase(); + void cleanup(); void newKey(); void importPEM(); + void exportFormat(); }; #endif diff --git a/test/pem.cpp b/test/pem.cpp index f4370374..b6944247 100644 --- a/test/pem.cpp +++ b/test/pem.cpp @@ -8,10 +8,10 @@ #include "main.h" const QMap test_main::pemdata = { -{ "RootCA", R"PEM( +{ "Root CA", R"PEM( -----BEGIN CERTIFICATE----- -MIIDGDCCAgCgAwIBAgIIHaqxKU04FSUwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE -AxMHUm9vdCBDQTAeFw0yMzA5MTgxNjQxMDBaFw0zMzA5MTgxNjQxMDBaMBIxEDAO +MIIDGDCCAgCgAwIBAgIIOrvWy5vvDTwwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE +AxMHUm9vdCBDQTAeFw0yMzA5MTkxMzE0MDBaFw0zMzA5MTkxMzE0MDBaMBIxEDAO BgNVBAMTB1Jvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCG ykFQQcbPjwcwY1iXd0U0zTWQA8R2UZCHveyZtza55e55vTRtfeVzAgTxSbmOkgyn TR4S3yM6inZiNcVRFUkhrFPYMFbHUSpw/DykgbxYAUszE+KTx77JZ3A3pNL/wjlr @@ -21,19 +21,19 @@ yGIy7tm1fLYkLIJzwtUz+MSyIWzg/K+82sPQEWGg3EBEj6zpT+01dk0AAGwOyZHz rjgu3ZCjGyRzHlpSFSlNAgMBAAGjcjBwMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O BBYEFGOFvC6Qk0JQDPYJ9tlJOFP30mKZMAsGA1UdDwQEAwIBBjARBglghkgBhvhC AQEEBAMCAAcwHgYJYIZIAYb4QgENBBEWD3hjYSBjZXJ0aWZpY2F0ZTANBgkqhkiG -9w0BAQsFAAOCAQEAWfDIgDR7K8vKdfnAyCYkSX3j8qBjdfuG1yAEGQDgH5N+xBNO -/0Jzq7GfAkv32lmWRwKqUEJ1tv2lhzTDakHldMn4jB+jSPYrMZN4oBjU8EP39Jns -yBixTblnKQeitcYM1U7U4rIPQApemY1C0tmMvT6A0fvslkUhrXIxVt+dTaQVta3w -CabsuzvdC7WeOrT2/g4OlKXs7jvXpNGEm0SY1Y8uOYabVtz6vGeLK7lkzWD78kGJ -qrCpmDuon1hp2wRlDCIvyulQpJT68jSyauOQ5rz29k0EkBOlN4VZiHkI8Zo/tMRc -nv3PQrI5EP7OyrpAZEhav0l25gSzDExzgziqCQ== +9w0BAQsFAAOCAQEAaSU6u95lFkvXxT2kbFvAfJRN1VmVr2GXDjsRQF+DdcSnu7eg +ush8bWWlI9tmQbnmu4Ob8/vBT/Ou+lQ2+BxvBi+6cCCHyK1Lg4zv0KuRb6KYy5On +aZNT6KJRnFggu/IgzDiR5LD/uWUEpOPHjbFuD60pOid6Obj3zcDeqHnN/S76QK+G +MgodESssqwuCWqVt4ACD2/tAKizoiXP85Hx/EtHOY0fKcPqPfcXlVAgMszRgQfsx +0kc3z3mlwPxvFf8V7h0wcjjaIbkeDqsQxfdfeyErSIgRJj7h1rXFOUNLjksg9877 +zul7Vd4O/kY0MN/PmL6no6CtLm7DavZNbYLVQw== -----END CERTIFICATE----- )PEM" }, -{ "interca1", R"PEM( +{ "Inter CA 1", R"PEM( -----BEGIN CERTIFICATE----- -MIIDGzCCAgOgAwIBAgIIOvC5r9Smk3wwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE -AxMHUm9vdCBDQTAeFw0yMzEwMjUxODU1MDBaFw0zMzA5MTgxNjQxMDBaMBUxEzAR +MIIDGzCCAgOgAwIBAgIILcPKkEW8ow0wDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE +AxMHUm9vdCBDQTAeFw0yMzA5MTgxNjQyMDBaFw0zMzA5MTgxNjQyMDBaMBUxEzAR BgNVBAMTCkludGVyIENBIDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB AQDJlExG3+oLO6GFuD4pa9sI5mJL/9fADcoe9bv1SAnRKu/GPtCMaruVTKBAt5x4 mwt5My6WhyPdpYp0z6yUgSCrbkSAMvMzGlS1W5Ke4UU4GaufCRjHeXNVpx9wXEPY @@ -43,46 +43,162 @@ OEa2WPiKhUnBvna5IDyrjjTKUGo5mxi9RVuArZwZ16kdJPG292WOVtQ8uTg391XQ YNvtUhZrXnHU/DzYkbhZrW7VAgMBAAGjcjBwMA8GA1UdEwEB/wQFMAMBAf8wHQYD VR0OBBYEFE5aAJMfa9ksd7718xYlNHF/YSjtMAsGA1UdDwQEAwIBBjARBglghkgB hvhCAQEEBAMCAAcwHgYJYIZIAYb4QgENBBEWD3hjYSBjZXJ0aWZpY2F0ZTANBgkq -hkiG9w0BAQsFAAOCAQEAbUcuBvX6jGBYDFBPn1dK9xEQuexrh7gUtNEEKumDF6vN -Bj6xHxZjjwrJsGeX8aTqS3J5BE8vsv4+xiPWaObalUtmkGrS0x/DAe6ZpzGl/aq7 -tknve6VF9s58SjnefQhf4ko18/sylX0Rp7dliMckM3fcUINe0HgzbO25MqNiTZo4 -/WPhDmvJPxbogzFoHWA/8jh1/EMyxd8JeiEG6SxZE2PL8oY0i52zHDQCFr6S8dyc -OWuGL0dQBu7Oi0f/eKBxWPf8uExTjSj44z6y0y/ioJ4+vvxVJZu9eBY6GtQ3Zjck -HcE+16987H74MazZVpmS1O23RxxT9SSJCTsqQRl58g== +hkiG9w0BAQsFAAOCAQEAd4H25/1Dkt3mqhGI+fepvlskg+FHPQgLNQR7cmJOcYJf +4uHR3MuxXUDQrY4PcFgZQRiUNHrEDtifZHENLCYOSr1xNKX6HzO/b1jpIOvgPgc5 +JJBFziq0ShtP8l+2aXZwgifwxL3wcrqKXqSYJkox+xUvNU5gQ1+ea7zCZoRHp8Vd +3iLNYSkb8QnoffZXr6WLENQjHd3n7ETMTgGnQy0zXhTOAounnkp3uFUa0QUwiwLj +qi43yEB8GCW+5k96q/bPLgYDnGsSGUgdT9Fq+zHWnW0FqycRR5JivqdUkRrkJP/G +9UaWItdzIg7z3JJnWocON2UPXiRCwXIinFOY8B3cZg== -----END CERTIFICATE----- )PEM" }, -{ "interpk8", R"PEM( +{ "Endentity", R"PEM( +-----BEGIN CERTIFICATE----- +MIIDRzCCAi+gAwIBAgIIRnZrxljA1L0wDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE +AxMKSW50ZXIgQ0EgMTAeFw0yMzA5MTgxNjQzMDBaFw0yNDA5MTcxNjQzMDBaMBQx +EjAQBgNVBAMTCUVuZGVudGl0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKaDk8SoilZ3HORi9Mn4p3iFO43ofabLF68XWdnrgtuBZODmLAXinElt62ed +Gfo96yzhSQdB3HG27XDRxxg+oR9XUlU/7B7BjeTJ5LUR03QSQ20VC8yKfD28eTdB +uDtDzWFhcibXqI62+dBfxvTixG0tlkWojXkAEnkcbfDSlFj+6C56T5+HN9rAofsD +pVcCWY2W71creO5Tb5M3ek79bwagjAI8zJOnggxMNRWYBietQHU2ki8cUuo9IOdk +DR3sa87ADAtTkDjS4LPw+w7YQDFoNmeb8y16dbmVtVP0AS6cKvAYaWFzIEC42vL6 +yirlf626+gIfVLxuaUh5n51cD5kCAwEAAaOBmzCBmDAMBgNVHRMBAf8EAjAAMB0G +A1UdDgQWBBR7iRaxAf1OaeoxkupqrpeexvALjjALBgNVHQ8EBAMCA+gwEwYDVR0l +BAwwCgYIKwYBBQUHAwEwFAYDVR0RBA0wC4IJRW5kZW50aXR5MBEGCWCGSAGG+EIB +AQQEAwIGQDAeBglghkgBhvhCAQ0EERYPeGNhIGNlcnRpZmljYXRlMA0GCSqGSIb3 +DQEBCwUAA4IBAQAWUnY5on/xftnW8YsFeChSLWKEAP0YaaOGoCETdfWd4OIZnr8s +AzbHV3V+7I57wz956R2WB8m98FuOrtosqwyAmKwvA5Jv5jYSj7DytY3RP8w/mb9u ++W1NTpKQ5UvaMjVorA/lgvd/IccoSK0oUBfjUD2+0kzUGHUtaNJivCe2DG0pUfTP +qAwuZyTlUyVW77eCBj5Pl+R/eFjlDQaxikG+AKlmJ77eu15Pw3nOPTVBme8wTzQH +Sb2DLoQbi4Fe69NXtOZ8SX2H3M2w2+HnjhYTYWZN7/6OR6cafJAlRzBPeGQHC0MG +LWMLAcF8GJP9sx58pIJ3g9gOzOuWTs/KqUSu +-----END CERTIFICATE----- +)PEM" +}, +{ "Inter CA 1 Key", R"PEM( +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDJlExG3+oLO6GF +uD4pa9sI5mJL/9fADcoe9bv1SAnRKu/GPtCMaruVTKBAt5x4mwt5My6WhyPdpYp0 +z6yUgSCrbkSAMvMzGlS1W5Ke4UU4GaufCRjHeXNVpx9wXEPYy46HO5vSZZiGzl46 +UMmeVkV5kGeh8y2giS/M0prVqpOLdIloeJykgp8k29llkFj/OEa2WPiKhUnBvna5 +IDyrjjTKUGo5mxi9RVuArZwZ16kdJPG292WOVtQ8uTg391XQ2uOho/nv/IoWysZL +nwvHIZkcH413owvBULqM5fVda0j9qkvQEKa4GAiitx2HX0wWYNvtUhZrXnHU/DzY +kbhZrW7VAgMBAAECggEAAJXerCywTadhrmASRiUwYKk9Sy02CjJquNuhLR2FcccR +z93qKyOQNzx0EVwMfJ6D/QPKCdk19cnU9MaO9hYd6yw0eg7WfU/WmSQ9RnAvDEep +eO/E06d6+e+0pqs4mI5BdAvB8G19G5Rz2kWBIGWYaWRNo2nFDdH4LTxwrgLYei+I +zeOJE1KURbLG3Ni1vpTYO5qB5DOC5+KobiO4qLA7I1r3ne2fMDPnnO+r4Onky2gg +O8uCVKjbb2VEPk1tLShj3e5USsewdUMkc6es4XN9nNOFISEzsbfmhsRNERXUKN2h +lVRjNNaXxn4FogOFlmEu6+ZfL8Eg0wusuLGi5q8HzQKBgQDsno5nvGPSa6dC4j7Q +jbpwaKVY3MrUDWIxZwhpRWzR4yDuhYwcnDV7jsyiXxQRAknjIsvqgVSU+tWlB9Hz +2PGhC3Pu2FBSJMAwMrT4IUE66lXH4bbbPj4v1YD7AC0rRCPN1b6/sXq/K4U3bdgz ++Uhedqr0jA1dVxMML+HHXH/P2wKBgQDaFwUnpcheDzhRFEHxaRPUzdOvwye1uzlT +Cw1WNdut4hywv2Too2cZi7lSnCKV6Ez4RLZLv6Rq2rl3+IxN53UbmQ/pUftA+jNS +OQYMwLm5rVm/VOqiDFwwJtBar23COgfn1n4D3TBpoZxvPhVOC+tAYLz8CtSPjO5v +YOuqnjkTDwKBgQCGlmVPsgNhY6Re0cuC2qqqV4t9MREZbodkrENHhZqxA6zunv2k +JGHzZrUUwlnqls+qJ1evimKu3sH4var5NXOy84281ENThd3fTGhNHrU4qqOiLsvQ +huXeVWWo31/R1U88PBDkfjHgXQPIFG2+mlNEj+ELekH7zpRiATY+LGnyswKBgGkM +zZ7bbDhifG4Ro/H0+2h1/JGdH3OaS3KE0FfGeZq4X7mDd8fXbcIQW6Q/MSmkS+8K +qgSyuy/9S3lOb8bWxsIUaGWq56vU2QOyFAfwjYWyypgu/xYvffhzucj015nG+ifi +WbDiyn8XKZMsBHcjpp3L+zwsu1aqO+/Kb7ovnK5hAoGAf2ILN0I+/pDWg1nsEe1T +dX+8tSj9hdbKO/vMNaBEYseY+MwHIFD511QmtTF0180LmxEgoHfZh+XFYE+RWYz+ +saTRaU4ruH+IW2N+sKB7tXsLwEWmfaTNnn0PRdHCxxPABmMDphHNHXmJ/16hkjGZ +xCjKmukuObM35daa7xgorTo= +-----END PRIVATE KEY----- +)PEM" +}, +{ "Inter CA 1 EncKey", R"PEM( -----BEGIN ENCRYPTED PRIVATE KEY----- -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 -----END ENCRYPTED PRIVATE KEY----- )PEM" +}, +{ "Endentity Key", R"PEM( +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmg5PEqIpWdxzk +YvTJ+Kd4hTuN6H2myxevF1nZ64LbgWTg5iwF4pxJbetnnRn6Pess4UkHQdxxtu1w +0ccYPqEfV1JVP+wewY3kyeS1EdN0EkNtFQvMinw9vHk3Qbg7Q81hYXIm16iOtvnQ +X8b04sRtLZZFqI15ABJ5HG3w0pRY/uguek+fhzfawKH7A6VXAlmNlu9XK3juU2+T +N3pO/W8GoIwCPMyTp4IMTDUVmAYnrUB1NpIvHFLqPSDnZA0d7GvOwAwLU5A40uCz +8PsO2EAxaDZnm/MtenW5lbVT9AEunCrwGGlhcyBAuNry+soq5X+tuvoCH1S8bmlI +eZ+dXA+ZAgMBAAECggEAAWsbAeCWpxRmKlreb27/DDOEVZneoiJWPg9SnFzZdUG1 +qIXFZ7ptruFxESWhMETBQVVc8CMjO9O9U4nx6na0HCZ8BM5h1kQ7T3DZ0iIH/VPf +f6kfe9tNIiB+HtagOV8DO5pfJM4O+UI9QAVk1jatSinKfybnp5l0ztPLavL8QQLN +VaokyyWusQ+PsMHHI7EYr+C1WNFUxCibTuBc21+ToETu6NSWpWVffjTM9+Lw5y3A +qrXXjeehPJ8xVoDXlTB4D4ooW68/5+pHrYoy5OwyVYwZFSte5B92mhw2+VPLYZ7P +qN4CKVIbCHjgDNwPs3YYUUwMyQ/yC+VpYJa5zSapJQKBgQDX0VpAzJ8VU3ldW/EG +OvVrXmSlTayGBqn8OTAjHW86nYZ/uYmeYPQns4cIhpRlqGyO8eAro5dpIB6929yG +QTGdxaXF3JmZFYFjgEW2j9PFtLlovV3mLEokzAeVIsj/HyMIIEEEaoC+kFaVr4Hy +bsWcQmWqDspnuybKt0iK5kxcdQKBgQDFhDqPUiwxUhUNrPWMqnHfaDjzPVkT7VZ2 +ryF8sER9Kl1DE9mY0vbQgiL8x+tN5gRYy6x90bYlLKFUKoPu8czrJrH+t4/Ze/eu +H27+vKd/w3MDp4MnQtT01csT5+btsWB5SUlzt6fQvkw/pylAglKpktTfjEaLoVeL +TolK4i9SFQKBgQCiKfDDF/3BLIPZHqiwocSe9MJzYzXrTz6TAvWkrQ1S4eKaPnOl +yfwriL5CLb171V4c2qoyoC25FCWFS5scVghOIKE7V1MisAIVHeFEGDZsLCvVA9V2 +inj7xUM9UHHvIRpVlMbF5rDuf85Pkxz1aT6a91kkuxBjeUDguW+Mzxc5sQKBgBp/ +WrvPck5LjLeA9JAibZRjDwDSwhiCRo81f3CS1R1ViZ1uFEsEQkhGrRrsVwzkRsEC +0ekvMRhbnGkGKxzrI2qIjmh1h77NerTJUsKp3G6vccaTumyR8q/F3rCU883/dcnP +oyL7CHBglyoS6t3UnfJR2G0FDJG+21e8+X4qSd7hAoGBAIMUKIZGAKpjB4wgN9KN +0ezKyt/Gdt//G7DH2Fqn8QL1SkJwAvHVpGlhhv905drBKdfpKrej8C4gE2mmFKY+ +Jj5XLLPNFBbi/YqYTED7mJvmwDz2f84VuUglc9WYiqir0iDgwJCXXOefTfFfETu2 +ImjXbn2e+yf50r3jk6yMk4Mk +-----END PRIVATE KEY----- +)PEM" +}, +{ "Root CA Key", R"PEM( +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCGykFQQcbPjwcw +Y1iXd0U0zTWQA8R2UZCHveyZtza55e55vTRtfeVzAgTxSbmOkgynTR4S3yM6inZi +NcVRFUkhrFPYMFbHUSpw/DykgbxYAUszE+KTx77JZ3A3pNL/wjlrXQOGeGENBeVd +rMtzSgeeM8jLys3P5FE3s3itKV5xxlZZthcbxrADSTLF85a69iJ+yGIy7tm1fLYk +LIJzwtUz+MSyIWzg/K+82sPQEWGg3EBEj6zpT+01dk0AAGwOyZHz0HlvEoZl/py2 +CYFItycMM8fpvh/u2QIVCYxeqM2TwojwKhDrtmD/Vd9SfCoKqYRjrjgu3ZCjGyRz +HlpSFSlNAgMBAAECggEACJu7Lx58TX4zddYqbsX5wAUJEjEDZ5EO+3RYO3r1fkre +f5zjT8jWEhHDcguE08OlFjw5GvhwPglgxSXs+6Ax4vooAqsXp7QZ00xwrQryFebD +QDhZZJnBqAg4pP0cUqQ7WY1/r6NZg2TogKKL/clxEhv5i3ZgzZGl1DpcjyaQtxBd +cagQfuSD8fr0FMB2EwmUFtYaWO7b22wwHFCDb//KLsDPyss1rZkzRMp3i5qNDM1r ++i5apaldMSTInEXGIzH9SLUXF4s7eH5hkaXfFueNwVo9kSMOOIadVO+KuiKxBJF9 +hnukhIRuwXeXdJITTdvHCX569ubYlamqolBZMor+uwKBgQC8auGD7XZ6jTn/WQ5u +8gCIwxyPKOLSfhQGb9pXPloSairBxuhLCceg0tbkbN4IixgDBeRhjtOYy2MJJP3d +DQUWfdXucicptBvjgWiSQxbIdFObdiYs3/5syxrhZIBSbDxp+T3Auad/MbKA8VS5 +Cf6Tay/cTPdtOAnW8kcUf24tqwKBgQC3Ix/rSiRdTzoKzNuviZ18mAgb4gC5y/jw +QyksfgbrLycvYNcuNRnJlfZGE6bI5qRLdYM2T+8sWg1JPAW+n+XozymuhApXLSOl +DOgVO3jg8EHbU1pV/C2uutE8/olLJ9wcdV5ScuT/t+djI+1vekGAcZipahdYYqRp +2pGjKkHc5wKBgQCOLSSg9ZYaoejqyxu+EjTe/kPVryeBaIvZ05txi+ZR0PUUpfBx +dx7BN3fahGADGFp1JYQRLcJzoL3Jj7VUkVeOgkPlGxR8PqFwyhzLiX5uooDYZY4m +/JszIwvUmo7OI0hvOxkjDbTboPeunuyseGM2gIJr/VQTgV7DaKzERegw4wKBgCsd +bNVS9i61iT+jrRCtgFMl47rUwUD8oVXHKflcjjpuNnZ0ARTS0Yhg8sLau4hOrF1L +hCGUPgWeCcS1CdEwCct+ghyg32JYsSyZVkYxYyrpxUdiX0EhBow9VSdb8WytLKfS +hes5O2psnF/9xFdbW/JmS/+Oh4J6bqSxHVphh3H/AoGAWqJA+jO2d75r9JI6+GVx +9XDO7qQt6XgCELypxDXI+rr9qJP1uIKEGrbrw3gs8Z6QVtTWCvwOfa6AiQf4f9aD +0xO29mRaLHZgvh9/iPtROM43CVPnS3fb+H+C5oA2aAXrbqU4kEcvCHaEkNw2oONx +PHyIfBzGNa1JrRLkQwrfR2E= +-----END PRIVATE KEY----- +)PEM" } };