master
carsten.pluntke 14 years ago
parent 9a8b208c62
commit f5e983f5b9
  1. 16
      Test_Qt_Frontend/QtSslTest.pro
  2. 37
      Test_Qt_Frontend/pindialog.cpp
  3. 22
      Test_Qt_Frontend/pindialog.h
  4. 48
      Test_Qt_Frontend/qtssltest.cpp
  5. 162
      Test_Qt_Frontend/smartcardauth.cpp
  6. 27
      Test_Qt_Frontend/smartcardauth.h
  7. 39
      Test_Qt_Frontend/swsign_cert.pem
  8. 41
      Test_Qt_Frontend/swsign_interm.pem
  9. 37
      Test_Qt_Frontend/swsign_root.pem

@ -0,0 +1,16 @@
# #####################################################################
# Automatically generated by qmake (2.01a) Fr 18. Jun 11:12:34 2010
# #####################################################################
TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += ../engine_securetoken .
# Input
SOURCES += qtssltest.cpp \
smartcardauth.cpp \
pindialog.cpp
QT += webkit
QT += network
HEADERS += smartcardauth.h \
pindialog.h

@ -0,0 +1,37 @@
#include <QtGui>
#include "pindialog.h"
PinDialog::PinDialog(QWidget *parent)
: QDialog(parent)
{
label=new QLabel(tr("Enter &PIN:"));
lineEdit=new QLineEdit;
lineEdit->setEchoMode(QLineEdit::Password);
label->setBuddy(lineEdit);
okButton=new QPushButton(tr("&OK"));
okButton->setDefault(true);
cancelButton=new QPushButton(tr("&Cancel"));
connect(okButton, SIGNAL(clicked()), this, SLOT(accept()));
connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
QHBoxLayout* tl= new QHBoxLayout;
tl->addWidget(label);
tl->addWidget(lineEdit);
QHBoxLayout* bl= new QHBoxLayout;
bl->addStretch();
bl->addWidget(okButton);
bl->addWidget(cancelButton);
QVBoxLayout* ml= new QVBoxLayout;
ml->addLayout(tl);
ml->addLayout(bl);
setLayout(ml);
}
QString PinDialog::pin() const {
// TODO: Cleanup of internal strings as soon as the PIN is retrieved
return lineEdit ? lineEdit->text() : "";
}

@ -0,0 +1,22 @@
#ifndef PINDIALOG_H
#define PINDIALOG_H
#include <QDialog>
class QLabel;
class QLineEdit;
class QPushButton;
class PinDialog : public QDialog
{
Q_OBJECT
public:
PinDialog(QWidget *parent = 0);
QString pin() const;
private:
QLabel* label;
QLineEdit* lineEdit;
QPushButton* okButton;
QPushButton* cancelButton;
};
#endif // PINDIALOG_H

@ -0,0 +1,48 @@
#include <QApplication>
#include <QtNetwork>
#include <QtWebKit>
#include <QList>
#include <QFile>
#include <iostream>
#include "smartcardauth.h"
SmartCardAuth g_scard_auth;
int main(int argc, char *argv[])
{
SmartCardAuth::initialize();
QApplication app(argc, argv);
QSslConfiguration sslConf(QSslConfiguration::defaultConfiguration());
// Works even without specifying the root certificate, we just need to add the intermediates,
// and that's done in SmartCardAuth.cpp
#if 0
QFile caCertsFile("D:\\QtSmartCardAuth_TMI\\QtSslTest\\swsign_root.pem");
caCertsFile.open(QIODevice::ReadOnly);
QList<QSslCertificate> chain( QSslCertificate::fromDevice(&caCertsFile) );
sslConf.setCaCertificates(chain);
#endif
sslConf.setPeerVerifyMode(QSslSocket::QueryPeer);
sslConf.setOpenSslHook(&g_scard_auth);
QSslConfiguration::setDefaultConfiguration(sslConf);
// TODO - IMPORTANT: Error reporting!
// If there is ANY failure (no network, no host resolution, no SSL connection, timeout) we just see a
// blank page!
QWebView web;
// Works - NEEDS AN INTERMEDIATE CERTIFICATE, either loaded from card or from file, see SmartCardAuth
web.load(QUrl("https://dev.swisssign.com/test/"));
// web.load(QUrl("https://e2k7.demo8.cryptovision.com/ssl/"));
web.show();
int rv=app.exec();
SmartCardAuth::deinitialize();
return rv;
}

@ -0,0 +1,162 @@
#include "smartcardauth.h"
#include "pindialog.h"
#include <QtNetwork/private/qsslsocket_openssl_symbols_p.h>
#include "engine_sct.h"
#include <string>
ENGINE* SmartCardAuth::e=NULL;
enum_certs_s* SmartCardAuth::certs_found=NULL;
QWidget* SmartCardAuth::parent=0;
bool SmartCardAuth::pin_configured=false;
bool SmartCardAuth::pin_rejected=false;
void SmartCardAuth::initialize() {
QSslSocketPrivate::ensureInitialized();
q_ENGINE_load_dynamic();
e = q_ENGINE_by_id("dynamic");
int r=q_ENGINE_ctrl_cmd_string(e, "SO_PATH", "C:\\Windows\\System32\\engine_act.dll", 0);
r=q_ENGINE_ctrl_cmd_string(e, "ID", "act", 0);
r=q_ENGINE_ctrl_cmd_string(e, "LIST_ADD", "1", 0);
r=q_ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0);
if(!r)
{
unsigned int err = 0;
while((err = q_ERR_get_error()))
{
char *str = q_ERR_error_string(err, NULL);
fprintf(stderr,"%s\n", str);
}
}
r=q_ENGINE_init(e);
}
void SmartCardAuth::deinitialize() {
q_ENGINE_finish(e);
q_ENGINE_cleanup();
}
void SmartCardAuth::setPinDlgParent(QWidget* p) {
parent=p;
}
int SmartCardAuth::client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
{
// NB: Keep in mind that this function is called for EVERY SSL connection to be opened.
for(size_t i=certs_found->num_certs;i--;)
{
const char *id_p = certs_found->certificate[i].id;
if(id_p == NULL) continue;
// Name has the format "slot-x-name-SwissSign_digSig" for the certificate/key we're looking for
std::string name(certs_found->certificate[i].name);
std::string compare("-name-SwissSign_digSig");
// Compare the rightmost part of the retrieved name to locate the certificate/keypair
size_t pos = name.length() - compare.length();
if(name.substr(pos) != compare)
continue;
// // Filter out the correct certificate depending on well-known sorting criteria
// // Given example searches for a keyword in the certificate's subject, but since we've got
// // the decoded certificate as an X509* structure we can check all the fields.
// X509_NAME* subj_name = q_X509_get_subject_name(certs_found->certificate[i].cert);
// int maxlen = 2048;
// char buf[maxlen+1];
// buf[maxlen]=0;
// q_X509_NAME_oneline(subj_name, buf, maxlen);
// std::string subject(buf);
// const char *compare="CN=Carsten Pluntke";
// if(subject.find(compare) == std::string::npos)
// continue;
// Here we found a suitable certificate.
// Now prepare the reference to the SmartCard's private key and a copy of the certificate
// to pass back to the caller.
*x509 = q_X509_dup(certs_found->certificate[i].cert);
*pkey = NULL;
// If we don't have a PIN yet, pop up a dialog, ask for a PIN and pass it along to the engine
// for usage.
if(!pin_configured)
{
PinDialog dlg(parent);
int ok=dlg.exec();
if(ok!=1) return 0; // User cancelled
QByteArray pinByteArray=dlg.pin().toAscii();
char *pin_str = pinByteArray.data();
// The engine control command takes a copy and overwrites the source array
if(q_ENGINE_ctrl_cmd_string(e, "PIN", pin_str, 0))
pin_configured = true;
else
return 0; // Engine refuses to take the PIN
*pkey = q_ENGINE_load_private_key(e, id_p, NULL, NULL);
// We do a test authorization on loading of the private key. If the operation fails at all,
// DON'T try again (see below) or we would instantly lock the card in a single session because
// of the retries!
if(!*pkey)
pin_rejected = true;
}
// Second to nth iteration: We skipped the PIN dialog here, now load the key if we don't have the
// explicit information not to do it (because the PIN is wrong)
if(!*pkey && !pin_rejected)
*pkey = q_ENGINE_load_private_key(e, id_p, NULL, NULL);
break;
}
if(!*x509) {
qWarning("Unable to load certificate");
return 0;
}
if(!*pkey) {
qWarning("Unable to load key");
return 0;
}
return 1;
}
bool SmartCardAuth::hookInitSslContext(SSL_CTX *ctx)
{
bool result = false;
if(!certs_found)
result = (q_ENGINE_ctrl_cmd(e, "ENUM_CERTS", 0, &certs_found, NULL, 0) != 0);
else
result = true;
#ifdef USE_CERTIFICATE_FILE
// Load a specific intermediate certificate from a file
BIO* cert_file= q_BIO_new_file("D:\\QtSmartCardAuth_TMI\\QtSslTest\\swsign_interm.pem", "r");
X509* interm=q_PEM_read_bio_X509(cert_file,NULL,NULL, NULL);
q_BIO_free(cert_file);
q_SSL_CTX_add_extra_chain_cert(ctx,interm);
#else
// Add all of the card's certificates without a private key as intermediate certs
for(size_t i=certs_found->num_certs;i--;)
{
if(certs_found->certificate[i].id == NULL)
q_SSL_CTX_add_extra_chain_cert(ctx, q_X509_dup(certs_found->certificate[i].cert));
}
#endif
q_SSL_CTX_set_client_cert_cb(ctx,client_cert_cb);
return true;
}

@ -0,0 +1,27 @@
#ifndef SMARTCARDAUTH_H
#define SMARTCARDAUTH_H
#include <Qt/private/qopensslhook_p.h>
class QWidget;
struct enum_certs_s;
class SmartCardAuth : public QOpenSslHook {
public:
static void initialize();
static void deinitialize();
static void setPinDlgParent(QWidget* parent);
virtual bool hookInitSslContext(SSL_CTX* ctx);
private:
static int client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey);
static ENGINE* e;
static enum_certs_s* certs_found;
static QWidget* parent;
static bool pin_configured;
static bool pin_rejected;
};
#endif // SMARTCARDAUTH_H

@ -0,0 +1,39 @@
Bag Attributes
localKeyID: 01 00 00 00
1.3.6.1.4.1.311.17.3.20: AB FC 95 89 38 6A 06 C3 21 78 B7 AE 9E 87 06 8A C8 79 33 02
friendlyName: My SwissSign ID
subject=/CN=Markus Tesche/emailAddress=markus.tesche@cryptovision.com
issuer=/C=CH/O=SwissSign AG/CN=SwissSign Personal Silver CA 2008 - G2
-----BEGIN CERTIFICATE-----
MIIFzDCCBLSgAwIBAgIPCfGkMO74CroouYxQ+mKcMA0GCSqGSIb3DQEBBQUAMFUx
CzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxLzAtBgNVBAMTJlN3
aXNzU2lnbiBQZXJzb25hbCBTaWx2ZXIgQ0EgMjAwOCAtIEcyMB4XDTEwMDQxNjEw
MDI0N1oXDTExMDQxNjEwMDI0N1owRzEWMBQGA1UEAxMNTWFya3VzIFRlc2NoZTEt
MCsGCSqGSIb3DQEJARYebWFya3VzLnRlc2NoZUBjcnlwdG92aXNpb24uY29tMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu0DvJNEgXJefs4pdCnGzn6zS
8jTO20XS0Gzu16exDDXZ1aqIUdfj/dV5ya25YiJmOqwkOIvi3ruaXTYEGhGAMXUb
5/AqzjSwJ5Jn5KG2s8zD8GchJf72hUPTF4Pke0aS1tQ+0r3L5uVCDLzMZ839GF1/
bbp1bb8vgXa7UL0qbeLH4nITT0QgEVoXhx/xGDnvOBfvmy7OQ3/GuOqnFHz6REIe
daYfEMYyxIziYa1aYlyUw1xGIkOcNahznkN6wK+enrJPzLORFcF9soZOGO3cMH7k
tKPxB1P8LeZtgoIXLmrns5aJ11omB+zCRXieZTyj6OAFPR6Z700ozZG4PWAdxwID
AQABo4ICpTCCAqEwWQYDVR0RBFIwUIEebWFya3VzLnRlc2NoZUBjcnlwdG92aXNp
b24uY29toC4GCisGAQQBgjcUAgOgIAwebWFya3VzLnRlc2NoZUBjcnlwdG92aXNp
b24uY29tMA4GA1UdDwEB/wQEAwID+DA1BgNVHSUELjAsBggrBgEFBQcDAgYIKwYB
BQUHAwQGCisGAQQBgjcKAwQGCisGAQQBgjcUAgIwHwYDVR0jBBgwFoAU6zWxVm0V
YFj04SLNHEYcrtAEAGUwgf8GA1UdHwSB9zCB9DBHoEWgQ4ZBaHR0cDovL2NybC5z
d2lzc3NpZ24ubmV0L0VCMzVCMTU2NkQxNTYwNThGNEUxMjJDRDFDNDYxQ0FFRDAw
NDAwNjUwgaiggaWggaKGgZ9sZGFwOi8vZGlyZWN0b3J5LnN3aXNzc2lnbi5uZXQv
Q049RUIzNUIxNTY2RDE1NjA1OEY0RTEyMkNEMUM0NjFDQUVEMDA0MDA2NSUyQ089
U3dpc3NTaWduJTJDQz1DSD9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0P2Jhc2U/
b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwZAYDVR0gBF0wWzBZBglg
hXQBWQEDAQQwTDBKBggrBgEFBQcCARY+aHR0cDovL3JlcG9zaXRvcnkuc3dpc3Nz
aWduLmNvbS9Td2lzc1NpZ24tU2lsdmVyLUNQLUNQUy1SNC5wZGYwdAYIKwYBBQUH
AQEEaDBmMGQGCCsGAQUFBzAChlhodHRwOi8vc3dpc3NzaWduLm5ldC9jZ2ktYmlu
L2F1dGhvcml0eS9kb3dubG9hZC9FQjM1QjE1NjZEMTU2MDU4RjRFMTIyQ0QxQzQ2
MUNBRUQwMDQwMDY1MA0GCSqGSIb3DQEBBQUAA4IBAQA6L3y3A9mZ+xziOo71CcWq
WjFl8sqtk0ul6oh0uA7qDT8DP2BjP+uVUZr3fdnzWenL1qLbnh9M90yWLst0vGZ1
dOBO5Ex+6VgHb95jcK4BztfCYGhGvdtBbWbdJELmPBF+87OL/C/PYRrLvYqDo0XX
P5R1DZeJu732qaFB30Z28Fino7RzuSmngwwWujRQGJs2sVmxjkHqD0Lrxkp9eOQl
XwLo9uhMlEgDhah/dPA+S/+zApB8mNjhILw+3WkPgEsNLob15u5wvJEZ4GD/vK7E
kbMxGDlvSkM6+tWVizxeqhFJgYA2kLeY/pKJ9ZRT2d+k+DJufdWeWMCbWPVEiILc
-----END CERTIFICATE-----

@ -0,0 +1,41 @@
Bag Attributes
1.3.6.1.4.1.311.17.3.20: EB 35 B1 56 6D 15 60 58 F4 E1 22 CD 1C 46 1C AE D0 04 00 65
subject=/C=CH/O=SwissSign AG/CN=SwissSign Personal Silver CA 2008 - G2
issuer=/C=CH/O=SwissSign AG/CN=SwissSign Silver CA - G2
-----BEGIN CERTIFICATE-----
MIIGZzCCBE+gAwIBAgIJAOJWt1OXa3ZYMA0GCSqGSIb3DQEBBQUAMEcxCzAJBgNV
BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxITAfBgNVBAMTGFN3aXNzU2ln
biBTaWx2ZXIgQ0EgLSBHMjAeFw0wODA3MDkxMTExMDlaFw0yMzA3MDkxMTExMDla
MFUxCzAJBgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxLzAtBgNVBAMT
JlN3aXNzU2lnbiBQZXJzb25hbCBTaWx2ZXIgQ0EgMjAwOCAtIEcyMIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9/NTXkltjAPlJxchGGCldpQ/FRC4IUDP
NjOsKnKaj2HDa956SQhYPYDYO/CdHUEQAb9rB1YajbM9v2O6MX7ickYYaIfXhU+g
yXsTqdA50YnWNWdodsFflgnNzzoF0T8GBQraFvJD8qQHHaKsgHUBnaDo9zSnv7bm
OWhmUkc5KU20negqrRVhtKIx4BCR2x7kQ/Er3hDBNMtshO5iFCdE2DHx3zwhzMCs
kGjTdGjJF0qOOwmnsQVljQekkK4uet56RG+wAv50/xqH9VjppiXxzIgiJ9jLMcEv
KAxxch73+whObnoFrCF/PwpaZvi/5RYU+RubxJ+6Mw2GlxVdrqEGjwIDAQABo4IC
RjCCAkIwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0O
BBYEFOs1sVZtFWBY9OEizRxGHK7QBABlMB8GA1UdIwQYMBaAFBegzcHkQbY6WzvL
RZ29HMKY+oZYMIH/BgNVHR8EgfcwgfQwR6BFoEOGQWh0dHA6Ly9jcmwuc3dpc3Nz
aWduLm5ldC8xN0EwQ0RDMUU0NDFCNjNBNUIzQkNCNDU5REJEMUNDMjk4RkE4NjU4
MIGooIGloIGihoGfbGRhcDovL2RpcmVjdG9yeS5zd2lzc3NpZ24ubmV0L0NOPTE3
QTBDREMxRTQ0MUI2M0E1QjNCQ0I0NTlEQkQxQ0MyOThGQTg2NTglMkNPPVN3aXNz
U2lnbiUyQ0M9Q0g/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdD9iYXNlP29iamVj
dENsYXNzPWNSTERpc3RyaWJ1dGlvblBvaW50MGQGA1UdIARdMFswWQYJYIV0AVkB
AwEDMEwwSgYIKwYBBQUHAgEWPmh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5j
b20vU3dpc3NTaWduLVNpbHZlci1DUC1DUFMtUjMucGRmMHQGCCsGAQUFBwEBBGgw
ZjBkBggrBgEFBQcwAoZYaHR0cDovL3N3aXNzc2lnbi5uZXQvY2dpLWJpbi9hdXRo
b3JpdHkvZG93bmxvYWQvMTdBMENEQzFFNDQxQjYzQTVCM0JDQjQ1OURCRDFDQzI5
OEZBODY1ODANBgkqhkiG9w0BAQUFAAOCAgEALip22pfzTN9kJ+FbLZXvuVUu27gJ
ZTFAsEu9fJCx2dhxGFPO6DUsmxS6H3SC1FeSwFeTm1AFJXvgldRduER46TOQQf7h
v0abeX1yvDhQGcBoWgay0xveXWfPaZL49awJhdTdWi5qOSPv9O9zWjYew+mNIEdk
Nx85eRPXDlCyrLoZnuqD5EVGBL7NLzkQCJsNifBVsiYkUbNr0XxpPVjVgTU8aEdX
jcYSs88qahVFL4SpTj3BOcrr+95KnK0buGGWBiC4gCMZtHZDHJ+umjheENI49R6e
2QwR1S74yYqHpwyz0ihdI2xOZgXxmGOg3GKBxEficqLgbRl+PV2FRZdogAOl8PXt
25iCzummltrfbjXaGQNg9rBHmAM05bxgtMCQwCj0BG48ufpJhzplOLS1YYIehiHv
mojFdSg1q15SYC867zGLpnv3SAxwPLXXYsu0QsP0jbhEcYwk/NfunyHoqmOWw88H
W/KB0ppwJ1QYzO6h0Qwijl7QmUM5qlJOZxuP2jK8WoOS2jarU3r0EXfq/Nfo3uqX
xm5QDg9E4M8wyouaoaCn/LnoxS3zc/VU/p0o15AjIylPSTN/kHmDSl4eYZFmBBhd
0YhvjC4hW2UBRM12SQAAEPIaEqUdxrYFfogWojYCoymheKskXfAKdA1k2gp5mhVh
SFXeRgS5uQTbqpY=
-----END CERTIFICATE-----

@ -0,0 +1,37 @@
Bag Attributes
1.3.6.1.4.1.311.17.3.20: 17 A0 CD C1 E4 41 B6 3A 5B 3B CB 45 9D BD 1C C2 98 FA 86 58
subject=/C=CH/O=SwissSign AG/CN=SwissSign Silver CA - G2
issuer=/C=CH/O=SwissSign AG/CN=SwissSign Silver CA - G2
-----BEGIN CERTIFICATE-----
MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE
BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu
IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow
RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY
U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv
Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br
YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF
nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH
6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt
eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/
c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ
MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH
HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf
jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6
5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB
rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c
wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB
AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp
WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9
xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ
2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ
IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8
aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X
em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR
dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/
OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+
hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy
tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
-----END CERTIFICATE-----
Loading…
Cancel
Save