Test export functionality and indirectly the import

This commit is contained in:
Christian Hohnstaedt 2024-01-19 09:32:24 +01:00
parent 2f482fc782
commit 469d24eb7c
7 changed files with 554 additions and 72 deletions

View File

@ -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/)

View File

@ -5,6 +5,8 @@
* All rights reserved.
*/
#ifndef __PWDIALOGMOCK_H
#define __PWDIALOGMOCK_H
#include <QDebug>
#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_expect*> pw_expectations{};
void setExpectations(const QList<pw_expect*> pwe)
{
qDeleteAll(pw_expectations);
pw_expectations = pwe;
expect_idx = 0;
}
int expect_idx{};
QList<pw_expect*> pw_expectations{};
void setExpectations(const QList<pw_expect*> pwe)
{
qDeleteAll(pw_expectations);
pw_expectations = pwe;
expect_idx = 0;
}
};
#endif

305
test/export.cpp Normal file
View File

@ -0,0 +1,305 @@
/* vi: set sw=4 ts=4:
*
* Copyright (C) 2023 Christian Hohnstaedt.
*
* All rights reserved.
*/
#include <QTest>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#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 <widgets/MainWindow.h>
#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<unsigned> 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_key*>(pki);
if (key) {
QCOMPARE(key->isPrivKey(), priv);
}
}
}
void verify_file(const QString &name, QList<unsigned> 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<db_x509>();
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<unsigned> { 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<unsigned> { 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<unsigned> { 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<unsigned> { 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<pw_expect*>{
new pw_expect("pass", pw_ok),
new pw_expect("pass", pw_ok),
});
export_by_id(7, file, list, certs);
verify_key(file, QList<unsigned> { 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<unsigned> { 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<unsigned> { 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<unsigned> { END_HASH });
check_pems(file, 0);
// Export Endentity key
list.clear();
key = dynamic_cast<pki_x509*>(certs->getByName("Endentity"))->getRefKey();
db_base *keys = Database.model<db_key>();
list << keys->index(key);
// Public Key
file = AUTOFILE(PUBKEY)
export_by_id(19, file, list, keys);
verify_key(file, QList<unsigned> { 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<unsigned> { ENDKEY_HASH }, true);
check_pems(file, 1, QStringList{ "RSA PRIVATE KEY" });
// Private Key Openssl Encrypted
file = AUTOFILE(PRIVKEY)
pwdialog->setExpectations(QList<pw_expect*>{
new pw_expect("pass", pw_ok),
new pw_expect("pass", pw_ok),
});
export_by_id(21, file, list, keys);
verify_key(file, QList<unsigned> { 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<unsigned> { 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<unsigned> { 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<unsigned> { ENDKEY_HASH }, false);
check_pems(file, 0);
// Private DER Key
file = AUTOFILE(PRIVDER)
export_by_id(25, file, list, keys);
verify_key(file, QList<unsigned> { ENDKEY_HASH }, true);
check_pems(file, 0);
// Private PVK Key
file = AUTOFILE(PVK)
export_by_id(26, file, list, keys);
verify_key(file, QList<unsigned> { ENDKEY_HASH }, true);
check_pems(file, 0);
// Private PVK Key encrypted
file = AUTOFILE(PVK)
pwdialog->setExpectations(QList<pw_expect*>{
new pw_expect("pass", pw_ok),
new pw_expect("pass", pw_ok),
});
export_by_id(27, file, list, keys);
verify_key(file, QList<unsigned> { ENDKEY_HASH }, true);
check_pems(file, 0);
} catch (...) {
QString m = QString("Exception thrown L %1").arg(l);
QVERIFY2(false, m.toUtf8().constData());
}
}

View File

@ -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<pw_expect*>{
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<pw_expect*>{
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<pki_base*> allitems = Store.getAll<pki_base>();
QCOMPARE(allitems.count() , 3);
} catch (...) {
QVERIFY2(false, "Exception thrown");
}
dbstatus();
}

View File

@ -6,6 +6,14 @@
*/
#include <QTest>
#include <QFile>
#include <openssl/opensslv.h>
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
#include <openssl/provider.h>
#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<pw_expect*>{
new pw_expect("testdbpass", pw_ok),
});
mainwin->init_database("testdb.xdb");
dbstatus();
}
void test_main::dbstatus()
{
QList<pki_base*> allitems = Store.getAll<pki_base>();
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)

View File

@ -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<QString, QString> pemdata;
private slots:
void initTestCase();
void cleanupTestCase();
void cleanup();
void newKey();
void importPEM();
void exportFormat();
};
#endif

View File

@ -8,10 +8,10 @@
#include "main.h"
const QMap<QString,QString> 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"
}
};