summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Walter <stefw@src.gnome.org>2009-01-18 22:24:09 +0000
committerStefan Walter <stefw@src.gnome.org>2009-01-18 22:24:09 +0000
commit300ee9603c89d801e16b5f4696927e1f36bed939 (patch)
tree3b2ec41f73e6a3398ca7d957620ea61de41ea521
parent6611cd8c03723bf7e821098756ff34057c3fae22 (diff)
Add new gcr library for crypto UI and related tasks. Implement GckParser
* egg/egg-asn1.c: * egg/egg-hex.c: (split from pkcs11/gck/gck-util.c) * egg/egg-hex.h: (split from pkcs11/gck/gck-util.h) * egg/egg-openssl.c: (moved from pkcs11/gck/gck-data-openssl.c) * egg/egg-openssl.h: (moved from pkcs11/gck/gck-data-openssl.h) * egg/egg-symkey.c: (split from pkcs11/gck/gck-crypto.c) * egg/egg-symkey.h: (split from pkcs11/gck/gck-crypto.h) * egg/Makefile.am: * egg/tests/Makefile.am: * egg/tests/unit-test-asn1.c: * egg/tests/unit-test-hex.c: (moved from pkcs11/gck/tests/unit-test-util.c) * egg/tests/unit-test-openssl.c: (moved from pkcs11/gck/tests/unit-test-data-openssl.c) * egg/tests/unit-test-symkey.c: (split from pkcs11/gck/tests/unit-test-crypto.c) * gcr/gcr.pc.in: (added) * gcr/gcr-internal.c: (added) * gcr/gcr-internal.h: (added) * gcr/gcr-marshal.list: (added) * gcr/gcr-parser.c: (added) * gcr/gcr-parser.h: (added) * gcr/gcr-types.h: (added) * gcr/Makefile.am: (added) * gcr/template/*: (added) * gcr/tests/Makefile.am: (added) * gcr/tests/unit-test-parser.c: (added) * gcr/tests/test-data: (copied from daemon/pkix/test/test-data) * gp11/gp11.h: * pkcs11/gck/gck-crypto.c: * pkcs11/gck/gck-crypto.h: * pkcs11/gck/gck-data-der.c: * pkcs11/gck/gck-data-der.h: * pkcs11/gck/gck-data-file.c: * pkcs11/gck/gck-data-openssl.c: (moved) * pkcs11/gck/gck-data-openssl.h: (moved) * pkcs11/gck/gck-data-pem.c: (combined into egg/egg-openssl.c) * pkcs11/gck/gck-data-pem.c: (combined into egg/egg-openssl.h) * pkcs11/gck/gck-util.c: * pkcs11/gck/gck-util.h: * pkcs11/gck/Makefile.am: * pkcs11/gck/tests/unit-test-crypto.c: * pkcs11/gck/tests/unit-test-data-openssl.c: (moved) * pkcs11/gck/tests/unit-test-util.c: (moved) * pkcs11/roots-store/gck-roots-module.c: * pkcs11/ssh-store/gck-ssh-openssh.c: * pkcs11/user-store/gck-user-storage.c: * configure.in: * Makefile.am: Add new gcr library for crypto UI and related tasks. Implement GckParser class. svn path=/trunk/; revision=1463
-rw-r--r--ChangeLog50
-rw-r--r--Makefile.am1
-rw-r--r--configure.in13
-rw-r--r--egg/Makefile.am5
-rw-r--r--egg/egg-asn1.c5
-rw-r--r--egg/egg-hex.c104
-rw-r--r--egg/egg-hex.h34
-rw-r--r--egg/egg-openssl.c (renamed from pkcs11/gck/gck-data-openssl.c)376
-rw-r--r--egg/egg-openssl.h56
-rw-r--r--egg/egg-symkey.c1027
-rw-r--r--egg/egg-symkey.h76
-rw-r--r--egg/tests/Makefile.am3
-rw-r--r--egg/tests/unit-test-asn1.c5
-rw-r--r--egg/tests/unit-test-hex.c (renamed from pkcs11/gck/tests/unit-test-util.c)12
-rw-r--r--egg/tests/unit-test-openssl.c (renamed from pkcs11/gck/tests/unit-test-data-openssl.c)31
-rw-r--r--egg/tests/unit-test-symkey.c226
-rw-r--r--gcr/Makefile.am62
-rw-r--r--gcr/gcr-import-dialog.glade167
-rw-r--r--gcr/gcr-importer.c213
-rw-r--r--gcr/gcr-importer.h81
-rw-r--r--gcr/gcr-internal.c116
-rw-r--r--gcr/gcr-internal.h8
-rw-r--r--gcr/gcr-marshal.list1
-rw-r--r--gcr/gcr-parser.c1710
-rw-r--r--gcr/gcr-parser.h92
-rw-r--r--gcr/gcr-types.h45
-rw-r--r--gcr/gcr.pc.in14
-rw-r--r--gcr/template/gcr-xxx.c146
-rw-r--r--gcr/template/gcr-xxx.h55
-rw-r--r--gcr/tests/Makefile.am13
-rw-r--r--gcr/tests/test-data/RSA_Root_Certificate_1.pem19
-rw-r--r--gcr/tests/test-data/RSA_Security_1024_v3.pem16
-rw-r--r--gcr/tests/test-data/RSA_Security_2048_v3.pem22
-rw-r--r--gcr/tests/test-data/Thawte_Personal_Basic_CA.pem20
-rw-r--r--gcr/tests/test-data/Thawte_Personal_Freemail_CA.pem21
-rw-r--r--gcr/tests/test-data/Thawte_Personal_Premium_CA.pem21
-rw-r--r--gcr/tests/test-data/Thawte_Premium_Server_CA.pem21
-rw-r--r--gcr/tests/test-data/Thawte_Server_CA.pem20
-rw-r--r--gcr/tests/test-data/Thawte_Time_Stamping_CA.pem18
-rw-r--r--gcr/tests/test-data/ca-certificates.crt2560
-rw-r--r--gcr/tests/test-data/cacert.org.pem41
-rw-r--r--gcr/tests/test-data/der-certificate.crtbin0 -> 747 bytes
-rw-r--r--gcr/tests/test-data/der-dsa-1024.keybin0 -> 447 bytes
-rw-r--r--gcr/tests/test-data/der-pkcs8-PBE-MD5-DES.keybin0 -> 677 bytes
-rw-r--r--gcr/tests/test-data/der-pkcs8-PBE-SHA1-3DES.keybin0 -> 678 bytes
-rw-r--r--gcr/tests/test-data/der-pkcs8-PBE-SHA1-DES.keybin0 -> 677 bytes
-rw-r--r--gcr/tests/test-data/der-pkcs8-PBE-SHA1-RC2-40.keybin0 -> 678 bytes
-rw-r--r--gcr/tests/test-data/der-pkcs8-PBE-SHA1-RC4-128.keybin0 -> 673 bytes
-rw-r--r--gcr/tests/test-data/der-pkcs8-dsa.keybin0 -> 335 bytes
-rw-r--r--gcr/tests/test-data/der-pkcs8-encrypted-pkcs5.keybin0 -> 677 bytes
-rw-r--r--gcr/tests/test-data/der-pkcs8-v2-des.keybin0 -> 711 bytes
-rw-r--r--gcr/tests/test-data/der-pkcs8-v2-des3.keybin0 -> 714 bytes
-rw-r--r--gcr/tests/test-data/der-pkcs8.keybin0 -> 635 bytes
-rw-r--r--gcr/tests/test-data/der-rsa-1024.keybin0 -> 609 bytes
-rw-r--r--gcr/tests/test-data/email.p12bin0 -> 2488 bytes
-rw-r--r--gcr/tests/test-data/pem-dsa-1024.key12
-rw-r--r--gcr/tests/test-data/pem-pkcs8.key17
-rw-r--r--gcr/tests/test-data/pem-rsa-enc.key18
-rwxr-xr-xgcr/tests/test-data/test-x509-swiss.p7bbin0 -> 1002 bytes
-rw-r--r--gcr/tests/test-data/unclient.p12bin0 -> 1476 bytes
-rw-r--r--gcr/tests/unit-test-parser.c136
-rw-r--r--gp11/gp11.h6
-rw-r--r--pkcs11/gck/Makefile.am2
-rw-r--r--pkcs11/gck/gck-crypto.c514
-rw-r--r--pkcs11/gck/gck-crypto.h40
-rw-r--r--pkcs11/gck/gck-data-der.c481
-rw-r--r--pkcs11/gck/gck-data-der.h20
-rw-r--r--pkcs11/gck/gck-data-file.c3
-rw-r--r--pkcs11/gck/gck-data-openssl.h43
-rw-r--r--pkcs11/gck/gck-data-pem.c345
-rw-r--r--pkcs11/gck/gck-data-pem.h42
-rw-r--r--pkcs11/gck/gck-util.c78
-rw-r--r--pkcs11/gck/gck-util.h7
-rw-r--r--pkcs11/gck/tests/Makefile.am2
-rw-r--r--pkcs11/gck/tests/unit-test-crypto.c184
-rw-r--r--pkcs11/roots-store/gck-roots-module.c5
-rw-r--r--pkcs11/ssh-store/gck-ssh-openssh.c9
-rw-r--r--pkcs11/user-store/gck-user-storage.c4
78 files changed, 7664 insertions, 1830 deletions
diff --git a/ChangeLog b/ChangeLog
index 240019cc..d0b15adf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,55 @@
2009-01-17 Stef Walter <stef@memberwebs.com>
+ * egg/egg-asn1.c:
+ * egg/egg-hex.c: (split from pkcs11/gck/gck-util.c)
+ * egg/egg-hex.h: (split from pkcs11/gck/gck-util.h)
+ * egg/egg-openssl.c: (moved from pkcs11/gck/gck-data-openssl.c)
+ * egg/egg-openssl.h: (moved from pkcs11/gck/gck-data-openssl.h)
+ * egg/egg-symkey.c: (split from pkcs11/gck/gck-crypto.c)
+ * egg/egg-symkey.h: (split from pkcs11/gck/gck-crypto.h)
+ * egg/Makefile.am:
+ * egg/tests/Makefile.am:
+ * egg/tests/unit-test-asn1.c:
+ * egg/tests/unit-test-hex.c: (moved from pkcs11/gck/tests/unit-test-util.c)
+ * egg/tests/unit-test-openssl.c: (moved from pkcs11/gck/tests/unit-test-data-openssl.c)
+ * egg/tests/unit-test-symkey.c: (split from pkcs11/gck/tests/unit-test-crypto.c)
+ * gcr/gcr.pc.in: (added)
+ * gcr/gcr-internal.c: (added)
+ * gcr/gcr-internal.h: (added)
+ * gcr/gcr-marshal.list: (added)
+ * gcr/gcr-parser.c: (added)
+ * gcr/gcr-parser.h: (added)
+ * gcr/gcr-types.h: (added)
+ * gcr/Makefile.am: (added)
+ * gcr/template/*: (added)
+ * gcr/tests/Makefile.am: (added)
+ * gcr/tests/unit-test-parser.c: (added)
+ * gcr/tests/test-data: (copied from daemon/pkix/test/test-data)
+ * gp11/gp11.h:
+ * pkcs11/gck/gck-crypto.c:
+ * pkcs11/gck/gck-crypto.h:
+ * pkcs11/gck/gck-data-der.c:
+ * pkcs11/gck/gck-data-der.h:
+ * pkcs11/gck/gck-data-file.c:
+ * pkcs11/gck/gck-data-openssl.c: (moved)
+ * pkcs11/gck/gck-data-openssl.h: (moved)
+ * pkcs11/gck/gck-data-pem.c: (combined into egg/egg-openssl.c)
+ * pkcs11/gck/gck-data-pem.c: (combined into egg/egg-openssl.h)
+ * pkcs11/gck/gck-util.c:
+ * pkcs11/gck/gck-util.h:
+ * pkcs11/gck/Makefile.am:
+ * pkcs11/gck/tests/unit-test-crypto.c:
+ * pkcs11/gck/tests/unit-test-data-openssl.c: (moved)
+ * pkcs11/gck/tests/unit-test-util.c: (moved)
+ * pkcs11/roots-store/gck-roots-module.c:
+ * pkcs11/ssh-store/gck-ssh-openssh.c:
+ * pkcs11/user-store/gck-user-storage.c:
+ * configure.in:
+ * Makefile.am: Add new gcr library for crypto UI and related tasks. Implement
+ GckParser class.
+
+2009-01-17 Stef Walter <stef@memberwebs.com>
+
* egg/egg-asn1.c: (moved from pkcs11/gck/gck-data-asn1.c)
* egg/egg-asn1.h: (moved from pkcs11/gck/gck-data-asn1.h)
* egg/egg-buffer.c: (moved from common/gkr-buffer.c)
diff --git a/Makefile.am b/Makefile.am
index 04a9869b..2de8f7de 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -14,6 +14,7 @@ SUBDIRS = \
. \
gp11 \
egg \
+ gcr \
common \
library \
pkcs11 \
diff --git a/configure.in b/configure.in
index 6f22c6fb..65781eb6 100644
--- a/configure.in
+++ b/configure.in
@@ -4,12 +4,16 @@ AM_INIT_AUTOMAKE(gnome-keyring, 2.25.4.2)
AM_CONFIG_HEADER(config.h)
dnl ****************************************************************************
-dnl GP11 library libtool versioning
+dnl Library libtool versioning
GP11_MAJOR=0 # Increment for major version number, breaks old apps.
GP11_REVISION=0 # Increment for internal changes, nothing affected.
GP11_AGE=0 # Increment for interface that doesn't break anything
+GCR_MAJOR=0 # Increment for major version number, breaks old apps.
+GCR_REVISION=0 # Increment for internal changes, nothing affected.
+GCR_AGE=0 # Increment for interface that doesn't break anything
+
dnl ****************************************************************************
AM_SANITY_CHECK
@@ -469,6 +473,10 @@ GP11_LT_RELEASE=$GP11_MAJOR:$GP11_REVISION:$GP11_AGE
AC_SUBST(GP11_LT_RELEASE)
AC_SUBST(GP11_MAJOR)
+GCR_LT_RELEASE=$GCR_MAJOR:$GCR_REVISION:$GCR_AGE
+AC_SUBST(GCR_LT_RELEASE)
+AC_SUBST(GCR_MAJOR)
+
AC_SUBST(DAEMON_CFLAGS)
AC_SUBST(DAEMON_LIBS)
@@ -497,6 +505,9 @@ daemon/ssh/tests/Makefile
daemon/ui/Makefile
egg/Makefile
egg/tests/Makefile
+gcr/gcr.pc
+gcr/Makefile
+gcr/tests/Makefile
gp11/gp11.pc
gp11/Makefile
gp11/reference/Makefile
diff --git a/egg/Makefile.am b/egg/Makefile.am
index 4024b7a6..439a5891 100644
--- a/egg/Makefile.am
+++ b/egg/Makefile.am
@@ -19,8 +19,11 @@ libegg_la_CFLAGS = \
libegg_la_SOURCES = \
egg-asn1.c egg-asn1.h \
egg-buffer.c egg-buffer.h \
+ egg-hex.c egg-hex.h \
+ egg-openssl.c egg-openssl.h \
egg-unix-credentials.c egg-unix-credentials.h \
- egg-secure-memory.c egg-secure-memory.h
+ egg-secure-memory.c egg-secure-memory.h \
+ egg-symkey.c egg-symkey.h
asn1-def-pk.h: pk.asn
asn1Parser -o asn1-def-pk.h pk.asn
diff --git a/egg/egg-asn1.c b/egg/egg-asn1.c
index 94f13316..2910ca02 100644
--- a/egg/egg-asn1.c
+++ b/egg/egg-asn1.c
@@ -335,12 +335,9 @@ egg_asn1_read_oid (ASN1_TYPE asn, const gchar *part)
if (!buf)
return 0;
- quark = g_quark_try_string ((gchar*)buf);
+ quark = g_quark_from_string ((gchar*)buf);
g_free (buf);
- if (quark == 0)
- quark = g_quark_from_static_string ("0.UNKNOWN.OID");
-
return quark;
}
diff --git a/egg/egg-hex.c b/egg/egg-hex.c
new file mode 100644
index 00000000..cdfc33e8
--- /dev/null
+++ b/egg/egg-hex.c
@@ -0,0 +1,104 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "egg-hex.h"
+
+#include <string.h>
+
+static const char HEXC[] = "0123456789ABCDEF";
+
+guchar*
+egg_hex_decode (const gchar *data, gssize n_data, gsize *n_decoded)
+{
+ guchar *result;
+ guchar *decoded;
+ gushort j;
+ gint state = 0;
+ const gchar* pos;
+
+ g_return_val_if_fail (data || !n_data, NULL);
+ g_return_val_if_fail (n_decoded, NULL);
+
+ if (n_data == -1)
+ n_data = strlen (data);
+
+ decoded = result = g_malloc0 ((n_data / 2) + 1);
+ *n_decoded = 0;
+
+ while (n_data > 0) {
+ if (!g_ascii_isspace (*data)) {
+
+ /* Find the position */
+ pos = strchr (HEXC, g_ascii_toupper (*data));
+ if (pos == 0)
+ break;
+
+ j = pos - HEXC;
+ if(!state) {
+ *decoded = (j & 0xf) << 4;
+ state = 1;
+ } else {
+ *decoded |= (j & 0xf);
+ (*n_decoded)++;
+ decoded++;
+ state = 0;
+ }
+ }
+
+ ++data;
+ --n_data;
+ }
+
+ /* Parsing error */
+ if (state != 0) {
+ g_free (result);
+ result = NULL;
+ }
+
+ return result;
+}
+
+gchar*
+egg_hex_encode (const guchar *data, gsize n_data)
+{
+ gchar *result, *encoded;
+ guchar j;
+
+ g_return_val_if_fail (data || !n_data, NULL);
+
+ encoded = result = g_malloc0 (n_data * 2 + 1);
+
+ while(n_data > 0) {
+ j = *(data) >> 4 & 0xf;
+ *(encoded++) = HEXC[j];
+
+ j = *(data++) & 0xf;
+ *(encoded++) = HEXC[j];
+
+ n_data--;
+ }
+
+ /* Make sure still null terminated */
+ g_assert (encoded[n_data * 2] == 0);
+ return result;
+}
diff --git a/egg/egg-hex.h b/egg/egg-hex.h
new file mode 100644
index 00000000..ddcd9238
--- /dev/null
+++ b/egg/egg-hex.h
@@ -0,0 +1,34 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef EGG_HEX_H_
+#define EGG_HEX_H_
+
+#include <glib.h>
+
+guchar* egg_hex_decode (const gchar *data,
+ gssize n_data,
+ gsize *n_decoded);
+
+gchar* egg_hex_encode (const guchar *data,
+ gsize n_data);
+
+#endif /* EGG_HEX_H_ */
diff --git a/pkcs11/gck/gck-data-openssl.c b/egg/egg-openssl.c
index bb0f7a5a..9ac1e2ae 100644
--- a/pkcs11/gck/gck-data-openssl.c
+++ b/egg/egg-openssl.c
@@ -1,5 +1,5 @@
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
-/* gck-data-openssl.c - OpenSSL compatibility functionality
+/* egg-openssl.c - OpenSSL compatibility functionality
Copyright (C) 2007 Stefan Walter
@@ -23,15 +23,331 @@
#include "config.h"
-#include "gck-crypto.h"
-#include "gck-data-openssl.h"
-#include "gck-util.h"
+#include "egg-hex.h"
+#include "egg-openssl.h"
+#include "egg-secure-memory.h"
+#include "egg-symkey.h"
#include <gcrypt.h>
#include <libtasn1.h>
#include <glib.h>
+#include <ctype.h>
+#include <string.h>
+
+/*
+ * PEM looks like:
+ *
+ * -----BEGIN RSA PRIVATE KEY-----
+ * Proc-Type: 4,ENCRYPTED
+ * DEK-Info: DES-EDE3-CBC,704CFFD62FBA03E9
+ *
+ * 4AV/g0BiTeb07hzo4/Ct47HGhHEshMhBPGJ843QzuAinpZBbg3OxwPsQsLgoPhJL
+ * Bg6Oxyz9M4UN1Xlx6Lyo2lRT908mBP6dl/OItLsVArqAzM+e29KHQVNjV1h7xN9F
+ * u84tOgZftKun+ZkQUOoRvMLLu4yV4CUraks9tgyXquugGba/tbeyj2MYsC8wwSJX
+ * ....
+ * -----END RSA PRIVATE KEY-----
+ */
+
+#define PEM_SUFF "-----"
+#define PEM_SUFF_L 5
+#define PEM_PREF_BEGIN "-----BEGIN "
+#define PEM_PREF_BEGIN_L 11
+#define PEM_PREF_END "-----END "
+#define PEM_PREF_END_L 9
+
+static void
+parse_header_lines (const gchar *hbeg, const gchar *hend, GHashTable **result)
+{
+ gchar **lines, **l;
+ gchar *line, *name, *value;
+ gchar *copy;
+
+ copy = g_strndup (hbeg, hend - hbeg);
+ lines = g_strsplit (copy, "\n", 0);
+ g_free (copy);
+
+ for (l = lines; l && *l; ++l) {
+ line = *l;
+ g_strstrip (line);
+
+ /* Look for the break between name: value */
+ value = strchr (line, ':');
+ if (value == NULL)
+ continue;
+
+ *value = 0;
+ value = g_strdup (value + 1);
+ g_strstrip (value);
+
+ name = g_strdup (line);
+ g_strstrip (name);
+
+ if (!*result)
+ *result = egg_openssl_headers_new ();
+ g_hash_table_replace (*result, name, value);
+ }
+
+ g_strfreev (lines);
+}
+
+static const gchar*
+pem_find_begin (const gchar *data, gsize n_data, GQuark *type)
+{
+ const gchar *pref, *suff;
+ gchar *stype;
+
+ /* Look for a prefix */
+ pref = g_strstr_len ((gchar*)data, n_data, PEM_PREF_BEGIN);
+ if (!pref)
+ return NULL;
+
+ n_data -= (pref - data) + PEM_PREF_BEGIN_L;
+ data = pref + PEM_PREF_BEGIN_L;
+
+ /* Look for the end of that begin */
+ suff = g_strstr_len ((gchar*)data, n_data, PEM_SUFF);
+ if (!suff)
+ return NULL;
+
+ /* Make sure on the same line */
+ if (memchr (pref, '\n', suff - pref))
+ return NULL;
+
+ if (type) {
+ *type = 0;
+ pref += PEM_PREF_BEGIN_L;
+ g_assert (suff > pref);
+ stype = g_alloca (suff - pref + 1);
+ memcpy (stype, pref, suff - pref);
+ stype[suff - pref] = 0;
+ *type = g_quark_from_string (stype);
+ }
+
+ /* The byte after this ---BEGIN--- */
+ return suff + PEM_SUFF_L;
+}
+
+static const gchar*
+pem_find_end (const gchar *data, gsize n_data, GQuark type)
+{
+ const gchar *stype;
+ const gchar *pref;
+ gsize n_type;
+
+ /* Look for a prefix */
+ pref = g_strstr_len (data, n_data, PEM_PREF_END);
+ if (!pref)
+ return NULL;
+
+ n_data -= (pref - data) + PEM_PREF_END_L;
+ data = pref + PEM_PREF_END_L;
+
+ /* Next comes the type string */
+ stype = g_quark_to_string (type);
+ n_type = strlen (stype);
+ if (strncmp ((gchar*)data, stype, n_type) != 0)
+ return NULL;
+
+ n_data -= n_type;
+ data += n_type;
+
+ /* Next comes the suffix */
+ if (strncmp ((gchar*)data, PEM_SUFF, PEM_SUFF_L) != 0)
+ return NULL;
+
+ /* The beginning of this ---END--- */
+ return pref;
+}
+
+static gboolean
+pem_parse_block (const gchar *data, gsize n_data, guchar **decoded, gsize *n_decoded,
+ GHashTable **headers)
+{
+ const gchar *x, *hbeg, *hend;
+ const gchar *p, *end;
+ gint state = 0;
+ guint save = 0;
+
+ g_assert (data);
+ g_assert (n_data);
+
+ g_assert (decoded);
+ g_assert (n_decoded);
+
+ p = data;
+ end = p + n_data;
+
+ hbeg = hend = NULL;
+
+ /* Try and find a pair of blank lines with only white space between */
+ while (hend == NULL) {
+ x = memchr (p, '\n', end - p);
+ if (!x)
+ break;
+ ++x;
+ while (isspace (*x)) {
+ /* Found a second line, with only spaces between */
+ if (*x == '\n') {
+ hbeg = data;
+ hend = x;
+ break;
+ /* Found a space between two lines */
+ } else {
+ ++x;
+ }
+ }
+
+ /* Try next line */
+ p = x;
+ }
+
+ /* Headers found? */
+ if (hbeg && hend) {
+ data = hend;
+ n_data = end - data;
+ }
+
+ *n_decoded = (n_data * 3) / 4 + 1;
+ if (egg_secure_check (data))
+ *decoded = egg_secure_alloc (*n_decoded);
+ else
+ *decoded = g_malloc0 (*n_decoded);
+ g_return_val_if_fail (*decoded, FALSE);
+
+ *n_decoded = g_base64_decode_step (data, n_data, *decoded, &state, &save);
+ if (!*n_decoded) {
+ egg_secure_free (*decoded);
+ return FALSE;
+ }
+
+ if (headers && hbeg && hend)
+ parse_header_lines (hbeg, hend, headers);
+
+ return TRUE;
+}
+
+GHashTable*
+egg_openssl_headers_new (void)
+{
+ return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+}
+
+guint
+egg_openssl_pem_parse (const guchar *data, gsize n_data,
+ EggOpensslPemCallback callback, gpointer user_data)
+{
+ const gchar *beg, *end;
+ guint nfound = 0;
+ guchar *decoded = NULL;
+ gsize n_decoded = 0;
+ GHashTable *headers = NULL;
+ GQuark type;
+
+ g_return_val_if_fail (data, 0);
+ g_return_val_if_fail (n_data, 0);
+ g_return_val_if_fail (callback, 0);
+
+ while (n_data > 0) {
+
+ /* This returns the first character after the PEM BEGIN header */
+ beg = pem_find_begin ((const gchar*)data, n_data, &type);
+ if (!beg)
+ break;
+
+ g_assert (type);
+
+ /* This returns the character position before the PEM END header */
+ end = pem_find_end ((const gchar*)beg, n_data - ((const guchar*)beg - data), type);
+ if (!end)
+ break;
+
+ if (beg != end) {
+ if (pem_parse_block (beg, end - beg, &decoded, &n_decoded, &headers)) {
+ (callback) (type, decoded, n_decoded, headers, user_data);
+ ++nfound;
+ egg_secure_free (decoded);
+ if (headers)
+ g_hash_table_remove_all (headers);
+ }
+ }
+
+ /* Try for another block */
+ end += PEM_SUFF_L;
+ n_data -= (const guchar*)end - data;
+ data = (const guchar*)end;
+ }
+
+ if (headers)
+ g_hash_table_destroy (headers);
+
+ return nfound;
+}
+
+#ifdef UNTESTED_CODE
+
+static void
+append_each_header (gpointer key, gpointer value, gpointer user_data)
+{
+ GString *string = (GString*)user_data;
+
+ g_string_append (string, (gchar*)key);
+ g_string_append (string, ": ");
+ g_string_append (string, (gchar*)value);
+ g_string_append_c (string, '\n');
+}
+
+guchar*
+egg_openssl_pem_write (const guchar *data, gsize n_data, GQuark type,
+ GHashTable *headers, gsize *n_result)
+{
+ GString *string;
+ gint state, save;
+ gsize length, n_prefix;
+
+ g_return_val_if_fail (data || !n_data, NULL);
+ g_return_val_if_fail (type, NULL);
+ g_return_val_if_fail (n_result, NULL);
+
+ string = g_string_sized_new (4096);
+
+ /* The prefix */
+ g_string_append_len (string, PEM_PREF_BEGIN, PEM_PREF_BEGIN_L);
+ g_string_append (string, g_quark_to_string (type));
+ g_string_append_len (string, PEM_SUFF, PEM_SUFF_L);
+ g_string_append_c (string, '\n');
+
+ /* The headers */
+ if (headers && g_hash_table_size (headers) > 0) {
+ g_hash_table_foreach (headers, append_each_header, string);
+ g_string_append_c (string, '\n');
+ }
+
+ /* Resize string to fit the base64 data. Algorithm from Glib reference */
+ length = n_data * 4 / 3 + n_data * 4 / (3 * 72) + 7;
+ n_prefix = string->len;
+ g_string_set_size (string, n_prefix + length);
+
+ /* The actual base64 data */
+ state = save = 0;
+ length = g_base64_encode_step (data, n_data, TRUE,
+ string->str + string->len, &state, &save);
+ g_string_set_size (string, n_prefix + length);
+
+ /* The suffix */
+ g_string_append_c (string, '\n');
+ g_string_append_len (string, PEM_PREF_END, PEM_PREF_END_L);
+ g_string_append (string, g_quark_to_string (type));
+ g_string_append_len (string, PEM_SUFF, PEM_SUFF_L);
+ g_string_append_c (string, '\n');
+
+ *n_result = string->len;
+ return (guchar*)g_string_free (string, FALSE);
+}
+
+#endif /* UNTESTED_CODE */
+
/* ----------------------------------------------------------------------------
* DEFINITIONS
*/
@@ -137,7 +453,7 @@ const static struct {
/* ------------------------------------------------------------------------- */
int
-gck_data_openssl_parse_algo (const char *name, int *mode)
+egg_openssl_parse_algo (const char *name, int *mode)
{
static GQuark openssl_quarks[G_N_ELEMENTS(openssl_algos)] = { 0, };
static gsize openssl_quarks_inited = 0;
@@ -176,7 +492,7 @@ parse_dekinfo (const gchar *dek, int *algo, int *mode, guchar **iv)
goto done;
/* Parse the algorithm name */
- *algo = gck_data_openssl_parse_algo (parts[0], mode);
+ *algo = egg_openssl_parse_algo (parts[0], mode);
if (!*algo)
goto done;
@@ -188,7 +504,7 @@ parse_dekinfo (const gchar *dek, int *algo, int *mode, guchar **iv)
/* Parse the IV */
ivlen = gcry_cipher_get_algo_blklen (*algo);
- *iv = gck_util_hex_decode (parts[1], strlen(parts[1]), &len);
+ *iv = egg_hex_decode (parts[1], strlen(parts[1]), &len);
if (!*iv || ivlen != len) {
g_free (*iv);
goto done;
@@ -201,10 +517,10 @@ done:
return success;
}
-GckDataResult
-gck_data_openssl_decrypt_block (const gchar *dekinfo, const gchar *password,
- gssize n_password, const guchar *data, gsize n_data,
- guchar **decrypted, gsize *n_decrypted)
+gboolean
+egg_openssl_decrypt_block (const gchar *dekinfo, const gchar *password,
+ gssize n_password, const guchar *data, gsize n_data,
+ guchar **decrypted, gsize *n_decrypted)
{
gcry_cipher_hd_t ch;
guchar *key = NULL;
@@ -214,7 +530,7 @@ gck_data_openssl_decrypt_block (const gchar *dekinfo, const gchar *password,
int mode = 0;
if (!parse_dekinfo (dekinfo, &algo, &mode, &iv))
- return GCK_DATA_UNRECOGNIZED;
+ return FALSE;
ivlen = gcry_cipher_get_algo_blklen (algo);
@@ -222,42 +538,42 @@ gck_data_openssl_decrypt_block (const gchar *dekinfo, const gchar *password,
g_return_val_if_fail (ivlen >= 8, FALSE);
/* IV is already set from the DEK info */
- if (!gck_crypto_symkey_generate_simple (algo, GCRY_MD_MD5, password,
+ if (!egg_symkey_generate_simple (algo, GCRY_MD_MD5, password,
n_password, iv, 8, 1, &key, NULL)) {
g_free (iv);
- return GCK_DATA_FAILURE;
+ return FALSE;
}
/* TODO: Use secure memory */
gcry = gcry_cipher_open (&ch, algo, mode, 0);
- g_return_val_if_fail (!gcry, GCK_DATA_FAILURE);
+ g_return_val_if_fail (!gcry, FALSE);
gcry = gcry_cipher_setkey (ch, key, gcry_cipher_get_algo_keylen (algo));
- g_return_val_if_fail (!gcry, GCK_DATA_UNRECOGNIZED);
- gcry_free (key);
+ g_return_val_if_fail (!gcry, FALSE);
+ egg_secure_free (key);
/* 16 = 128 bits */
gcry = gcry_cipher_setiv (ch, iv, ivlen);
- g_return_val_if_fail (!gcry, GCK_DATA_UNRECOGNIZED);
+ g_return_val_if_fail (!gcry, FALSE);
g_free (iv);
/* Allocate output area */
*n_decrypted = n_data;
- *decrypted = gcry_calloc_secure (n_data, 1);
+ *decrypted = egg_secure_alloc (n_data);
gcry = gcry_cipher_decrypt (ch, *decrypted, *n_decrypted, (void*)data, n_data);
if (gcry) {
- gcry_free (*decrypted);
- g_return_val_if_reached (GCK_DATA_FAILURE);
+ egg_secure_free (*decrypted);
+ g_return_val_if_reached (FALSE);
}
gcry_cipher_close (ch);
- return GCK_DATA_SUCCESS;
+ return TRUE;
}
gboolean
-gck_data_openssl_encrypt_block (const gchar *dekinfo, const gchar *password,
+egg_openssl_encrypt_block (const gchar *dekinfo, const gchar *password,
gssize n_password, const guchar *data, gsize n_data,
guchar **encrypted, gsize *n_encrypted)
{
@@ -279,7 +595,7 @@ gck_data_openssl_encrypt_block (const gchar *dekinfo, const gchar *password,
g_return_val_if_fail (ivlen >= 8, FALSE);
/* IV is already set from the DEK info */
- if (!gck_crypto_symkey_generate_simple (algo, GCRY_MD_MD5, password,
+ if (!egg_symkey_generate_simple (algo, GCRY_MD_MD5, password,
n_password, iv, 8, 1, &key, NULL))
g_return_val_if_reached (FALSE);
@@ -288,7 +604,7 @@ gck_data_openssl_encrypt_block (const gchar *dekinfo, const gchar *password,
gcry = gcry_cipher_setkey (ch, key, gcry_cipher_get_algo_keylen (algo));
g_return_val_if_fail (!gcry, FALSE);
- gcry_free (key);
+ egg_secure_free (key);
/* 16 = 128 bits */
gcry = gcry_cipher_setiv (ch, iv, ivlen);
@@ -315,11 +631,11 @@ gck_data_openssl_encrypt_block (const gchar *dekinfo, const gchar *password,
/* Encrypt the padded block */
if (n_overflow) {
- padded = gcry_calloc_secure (ivlen, 1);
+ padded = egg_secure_alloc (ivlen);
memset (padded, 0, ivlen);
memcpy (padded, data + n_batch, n_overflow);
gcry = gcry_cipher_encrypt (ch, *encrypted + n_batch, ivlen, padded, ivlen);
- gcry_free (padded);
+ egg_secure_free (padded);
if (gcry) {
g_free (*encrypted);
g_return_val_if_reached (FALSE);
@@ -331,7 +647,7 @@ gck_data_openssl_encrypt_block (const gchar *dekinfo, const gchar *password,
}
const gchar*
-gck_data_openssl_get_dekinfo (GHashTable *headers)
+egg_openssl_get_dekinfo (GHashTable *headers)
{
const gchar *val;
if (!headers)
@@ -345,7 +661,7 @@ gck_data_openssl_get_dekinfo (GHashTable *headers)
}
const gchar*
-gck_data_openssl_prep_dekinfo (GHashTable *headers)
+egg_openssl_prep_dekinfo (GHashTable *headers)
{
gchar *dekinfo, *hex;
gsize ivlen;
@@ -358,7 +674,7 @@ gck_data_openssl_prep_dekinfo (GHashTable *headers)
gcry_create_nonce (iv, ivlen);
/* And encode it into the string */
- hex = gck_util_hex_encode (iv, ivlen);
+ hex = egg_hex_encode (iv, ivlen);
g_return_val_if_fail (hex, NULL);
dekinfo = g_strdup_printf ("DES-EDE3-CBC,%s", hex);
g_free (hex);
diff --git a/egg/egg-openssl.h b/egg/egg-openssl.h
new file mode 100644
index 00000000..878cf589
--- /dev/null
+++ b/egg/egg-openssl.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* egg-openssl.h - OpenSSL compatibility functionality
+
+ Copyright (C) 2007 Stefan Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Stef Walter <stef@memberwebs.com>
+*/
+
+#ifndef EGG_OPENSSL_H_
+#define EGG_OPENSSL_H_
+
+#include <glib.h>
+
+typedef void (*EggOpensslPemCallback) (GQuark type, const guchar *data, gsize n_data,
+ GHashTable *headers, gpointer user_data);
+
+GHashTable* egg_openssl_headers_new (void);
+
+guint egg_openssl_pem_parse (const guchar *data, gsize n_data,
+ EggOpensslPemCallback callback,
+ gpointer user_data);
+
+guchar* egg_openssl_pem_write (const guchar *data, gsize n_data,
+ GQuark type, GHashTable *headers,
+ gsize *n_result);
+
+int egg_openssl_parse_algo (const gchar *name, int *mode);
+
+gboolean egg_openssl_encrypt_block (const gchar *dekinfo, const gchar *password,
+ gssize n_password, const guchar *data, gsize n_data,
+ guchar **encrypted, gsize *n_encrypted);
+
+gboolean egg_openssl_decrypt_block (const gchar *dekinfo, const gchar *password,
+ gssize n_password, const guchar *data, gsize n_data,
+ guchar **decrypted, gsize *n_decrypted);
+
+const gchar* egg_openssl_get_dekinfo (GHashTable *headers);
+
+const gchar* egg_openssl_prep_dekinfo (GHashTable *headers);
+
+#endif /* EGG_OPENSSL_H_ */
diff --git a/egg/egg-symkey.c b/egg/egg-symkey.c
new file mode 100644
index 00000000..426c7632
--- /dev/null
+++ b/egg/egg-symkey.c
@@ -0,0 +1,1027 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "egg-asn1.h"
+#include "egg-secure-memory.h"
+#include "egg-symkey.h"
+
+/* -----------------------------------------------------------------------------
+ * QUARKS
+ */
+
+static GQuark OID_PBE_MD2_DES_CBC;
+static GQuark OID_PBE_MD5_DES_CBC;
+static GQuark OID_PBE_MD2_RC2_CBC;
+static GQuark OID_PBE_MD5_RC2_CBC;
+static GQuark OID_PBE_SHA1_DES_CBC;
+static GQuark OID_PBE_SHA1_RC2_CBC;
+static GQuark OID_PBES2;
+static GQuark OID_PBKDF2;
+
+static GQuark OID_DES_CBC;
+static GQuark OID_DES_RC2_CBC;
+static GQuark OID_DES_EDE3_CBC;
+static GQuark OID_DES_RC5_CBC;
+
+static GQuark OID_PKCS12_PBE_ARCFOUR_SHA1;
+static GQuark OID_PKCS12_PBE_RC4_40_SHA1;
+static GQuark OID_PKCS12_PBE_3DES_SHA1;
+static GQuark OID_PKCS12_PBE_2DES_SHA1;
+static GQuark OID_PKCS12_PBE_RC2_128_SHA1;
+static GQuark OID_PKCS12_PBE_RC2_40_SHA1;
+
+static void
+init_quarks (void)
+{
+ static volatile gsize quarks_inited = 0;
+
+ if (g_once_init_enter (&quarks_inited)) {
+
+ #define QUARK(name, value) \
+ name = g_quark_from_static_string(value)
+
+ QUARK (OID_PBE_MD2_DES_CBC, "1.2.840.113549.1.5.1");
+ QUARK (OID_PBE_MD5_DES_CBC, "1.2.840.113549.1.5.3");
+ QUARK (OID_PBE_MD2_RC2_CBC, "1.2.840.113549.1.5.4");
+ QUARK (OID_PBE_MD5_RC2_CBC, "1.2.840.113549.1.5.6");
+ QUARK (OID_PBE_SHA1_DES_CBC, "1.2.840.113549.1.5.10");
+ QUARK (OID_PBE_SHA1_RC2_CBC, "1.2.840.113549.1.5.11");
+
+ QUARK (OID_PBES2, "1.2.840.113549.1.5.13");
+
+ QUARK (OID_PBKDF2, "1.2.840.113549.1.5.12");
+
+ QUARK (OID_DES_CBC, "1.3.14.3.2.7");
+ QUARK (OID_DES_RC2_CBC, "1.2.840.113549.3.2");
+ QUARK (OID_DES_EDE3_CBC, "1.2.840.113549.3.7");
+ QUARK (OID_DES_RC5_CBC, "1.2.840.113549.3.9");
+
+ QUARK (OID_PKCS12_PBE_ARCFOUR_SHA1, "1.2.840.113549.1.12.1.1");
+ QUARK (OID_PKCS12_PBE_RC4_40_SHA1, "1.2.840.113549.1.12.1.2");
+ QUARK (OID_PKCS12_PBE_3DES_SHA1, "1.2.840.113549.1.12.1.3");
+ QUARK (OID_PKCS12_PBE_2DES_SHA1, "1.2.840.113549.1.12.1.4");
+ QUARK (OID_PKCS12_PBE_RC2_128_SHA1, "1.2.840.113549.1.12.1.5");
+ QUARK (OID_PKCS12_PBE_RC2_40_SHA1, "1.2.840.113549.1.12.1.6");
+
+ #undef QUARK
+
+ g_once_init_leave (&quarks_inited, 1);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * PASSWORD TO KEY/IV
+ */
+
+gboolean
+egg_symkey_generate_simple (int cipher_algo, int hash_algo,
+ const gchar *password, gssize n_password,
+ const guchar *salt, gsize n_salt, int iterations,
+ guchar **key, guchar **iv)
+{
+ gcry_md_hd_t mdh;
+ gcry_error_t gcry;
+ guchar *digest;
+ guchar *digested;
+ guint n_digest;
+ gint pass, i;
+ gint needed_iv, needed_key;
+ guchar *at_iv, *at_key;
+
+ g_assert (cipher_algo);
+ g_assert (hash_algo);
+
+ g_return_val_if_fail (iterations >= 1, FALSE);
+
+ if (!password)
+ n_password = 0;
+ if (n_password == -1)
+ n_password = strlen (password);
+
+ /*
+ * If cipher algo needs more bytes than hash algo has available
+ * then the entire hashing process is done again (with the previous
+ * hash bytes as extra input), and so on until satisfied.
+ */
+
+ needed_key = gcry_cipher_get_algo_keylen (cipher_algo);
+ needed_iv = gcry_cipher_get_algo_blklen (cipher_algo);
+
+ gcry = gcry_md_open (&mdh, hash_algo, 0);
+ if (gcry) {
+ g_warning ("couldn't create '%s' hash context: %s",
+ gcry_md_algo_name (hash_algo), gcry_strerror (gcry));
+ return FALSE;
+ }
+
+ n_digest = gcry_md_get_algo_dlen (hash_algo);
+ g_return_val_if_fail (n_digest > 0, FALSE);
+
+ digest = egg_secure_alloc (n_digest);
+ g_return_val_if_fail (digest, FALSE);
+ if (key) {
+ *key = egg_secure_alloc (needed_key);
+ g_return_val_if_fail (*key, FALSE);
+ }
+ if (iv)
+ *iv = g_new0 (guchar, needed_iv);
+
+ at_key = key ? *key : NULL;
+ at_iv = iv ? *iv : NULL;
+
+ for (pass = 0; TRUE; ++pass) {
+ gcry_md_reset (mdh);
+
+ /* Hash in the previous buffer on later passes */
+ if (pass > 0)
+ gcry_md_write (mdh, digest, n_digest);
+
+ if (password)
+ gcry_md_write (mdh, password, n_password);
+ if (salt && n_salt)
+ gcry_md_write (mdh, salt, n_salt);
+ gcry_md_final (mdh);
+ digested = gcry_md_read (mdh, 0);
+ g_return_val_if_fail (digested, FALSE);
+ memcpy (digest, digested, n_digest);
+
+ for (i = 1; i < iterations; ++i) {
+ gcry_md_reset (mdh);
+ gcry_md_write (mdh, digest, n_digest);
+ gcry_md_final (mdh);
+ digested = gcry_md_read (mdh, 0);
+ g_return_val_if_fail (digested, FALSE);
+ memcpy (digest, digested, n_digest);
+ }
+
+ /* Copy as much as possible into the destinations */
+ i = 0;
+ while (needed_key && i < n_digest) {
+ if (at_key)
+ *(at_key++) = digest[i];
+ needed_key--;
+ i++;
+ }
+ while (needed_iv && i < n_digest) {
+ if (at_iv)
+ *(at_iv++) = digest[i];
+ needed_iv--;
+ i++;
+ }
+
+ if (needed_key == 0 && needed_iv == 0)
+ break;
+ }
+
+ egg_secure_free (digest);
+ gcry_md_close (mdh);
+
+ return TRUE;
+}
+
+gboolean
+egg_symkey_generate_pbe (int cipher_algo, int hash_algo, const gchar *password,
+ gssize n_password, const guchar *salt, gsize n_salt, int iterations,
+ guchar **key, guchar **iv)
+{
+ gcry_md_hd_t mdh;
+ gcry_error_t gcry;
+ guchar *digest;
+ guchar *digested;
+ guint i, n_digest;
+ gint needed_iv, needed_key;
+
+ g_assert (cipher_algo);
+ g_assert (hash_algo);
+
+ g_return_val_if_fail (iterations >= 1, FALSE);
+
+ if (!password)
+ n_password = 0;
+ if (n_password == -1)
+ n_password = strlen (password);
+
+ /*
+ * We only do one pass here.
+ *
+ * The key ends up as the first needed_key bytes of the hash buffer.
+ * The iv ends up as the last needed_iv bytes of the hash buffer.
+ *
+ * The IV may overlap the key (which is stupid) if the wrong pair of
+ * hash/cipher algorithms are chosen.
+ */
+
+ n_digest = gcry_md_get_algo_dlen (hash_algo);
+ g_return_val_if_fail (n_digest > 0, FALSE);
+
+ needed_key = gcry_cipher_get_algo_keylen (cipher_algo);
+ needed_iv = gcry_cipher_get_algo_blklen (cipher_algo);
+ if (needed_iv + needed_key > 16 || needed_iv + needed_key > n_digest) {
+ g_warning ("using PBE symkey generation with %s using an algorithm that needs "
+ "too many bytes of key and/or IV: %s",
+ gcry_cipher_algo_name (hash_algo),
+ gcry_cipher_algo_name (cipher_algo));
+ return FALSE;
+ }
+
+ gcry = gcry_md_open (&mdh, hash_algo, 0);
+ if (gcry) {
+ g_warning ("couldn't create '%s' hash context: %s",
+ gcry_md_algo_name (hash_algo), gcry_strerror (gcry));
+ return FALSE;
+ }
+
+ digest = egg_secure_alloc (n_digest);
+ g_return_val_if_fail (digest, FALSE);
+ if (key) {
+ *key = egg_secure_alloc (needed_key);
+ g_return_val_if_fail (*key, FALSE);
+ }
+ if (iv)
+ *iv = g_new0 (guchar, needed_iv);
+
+ if (password)
+ gcry_md_write (mdh, password, n_password);
+ if (salt && n_salt)
+ gcry_md_write (mdh, salt, n_salt);
+ gcry_md_final (mdh);
+ digested = gcry_md_read (mdh, 0);
+ g_return_val_if_fail (digested, FALSE);
+ memcpy (digest, digested, n_digest);
+
+ for (i = 1; i < iterations; ++i)
+ gcry_md_hash_buffer (hash_algo, digest, digest, n_digest);
+
+ /* The first x bytes are the key */
+ if (key) {
+ g_assert (needed_key <= n_digest);
+ memcpy (*key, digest, needed_key);
+ }
+
+ /* The last 16 - x bytes are the iv */
+ if (iv) {
+ g_assert (needed_iv <= n_digest && n_digest >= 16);
+ memcpy (*iv, digest + (16 - needed_iv), needed_iv);
+ }
+
+ egg_secure_free (digest);
+ gcry_md_close (mdh);
+
+ return TRUE;
+}
+
+static gboolean
+generate_pkcs12 (int hash_algo, int type, const gchar *utf8_password,
+ gssize n_password, const guchar *salt, gsize n_salt,
+ int iterations, guchar *output, gsize n_output)
+{
+ gcry_mpi_t num_b1, num_ij;
+ guchar *hash, *buf_i, *buf_b;
+ const gchar *end_password;
+ gcry_md_hd_t mdh;
+ const gchar *p2;
+ guchar *p;
+ gsize n_hash, i;
+ gunichar unich;
+ gcry_error_t gcry;
+
+ num_b1 = num_ij = NULL;
+
+ n_hash = gcry_md_get_algo_dlen (hash_algo);
+ g_return_val_if_fail (n_hash > 0, FALSE);
+
+ if (!utf8_password)
+ n_password = 0;
+ if (n_password == -1)
+ end_password = utf8_password + strlen (utf8_password);
+ else
+ end_password = utf8_password + n_password;
+
+ gcry = gcry_md_open (&mdh, hash_algo, 0);
+ if (gcry) {
+ g_warning ("couldn't create '%s' hash context: %s",
+ gcry_md_algo_name (hash_algo), gcry_strerror (gcry));
+ return FALSE;
+ }
+
+ /* Reqisition me a buffer */
+ hash = egg_secure_alloc (n_hash);
+ buf_i = egg_secure_alloc (128);
+ buf_b = egg_secure_alloc (64);
+ g_return_val_if_fail (hash && buf_i && buf_b, FALSE);
+
+ /* Bring in the salt */
+ p = buf_i;
+ if (salt) {
+ for (i = 0; i < 64; ++i)
+ *(p++) = salt[i % n_salt];
+ } else {
+ memset (p, 0, 64);
+ p += 64;
+ }
+
+ /* Bring in the password, as 16bits per character BMP string, ie: UCS2 */
+ if (utf8_password) {
+ p2 = utf8_password;
+ for (i = 0; i < 64; i += 2) {
+
+ /* Get a character from the string */
+ if (p2 < end_password) {
+ unich = g_utf8_get_char (p2);
+ p2 = g_utf8_next_char (p2);
+
+ /* Get zero null terminator, and loop back to beginning */
+ } else {
+ unich = 0;
+ p2 = utf8_password;
+ }
+
+ /* Encode the bytes received */
+ *(p++) = (unich & 0xFF00) >> 8;
+ *(p++) = (unich & 0xFF);
+ }
+ } else {
+ memset (p, 0, 64);
+ p += 64;
+ }
+
+ /* Hash and bash */
+ for (;;) {
+ gcry_md_reset (mdh);
+
+ /* Put in the PKCS#12 type of key */
+ for (i = 0; i < 64; ++i)
+ gcry_md_putc (mdh, type);
+
+ /* Bring in the password */
+ gcry_md_write (mdh, buf_i, utf8_password ? 128 : 64);
+
+ /* First iteration done */
+ memcpy (hash, gcry_md_read (mdh, hash_algo), n_hash);
+
+ /* All the other iterations */
+ for (i = 1; i < iterations; i++)
+ gcry_md_hash_buffer (hash_algo, hash, hash, n_hash);
+
+ /* Take out as much as we need */
+ for (i = 0; i < n_hash && n_output; ++i) {
+ *(output++) = hash[i];
+ --n_output;
+ }
+
+ /* Is that enough generated keying material? */
+ if (!n_output)
+ break;
+
+ /* Need more bytes, do some voodoo */
+ for (i = 0; i < 64; ++i)
+ buf_b[i] = hash[i % n_hash];
+ gcry = gcry_mpi_scan (&num_b1, GCRYMPI_FMT_USG, buf_b, 64, NULL);
+ g_return_val_if_fail (gcry == 0, FALSE);
+ gcry_mpi_add_ui (num_b1, num_b1, 1);
+ for (i = 0; i < 128; i += 64) {
+ gcry = gcry_mpi_scan (&num_ij, GCRYMPI_FMT_USG, buf_i + i, 64, NULL);
+ g_return_val_if_fail (gcry == 0, FALSE);
+ gcry_mpi_add (num_ij, num_ij, num_b1);
+ gcry_mpi_clear_highbit (num_ij, 64 * 8);
+ gcry = gcry_mpi_print (GCRYMPI_FMT_USG, buf_i + i, 64, NULL, num_ij);
+ g_return_val_if_fail (gcry == 0, FALSE);
+ gcry_mpi_release (num_ij);
+ }
+ }
+
+ egg_secure_free (buf_i);
+ egg_secure_free (buf_b);
+ egg_secure_free (hash);
+ gcry_mpi_release (num_b1);
+ gcry_md_close (mdh);
+
+ return TRUE;
+}
+
+gboolean
+egg_symkey_generate_pkcs12 (int cipher_algo, int hash_algo, const gchar *password,
+ gssize n_password, const guchar *salt, gsize n_salt,
+ int iterations, guchar **key, guchar **iv)
+{
+ gsize n_block, n_key;
+ gboolean ret = TRUE;
+
+ g_return_val_if_fail (cipher_algo, FALSE);
+ g_return_val_if_fail (hash_algo, FALSE);
+ g_return_val_if_fail (iterations > 0, FALSE);
+
+ n_key = gcry_cipher_get_algo_keylen (cipher_algo);
+ n_block = gcry_cipher_get_algo_blklen (cipher_algo);
+
+ if (password && !g_utf8_validate (password, n_password, NULL)) {
+ g_warning ("invalid non-UTF8 password");
+ g_return_val_if_reached (FALSE);
+ }
+
+ if (key)
+ *key = NULL;
+ if (iv)
+ *iv = NULL;
+
+ /* Generate us an key */
+ if (key) {
+ *key = egg_secure_alloc (n_key);
+ g_return_val_if_fail (*key != NULL, FALSE);
+ ret = generate_pkcs12 (hash_algo, 1, password, n_password, salt, n_salt,
+ iterations, *key, n_key);
+ }
+
+ /* Generate us an iv */
+ if (ret && iv) {
+ if (n_block > 1) {
+ *iv = g_malloc (n_block);
+ ret = generate_pkcs12 (hash_algo, 2, password, n_password, salt, n_salt,
+ iterations, *iv, n_block);
+ } else {
+ *iv = NULL;
+ }
+ }
+
+ /* Cleanup in case of failure */
+ if (!ret) {
+ g_free (iv ? *iv : NULL);
+ egg_secure_free (key ? *key : NULL);
+ }
+
+ return ret;
+}
+
+static gboolean
+generate_pbkdf2 (int hash_algo, const gchar *password, gsize n_password,
+ const guchar *salt, gsize n_salt, guint iterations,
+ guchar *output, gsize n_output)
+{
+ gcry_md_hd_t mdh;
+ guint u, l, r, i, k;
+ gcry_error_t gcry;
+ guchar *U, *T, *buf;
+ gsize n_buf, n_hash;
+
+ g_return_val_if_fail (hash_algo > 0, FALSE);
+ g_return_val_if_fail (iterations > 0, FALSE);
+ g_return_val_if_fail (n_output > 0, FALSE);
+ g_return_val_if_fail (n_output < G_MAXUINT32, FALSE);
+
+ n_hash = gcry_md_get_algo_dlen (hash_algo);
+ g_return_val_if_fail (n_hash > 0, FALSE);
+
+ gcry = gcry_md_open (&mdh, hash_algo, GCRY_MD_FLAG_HMAC);
+ if (gcry != 0) {
+ g_warning ("couldn't create '%s' hash context: %s",
+ gcry_md_algo_name (hash_algo), gcry_strerror (gcry));
+ return FALSE;
+ }
+
+ /* Get us a temporary buffers */
+ T = egg_secure_alloc (n_hash);
+ U = egg_secure_alloc (n_hash);
+ n_buf = n_salt + 4;
+ buf = egg_secure_alloc (n_buf);
+ g_return_val_if_fail (buf && T && U, FALSE);
+
+ /* n_hash blocks in output, rounding up */
+ l = ((n_output - 1) / n_hash) + 1;
+
+ /* number of bytes in last, rounded up, n_hash block */
+ r = n_output - (l - 1) * n_hash;
+
+ memcpy (buf, salt, n_salt);
+ for (i = 1; i <= l; i++) {
+ memset (T, 0, n_hash);
+ for (u = 1; u <= iterations; u++) {
+ gcry_md_reset (mdh);
+
+ gcry = gcry_md_setkey (mdh, password, n_password);
+ g_return_val_if_fail (gcry == 0, FALSE);
+
+ /* For first iteration on each block add 4 extra bytes */
+ if (u == 1) {
+ buf[n_salt + 0] = (i & 0xff000000) >> 24;
+ buf[n_salt + 1] = (i & 0x00ff0000) >> 16;
+ buf[n_salt + 2] = (i & 0x0000ff00) >> 8;
+ buf[n_salt + 3] = (i & 0x000000ff) >> 0;
+
+ gcry_md_write (mdh, buf, n_buf);
+
+ /* Other iterations, any block */
+ } else {
+ gcry_md_write (mdh, U, n_hash);
+ }
+
+ memcpy (U, gcry_md_read (mdh, hash_algo), n_hash);
+
+ for (k = 0; k < n_hash; k++)
+ T[k] ^= U[k];
+ }
+
+ memcpy (output + (i - 1) * n_hash, T, i == l ? r : n_hash);
+ }
+
+ egg_secure_free (T);
+ egg_secure_free (U);
+ egg_secure_free (buf);
+ gcry_md_close (mdh);
+ return TRUE;
+}
+
+gboolean
+egg_symkey_generate_pbkdf2 (int cipher_algo, int hash_algo,
+ const gchar *password, gssize n_password,
+ const guchar *salt, gsize n_salt, int iterations,
+ guchar **key, guchar **iv)
+{
+ gsize n_key, n_block;
+ gboolean ret = TRUE;
+
+ g_return_val_if_fail (hash_algo, FALSE);
+ g_return_val_if_fail (cipher_algo, FALSE);
+ g_return_val_if_fail (iterations > 0, FALSE);
+
+ n_key = gcry_cipher_get_algo_keylen (cipher_algo);
+ n_block = gcry_cipher_get_algo_blklen (cipher_algo);
+
+ if (key)
+ *key = NULL;
+ if (iv)
+ *iv = NULL;
+
+ if (!password)
+ n_password = 0;
+ if (n_password == -1)
+ n_password = strlen (password);
+
+ /* Generate us an key */
+ if (key) {
+ *key = egg_secure_alloc (n_key);
+ g_return_val_if_fail (*key != NULL, FALSE);
+ ret = generate_pbkdf2 (hash_algo, password, n_password, salt, n_salt,
+ iterations, *key, n_key);
+ }
+
+ /* Generate us an iv */
+ if (ret && iv) {
+ if (n_block > 1) {
+ *iv = g_malloc (n_block);
+ gcry_create_nonce (*iv, n_block);
+ } else {
+ *iv = NULL;
+ }
+ }
+
+ /* Cleanup in case of failure */
+ if (!ret) {
+ g_free (iv ? *iv : NULL);
+ egg_secure_free (key ? *key : NULL);
+ }
+
+ return ret;
+}
+
+/* ----------------------------------------------------------------------------
+ * DER encoded cipher params
+ */
+
+
+static gboolean
+read_cipher_pkcs5_pbe (int cipher_algo, int cipher_mode, int hash_algo,
+ const gchar *password, gsize n_password, const guchar *data,
+ gsize n_data, gcry_cipher_hd_t *cih)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gcry_error_t gcry;
+ const guchar *salt;
+ gsize n_salt;
+ gsize n_block, n_key;
+ guint iterations;
+ guchar *key = NULL;
+ guchar *iv = NULL;
+ gboolean ret;
+
+ g_return_val_if_fail (cipher_algo != 0 && cipher_mode != 0, FALSE);
+ g_return_val_if_fail (cih != NULL, FALSE);
+ g_return_val_if_fail (data != NULL && n_data != 0, FALSE);
+
+ *cih = NULL;
+ ret = FALSE;
+
+ /* Check if we can use this algorithm */
+ if (gcry_cipher_algo_info (cipher_algo, GCRYCTL_TEST_ALGO, NULL, 0) != 0 ||
+ gcry_md_test_algo (hash_algo) != 0)
+ goto done;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-5-PBE-params", data, n_data);
+ if (!asn)
+ goto done;
+
+ salt = egg_asn1_read_content (asn, data, n_data, "salt", &n_salt);
+ if (!salt)
+ goto done;
+ if (!egg_asn1_read_uint (asn, "iterationCount", &iterations))
+ iterations = 1;
+
+ n_key = gcry_cipher_get_algo_keylen (cipher_algo);
+ g_return_val_if_fail (n_key > 0, FALSE);
+ n_block = gcry_cipher_get_algo_blklen (cipher_algo);
+
+ if (!egg_symkey_generate_pbe (cipher_algo, hash_algo, password, n_password, salt,
+ n_salt, iterations, &key, n_block > 1 ? &iv : NULL))
+ goto done;
+
+ gcry = gcry_cipher_open (cih, cipher_algo, cipher_mode, 0);
+ if (gcry != 0) {
+ g_warning ("couldn't create cipher: %s", gcry_strerror (gcry));
+ goto done;
+ }
+
+ if (iv)
+ gcry_cipher_setiv (*cih, iv, n_block);
+ gcry_cipher_setkey (*cih, key, n_key);
+
+ ret = TRUE;
+
+done:
+ g_free (iv);
+ egg_secure_free (key);
+
+ if (asn)
+ asn1_delete_structure (&asn);
+
+ return ret;
+}
+
+static gboolean
+setup_pkcs5_rc2_params (const guchar *data, guchar n_data, gcry_cipher_hd_t cih)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gcry_error_t gcry;
+ const guchar *iv;
+ gsize n_iv;
+ guint version;
+
+ g_assert (data);
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-5-rc2-CBC-params", data, n_data);
+ if (!asn)
+ return FALSE;
+
+ if (!egg_asn1_read_uint (asn, "rc2ParameterVersion", &version))
+ return FALSE;
+
+ iv = egg_asn1_read_content (asn, data, n_data, "iv", &n_iv);
+ asn1_delete_structure (&asn);
+
+ if (!iv)
+ return FALSE;
+
+ gcry = gcry_cipher_setiv (cih, iv, n_iv);
+
+ if (gcry != 0) {
+ g_message ("couldn't set %lu byte iv on cipher", (gulong)n_iv);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+setup_pkcs5_des_params (const guchar *data, guchar n_data, gcry_cipher_hd_t cih)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gcry_error_t gcry;
+ const guchar *iv;
+ gsize n_iv;
+
+ g_assert (data);
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-5-des-EDE3-CBC-params", data, n_data);
+ if (!asn)
+ asn = egg_asn1_decode ("PKIX1.pkcs-5-des-CBC-params", data, n_data);
+ if (!asn)
+ return FALSE;
+
+ iv = egg_asn1_read_content (asn, data, n_data, "", &n_iv);
+ asn1_delete_structure (&asn);
+
+ if (!iv)
+ return FALSE;
+
+ gcry = gcry_cipher_setiv (cih, iv, n_iv);
+
+ if (gcry != 0) {
+ g_message ("couldn't set %lu byte iv on cipher", (gulong)n_iv);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+setup_pkcs5_pbkdf2_params (const gchar *password, gsize n_password, const guchar *data,
+ gsize n_data, int cipher_algo, gcry_cipher_hd_t cih)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gboolean ret;
+ gcry_error_t gcry;
+ guchar *key = NULL;
+ const guchar *salt;
+ gsize n_salt, n_key;
+ guint iterations;
+
+ g_assert (cipher_algo);
+ g_assert (data);
+
+ ret = FALSE;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-5-PBKDF2-params", data, n_data);
+ if (!asn)
+ goto done;
+
+ if (!egg_asn1_read_uint (asn, "iterationCount", &iterations))
+ iterations = 1;
+ salt = egg_asn1_read_content (asn, data, n_data, "salt.specified", &n_salt);
+ if (!salt)
+ goto done;
+
+ if (!egg_symkey_generate_pbkdf2 (cipher_algo, GCRY_MD_SHA1, password, n_password,
+ salt, n_salt, iterations, &key, NULL))
+ goto done;
+
+ n_key = gcry_cipher_get_algo_keylen (cipher_algo);
+ g_return_val_if_fail (n_key > 0, FALSE);
+
+ gcry = gcry_cipher_setkey (cih, key, n_key);
+ if (gcry != 0) {
+ g_message ("couldn't set %lu byte key on cipher", (gulong)n_key);
+ goto done;
+ }
+
+ ret = TRUE;
+
+done:
+ egg_secure_free (key);
+ if (asn)
+ asn1_delete_structure (&asn);
+ return ret;
+}
+
+static gboolean
+read_cipher_pkcs5_pbes2 (const gchar *password, gsize n_password, const guchar *data,
+ gsize n_data, gcry_cipher_hd_t *cih)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gboolean r, ret;
+ GQuark key_deriv_algo, enc_oid;
+ gcry_error_t gcry;
+ int algo, mode;
+ int beg, end;
+
+ g_return_val_if_fail (cih != NULL, FALSE);
+ g_return_val_if_fail (data != NULL && n_data != 0, FALSE);
+
+ init_quarks ();
+
+ *cih = NULL;
+ ret = FALSE;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-5-PBES2-params", data, n_data);
+ if (!asn)
+ goto done;
+
+ algo = mode = 0;
+
+ /* Read in all the encryption type */
+ enc_oid = egg_asn1_read_oid (asn, "encryptionScheme.algorithm");
+ if (!enc_oid)
+ goto done;
+ if (enc_oid == OID_DES_EDE3_CBC)
+ algo = GCRY_CIPHER_3DES;
+ else if (enc_oid == OID_DES_CBC)
+ algo = GCRY_CIPHER_DES;
+ else if (enc_oid == OID_DES_RC2_CBC)
+ algo = GCRY_CIPHER_RFC2268_128;
+ else if (enc_oid == OID_DES_RC5_CBC)
+ /* RC5 doesn't exist in libgcrypt */;
+
+ /* Unsupported? */
+ if (algo == 0 || gcry_cipher_algo_info (algo, GCRYCTL_TEST_ALGO, NULL, 0) != 0)
+ goto done;
+
+ /* Instantiate our cipher */
+ gcry = gcry_cipher_open (cih, algo, GCRY_CIPHER_MODE_CBC, 0);
+ if (gcry != 0) {
+ g_warning ("couldn't create cipher: %s", gcry_cipher_algo_name (algo));
+ goto done;
+ }
+
+ /* Read out the parameters */
+ if (asn1_der_decoding_startEnd (asn, data, n_data, "encryptionScheme.parameters",
+ &beg, &end) != ASN1_SUCCESS)
+ goto done;
+
+ switch (algo) {
+ case GCRY_CIPHER_3DES:
+ case GCRY_CIPHER_DES:
+ r = setup_pkcs5_des_params (data + beg, end - beg + 1, *cih);
+ break;
+ case GCRY_CIPHER_RFC2268_128:
+ r = setup_pkcs5_rc2_params (data + beg, end - beg + 1, *cih);
+ break;
+ default:
+ /* Should have been caught on the oid check above */
+ g_assert_not_reached ();
+ r = FALSE;
+ break;
+ };
+
+ if (r != TRUE)
+ goto done;
+
+ /* Read out the key creation paramaters */
+ key_deriv_algo = egg_asn1_read_oid (asn, "keyDerivationFunc.algorithm");
+ if (!key_deriv_algo)
+ goto done;
+ if (key_deriv_algo != OID_PBKDF2) {
+ g_message ("unsupported key derivation algorithm: %s", g_quark_to_string (key_deriv_algo));
+ goto done;
+ }
+
+ if (asn1_der_decoding_startEnd (asn, data, n_data, "keyDerivationFunc.parameters",
+ &beg, &end) != ASN1_SUCCESS)
+ goto done;
+
+ ret = setup_pkcs5_pbkdf2_params (password, n_password, data + beg, end - beg + 1, algo, *cih);
+
+done:
+ if (ret != TRUE && *cih) {
+ gcry_cipher_close (*cih);
+ *cih = NULL;
+ }
+
+ if (asn)
+ asn1_delete_structure (&asn);
+
+ return ret;
+}
+
+static gboolean
+read_cipher_pkcs12_pbe (int cipher_algo, int cipher_mode, const gchar *password,
+ gsize n_password, const guchar *data, gsize n_data,
+ gcry_cipher_hd_t *cih)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gcry_error_t gcry;
+ gboolean ret;
+ const guchar *salt;
+ gsize n_salt;
+ gsize n_block, n_key;
+ guint iterations;
+ guchar *key = NULL;
+ guchar *iv = NULL;
+
+ g_return_val_if_fail (cipher_algo != 0 && cipher_mode != 0, FALSE);
+ g_return_val_if_fail (cih != NULL, FALSE);
+ g_return_val_if_fail (data != NULL && n_data != 0, FALSE);
+
+ *cih = NULL;
+ ret = FALSE;
+
+ /* Check if we can use this algorithm */
+ if (gcry_cipher_algo_info (cipher_algo, GCRYCTL_TEST_ALGO, NULL, 0) != 0)
+ goto done;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-12-PbeParams", data, n_data);
+ if (!asn)
+ goto done;
+
+ salt = egg_asn1_read_content (asn, data, n_data, "salt", &n_salt);
+ if (!salt)
+ goto done;
+ if (!egg_asn1_read_uint (asn, "iterations", &iterations))
+ goto done;
+
+ n_block = gcry_cipher_get_algo_blklen (cipher_algo);
+ n_key = gcry_cipher_get_algo_keylen (cipher_algo);
+
+ /* Generate IV and key using salt read above */
+ if (!egg_symkey_generate_pkcs12 (cipher_algo, GCRY_MD_SHA1, password,
+ n_password, salt, n_salt, iterations, &key,
+ n_block > 1 ? &iv : NULL))
+ goto done;
+
+ gcry = gcry_cipher_open (cih, cipher_algo, cipher_mode, 0);
+ if (gcry != 0) {
+ g_warning ("couldn't create encryption cipher: %s", gcry_strerror (gcry));
+ goto done;
+ }
+
+ if (iv)
+ gcry_cipher_setiv (*cih, iv, n_block);
+ gcry_cipher_setkey (*cih, key, n_key);
+
+ ret = TRUE;
+
+done:
+ if (ret != TRUE && *cih) {
+ gcry_cipher_close (*cih);
+ *cih = NULL;
+ }
+
+ g_free (iv);
+ egg_secure_free (key);
+
+ if (asn)
+ asn1_delete_structure (&asn);
+
+ return ret;
+}
+
+gboolean
+egg_symkey_read_cipher (GQuark oid_scheme, const gchar *password, gsize n_password,
+ const guchar *data, gsize n_data, gcry_cipher_hd_t *cih)
+{
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (oid_scheme != 0, FALSE);
+ g_return_val_if_fail (cih != NULL, FALSE);
+ g_return_val_if_fail (data != NULL && n_data != 0, FALSE);
+
+ init_quarks ();
+
+ /* PKCS#5 PBE */
+ if (oid_scheme == OID_PBE_MD2_DES_CBC)
+ ret = read_cipher_pkcs5_pbe (GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC,
+ GCRY_MD_MD2, password, n_password, data, n_data, cih);
+
+ else if (oid_scheme == OID_PBE_MD2_RC2_CBC)
+ /* RC2-64 has no implementation in libgcrypt */;
+
+ else if (oid_scheme == OID_PBE_MD5_DES_CBC)
+ ret = read_cipher_pkcs5_pbe (GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC,
+ GCRY_MD_MD5, password, n_password, data, n_data, cih);
+ else if (oid_scheme == OID_PBE_MD5_RC2_CBC)
+ /* RC2-64 has no implementation in libgcrypt */;
+
+ else if (oid_scheme == OID_PBE_SHA1_DES_CBC)
+ ret = read_cipher_pkcs5_pbe (GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC,
+ GCRY_MD_SHA1, password, n_password, data, n_data, cih);
+ else if (oid_scheme == OID_PBE_SHA1_RC2_CBC)
+ /* RC2-64 has no implementation in libgcrypt */;
+
+
+ /* PKCS#5 PBES2 */
+ else if (oid_scheme == OID_PBES2)
+ ret = read_cipher_pkcs5_pbes2 (password, n_password, data, n_data, cih);
+
+
+ /* PKCS#12 PBE */
+ else if (oid_scheme == OID_PKCS12_PBE_ARCFOUR_SHA1)
+ ret = read_cipher_pkcs12_pbe (GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM,
+ password, n_password, data, n_data, cih);
+ else if (oid_scheme == OID_PKCS12_PBE_RC4_40_SHA1)
+ /* RC4-40 has no implementation in libgcrypt */;
+
+ else if (oid_scheme == OID_PKCS12_PBE_3DES_SHA1)
+ ret = read_cipher_pkcs12_pbe (GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC,
+ password, n_password, data, n_data, cih);
+ else if (oid_scheme == OID_PKCS12_PBE_2DES_SHA1)
+ /* 2DES has no implementation in libgcrypt */;
+
+ else if (oid_scheme == OID_PKCS12_PBE_RC2_128_SHA1)
+ ret = read_cipher_pkcs12_pbe (GCRY_CIPHER_RFC2268_128, GCRY_CIPHER_MODE_CBC,
+ password, n_password, data, n_data, cih);
+
+ else if (oid_scheme == OID_PKCS12_PBE_RC2_40_SHA1)
+ ret = read_cipher_pkcs12_pbe (GCRY_CIPHER_RFC2268_40, GCRY_CIPHER_MODE_CBC,
+ password, n_password, data, n_data, cih);
+
+ if (ret == FALSE)
+ g_message ("unsupported or invalid cipher: %s", g_quark_to_string (oid_scheme));
+
+ return ret;
+}
diff --git a/egg/egg-symkey.h b/egg/egg-symkey.h
new file mode 100644
index 00000000..42317087
--- /dev/null
+++ b/egg/egg-symkey.h
@@ -0,0 +1,76 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef EGG_SYMKEY_H_
+#define EGG_SYMKEY_H_
+
+#include <glib.h>
+
+#include <gcrypt.h>
+
+gboolean egg_symkey_generate_simple (int cipher_algo,
+ int hash_algo,
+ const gchar *password,
+ gssize n_password,
+ const guchar *salt,
+ gsize n_salt,
+ int iterations,
+ guchar **key,
+ guchar **iv);
+
+gboolean egg_symkey_generate_pbe (int cipher_algo,
+ int hash_algo,
+ const gchar *password,
+ gssize n_password,
+ const guchar *salt,
+ gsize n_salt,
+ int iterations,
+ guchar **key,
+ guchar **iv);
+
+gboolean egg_symkey_generate_pkcs12 (int cipher_algo,
+ int hash_algo,
+ const gchar *password,
+ gssize n_password,
+ const guchar *salt,
+ gsize n_salt,
+ int iterations,
+ guchar **key,
+ guchar **iv);
+
+gboolean egg_symkey_generate_pbkdf2 (int cipher_algo,
+ int hash_algo,
+ const gchar *password,
+ gssize n_password,
+ const guchar *salt,
+ gsize n_salt,
+ int iterations,
+ guchar **key,
+ guchar **iv);
+
+gboolean egg_symkey_read_cipher (GQuark oid_scheme,
+ const gchar *password,
+ gsize n_password,
+ const guchar *data,
+ gsize n_data,
+ gcry_cipher_hd_t *cih);
+
+#endif /* EGG_SYMKEY_H_ */
diff --git a/egg/tests/Makefile.am b/egg/tests/Makefile.am
index 9b72249c..655c2c04 100644
--- a/egg/tests/Makefile.am
+++ b/egg/tests/Makefile.am
@@ -8,7 +8,10 @@ asn1-def-test.h: test.asn
# Test files should be listed in order they need to run
UNIT_AUTO = \
unit-test-asn1.c \
+ unit-test-hex.c \
unit-test-secmem.c \
+ unit-test-symkey.c \
+ unit-test-openssl.c \
$(BUILT_SOURCES)
UNIT_PROMPT =
diff --git a/egg/tests/unit-test-asn1.c b/egg/tests/unit-test-asn1.c
index 7728f8d3..f4cc44fe 100644
--- a/egg/tests/unit-test-asn1.c
+++ b/egg/tests/unit-test-asn1.c
@@ -274,11 +274,6 @@ DEFINE_TEST(oid)
oid = egg_asn1_read_oid (asn, "nonExistant");
g_assert (oid == 0);
- /* No quark of this has been defined, so should return an invalid OID */
- oid = egg_asn1_read_oid (asn, "data");
- g_assert (oid != 0);
- g_assert_cmpstr (g_quark_to_string (oid), !=, "SOME DATA");
-
/* Now a quark has been defined */
check = g_quark_from_static_string ("SOME DATA");
oid = egg_asn1_read_oid (asn, "data");
diff --git a/pkcs11/gck/tests/unit-test-util.c b/egg/tests/unit-test-hex.c
index 409b5a53..1664ef71 100644
--- a/pkcs11/gck/tests/unit-test-util.c
+++ b/egg/tests/unit-test-hex.c
@@ -27,7 +27,7 @@
#include "run-auto-test.h"
-#include "gck/gck-util.h"
+#include "egg-hex.h"
static const guchar TEST_DATA[] = { 0x05, 0xD6, 0x95, 0x96, 0x10, 0x12, 0xAE, 0x35 };
static const gchar *TEST_HEX = "05D695961012AE35";
@@ -37,7 +37,7 @@ DEFINE_TEST(hex_encode)
{
gchar *hex;
- hex = gck_util_hex_encode (TEST_DATA, sizeof (TEST_DATA));
+ hex = egg_hex_encode (TEST_DATA, sizeof (TEST_DATA));
g_assert (hex);
g_assert_cmpstr (hex, ==, TEST_HEX);
}
@@ -47,23 +47,23 @@ DEFINE_TEST(hex_decode)
guchar *data;
gsize n_data;
- data = gck_util_hex_decode (TEST_HEX, -1, &n_data);
+ data = egg_hex_decode (TEST_HEX, -1, &n_data);
g_assert (data);
g_assert (n_data == sizeof (TEST_DATA));
g_assert (memcmp (data, TEST_DATA, n_data) == 0);
/* Spaces should be ignored */
- data = gck_util_hex_decode (TEST_HEX_SPACE, -1, &n_data);
+ data = egg_hex_decode (TEST_HEX_SPACE, -1, &n_data);
g_assert (data);
g_assert (n_data == sizeof (TEST_DATA));
g_assert (memcmp (data, TEST_DATA, n_data) == 0);
/* Invalid input, null out */
- data = gck_util_hex_decode ("AB", 1, &n_data);
+ data = egg_hex_decode ("AB", 1, &n_data);
g_assert (!data);
/* Nothing in, empty out */
- data = gck_util_hex_decode ("AB", 0, &n_data);
+ data = egg_hex_decode ("AB", 0, &n_data);
g_assert (data);
g_assert (n_data == 0);
}
diff --git a/pkcs11/gck/tests/unit-test-data-openssl.c b/egg/tests/unit-test-openssl.c
index 91bf7bc9..514e9b70 100644
--- a/pkcs11/gck/tests/unit-test-data-openssl.c
+++ b/egg/tests/unit-test-openssl.c
@@ -25,9 +25,8 @@
#include "run-auto-test.h"
-#include "gck/gck-crypto.h"
-#include "gck/gck-data-pem.h"
-#include "gck/gck-data-openssl.h"
+#include "egg-symkey.h"
+#include "egg-openssl.h"
#include <glib.h>
@@ -51,7 +50,7 @@ static void
parse_reference (GQuark type, const guchar *data, gsize n_data,
GHashTable *headers, gpointer user_data)
{
- GckDataResult res;
+ gboolean res;
const gchar *dekinfo;
g_assert ("no data in PEM callback" && data != NULL);
@@ -60,13 +59,13 @@ parse_reference (GQuark type, const guchar *data, gsize n_data,
n_refenc = n_data;
g_assert ("no headers present in file" && headers != NULL);
- refheaders = gck_data_pem_headers_new ();
+ refheaders = egg_openssl_headers_new ();
g_hash_table_foreach (headers, copy_each_key_value, refheaders);
- dekinfo = gck_data_openssl_get_dekinfo (headers);
+ dekinfo = egg_openssl_get_dekinfo (headers);
g_assert ("no dekinfo in headers" && dekinfo != NULL);
- res = gck_data_openssl_decrypt_block (dekinfo, "booo", 4, data, n_data, &refdata, &n_refdata);
- g_assert ("couldn't openssl decrypt block" && res == GCK_DATA_SUCCESS);
+ res = egg_openssl_decrypt_block (dekinfo, "booo", 4, data, n_data, &refdata, &n_refdata);
+ g_assert ("couldn't openssl decrypt block" && res == TRUE);
g_assert ("no data returned from openssl decrypt" && refdata != NULL);
g_assert ("invalid amount of data returned from openssl decrypt" && n_refdata == n_data);
}
@@ -79,7 +78,7 @@ DEFINE_TEST(parse_reference)
input = test_read_testdata ("pem-rsa-enc.key", &n_input);
- num = gck_data_pem_parse (input, n_input, parse_reference, NULL);
+ num = egg_openssl_pem_parse (input, n_input, parse_reference, NULL);
g_assert ("couldn't PEM block in reference data" && num == 1);
g_assert ("parse_reference() wasn't called" && refdata != NULL);
@@ -92,10 +91,10 @@ DEFINE_TEST(write_reference)
gsize n_encrypted;
gboolean ret;
- dekinfo = gck_data_openssl_get_dekinfo (refheaders);
+ dekinfo = egg_openssl_get_dekinfo (refheaders);
g_assert ("no dekinfo in headers" && dekinfo != NULL);
- ret = gck_data_openssl_encrypt_block (dekinfo, "booo", 4, refdata, n_refdata, &encrypted, &n_encrypted);
+ ret = egg_openssl_encrypt_block (dekinfo, "booo", 4, refdata, n_refdata, &encrypted, &n_encrypted);
g_assert ("couldn't openssl encrypt block" && ret == TRUE);
g_assert ("no data returned from openssl encrypt" && encrypted != NULL);
g_assert ("invalid amount of data returned from openssl encrypt" && n_refdata <= n_encrypted);
@@ -111,21 +110,21 @@ const gsize TEST_DATA_L = 29;
DEFINE_TEST(openssl_roundtrip)
{
const gchar *dekinfo;
- GckDataResult res;
+ gboolean res;
gboolean ret;
guchar *encrypted, *decrypted;
gsize n_encrypted, n_decrypted;
int i;
- dekinfo = gck_data_openssl_prep_dekinfo (refheaders);
+ dekinfo = egg_openssl_prep_dekinfo (refheaders);
- ret = gck_data_openssl_encrypt_block (dekinfo, "password", -1, TEST_DATA, TEST_DATA_L, &encrypted, &n_encrypted);
+ ret = egg_openssl_encrypt_block (dekinfo, "password", -1, TEST_DATA, TEST_DATA_L, &encrypted, &n_encrypted);
g_assert ("couldn't openssl encrypt block" && ret == TRUE);
g_assert ("no data returned from openssl encrypt" && encrypted != NULL);
g_assert ("invalid amount of data returned from openssl encrypt" && TEST_DATA_L <= n_encrypted);
- res = gck_data_openssl_decrypt_block (dekinfo, "password", 8, encrypted, n_encrypted, &decrypted, &n_decrypted);
- g_assert ("couldn't openssl decrypt block" && res == GCK_DATA_SUCCESS);
+ res = egg_openssl_decrypt_block (dekinfo, "password", 8, encrypted, n_encrypted, &decrypted, &n_decrypted);
+ g_assert ("couldn't openssl decrypt block" && res == TRUE);
g_assert ("no data returned from openssl decrypt" && decrypted != NULL);
/* Check that the data was decrypted properly */
diff --git a/egg/tests/unit-test-symkey.c b/egg/tests/unit-test-symkey.c
new file mode 100644
index 00000000..6b8d449a
--- /dev/null
+++ b/egg/tests/unit-test-symkey.c
@@ -0,0 +1,226 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* unit-test-crypto.c: Test crypto stuff
+
+ Copyright (C) 2007 Stefan Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Stef Walter <stef@memberwebs.com>
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "run-auto-test.h"
+
+#include "egg-symkey.h"
+
+#include <gcrypt.h>
+
+DEFINE_SETUP(crypto_setup)
+{
+ gcry_check_version (LIBGCRYPT_VERSION);
+}
+
+DEFINE_TEARDOWN(crypto_setup)
+{
+
+}
+
+const static struct {
+ const gchar *password;
+ int cipher_algo;
+ int hash_algo;
+ int iterations;
+ const gchar *salt;
+
+ const gchar *result_simple;
+ const gchar *result_pkcs12;
+ const gchar *result_pbkdf2;
+ const gchar *result_pbe;
+} all_generation_tests[] = {
+
+ { /* 24 byte output */
+ "booo", GCRY_CIPHER_3DES, GCRY_MD_MD5, 1,
+ "\x70\x4C\xFF\xD6\x2F\xBA\x03\xE9",
+ "\x84\x12\xBB\x34\x94\x8C\x40\xAD\x97\x57\x96\x74\x5B\x6A\xFB\xF8\xD6\x61\x33\x51\xEA\x8C\xCF\xD8",
+ NULL,
+ NULL,
+ NULL
+ },
+
+ { /* 5 byte output */
+ "booo", GCRY_CIPHER_RFC2268_40, GCRY_MD_SHA1, 2048,
+ "\x8A\x58\xC2\xE8\x7C\x1D\x80\x11",
+ NULL,
+ "\xD6\xA6\xF0\x76\x66",
+ NULL,
+ NULL
+ },
+
+ { /* Null Password, 5 byte output */
+ NULL, GCRY_CIPHER_RFC2268_40, GCRY_MD_SHA1, 2000,
+ "\x04\xE0\x1C\x3E\xF8\xF2\xE9\xFD",
+ NULL,
+ "\x98\x7F\x20\x97\x1E",
+ NULL,
+ NULL
+ },
+
+ { /* 24 byte output */
+ "booo", GCRY_CIPHER_3DES, GCRY_MD_SHA1, 2048,
+ "\xBD\xEE\x0B\xC6\xCF\x43\xAC\x25",
+ NULL,
+ "\x3F\x38\x1B\x0E\x87\xEB\x19\xBE\xD1\x39\xDC\x5B\xC2\xD2\xB3\x3C\x35\xA8\xB8\xF9\xEE\x66\x48\x94",
+ "\x20\x25\x90\xD8\xD6\x98\x3E\x71\x10\x17\x1F\x51\x49\x87\x27\xCA\x97\x27\xD1\xC9\x72\xF8\x11\xBB",
+ NULL
+ },
+
+ { /* Empty password, 24 byte output */
+ "", GCRY_CIPHER_3DES, GCRY_MD_SHA1, 2048,
+ "\xF7\xCF\xD9\xCF\x1F\xF3\xAD\xF6",
+ NULL,
+ NULL,
+ "\x53\xE3\x35\x9E\x5D\xC1\x85\x1A\x71\x3A\x67\x4E\x80\x56\x13\xD6\x4E\x3E\x89\x43\xB7\x1D\x5F\x7F",
+ NULL
+ },
+
+ { /* Empty password, 24 byte output */
+ "", GCRY_CIPHER_3DES, GCRY_MD_SHA1, 2048,
+ "\xD9\xB3\x2E\xC7\xBA\x1A\x8E\x15",
+ NULL,
+ "\x39\x70\x75\x7C\xF5\xE2\x13\x0B\x5D\xC2\x9D\x96\x8B\x71\xC7\xFC\x5B\x97\x1F\x79\x9F\x06\xFC\xA2",
+ NULL,
+ NULL
+ },
+
+ { /* 8 byte output */
+ "booo", GCRY_CIPHER_DES, GCRY_MD_MD5, 2048,
+ "\x93\x4C\x3D\x29\xA2\x42\xB0\xF5",
+ NULL,
+ NULL,
+ NULL,
+ "\x8C\x67\x19\x7F\xB9\x23\xE2\x8D"
+ }
+};
+
+#define N_GENERATION_TESTS (sizeof (all_generation_tests) / sizeof (all_generation_tests[0]))
+
+DEFINE_TEST(generate_key_simple)
+{
+ int i;
+ gboolean ret;
+ guchar *key;
+
+ for (i = 0; i < N_GENERATION_TESTS; ++i) {
+
+ if (!all_generation_tests[i].result_simple)
+ continue;
+
+ ret = egg_symkey_generate_simple (all_generation_tests[i].cipher_algo,
+ all_generation_tests[i].hash_algo,
+ all_generation_tests[i].password, -1,
+ (guchar*)all_generation_tests[i].salt, 8,
+ all_generation_tests[i].iterations,
+ &key, NULL);
+ g_assert (ret && "key generation failed");
+
+ ret = (memcmp (key, all_generation_tests[i].result_simple,
+ gcry_cipher_get_algo_keylen (all_generation_tests[i].cipher_algo)) == 0);
+
+ g_assert (ret && "invalid simple key generated");
+ }
+}
+
+DEFINE_TEST(generate_key_pkcs12)
+{
+ int i;
+ gboolean ret;
+ guchar *key;
+
+ for (i = 0; i < N_GENERATION_TESTS; ++i) {
+
+ if (!all_generation_tests[i].result_pkcs12)
+ continue;
+
+ ret = egg_symkey_generate_pkcs12 (all_generation_tests[i].cipher_algo,
+ all_generation_tests[i].hash_algo,
+ all_generation_tests[i].password, -1,
+ (guchar*)all_generation_tests[i].salt, 8,
+ all_generation_tests[i].iterations,
+ &key, NULL);
+ g_assert ("failed to generate pkcs12 key" && ret);
+
+ ret = (memcmp (key, all_generation_tests[i].result_pkcs12,
+ gcry_cipher_get_algo_keylen (all_generation_tests[i].cipher_algo)) == 0);
+
+ g_assert ("invalid pkcs12 key generated" && ret);
+ }
+}
+
+DEFINE_TEST(generate_key_pbkdf2)
+{
+ int i;
+ gboolean ret;
+ guchar *key;
+
+ for (i = 0; i < N_GENERATION_TESTS; ++i) {
+
+ if (!all_generation_tests[i].result_pbkdf2)
+ continue;
+
+ ret = egg_symkey_generate_pbkdf2 (all_generation_tests[i].cipher_algo,
+ all_generation_tests[i].hash_algo,
+ all_generation_tests[i].password, -1,
+ (guchar*)all_generation_tests[i].salt, 8,
+ all_generation_tests[i].iterations,
+ &key, NULL);
+ g_assert ("failed to generate pbkdf2 key" && ret);
+
+ ret = (memcmp (key, all_generation_tests[i].result_pbkdf2,
+ gcry_cipher_get_algo_keylen (all_generation_tests[i].cipher_algo)) == 0);
+
+ g_assert ("invalid pbkdf2 key generated" && ret);
+ }
+}
+
+DEFINE_TEST(generate_key_pbe)
+{
+ int i;
+ gboolean ret;
+ guchar *key;
+
+ for (i = 0; i < N_GENERATION_TESTS; ++i) {
+
+ if (!all_generation_tests[i].result_pbe)
+ continue;
+
+ ret = egg_symkey_generate_pbe (all_generation_tests[i].cipher_algo,
+ all_generation_tests[i].hash_algo,
+ all_generation_tests[i].password, -1,
+ (guchar*)all_generation_tests[i].salt, 8,
+ all_generation_tests[i].iterations,
+ &key, NULL);
+ g_assert ("failed to generate pbe key" && ret);
+
+ ret = (memcmp (key, all_generation_tests[i].result_pbe,
+ gcry_cipher_get_algo_keylen (all_generation_tests[i].cipher_algo)) == 0);
+
+ g_assert ("invalid pbe key generated" && ret);
+
+ }
+}
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
new file mode 100644
index 00000000..fc6d492d
--- /dev/null
+++ b/gcr/Makefile.am
@@ -0,0 +1,62 @@
+incdir = $(includedir)/gcr
+
+inc_HEADERS = \
+ gcr-parser.h \
+ gcr-types.h
+
+INCLUDES = \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ $(GOBJECT_CFLAGS) \
+ $(GLIB_CFLAGS)
+
+BUILT_SOURCES = \
+ gcr-marshal.c gcr-marshal.h
+
+lib_LTLIBRARIES = libgcr.la
+
+libgcr_la_SOURCES = \
+ gcr-internal.c gcr-internal.h \
+ gcr-parser.c gcr-parser.h \
+ gcr-types.h \
+ $(BUILT_SOURCES)
+
+libgcr_la_LDFLAGS = \
+ -version-info $(GCR_LT_RELEASE) \
+ -no-undefined -export-symbols-regex 'gcr_*'
+
+libgcr_la_LIBADD = \
+ $(top_builddir)/egg/libegg.la \
+ $(top_builddir)/gp11/libgp11.la \
+ $(GOBJECT_LIBS) \
+ $(GLIB_LIBS)
+
+gcr-marshal.h: gcr-marshal.list $(GLIB_GENMARSHAL)
+ $(GLIB_GENMARSHAL) $< --header --prefix=_gcr_marshal > $@
+
+gcr-marshal.c: gcr-marshal.list $(GLIB_GENMARSHAL)
+ echo "#include \"gcr-marshal.h\"" > $@ && \
+ $(GLIB_GENMARSHAL) $< --body --prefix=_gcr_marshal >> $@
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = gcr-$(GCR_MAJOR).pc
+
+EXTRA_DIST = \
+ gcr.pc.in \
+ gcr-marshal.list
+
+DISTCLEANFILES = \
+ gcr-$(GCR_MAJOR).pc
+
+gcr-$(GCR_MAJOR).pc: gcr.pc
+ cp gcr.pc gcr-$(GCR_MAJOR).pc
+
+if WITH_TESTS
+TESTS_DIR = tests
+else
+TESTS_DIR =
+endif
+
+SUBDIRS = . \
+ $(TESTS_DIR)
+ \ No newline at end of file
diff --git a/gcr/gcr-import-dialog.glade b/gcr/gcr-import-dialog.glade
new file mode 100644
index 00000000..7da00a14
--- /dev/null
+++ b/gcr/gcr-import-dialog.glade
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.5 on Sat Jan 17 14:53:28 2009 -->
+<glade-interface>
+ <widget class="GtkDialog" id="dialog1">
+ <property name="border_width">5</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">1</property>
+ <property name="stock">gtk-dialog-authentication</property>
+ <property name="icon_size">6</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <widget class="GtkLabel" id="primary-text">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;span size='large' weight='bold'&gt;Import Certificates and Keys&lt;/span&gt;</property>
+ <property name="use_markup">True</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="secondary-text">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Secondary prompt text</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <widget class="GtkEntry" id="password-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkComboBox" id="location-combo">
+ <property name="visible">True</property>
+ <property name="button_sensitivity">GTK_SENSITIVITY_ON</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Password:</property>
+ </widget>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Import Into:</property>
+ </widget>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <widget class="GtkButton" id="button1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="label" translatable="yes">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkButton" id="button2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="label" translatable="yes">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="response_id">0</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
diff --git a/gcr/gcr-importer.c b/gcr/gcr-importer.c
new file mode 100644
index 00000000..1ba00d6a
--- /dev/null
+++ b/gcr/gcr-importer.c
@@ -0,0 +1,213 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gcr-importer.h"
+
+enum {
+ PROP_0,
+ PROP_IMPORTER
+};
+
+enum {
+ SIGNAL,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GcrImporter, gcr_importer, G_TYPE_OBJECT);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+
+static GObject*
+gcr_importer_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+ GcrImporter *self = GCR_IMPORTER (G_OBJECT_CLASS (gcr_importer_parent_class)->constructor(type, n_props, props));
+ g_return_val_if_fail (self, NULL);
+
+
+
+ return G_OBJECT (self);
+}
+
+static void
+gcr_importer_init (GcrImporter *self)
+{
+
+}
+
+static void
+gcr_importer_dispose (GObject *obj)
+{
+ GcrImporter *self = GCR_IMPORTER (obj);
+
+ G_OBJECT_CLASS (gcr_importer_parent_class)->dispose (obj);
+}
+
+static void
+gcr_importer_finalize (GObject *obj)
+{
+ GcrImporter *self = GCR_IMPORTER (obj);
+
+ G_OBJECT_CLASS (gcr_importer_parent_class)->finalize (obj);
+}
+
+static void
+gcr_importer_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ GcrImporter *self = GCR_IMPORTER (obj);
+
+ switch (prop_id) {
+ case PROP_IMPORTER:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_importer_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ GcrImporter *self = GCR_IMPORTER (obj);
+
+ switch (prop_id) {
+ case PROP_IMPORTER:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_importer_class_init (GcrImporterClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->constructor = gcr_importer_constructor;
+ gobject_class->dispose = gcr_importer_dispose;
+ gobject_class->finalize = gcr_importer_finalize;
+ gobject_class->set_property = gcr_importer_set_property;
+ gobject_class->get_property = gcr_importer_get_property;
+
+ g_object_class_install_property (gobject_class, PROP_IMPORTER,
+ g_param_spec_pointer ("importer", "Importer", "Importer.", G_PARAM_READWRITE));
+
+ signals[SIGNAL] = g_signal_new ("signal", GCR_TYPE_IMPORTER,
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrImporterClass, signal),
+ NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 0);
+
+ _gcr_initialize ();
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GcrImporter*
+gcr_importer_new (void)
+{
+ return g_object_new (GCR_TYPE_IMPORTER, NULL);
+}
+
+gboolean
+gcr_importer_import_data (GcrImporter *self, const guchar *data, gsize n_data,
+ GError *err)
+{
+ GckParser *parser;
+ gulong parsed_conn;
+ gulong auth_conn;
+ gboolean ret;
+
+ g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE);
+ g_return_val_if_fail (data || !n_data, FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
+
+
+ xxxx;
+
+
+ /*
+ * Parse to see if it's something that needs a password
+ * if we can't prompt,
+ * return an error
+ * Possibly prompt, if password needed, with all information necessary
+ *
+ */
+
+
+ g_object_ref (self);
+
+ parser = gcr_importer_get_parser (self);
+
+ /* Listen in to the parser */
+ g_object_ref (parser);
+ parsed_conn = g_signal_connect (parser, "parsed-item", G_CALLBACK (parser_parsed_item), self);
+ auth_conn = g_signal_connect (parser, "authenticate", G_CALLBACK (parser_authenticate), self);
+
+ /* Feed the parser the data */
+ ret = gcr_parser_parse_data (parser, data, n_data, err);
+
+ /* Now we should have all the data ready, check if we should prompt... */
+ /* Import data one by one into module */
+
+ g_signal_handler_disconnect (parser, parsed_conn);
+ g_signal_handler_disconnect (parser, auth_conn);
+ g_object_unref (parser);
+
+ g_object_unref (self);
+
+ return ret;
+}
+
+gboolean
+gcr_importer_import_file (GcrImporter *self, const gchar *filename,
+ GError *err)
+{
+ gboolean ret;
+ gchar *data;
+ gsize n_data;
+
+ g_return_val_if_fail (GCR_IS_IMPORTER (self), FALSE);
+ g_return_val_if_fail (filename, FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
+
+ if (!g_file_get_contents (filename, &data, &n_data, err))
+ return FALSE;
+
+ ret = gcr_importer_import_data (self, (const guchar*)data, n_data, error);
+ g_free (data);
+
+ return ret;
+}
diff --git a/gcr/gcr-importer.h b/gcr/gcr-importer.h
new file mode 100644
index 00000000..1fad27cf
--- /dev/null
+++ b/gcr/gcr-importer.h
@@ -0,0 +1,81 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCR_IMPORTER_H__
+#define __GCR_IMPORTER_H__
+
+#include <glib-object.h>
+
+#define GCR_TYPE_IMPORTER (gcr_importer_get_type ())
+#define GCR_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_IMPORTER, GcrImporter))
+#define GCR_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_IMPORTER, GcrImporterClass))
+#define GCR_IS_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_IMPORTER))
+#define GCR_IS_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_IMPORTER))
+#define GCR_IMPORTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_IMPORTER, GcrImporterClass))
+
+typedef struct _GcrImporter GcrImporter;
+typedef struct _GcrImporterClass GcrImporterClass;
+
+struct _GcrImporter {
+ GObject parent;
+};
+
+struct _GcrImporterClass {
+ GObjectClass parent_class;
+
+ /* signals --------------------------------------------------------- */
+
+ void (*signal) (GcrImporter *self, GkrImportedItem *item);
+};
+
+GType gcr_importer_get_type (void);
+
+GcrImporter* gcr_importer_new (void);
+
+GcrImporter* gcr_importer_new_for_module (GP11Module *module);
+
+GcrImporter* gcr_importer_new_for_module_funcs (gpointer pkcs11_funcs);
+
+void gcr_importer_set_slot (GcrImporter *self,
+ GP11Slot *slot);
+
+void gcr_importer_set_slot_id (GcrImporter *self,
+ gulong slot_id);
+
+void gcr_importer_set_parser (GcrImporter *self,
+ GcrParser *parser);
+
+void gcr_importer_set_window (GcrImporter *self,
+ GtkWindow *window);
+
+void gcr_importer_set_prompt_behavior (GcrImporter *self,
+ GcrImporterPromptBehavior behavior);
+
+gboolean gcr_importer_import_data (GcrImporter *self,
+ const guchar *data,
+ gsize n_data,
+ GError *error);
+
+gboolean gcr_importer_import_file (GcrImporter *self,
+ const gchar *filename,
+ GError *error);
+
+#endif /* __GCR_IMPORTER_H__ */
diff --git a/gcr/gcr-internal.c b/gcr/gcr-internal.c
new file mode 100644
index 00000000..2996208b
--- /dev/null
+++ b/gcr/gcr-internal.c
@@ -0,0 +1,116 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gcr-internal.h"
+
+#include "egg/egg-secure-memory.h"
+
+#include <gcrypt.h>
+
+static void
+log_handler (gpointer unused, int unknown, const gchar *msg, va_list va)
+{
+ /* TODO: Figure out additional arguments */
+ g_logv ("gcrypt", G_LOG_LEVEL_MESSAGE, msg, va);
+}
+
+static int
+no_mem_handler (gpointer unused, size_t sz, unsigned int unknown)
+{
+ /* TODO: Figure out additional arguments */
+ g_error ("couldn't allocate %lu bytes of memory",
+ (unsigned long int)sz);
+ return 0;
+}
+
+static void
+fatal_handler (gpointer unused, int unknown, const gchar *msg)
+{
+ /* TODO: Figure out additional arguments */
+ g_log ("gcrypt", G_LOG_LEVEL_ERROR, "%s", msg);
+}
+
+static int
+glib_thread_mutex_init (void **lock)
+{
+ *lock = g_mutex_new ();
+ return 0;
+}
+
+static int
+glib_thread_mutex_destroy (void **lock)
+{
+ g_mutex_free (*lock);
+ return 0;
+}
+
+static int
+glib_thread_mutex_lock (void **lock)
+{
+ g_mutex_lock (*lock);
+ return 0;
+}
+
+static int
+glib_thread_mutex_unlock (void **lock)
+{
+ g_mutex_unlock (*lock);
+ return 0;
+}
+
+static struct gcry_thread_cbs glib_thread_cbs = {
+ GCRY_THREAD_OPTION_USER, NULL,
+ glib_thread_mutex_init, glib_thread_mutex_destroy,
+ glib_thread_mutex_lock, glib_thread_mutex_unlock,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+void
+_gcr_initialize (void)
+{
+ static gsize gcrypt_initialized = FALSE;
+ unsigned seed;
+
+ if (g_once_init_enter (&gcrypt_initialized)) {
+
+ /* Only initialize libgcrypt if it hasn't already been initialized */
+ if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) {
+ gcry_control (GCRYCTL_SET_THREAD_CBS, &glib_thread_cbs);
+ gcry_check_version (LIBGCRYPT_VERSION);
+ gcry_set_log_handler (log_handler, NULL);
+ gcry_set_outofcore_handler (no_mem_handler, NULL);
+ gcry_set_fatalerror_handler (fatal_handler, NULL);
+ gcry_set_allocation_handler ((gcry_handler_alloc_t)g_malloc,
+ (gcry_handler_alloc_t)egg_secure_alloc,
+ egg_secure_check,
+ (gcry_handler_realloc_t)egg_secure_realloc,
+ egg_secure_free);
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+ }
+
+ gcry_create_nonce (&seed, sizeof (seed));
+ srand (seed);
+
+ g_once_init_leave (&gcrypt_initialized, 1);
+ }
+}
diff --git a/gcr/gcr-internal.h b/gcr/gcr-internal.h
new file mode 100644
index 00000000..daad9903
--- /dev/null
+++ b/gcr/gcr-internal.h
@@ -0,0 +1,8 @@
+#ifndef GCR_INTERNAL_H_
+#define GCR_INTERNAL_H_
+
+#include <glib.h>
+
+void _gcr_initialize (void);
+
+#endif /* GCR_INTERNAL_H_ */
diff --git a/gcr/gcr-marshal.list b/gcr/gcr-marshal.list
new file mode 100644
index 00000000..8ff8506f
--- /dev/null
+++ b/gcr/gcr-marshal.list
@@ -0,0 +1 @@
+BOOLEAN:INT
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
new file mode 100644
index 00000000..0c58ce39
--- /dev/null
+++ b/gcr/gcr-parser.c
@@ -0,0 +1,1710 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gp11/gp11.h"
+
+#include "gcr-internal.h"
+#include "gcr-marshal.h"
+#include "gcr-parser.h"
+#include "gcr-types.h"
+
+#include "egg/egg-asn1.h"
+#include "egg/egg-openssl.h"
+#include "egg/egg-secure-memory.h"
+#include "egg/egg-symkey.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <stdlib.h>
+#include <gcrypt.h>
+#include <libtasn1.h>
+
+enum {
+ PROP_0,
+ PROP_PARSED_LABEL,
+ PROP_PARSED_ATTRIBUTES,
+ PROP_PARSED_DESCRIPTION
+};
+
+enum {
+ AUTHENTICATE,
+ PARSED,
+ LAST_SIGNAL
+};
+
+#define SUCCESS 0
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _GcrParserPrivate {
+ GTree *specific_formats;
+ gboolean normal_formats;
+ GPtrArray *passwords;
+
+ GP11Attributes *parsed_attrs;
+ const gchar *parsed_desc;
+ gchar *parsed_label;
+};
+
+G_DEFINE_TYPE (GcrParser, gcr_parser, G_TYPE_OBJECT);
+
+typedef struct {
+ gint ask_state;
+ gint seen;
+} PasswordState;
+
+#define PASSWORD_STATE_INIT { 0, 0 }
+
+typedef struct _ParserFormat {
+ gint format_id;
+ gint (*function) (GcrParser *self, const guchar *data, gsize n_data);
+} ParserFormat;
+
+/* Forward declarations */
+static const ParserFormat parser_normal[];
+static const ParserFormat parser_formats[];
+static ParserFormat* parser_format_lookup (gint format_id);
+
+/* -----------------------------------------------------------------------------
+ * QUARK DEFINITIONS
+ */
+
+/*
+ * PEM STRINGS
+ * The xxxxx in: ----- BEGIN xxxxx ------
+ */
+
+static GQuark PEM_CERTIFICATE;
+static GQuark PEM_RSA_PRIVATE_KEY;
+static GQuark PEM_DSA_PRIVATE_KEY;
+static GQuark PEM_ANY_PRIVATE_KEY;
+static GQuark PEM_ENCRYPTED_PRIVATE_KEY;
+static GQuark PEM_PRIVATE_KEY;
+static GQuark PEM_PKCS7;
+static GQuark PEM_PKCS12;
+
+/*
+ * OIDS
+ */
+
+static GQuark OID_PKIX1_RSA;
+static GQuark OID_PKIX1_DSA;
+static GQuark OID_PKCS7_DATA;
+static GQuark OID_PKCS7_SIGNED_DATA;
+static GQuark OID_PKCS7_ENCRYPTED_DATA;
+static GQuark OID_PKCS12_BAG_PKCS8_KEY;
+static GQuark OID_PKCS12_BAG_PKCS8_ENCRYPTED_KEY;
+static GQuark OID_PKCS12_BAG_CERTIFICATE;
+static GQuark OID_PKCS12_BAG_CRL;
+
+static void
+init_quarks (void)
+{
+ static volatile gsize quarks_inited = 0;
+
+ if (g_once_init_enter (&quarks_inited)) {
+
+ #define QUARK(name, value) \
+ name = g_quark_from_static_string(value)
+
+ QUARK (OID_PKIX1_RSA, "1.2.840.113549.1.1.1");
+ QUARK (OID_PKIX1_DSA, "1.2.840.10040.4.1");
+ QUARK (OID_PKCS7_DATA, "1.2.840.113549.1.7.1");
+ QUARK (OID_PKCS7_SIGNED_DATA, "1.2.840.113549.1.7.2");
+ QUARK (OID_PKCS7_ENCRYPTED_DATA, "1.2.840.113549.1.7.6");
+ QUARK (OID_PKCS12_BAG_PKCS8_KEY, "1.2.840.113549.1.12.10.1.1");
+ QUARK (OID_PKCS12_BAG_PKCS8_ENCRYPTED_KEY, "1.2.840.113549.1.12.10.1.2");
+ QUARK (OID_PKCS12_BAG_CERTIFICATE, "1.2.840.113549.1.12.10.1.3");
+ QUARK (OID_PKCS12_BAG_CRL, "1.2.840.113549.1.12.10.1.4");
+
+ QUARK (PEM_CERTIFICATE, "CERTIFICATE");
+ QUARK (PEM_PRIVATE_KEY, "PRIVATE KEY");
+ QUARK (PEM_RSA_PRIVATE_KEY, "RSA PRIVATE KEY");
+ QUARK (PEM_DSA_PRIVATE_KEY, "DSA PRIVATE KEY");
+ QUARK (PEM_ANY_PRIVATE_KEY, "ANY PRIVATE KEY");
+ QUARK (PEM_ENCRYPTED_PRIVATE_KEY, "ENCRYPTED PRIVATE KEY");
+ QUARK (PEM_PKCS7, "PKCS7");
+ QUARK (PEM_PKCS12, "PKCS12");
+
+ #undef QUARK
+
+ g_once_init_leave (&quarks_inited, 1);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+static gboolean
+parsed_asn1_attribute (GcrParser *self, ASN1_TYPE asn, const guchar *data, gsize n_data,
+ const gchar *part, CK_ATTRIBUTE_TYPE type)
+{
+ const guchar *value;
+ gsize n_value;
+
+ g_assert (GCR_IS_PARSER (self));
+ g_assert (asn);
+ g_assert (data);
+ g_assert (part);
+ g_assert (self->pv->parsed_attrs);
+
+ value = egg_asn1_read_content (asn, data, n_data, part, &n_value);
+ if (value == NULL)
+ return FALSE;
+
+ gp11_attributes_add_data (self->pv->parsed_attrs, type, value, n_value);
+ return TRUE;
+}
+
+static void
+parsed_clear (GcrParser *self, CK_OBJECT_CLASS klass)
+{
+ if (self->pv->parsed_attrs)
+ gp11_attributes_unref (self->pv->parsed_attrs);
+ if (klass == CKO_PRIVATE_KEY)
+ self->pv->parsed_attrs = gp11_attributes_new_full ((GP11Allocator)egg_secure_realloc);
+ else
+ self->pv->parsed_attrs = gp11_attributes_new ();
+ gp11_attributes_add_ulong (self->pv->parsed_attrs, CKA_CLASS, klass);
+
+ g_free (self->pv->parsed_label);
+ self->pv->parsed_label = NULL;
+
+ switch (klass) {
+ case CKO_PRIVATE_KEY:
+ self->pv->parsed_desc = _("Private Key");
+ break;
+ case CKO_CERTIFICATE:
+ self->pv->parsed_desc = _("Certificate");
+ break;
+ case CKO_PUBLIC_KEY:
+ self->pv->parsed_desc = _("Public Key");
+ break;
+ default:
+ self->pv->parsed_desc = NULL;
+ break;
+ }
+}
+
+static void
+parsed_label (GcrParser *self, const gchar *label)
+{
+ g_free (self->pv->parsed_label);
+ self->pv->parsed_label = g_strdup (label);
+}
+
+static void
+parsed_attribute (GcrParser *self, CK_ATTRIBUTE_TYPE type, gconstpointer data, gsize n_data)
+{
+ g_assert (GCR_IS_PARSER (self));
+ g_assert (self->pv->parsed_attrs);
+ gp11_attributes_add_data (self->pv->parsed_attrs, type, data, n_data);
+}
+
+static void
+parsed_ulong (GcrParser *self, CK_ATTRIBUTE_TYPE type, gulong value)
+{
+ g_assert (GCR_IS_PARSER (self));
+ g_assert (self->pv->parsed_attrs);
+ gp11_attributes_add_ulong (self->pv->parsed_attrs, type, value);
+}
+
+static gint
+enum_next_password (GcrParser *self, PasswordState *state, const gchar **password)
+{
+ gboolean result;
+
+ /*
+ * Next passes we look through all the passwords that the parser
+ * has seen so far. This is because different parts of a encrypted
+ * container (such as PKCS#12) often use the same password even
+ * if with different algorithms.
+ *
+ * If we didn't do this and the user chooses enters a password,
+ * but doesn't save it, they would get prompted for the same thing
+ * over and over, dumb.
+ */
+
+ /* Look in our list of passwords */
+ if (state->seen < self->pv->passwords->len) {
+ g_assert (state->seen >= 0);
+ *password = g_ptr_array_index (self->pv->passwords, state->seen);
+ ++state->seen;
+ return SUCCESS;
+ }
+
+ /* Fire off all the parsed property signals so anyone watching can update their state */
+ g_object_notify (G_OBJECT (self), "parsed-description");
+ g_object_notify (G_OBJECT (self), "parsed-attributes");
+ g_object_notify (G_OBJECT (self), "parsed-label");
+
+ g_signal_emit (self, signals[AUTHENTICATE], 0, state->ask_state, &result);
+ ++state->ask_state;
+
+ if (!result)
+ return GCR_PARSE_CANCELLED;
+
+ /* Return any passwords added */
+ if (state->seen < self->pv->passwords->len) {
+ g_assert (state->seen >= 0);
+ *password = g_ptr_array_index (self->pv->passwords, state->seen);
+ ++state->seen;
+ return SUCCESS;
+ }
+
+ return GCR_PARSE_LOCKED;
+}
+
+static void
+parsed_fire (GcrParser *self)
+{
+ g_object_notify (G_OBJECT (self), "parsed-description");
+ g_object_notify (G_OBJECT (self), "parsed-attributes");
+ g_object_notify (G_OBJECT (self), "parsed-label");
+
+ g_signal_emit (self, signals[PARSED], 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * RSA PRIVATE KEY
+ */
+
+static gint
+parse_der_private_key_rsa (GcrParser *self, const guchar *data, gsize n_data)
+{
+ gint res = GCR_PARSE_UNRECOGNIZED;
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ guint version;
+
+ asn = egg_asn1_decode ("PK.RSAPrivateKey", data, n_data);
+ if (!asn)
+ goto done;
+
+ parsed_clear (self, CKO_PRIVATE_KEY);
+ parsed_ulong (self, CKA_KEY_TYPE, CKK_RSA);
+ res = GCR_PARSE_FAILURE;
+
+ if (!egg_asn1_read_uint (asn, "version", &version))
+ goto done;
+
+ /* We only support simple version */
+ if (version != 0) {
+ res = GCR_PARSE_UNRECOGNIZED;
+ g_message ("unsupported version of RSA key: %u", version);
+ goto done;
+ }
+
+ if (!parsed_asn1_attribute (self, asn, data, n_data, "modulus", CKA_MODULUS) ||
+ !parsed_asn1_attribute (self, asn, data, n_data, "publicExponent", CKA_PUBLIC_EXPONENT) ||
+ !parsed_asn1_attribute (self, asn, data, n_data, "privateExponent", CKA_PRIVATE_EXPONENT) ||
+ !parsed_asn1_attribute (self, asn, data, n_data, "prime1", CKA_PRIME_1) ||
+ !parsed_asn1_attribute (self, asn, data, n_data, "prime2", CKA_PRIME_2) ||
+ !parsed_asn1_attribute (self, asn, data, n_data, "coefficient", CKA_COEFFICIENT))
+ goto done;
+
+ parsed_fire (self);
+ res = SUCCESS;
+
+done:
+ if (asn)
+ asn1_delete_structure (&asn);
+
+ if (res == GCR_PARSE_FAILURE)
+ g_message ("invalid RSA key");
+
+ return res;
+}
+
+/* -----------------------------------------------------------------------------
+ * DSA PRIVATE KEY
+ */
+
+static gint
+parse_der_private_key_dsa (GcrParser *self, const guchar *data, gsize n_data)
+{
+ gint ret = GCR_PARSE_UNRECOGNIZED;
+ int res;
+ ASN1_TYPE asn;
+
+ asn = egg_asn1_decode ("PK.DSAPrivateKey", data, n_data);
+ if (!asn)
+ goto done;
+
+ parsed_clear (self, CKO_PRIVATE_KEY);
+ parsed_ulong (self, CKA_KEY_TYPE, CKK_DSA);
+ res = GCR_PARSE_FAILURE;
+
+ if (!parsed_asn1_attribute (self, asn, data, n_data, "p", CKA_PRIME) ||
+ !parsed_asn1_attribute (self, asn, data, n_data, "q", CKA_SUBPRIME) ||
+ !parsed_asn1_attribute (self, asn, data, n_data, "g", CKA_BASE) ||
+ !parsed_asn1_attribute (self, asn, data, n_data, "priv", CKA_VALUE))
+ goto done;
+
+ parsed_fire (self);
+ ret = SUCCESS;
+
+done:
+ if (asn)
+ asn1_delete_structure (&asn);
+
+ if (ret == GCR_PARSE_FAILURE)
+ g_message ("invalid DSA key");
+
+ return ret;
+}
+
+static gint
+parse_der_private_key_dsa_parts (GcrParser *self, const guchar *keydata, gsize n_keydata,
+ const guchar *params, gsize n_params)
+{
+ gint ret = GCR_PARSE_UNRECOGNIZED;
+ int res;
+ ASN1_TYPE asn_params = ASN1_TYPE_EMPTY;
+ ASN1_TYPE asn_key = ASN1_TYPE_EMPTY;
+
+ asn_params = egg_asn1_decode ("PK.DSAParameters", params, n_params);
+ asn_key = egg_asn1_decode ("PK.DSAPrivatePart", keydata, n_keydata);
+ if (!asn_params || !asn_key)
+ goto done;
+
+ parsed_clear (self, CKO_PRIVATE_KEY);
+ parsed_ulong (self, CKA_KEY_TYPE, CKK_DSA);
+ res = GCR_PARSE_FAILURE;
+
+ if (!parsed_asn1_attribute (self, asn_params, params, n_params, "p", CKA_PRIME) ||
+ !parsed_asn1_attribute (self, asn_params, params, n_params, "q", CKA_SUBPRIME) ||
+ !parsed_asn1_attribute (self, asn_params, params, n_params, "g", CKA_BASE) ||
+ !parsed_asn1_attribute (self, asn_key, keydata, n_keydata, "", CKA_VALUE))
+ goto done;
+
+ parsed_fire (self);
+ ret = SUCCESS;
+
+done:
+ if (asn_key)
+ asn1_delete_structure (&asn_key);
+ if (asn_params)
+ asn1_delete_structure (&asn_params);
+
+ if (ret == GCR_PARSE_FAILURE)
+ g_message ("invalid DSA key");
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * PRIVATE KEY
+ */
+
+static gint
+parse_der_private_key (GcrParser *self, const guchar *data, gsize n_data)
+{
+ gint res;
+
+ res = parse_der_private_key_rsa (self, data, n_data);
+ if (res == GCR_PARSE_UNRECOGNIZED)
+ res = parse_der_private_key_dsa (self, data, n_data);
+
+ return res;
+}
+
+/* -----------------------------------------------------------------------------
+ * PKCS8
+ */
+
+static gint
+parse_der_pkcs8_plain (GcrParser *self, const guchar *data, gsize n_data)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gint ret;
+ CK_KEY_TYPE key_type;
+ GQuark key_algo;
+ const guchar *keydata;
+ gsize n_keydata;
+ const guchar *params;
+ gsize n_params;
+
+ ret = GCR_PARSE_UNRECOGNIZED;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-8-PrivateKeyInfo", data, n_data);
+ if (!asn)
+ goto done;
+
+ ret = GCR_PARSE_FAILURE;
+ key_type = GP11_INVALID;
+
+ key_algo = egg_asn1_read_oid (asn, "privateKeyAlgorithm.algorithm");
+ if (!key_algo)
+ goto done;
+ else if (key_algo == OID_PKIX1_RSA)
+ key_type = CKK_RSA;
+ else if (key_algo == OID_PKIX1_DSA)
+ key_type = CKK_DSA;
+
+ if (key_type == GP11_INVALID) {
+ ret = GCR_PARSE_UNRECOGNIZED;
+ goto done;
+ }
+
+ keydata = egg_asn1_read_content (asn, data, n_data, "privateKey", &n_keydata);
+ if (!keydata)
+ goto done;
+
+ params = egg_asn1_read_element (asn, data, n_data, "privateKeyAlgorithm.parameters",
+ &n_params);
+
+ ret = SUCCESS;
+
+done:
+ if (ret == SUCCESS) {
+ switch (key_type) {
+ case CKK_RSA:
+ ret = parse_der_private_key_rsa (self, keydata, n_keydata);
+ break;
+ case CKK_DSA:
+ /* Try the normal sane format */
+ ret = parse_der_private_key_dsa (self, keydata, n_keydata);
+
+ /* Otherwise try the two part format that everyone seems to like */
+ if (ret == GCR_PARSE_UNRECOGNIZED && params && n_params)
+ ret = parse_der_private_key_dsa_parts (self, keydata, n_keydata,
+ params, n_params);
+ break;
+ default:
+ g_message ("invalid or unsupported key type in PKCS#8 key");
+ ret = GCR_PARSE_UNRECOGNIZED;
+ break;
+ };
+
+ } else if (ret == GCR_PARSE_FAILURE) {
+ g_message ("invalid PKCS#8 key");
+ }
+
+ if (asn)
+ asn1_delete_structure (&asn);
+ return ret;
+}
+
+static gint
+parse_der_pkcs8_encrypted (GcrParser *self, const guchar *data, gsize n_data)
+{
+ PasswordState pstate = PASSWORD_STATE_INIT;
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gcry_cipher_hd_t cih = NULL;
+ gcry_error_t gcry;
+ gint ret, r;
+ GQuark scheme;
+ guchar *crypted = NULL;
+ const guchar *params;
+ gsize n_crypted, n_params;
+ const gchar *password;
+ gint l;
+
+ ret = GCR_PARSE_UNRECOGNIZED;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-8-EncryptedPrivateKeyInfo", data, n_data);
+ if (!asn)
+ goto done;
+
+ ret = GCR_PARSE_FAILURE;
+
+ /* Figure out the type of encryption */
+ scheme = egg_asn1_read_oid (asn, "encryptionAlgorithm.algorithm");
+ if (!scheme)
+ goto done;
+
+ params = egg_asn1_read_element (asn, data, n_data, "encryptionAlgorithm.parameters", &n_params);
+
+ parsed_clear (self, CKO_PRIVATE_KEY);
+
+ /* Loop to try different passwords */
+ for (;;) {
+
+ g_assert (cih == NULL);
+
+ r = enum_next_password (self, &pstate, &password);
+ if (r != SUCCESS) {
+ ret = r;
+ break;
+ }
+
+ /* Parse the encryption stuff into a cipher. */
+ if (!egg_symkey_read_cipher (scheme, password, -1, params, n_params, &cih))
+ break;
+
+ crypted = egg_asn1_read_value (asn, "encryptedData", &n_crypted, egg_secure_realloc);
+ if (!crypted)
+ break;
+
+ gcry = gcry_cipher_decrypt (cih, crypted, n_crypted, NULL, 0);
+ gcry_cipher_close (cih);
+ cih = NULL;
+
+ if (gcry != 0) {
+ g_warning ("couldn't decrypt pkcs8 data: %s", gcry_strerror (gcry));
+ break;
+ }
+
+ /* Unpad the DER data */
+ l = egg_asn1_element_length (crypted, n_crypted);
+ if (l > 0)
+ n_crypted = l;
+
+ /* Try to parse the resulting key */
+ r = parse_der_pkcs8_plain (self, crypted, n_crypted);
+ egg_secure_free (crypted);
+ crypted = NULL;
+
+ if (r != GCR_PARSE_UNRECOGNIZED) {
+ ret = r;
+ break;
+ }
+
+ /* We assume unrecognized data, is a bad encryption key */
+ }
+
+done:
+ if (cih)
+ gcry_cipher_close (cih);
+ if (asn)
+ asn1_delete_structure (&asn);
+ egg_secure_free (crypted);
+
+ return ret;
+}
+
+static gint
+parse_der_pkcs8 (GcrParser *self, const guchar *data, gsize n_data)
+{
+ gint ret;
+
+ ret = parse_der_pkcs8_plain (self, data, n_data);
+ if (ret == GCR_PARSE_UNRECOGNIZED)
+ ret = parse_der_pkcs8_encrypted (self, data, n_data);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * CERTIFICATE
+ */
+
+static gint
+parse_der_certificate (GcrParser *self, const guchar *data, gsize n_data)
+{
+ ASN1_TYPE asn;
+ gchar *name;
+
+ asn = egg_asn1_decode ("PKIX1.Certificate", data, n_data);
+ if (asn == NULL)
+ return GCR_PARSE_UNRECOGNIZED;
+
+ parsed_clear (self, CKO_CERTIFICATE);
+ parsed_ulong (self, CKA_CERTIFICATE_TYPE, CKC_X_509);
+
+ name = egg_asn1_read_dn_part (asn, "tbsCertificate.subject.rdnSequence", "CN");
+ asn1_delete_structure (&asn);
+
+ if (name != NULL) {
+ parsed_label (self, name);
+ g_free (name);
+ }
+
+ parsed_attribute (self, CKA_VALUE, data, n_data);
+ parsed_fire (self);
+
+ return SUCCESS;
+}
+
+/* -----------------------------------------------------------------------------
+ * PKCS7
+ */
+
+static gint
+handle_pkcs7_signed_data (GcrParser *self, const guchar *data, gsize n_data)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gint ret;
+ gchar *part;
+ const guchar *certificate;
+ gsize n_certificate;
+ int i;
+
+ ret = GCR_PARSE_UNRECOGNIZED;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-7-SignedData", data, n_data);
+ if (!asn)
+ goto done;
+
+ ret = GCR_PARSE_FAILURE;
+
+ for (i = 0; TRUE; ++i) {
+
+ part = g_strdup_printf ("certificates.?%u", i + 1);
+ certificate = egg_asn1_read_element (asn, data, n_data, part, &n_certificate);
+ g_free (part);
+
+ /* No more certificates? */
+ if (!certificate)
+ break;
+
+ ret = parse_der_certificate (self, certificate, n_certificate);
+ if (ret != SUCCESS)
+ goto done;
+ }
+
+ /* TODO: Parse out all the CRLs */
+
+ ret = SUCCESS;
+
+done:
+ if (asn)
+ asn1_delete_structure (&asn);
+
+ return ret;
+}
+
+static gint
+parse_der_pkcs7 (GcrParser *self, const guchar *data, gsize n_data)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gint ret;
+ const guchar* content = NULL;
+ gsize n_content;
+ GQuark oid;
+
+ ret = GCR_PARSE_UNRECOGNIZED;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-7-ContentInfo", data, n_data);
+ if (!asn)
+ goto done;
+
+ ret = GCR_PARSE_FAILURE;
+
+ oid = egg_asn1_read_oid (asn, "contentType");
+ if (!oid)
+ goto done;
+
+ /* Outer most one must just be plain data */
+ if (oid != OID_PKCS7_SIGNED_DATA) {
+ g_message ("unsupported outer content type in pkcs7: %s", g_quark_to_string (oid));
+ goto done;
+ }
+
+ content = egg_asn1_read_content (asn, data, n_data, "content", &n_content);
+ if (!content)
+ goto done;
+
+ ret = handle_pkcs7_signed_data (self, content, n_content);
+
+done:
+ if (asn)
+ asn1_delete_structure (&asn);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * PKCS12
+ */
+
+static gint
+handle_pkcs12_cert_bag (GcrParser *self, const guchar *data, gsize n_data)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ const guchar *certificate;
+ gsize n_certificate;
+ gint ret;
+
+ ret = GCR_PARSE_UNRECOGNIZED;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-12-CertBag", data, n_data);
+ if (!asn)
+ goto done;
+
+ ret = GCR_PARSE_FAILURE;
+
+ certificate = egg_asn1_read_content (asn, data, n_data, "certValue", &n_certificate);
+ if (!certificate)
+ goto done;
+
+ /*
+ * Wrapped in an OCTET STRING, so unwrap here, rather than allocating
+ * a whole bunch more memory for a full ASN.1 parsing context.
+ */
+ certificate = egg_asn1_element_content (certificate, n_certificate, &n_certificate);
+ if (!certificate)
+ goto done;
+
+ ret = parse_der_certificate (self, certificate, n_certificate);
+
+done:
+ if (asn)
+ asn1_delete_structure (&asn);
+
+ return ret;
+}
+
+static gint
+handle_pkcs12_bag (GcrParser *self, const guchar *data, gsize n_data)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gint ret, r;
+ int res, count = 0;
+ GQuark oid;
+ const guchar *element;
+ gsize n_element;
+
+ ret = GCR_PARSE_UNRECOGNIZED;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-12-SafeContents", data, n_data);
+ if (!asn)
+ goto done;
+
+ ret = GCR_PARSE_FAILURE;
+
+ /* Get the number of elements in this bag */
+ res = asn1_number_of_elements (asn, "", &count);
+ if (res != ASN1_SUCCESS)
+ goto done;
+
+ /*
+ * Now inside each bag are multiple elements. Who comes up
+ * with this stuff?
+ *
+ * But this is where we draw the line. We only support one
+ * element per bag, not multiple elements, not strange
+ * nested bags, not fairy queens with magical wands in bags...
+ *
+ * Just one element per bag.
+ */
+ if (count >= 1) {
+
+ oid = egg_asn1_read_oid (asn, "?1.bagId");
+ if (!oid)
+ goto done;
+
+ element = egg_asn1_read_content (asn, data, n_data, "?1.bagValue", &n_element);
+ if (!element)
+ goto done;
+
+ /* A normal unencrypted key */
+ if (oid == OID_PKCS12_BAG_PKCS8_KEY) {
+ r = parse_der_pkcs8_plain (self, element, n_element);
+
+ /* A properly encrypted key */
+ } else if (oid == OID_PKCS12_BAG_PKCS8_ENCRYPTED_KEY) {
+ r = parse_der_pkcs8_encrypted (self, element, n_element);
+
+ /* A certificate */
+ } else if (oid == OID_PKCS12_BAG_CERTIFICATE) {
+ r = handle_pkcs12_cert_bag (self, element, n_element);
+
+ /* TODO: OID_PKCS12_BAG_CRL */
+ } else {
+ r = GCR_PARSE_UNRECOGNIZED;
+ }
+
+ if (r == GCR_PARSE_FAILURE || r == GCR_PARSE_CANCELLED) {
+ ret = r;
+ goto done;
+ }
+ }
+
+ ret = SUCCESS;
+
+done:
+ if (asn)
+ asn1_delete_structure (&asn);
+
+ return ret;
+}
+
+static gint
+handle_pkcs12_encrypted_bag (GcrParser *self, const guchar *data, gsize n_data)
+{
+ PasswordState pstate = PASSWORD_STATE_INIT;
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gcry_cipher_hd_t cih = NULL;
+ gcry_error_t gcry;
+ guchar *crypted = NULL;
+ const guchar *params;
+ gsize n_params, n_crypted;
+ const gchar *password;
+ GQuark scheme;
+ gint ret, r;
+ gint l;
+
+ ret = GCR_PARSE_UNRECOGNIZED;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-7-EncryptedData", data, n_data);
+ if (!asn)
+ goto done;
+
+ ret = GCR_PARSE_FAILURE;
+
+ /* Check the encryption schema OID */
+ scheme = egg_asn1_read_oid (asn, "encryptedContentInfo.contentEncryptionAlgorithm.algorithm");
+ if (!scheme)
+ goto done;
+
+ params = egg_asn1_read_element (asn, data, n_data, "encryptedContentInfo.contentEncryptionAlgorithm.parameters", &n_params);
+ if (!params)
+ goto done;
+
+ parsed_clear (self, 0);
+
+ /* Loop to try different passwords */
+ for (;;) {
+
+ g_assert (cih == NULL);
+
+ r = enum_next_password (self, &pstate, &password);
+ if (r != SUCCESS) {
+ ret = r;
+ goto done;
+ }
+
+ /* Parse the encryption stuff into a cipher. */
+ if (!egg_symkey_read_cipher (scheme, password, -1, params, n_params, &cih)) {
+ ret = GCR_PARSE_FAILURE;
+ goto done;
+ }
+
+ crypted = egg_asn1_read_value (asn, "encryptedContentInfo.encryptedContent",
+ &n_crypted, egg_secure_realloc);
+ if (!crypted)
+ goto done;
+
+ gcry = gcry_cipher_decrypt (cih, crypted, n_crypted, NULL, 0);
+ gcry_cipher_close (cih);
+ cih = NULL;
+
+ if (gcry != 0) {
+ g_warning ("couldn't decrypt pkcs7 data: %s", gcry_strerror (gcry));
+ goto done;
+ }
+
+ /* Unpad the DER data */
+ l = egg_asn1_element_length (crypted, n_crypted);
+ if (l > 0)
+ n_crypted = l;
+
+ /* Try to parse the resulting key */
+ r = handle_pkcs12_bag (self, crypted, n_crypted);
+ egg_secure_free (crypted);
+ crypted = NULL;
+
+ if (r != GCR_PARSE_UNRECOGNIZED) {
+ ret = r;
+ break;
+ }
+
+ /* We assume unrecognized data is a bad encryption key */
+ }
+
+done:
+ if (cih)
+ gcry_cipher_close (cih);
+ if (asn)
+ asn1_delete_structure (&asn);
+ egg_secure_free (crypted);
+
+ return ret;
+}
+
+static gint
+handle_pkcs12_safe (GcrParser *self, const guchar *data, gsize n_data)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gint ret, r;
+ const guchar *bag;
+ gsize n_bag;
+ gchar *part;
+ GQuark oid;
+ guint i;
+
+ ret = GCR_PARSE_UNRECOGNIZED;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-12-AuthenticatedSafe", data, n_data);
+ if (!asn)
+ goto done;
+
+ ret = GCR_PARSE_FAILURE;
+
+ /*
+ * Inside each PKCS12 safe there are multiple bags.
+ */
+ for (i = 0; TRUE; ++i) {
+
+ part = g_strdup_printf ("?%u.contentType", i + 1);
+ oid = egg_asn1_read_oid (asn, part);
+ g_free (part);
+
+ /* All done? no more bags */
+ if (!oid)
+ break;
+
+ part = g_strdup_printf ("?%u.content", i + 1);
+ bag = egg_asn1_read_content (asn, data, n_data, part, &n_bag);
+ g_free (part);
+
+ if (!bag) /* A parse error */
+ goto done;
+
+ /* A non encrypted bag, just parse */
+ if (oid == OID_PKCS7_DATA) {
+
+ /*
+ * Wrapped in an OCTET STRING, so unwrap here, rather than allocating
+ * a whole bunch more memory for a full ASN.1 parsing context.
+ */
+ bag = egg_asn1_element_content (bag, n_bag, &n_bag);
+ if (!bag)
+ goto done;
+
+ r = handle_pkcs12_bag (self, bag, n_bag);
+
+ /* Encrypted data first needs decryption */
+ } else if (oid == OID_PKCS7_ENCRYPTED_DATA) {
+ r = handle_pkcs12_encrypted_bag (self, bag, n_bag);
+
+ /* Hmmmm, not sure what this is */
+ } else {
+ g_warning ("unrecognized type of safe content in pkcs12: %s", g_quark_to_string (oid));
+ r = GCR_PARSE_UNRECOGNIZED;
+ }
+
+ if (r == GCR_PARSE_FAILURE || r == GCR_PARSE_CANCELLED) {
+ ret = r;
+ goto done;
+ }
+ }
+
+ ret = SUCCESS;
+
+done:
+ if (asn)
+ asn1_delete_structure (&asn);
+
+ return ret;
+}
+
+static gint
+parse_der_pkcs12 (GcrParser *self, const guchar *data, gsize n_data)
+{
+ ASN1_TYPE asn = ASN1_TYPE_EMPTY;
+ gint ret;
+ const guchar* content = NULL;
+ gsize n_content;
+ GQuark oid;
+
+ ret = GCR_PARSE_UNRECOGNIZED;
+
+ asn = egg_asn1_decode ("PKIX1.pkcs-12-PFX", data, n_data);
+ if (!asn)
+ goto done;
+
+ oid = egg_asn1_read_oid (asn, "authSafe.contentType");
+ if (!oid)
+ goto done;
+
+ /* Outer most one must just be plain data */
+ if (oid != OID_PKCS7_DATA) {
+ g_message ("unsupported safe content type in pkcs12: %s", g_quark_to_string (oid));
+ goto done;
+ }
+
+ content = egg_asn1_read_content (asn, data, n_data, "authSafe.content", &n_content);
+ if (!content)
+ goto done;
+
+ /*
+ * Wrapped in an OCTET STRING, so unwrap here, rather than allocating
+ * a whole bunch more memory for a full ASN.1 parsing context.
+ */
+ content = egg_asn1_element_content (content, n_content, &n_content);
+ if (!content)
+ goto done;
+
+ ret = handle_pkcs12_safe (self, content, n_content);
+
+done:
+ if (asn)
+ asn1_delete_structure (&asn);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * PEM PARSING
+ */
+
+static gint
+handle_plain_pem (GcrParser *self, GQuark type, gint subformat,
+ const guchar *data, gsize n_data)
+{
+ ParserFormat *format;
+ gint format_id;
+
+ if (type == PEM_RSA_PRIVATE_KEY)
+ format_id = GCR_FORMAT_DER_PRIVATE_KEY_RSA;
+
+ else if (type == PEM_DSA_PRIVATE_KEY)
+ format_id = GCR_FORMAT_DER_PRIVATE_KEY_DSA;
+
+ else if (type == PEM_ANY_PRIVATE_KEY)
+ format_id = GCR_FORMAT_DER_PRIVATE_KEY;
+
+ else if (type == PEM_PRIVATE_KEY)
+ format_id = GCR_FORMAT_DER_PKCS8_PLAIN;
+
+ else if (type == PEM_ENCRYPTED_PRIVATE_KEY)
+ format_id = GCR_FORMAT_DER_PKCS8_ENCRYPTED;
+
+ else if (type == PEM_CERTIFICATE)
+ format_id = GCR_FORMAT_DER_CERTIFICATE_X509;
+
+ else if (type == PEM_PKCS7)
+ format_id = GCR_FORMAT_DER_PKCS7;
+
+ else if (type == PEM_PKCS12)
+ format_id = GCR_FORMAT_DER_PKCS12;
+
+ else
+ return GCR_PARSE_UNRECOGNIZED;
+
+ if (subformat != 0 && subformat != format_id)
+ return GCR_PARSE_UNRECOGNIZED;
+
+ format = parser_format_lookup (format_id);
+ if (format == NULL)
+ return GCR_PARSE_UNRECOGNIZED;
+
+ return (format->function) (self, data, n_data);
+}
+
+static CK_OBJECT_CLASS
+pem_type_to_class (gint type)
+{
+ if (type == PEM_RSA_PRIVATE_KEY ||
+ type == PEM_DSA_PRIVATE_KEY ||
+ type == PEM_ANY_PRIVATE_KEY ||
+ type == PEM_PRIVATE_KEY ||
+ type == PEM_ENCRYPTED_PRIVATE_KEY)
+ return CKO_PRIVATE_KEY;
+
+ else if (type == PEM_CERTIFICATE)
+ return CKO_CERTIFICATE;
+
+ else if (type == PEM_PKCS7 ||
+ type == PEM_PKCS12)
+ return 0;
+
+ return 0;
+}
+
+static gint
+handle_encrypted_pem (GcrParser *self, GQuark type, gint subformat,
+ GHashTable *headers, const guchar *data, gsize n_data)
+{
+ PasswordState pstate = PASSWORD_STATE_INIT;
+ const gchar *password;
+ guchar *decrypted;
+ gsize n_decrypted;
+ const gchar *val;
+ gboolean ret;
+ gint res;
+ gint l;
+
+ g_assert (GCR_IS_PARSER (self));
+ g_assert (headers);
+ g_assert (type);
+
+ val = g_hash_table_lookup (headers, "DEK-Info");
+ if (!val) {
+ g_message ("missing encryption header");
+ return GCR_PARSE_FAILURE;
+ }
+
+ /* Fill in information necessary for prompting */
+ parsed_clear (self, pem_type_to_class (type));
+
+ for (;;) {
+
+ res = enum_next_password (self, &pstate, &password);
+ if (res != SUCCESS)
+ return res;
+
+ decrypted = NULL;
+ n_decrypted = 0;
+
+ /* Decrypt, this will result in garble if invalid password */
+ ret = egg_openssl_decrypt_block (val, password, -1, data, n_data,
+ &decrypted, &n_decrypted);
+ if (!ret)
+ return GCR_PARSE_FAILURE;
+
+ g_assert (decrypted);
+
+ /* Unpad the DER data */
+ l = egg_asn1_element_length (decrypted, n_decrypted);
+ if (l > 0)
+ n_decrypted = l;
+
+ /* Try to parse */
+ res = handle_plain_pem (self, type, subformat, decrypted, n_decrypted);
+ egg_secure_free (decrypted);
+
+ /* Unrecognized is a bad password */
+ if (res != GCR_PARSE_UNRECOGNIZED)
+ return res;
+ }
+
+ return GCR_PARSE_FAILURE;
+}
+
+typedef struct {
+ GcrParser *parser;
+ gint result;
+ gint subformat;
+} HandlePemArgs;
+
+static void
+handle_pem_data (GQuark type, const guchar *data, gsize n_data,
+ GHashTable *headers, gpointer user_data)
+{
+ HandlePemArgs *args = (HandlePemArgs*)user_data;
+ gint res = GCR_PARSE_FAILURE;
+ gboolean encrypted = FALSE;
+ const gchar *val;
+
+ /* Something already failed to parse */
+ if (args->result == GCR_PARSE_FAILURE)
+ return;
+
+ /* See if it's encrypted PEM all openssl like*/
+ if (headers) {
+ val = g_hash_table_lookup (headers, "Proc-Type");
+ if (val && strcmp (val, "4,ENCRYPTED") == 0)
+ encrypted = TRUE;
+ }
+
+ if (encrypted)
+ res = handle_encrypted_pem (args->parser, type, args->subformat,
+ headers, data, n_data);
+ else
+ res = handle_plain_pem (args->parser, type, args->subformat,
+ data, n_data);
+
+ if (res != GCR_PARSE_UNRECOGNIZED) {
+ if (args->result == GCR_PARSE_UNRECOGNIZED)
+ args->result = res;
+ else if (res > args->result)
+ args->result = res;
+ }
+}
+
+static gint
+handle_pem_format (GcrParser *self, gint subformat, const guchar *data, gsize n_data)
+{
+ HandlePemArgs ctx = { self, GCR_PARSE_UNRECOGNIZED, subformat };
+ guint found;
+
+ if (n_data == 0)
+ return GCR_PARSE_UNRECOGNIZED;
+
+ found = egg_openssl_pem_parse (data, n_data, handle_pem_data, &ctx);
+
+ if (found == 0)
+ return GCR_PARSE_UNRECOGNIZED;
+
+ return ctx.result;
+}
+
+
+static gint
+parse_pem (GcrParser *self, const guchar *data, gsize n_data)
+{
+ return handle_pem_format (self, 0, data, n_data);
+}
+
+static gint
+parse_pem_private_key_rsa (GcrParser *self, const guchar *data, gsize n_data)
+{
+ return handle_pem_format (self, GCR_FORMAT_DER_PRIVATE_KEY_RSA, data, n_data);
+}
+
+static gint
+parse_pem_private_key_dsa (GcrParser *self, const guchar *data, gsize n_data)
+{
+ return handle_pem_format (self, GCR_FORMAT_DER_PRIVATE_KEY_DSA, data, n_data);
+}
+
+static gint
+parse_pem_certificate (GcrParser *self, const guchar *data, gsize n_data)
+{
+ return handle_pem_format (self, GCR_FORMAT_PEM_CERTIFICATE_X509, data, n_data);
+}
+
+static gint
+parse_pem_pkcs8_plain (GcrParser *self, const guchar *data, gsize n_data)
+{
+ return handle_pem_format (self, GCR_FORMAT_PEM_PKCS8_PLAIN, data, n_data);
+}
+
+static gint
+parse_pem_pkcs8_encrypted (GcrParser *self, const guchar *data, gsize n_data)
+{
+ return handle_pem_format (self, GCR_FORMAT_PEM_PKCS8_ENCRYPTED, data, n_data);
+}
+
+static gint
+parse_pem_pkcs7 (GcrParser *self, const guchar *data, gsize n_data)
+{
+ return handle_pem_format (self, GCR_FORMAT_PEM_PKCS7, data, n_data);
+}
+
+static gint
+parse_pem_pkcs12 (GcrParser *self, const guchar *data, gsize n_data)
+{
+ return handle_pem_format (self, GCR_FORMAT_PEM_PKCS12, data, n_data);
+}
+
+/* -----------------------------------------------------------------------------
+ * FORMATS
+ */
+
+/* In order of parsing when no formats specified */
+static const ParserFormat parser_normal[] = {
+ { GCR_FORMAT_PEM, parse_pem },
+ { GCR_FORMAT_DER_PRIVATE_KEY_RSA, parse_der_private_key_rsa },
+ { GCR_FORMAT_DER_PRIVATE_KEY_DSA, parse_der_private_key_dsa },
+ { GCR_FORMAT_DER_CERTIFICATE_X509, parse_der_certificate },
+ { GCR_FORMAT_DER_PKCS7, parse_der_pkcs7 },
+ { GCR_FORMAT_DER_PKCS8_PLAIN, parse_der_pkcs8_plain },
+ { GCR_FORMAT_DER_PKCS8_ENCRYPTED, parse_der_pkcs8_encrypted },
+ { GCR_FORMAT_DER_PKCS12, parse_der_pkcs12 }
+};
+
+/* Must be in format_id numeric order */
+static const ParserFormat parser_formats[] = {
+ { GCR_FORMAT_DER_PRIVATE_KEY, parse_der_private_key },
+ { GCR_FORMAT_DER_PRIVATE_KEY_RSA, parse_der_private_key_rsa },
+ { GCR_FORMAT_DER_PRIVATE_KEY_DSA, parse_der_private_key_dsa },
+ { GCR_FORMAT_DER_CERTIFICATE_X509, parse_der_certificate },
+ { GCR_FORMAT_DER_PKCS7, parse_der_pkcs7 },
+ { GCR_FORMAT_DER_PKCS8, parse_der_pkcs8 },
+ { GCR_FORMAT_DER_PKCS8_PLAIN, parse_der_pkcs8_plain },
+ { GCR_FORMAT_DER_PKCS8_ENCRYPTED, parse_der_pkcs8_encrypted },
+ { GCR_FORMAT_DER_PKCS12, parse_der_pkcs12 },
+ { GCR_FORMAT_PEM, parse_pem },
+ { GCR_FORMAT_PEM_PRIVATE_KEY_RSA, parse_pem_private_key_rsa },
+ { GCR_FORMAT_PEM_PRIVATE_KEY_DSA, parse_pem_private_key_dsa },
+ { GCR_FORMAT_PEM_CERTIFICATE_X509, parse_pem_certificate },
+ { GCR_FORMAT_PEM_PKCS7, parse_pem_pkcs7 },
+ { GCR_FORMAT_PEM_PKCS8_PLAIN, parse_pem_pkcs8_plain },
+ { GCR_FORMAT_PEM_PKCS8_ENCRYPTED, parse_pem_pkcs8_encrypted },
+ { GCR_FORMAT_PEM_PKCS12, parse_pem_pkcs12 },
+};
+
+static int
+compar_id_to_parser_format (const void *a, const void *b)
+{
+ const gint *format_id = a;
+ const ParserFormat *format = b;
+
+ g_assert (format_id);
+ g_assert (format);
+
+ if (format->format_id == *format_id)
+ return 0;
+ return (*format_id < format->format_id) ? -1 : 1;
+}
+
+static ParserFormat*
+parser_format_lookup (gint format_id)
+{
+ return bsearch (&format_id, parser_formats, G_N_ELEMENTS (parser_formats),
+ sizeof (parser_formats[0]), compar_id_to_parser_format);
+}
+
+static gint
+compare_pointers (gconstpointer a, gconstpointer b)
+{
+ if (a == b)
+ return 0;
+ return a < b ? -1 : 1;
+}
+
+typedef struct _ForeachArgs {
+ GcrParser *parser;
+ const guchar *data;
+ gsize n_data;
+ gint result;
+} ForeachArgs;
+
+static gboolean
+parser_format_foreach (gpointer key, gpointer value, gpointer data)
+{
+ ForeachArgs *args = data;
+ ParserFormat *format = key;
+ gint result;
+
+ g_assert (format);
+ g_assert (format->function);
+ g_assert (GCR_IS_PARSER (args->parser));
+
+ result = (format->function) (args->parser, args->data, args->n_data);
+ if (result != GCR_PARSE_UNRECOGNIZED) {
+ args->result = result;
+ return TRUE;
+ }
+
+ /* Keep going */
+ return FALSE;
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+
+static GObject*
+gcr_parser_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+ GcrParser *self = GCR_PARSER (G_OBJECT_CLASS (gcr_parser_parent_class)->constructor(type, n_props, props));
+ g_return_val_if_fail (self, NULL);
+
+ /* Always try to parse with NULL and empty passwords first */
+ gcr_parser_add_password (self, NULL);
+ gcr_parser_add_password (self, "");
+
+ return G_OBJECT (self);
+}
+
+static void
+gcr_parser_init (GcrParser *self)
+{
+ self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_PARSER, GcrParserPrivate);
+ self->pv->passwords = g_ptr_array_new ();
+ self->pv->normal_formats = TRUE;
+}
+
+static void
+gcr_parser_dispose (GObject *obj)
+{
+ GcrParser *self = GCR_PARSER (obj);
+ gsize i;
+
+ if (self->pv->parsed_attrs)
+ gp11_attributes_unref (self->pv->parsed_attrs);
+ self->pv->parsed_attrs = NULL;
+
+ g_free (self->pv->parsed_label);
+ self->pv->parsed_label = NULL;
+
+ for (i = 0; i < self->pv->passwords->len; ++i)
+ egg_secure_strfree (g_ptr_array_index (self->pv->passwords, i));
+ g_ptr_array_set_size (self->pv->passwords, 0);
+
+ G_OBJECT_CLASS (gcr_parser_parent_class)->dispose (obj);
+}
+
+static void
+gcr_parser_finalize (GObject *obj)
+{
+ GcrParser *self = GCR_PARSER (obj);
+
+ g_assert (!self->pv->parsed_attrs);
+ g_assert (!self->pv->parsed_label);
+
+ g_ptr_array_free (self->pv->passwords, TRUE);
+ self->pv->passwords = NULL;
+
+ G_OBJECT_CLASS (gcr_parser_parent_class)->finalize (obj);
+}
+
+static void
+gcr_parser_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+#if 0
+ GcrParser *self = GCR_PARSER (obj);
+#endif
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_parser_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ GcrParser *self = GCR_PARSER (obj);
+
+ switch (prop_id) {
+ case PROP_PARSED_ATTRIBUTES:
+ g_value_set_boxed (value, gcr_parser_get_parsed_attributes (self));
+ break;
+ case PROP_PARSED_LABEL:
+ g_value_set_string (value, gcr_parser_get_parsed_label (self));
+ break;
+ case PROP_PARSED_DESCRIPTION:
+ g_value_set_string (value, gcr_parser_get_parsed_description (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_parser_class_init (GcrParserClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ gint i;
+
+ gobject_class->constructor = gcr_parser_constructor;
+ gobject_class->dispose = gcr_parser_dispose;
+ gobject_class->finalize = gcr_parser_finalize;
+ gobject_class->set_property = gcr_parser_set_property;
+ gobject_class->get_property = gcr_parser_get_property;
+
+ g_type_class_add_private (gobject_class, sizeof (GcrParserPrivate));
+
+ g_object_class_install_property (gobject_class, PROP_PARSED_ATTRIBUTES,
+ g_param_spec_boxed ("parsed-attributes", "Parsed Attributes", "Parsed PKCS#11 attributes",
+ GP11_TYPE_ATTRIBUTES, G_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class, PROP_PARSED_LABEL,
+ g_param_spec_string ("parsed-label", "Parsed Label", "Parsed item label",
+ "", G_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class, PROP_PARSED_DESCRIPTION,
+ g_param_spec_string ("parsed-description", "Parsed Description", "Parsed item description",
+ "", G_PARAM_READABLE));
+
+ signals[AUTHENTICATE] = g_signal_new ("authenticate", GCR_TYPE_PARSER,
+ G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GcrParserClass, authenticate),
+ g_signal_accumulator_true_handled, NULL, _gcr_marshal_BOOLEAN__INT,
+ G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
+
+ signals[PARSED] = g_signal_new ("parsed", GCR_TYPE_PARSER,
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrParserClass, parsed),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ init_quarks ();
+ _gcr_initialize ();
+
+ /* Check that the format tables are in order */
+ for (i = 1; i < G_N_ELEMENTS (parser_formats); ++i)
+ g_assert (parser_formats[i].format_id >= parser_formats[i - 1].format_id);
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GcrParser*
+gcr_parser_new (void)
+{
+ return g_object_new (GCR_TYPE_PARSER, NULL);
+}
+
+GQuark
+gcr_parser_get_error_domain (void)
+{
+ static GQuark domain = 0;
+ if (domain == 0)
+ domain = g_quark_from_static_string ("gcr-parser-error");
+ return domain;
+}
+
+void
+gcr_parser_add_password (GcrParser *self, const gchar *password)
+{
+ g_return_if_fail (GCR_IS_PARSER (self));
+ g_ptr_array_add (self->pv->passwords, egg_secure_strdup (password));
+}
+
+gboolean
+gcr_parser_parse_data (GcrParser *self, const guchar *data,
+ gsize n_data, GError **err)
+{
+ ForeachArgs args = { self, data, n_data, GCR_PARSE_UNRECOGNIZED };
+ const gchar *message;
+ gint i;
+
+ g_return_val_if_fail (GCR_IS_PARSER (self), FALSE);
+ g_return_val_if_fail (data || !n_data, FALSE);
+ g_return_val_if_fail (!err || !*err, FALSE);
+
+ /* Just the specific formats requested */
+ if (self->pv->specific_formats) {
+ g_tree_foreach (self->pv->specific_formats, parser_format_foreach, &args);
+
+ /* All the 'normal' formats */
+ } else if (self->pv->normal_formats) {
+ for (i = 0; i < G_N_ELEMENTS (parser_normal); ++i) {
+ if (parser_format_foreach ((gpointer)(parser_normal + i),
+ (gpointer)(parser_normal + i), &args))
+ break;
+ }
+ }
+
+ switch (args.result) {
+ case SUCCESS:
+ return TRUE;
+ case GCR_PARSE_CANCELLED:
+ message = _("The operation was cancelled");
+ break;
+ case GCR_PARSE_UNRECOGNIZED:
+ message = _("Unrecognized or unsupported data.");
+ break;
+ case GCR_PARSE_FAILURE:
+ message = _("Could not parse invalid or corrupted data.");
+ break;
+ case GCR_PARSE_LOCKED:
+ message = _("The data is locked");
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ };
+
+ g_set_error_literal (err, GCR_PARSER_ERROR, args.result, message);
+ return FALSE;
+}
+
+gboolean
+gcr_parser_parse_file (GcrParser *self, const gchar *filename, GError **err)
+{
+ GMappedFile *mapped;
+ gboolean ret;
+ const guchar *data;
+ gsize n_data;
+
+ g_return_val_if_fail (GCR_IS_PARSER (self), FALSE);
+ g_return_val_if_fail (filename, FALSE);
+ g_return_val_if_fail (!err || !*err, FALSE);
+
+ mapped = g_mapped_file_new (filename, FALSE, err);
+ if (mapped == NULL)
+ return FALSE;
+
+ data = (const guchar*)g_mapped_file_get_contents (mapped);
+ n_data = g_mapped_file_get_length (mapped);
+
+ ret = gcr_parser_parse_data (self, data, n_data, err);
+
+ g_mapped_file_free (mapped);
+
+ return ret;
+}
+
+gboolean
+gcr_parser_format_enable (GcrParser *self, gint format_id)
+{
+ ParserFormat *format;
+
+ g_return_val_if_fail (GCR_IS_PARSER (self), FALSE);
+
+ if (format_id == -1) {
+ if (self->pv->specific_formats)
+ g_tree_destroy (self->pv->specific_formats);
+ self->pv->specific_formats = NULL;
+ self->pv->normal_formats = TRUE;
+ return TRUE;
+ }
+
+ format = parser_format_lookup (format_id);
+ if (format == NULL)
+ return FALSE;
+
+ if (!self->pv->specific_formats) {
+ if (self->pv->normal_formats)
+ return TRUE;
+ self->pv->specific_formats = g_tree_new (compare_pointers);
+ }
+
+ g_tree_insert (self->pv->specific_formats, format, format);
+ return TRUE;
+}
+
+gboolean
+gcr_parser_format_disable (GcrParser *self, gint format_id)
+{
+ ParserFormat *format;
+
+ g_return_val_if_fail (GCR_IS_PARSER (self), FALSE);
+
+ if (format_id == -1) {
+ if (self->pv->specific_formats)
+ g_tree_destroy (self->pv->specific_formats);
+ self->pv->specific_formats = NULL;
+ self->pv->normal_formats = FALSE;
+ return TRUE;
+ }
+
+ if (!self->pv->specific_formats)
+ return TRUE;
+
+ format = parser_format_lookup (format_id);
+ if (format == NULL)
+ return FALSE;
+
+ g_tree_remove (self->pv->specific_formats, format);
+ return TRUE;
+}
+
+gboolean
+gcr_parser_format_supported (GcrParser *self, gint format_id)
+{
+ g_return_val_if_fail (GCR_IS_PARSER (self), FALSE);
+ g_return_val_if_fail (format_id != -1, FALSE);
+ return parser_format_lookup (format_id) ? TRUE : FALSE;
+}
+
+const gchar*
+gcr_parser_get_parsed_description (GcrParser *self)
+{
+ g_return_val_if_fail (GCR_IS_PARSER (self), NULL);
+ return self->pv->parsed_desc;
+}
+
+GP11Attributes*
+gcr_parser_get_parsed_attributes (GcrParser *self)
+{
+ g_return_val_if_fail (GCR_IS_PARSER (self), NULL);
+ return self->pv->parsed_attrs;
+}
+
+const gchar*
+gcr_parser_get_parsed_label (GcrParser *self)
+{
+ g_return_val_if_fail (GCR_IS_PARSER (self), NULL);
+ return self->pv->parsed_label;
+}
diff --git a/gcr/gcr-parser.h b/gcr/gcr-parser.h
new file mode 100644
index 00000000..e4c3e248
--- /dev/null
+++ b/gcr/gcr-parser.h
@@ -0,0 +1,92 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCR_PARSER_H__
+#define __GCR_PARSER_H__
+
+#include <glib-object.h>
+
+#include "gcr-types.h"
+
+#define GCR_PARSER_ERROR (gcr_parser_get_error_domain ())
+
+#define GCR_TYPE_PARSER (gcr_parser_get_type ())
+#define GCR_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_PARSER, GcrParser))
+#define GCR_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_PARSER, GcrParserClass))
+#define GCR_IS_PARSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_PARSER))
+#define GCR_IS_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_PARSER))
+#define GCR_PARSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_PARSER, GcrParserClass))
+
+typedef struct _GcrParser GcrParser;
+typedef struct _GcrParsedItem GcrParsedItem;
+typedef struct _GcrParserClass GcrParserClass;
+typedef struct _GcrParserPrivate GcrParserPrivate;
+
+struct _GcrParser {
+ GObject parent;
+ GcrParserPrivate *pv;
+};
+
+struct _GcrParserClass {
+ GObjectClass parent_class;
+
+ /* signals --------------------------------------------------------- */
+
+ /* A callback for each password needed */
+ gboolean (*authenticate) (GcrParser *self, gint count);
+
+ void (*parsed) (GcrParser *self);
+};
+
+GType gcr_parser_get_type (void);
+
+GQuark gcr_parser_get_error_domain (void) G_GNUC_CONST;
+
+GcrParser* gcr_parser_new (void);
+
+gboolean gcr_parser_format_enable (GcrParser *self,
+ gint format);
+
+gboolean gcr_parser_format_disable (GcrParser *self,
+ gint format);
+
+gboolean gcr_parser_format_supported (GcrParser *self,
+ gint format);
+
+gboolean gcr_parser_parse_data (GcrParser *self,
+ const guchar *data,
+ gsize n_data,
+ GError **err);
+
+gboolean gcr_parser_parse_file (GcrParser *self,
+ const gchar *filename,
+ GError **err);
+
+void gcr_parser_add_password (GcrParser *self,
+ const gchar *password);
+
+const gchar* gcr_parser_get_parsed_label (GcrParser *self);
+
+const gchar* gcr_parser_get_parsed_description (GcrParser *self);
+
+GP11Attributes* gcr_parser_get_parsed_attributes (GcrParser *self);
+
+#endif /* __GCR_PARSER_H__ */
diff --git a/gcr/gcr-types.h b/gcr/gcr-types.h
new file mode 100644
index 00000000..edef49b5
--- /dev/null
+++ b/gcr/gcr-types.h
@@ -0,0 +1,45 @@
+#ifndef GCRTYPES_H_
+#define GCRTYPES_H_
+
+enum {
+ GCR_PARSE_FAILURE = -1,
+ GCR_PARSE_UNRECOGNIZED = 1,
+ GCR_PARSE_CANCELLED = 2,
+ GCR_PARSE_LOCKED = 3
+};
+
+enum {
+ GCR_FORMAT_INVALID = 0,
+
+ GCR_FORMAT_DER_PRIVATE_KEY = 100,
+ GCR_FORMAT_DER_PRIVATE_KEY_RSA,
+ GCR_FORMAT_DER_PRIVATE_KEY_DSA,
+
+ GCR_FORMAT_DER_CERTIFICATE_X509 = 200,
+
+ GCR_FORMAT_DER_PKCS7 = 300,
+
+ GCR_FORMAT_DER_PKCS8 = 400,
+ GCR_FORMAT_DER_PKCS8_PLAIN,
+ GCR_FORMAT_DER_PKCS8_ENCRYPTED,
+
+ GCR_FORMAT_DER_PKCS12 = 500,
+
+ GCR_FORMAT_PEM = 1000,
+ GCR_FORMAT_PEM_PRIVATE_KEY_RSA,
+ GCR_FORMAT_PEM_PRIVATE_KEY_DSA,
+ GCR_FORMAT_PEM_CERTIFICATE_X509,
+ GCR_FORMAT_PEM_PKCS7,
+ GCR_FORMAT_PEM_PKCS8_PLAIN,
+ GCR_FORMAT_PEM_PKCS8_ENCRYPTED,
+ GCR_FORMAT_PEM_PKCS12
+};
+
+#ifndef GP11_H
+
+/* Forward declare some of the GP11 objects */
+typedef struct _GP11Attributes GP11Attributes;
+
+#endif /* GP11_H */
+
+#endif /* GCRTYPES_H_ */
diff --git a/gcr/gcr.pc.in b/gcr/gcr.pc.in
new file mode 100644
index 00000000..b0a812d4
--- /dev/null
+++ b/gcr/gcr.pc.in
@@ -0,0 +1,14 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+datarootdir=@datarootdir@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+
+Name: gp11
+Description: GObject bindings for PKCS#11
+Version: @VERSION@
+Requires: glib-2.0
+Libs: -L${libdir} -lgp11
+Cflags: -I${includedir}/gp11
diff --git a/gcr/template/gcr-xxx.c b/gcr/template/gcr-xxx.c
new file mode 100644
index 00000000..91f7e6c6
--- /dev/null
+++ b/gcr/template/gcr-xxx.c
@@ -0,0 +1,146 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gcr-xxx.h"
+
+enum {
+ PROP_0,
+ PROP_XXX
+};
+
+enum {
+ SIGNAL,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+struct _GcrXxxPrivate {
+};
+
+G_DEFINE_TYPE (GcrXxx, gcr_xxx, G_TYPE_OBJECT);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+
+static GObject*
+gcr_xxx_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+ GcrXxx *self = GCR_XXX (G_OBJECT_CLASS (gcr_xxx_parent_class)->constructor(type, n_props, props));
+ g_return_val_if_fail (self, NULL);
+
+
+
+ return G_OBJECT (self);
+}
+
+static void
+gcr_xxx_init (GcrXxx *self)
+{
+ self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_XXX, GcrXxxPrivate);
+}
+
+static void
+gcr_xxx_dispose (GObject *obj)
+{
+ GcrXxx *self = GCR_XXX (obj);
+
+ G_OBJECT_CLASS (gcr_xxx_parent_class)->dispose (obj);
+}
+
+static void
+gcr_xxx_finalize (GObject *obj)
+{
+ GcrXxx *self = GCR_XXX (obj);
+
+ G_OBJECT_CLASS (gcr_xxx_parent_class)->finalize (obj);
+}
+
+static void
+gcr_xxx_set_property (GObject *obj, guint prop_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ GcrXxx *self = GCR_XXX (obj);
+
+ switch (prop_id) {
+ case PROP_XXX:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_xxx_get_property (GObject *obj, guint prop_id, GValue *value,
+ GParamSpec *pspec)
+{
+ GcrXxx *self = GCR_XXX (obj);
+
+ switch (prop_id) {
+ case PROP_XXX:
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcr_xxx_class_init (GcrXxxClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->constructor = gcr_xxx_constructor;
+ gobject_class->dispose = gcr_xxx_dispose;
+ gobject_class->finalize = gcr_xxx_finalize;
+ gobject_class->set_property = gcr_xxx_set_property;
+ gobject_class->get_property = gcr_xxx_get_property;
+
+ g_type_class_add_private (gobject_class, sizeof (GcrXxxPrivate));
+
+ g_object_class_install_property (gobject_class, PROP_XXX,
+ g_param_spec_pointer ("xxx", "Xxx", "Xxx.", G_PARAM_READWRITE));
+
+ signals[SIGNAL] = g_signal_new ("signal", GCR_TYPE_XXX,
+ G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GcrXxxClass, signal),
+ NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+GcrXxx*
+gcr_xxx_new (void)
+{
+ return g_object_new (GCR_TYPE_XXX, NULL);
+}
diff --git a/gcr/template/gcr-xxx.h b/gcr/template/gcr-xxx.h
new file mode 100644
index 00000000..b85cd95e
--- /dev/null
+++ b/gcr/template/gcr-xxx.h
@@ -0,0 +1,55 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2008 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCR_XXX_H__
+#define __GCR_XXX_H__
+
+#include <glib-object.h>
+
+#define GCR_TYPE_XXX (gcr_xxx_get_type ())
+#define GCR_XXX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_XXX, GcrXxx))
+#define GCR_XXX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_XXX, GcrXxxClass))
+#define GCR_IS_XXX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_XXX))
+#define GCR_IS_XXX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_XXX))
+#define GCR_XXX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_XXX, GcrXxxClass))
+
+typedef struct _GcrXxx GcrXxx;
+typedef struct _GcrXxxClass GcrXxxClass;
+typedef struct _GcrXxxPrivate GcrXxxPrivate;
+
+struct _GcrXxx {
+ GObject parent;
+ GcrXxxPrivate *pv;
+};
+
+struct _GcrXxxClass {
+ GObjectClass parent_class;
+
+ /* signals --------------------------------------------------------- */
+
+ void (*signal) (GcrXxx *self);
+};
+
+GType gcr_xxx_get_type (void);
+
+GcrXxx* gcr_xxx_new (void);
+
+#endif /* __GCR_XXX_H__ */
diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am
new file mode 100644
index 00000000..c746135a
--- /dev/null
+++ b/gcr/tests/Makefile.am
@@ -0,0 +1,13 @@
+
+# Test files should be listed in order they need to run
+UNIT_AUTO = \
+ unit-test-parser.c
+
+UNIT_PROMPT =
+
+UNIT_LIBS = \
+ $(top_builddir)/gcr/libgcr.la \
+ $(top_builddir)/egg/libegg.la \
+ $(top_builddir)/gp11/libgp11.la
+
+include $(top_srcdir)/tests/gtest.make
diff --git a/gcr/tests/test-data/RSA_Root_Certificate_1.pem b/gcr/tests/test-data/RSA_Root_Certificate_1.pem
new file mode 100644
index 00000000..f6cdfcd0
--- /dev/null
+++ b/gcr/tests/test-data/RSA_Root_Certificate_1.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD
+ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRp
+b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv
+bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy
+NjAwMjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x
+NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x
+IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2f
+NUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChM
+MFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqYJJgpp0lZpd34
+t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs3x/b
+e0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0Wu
+PIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A
+PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu
+-----END CERTIFICATE-----
diff --git a/gcr/tests/test-data/RSA_Security_1024_v3.pem b/gcr/tests/test-data/RSA_Security_1024_v3.pem
new file mode 100644
index 00000000..94e0ad13
--- /dev/null
+++ b/gcr/tests/test-data/RSA_Security_1024_v3.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICXDCCAcWgAwIBAgIQCgEBAQAAAnwAAAALAAAAAjANBgkqhkiG9w0BAQUF
+ADA6MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0Eg
+U2VjdXJpdHkgMTAyNCBWMzAeFw0wMTAyMjIyMTAxNDlaFw0yNjAyMjIyMDAx
+NDlaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJT
+QSBTZWN1cml0eSAxMDI0IFYzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQDV3f5mCc8kPD6ugU5OisRpgFtZO9+5TUzKtS3DJy08rwBCbbwoppbPf9dY
+rIMKo1W1exeQFYRMiu4mmdxY78c4pqqv0I5CyGLXq6yp+0p9v+r+Ek3d/yYt
+bzZUaMjShFbuklNhCbM/OZuoyZu9zp9+1BlqFikYvtc6adwlWzMaUQIDAQAB
+o2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSME
+GDAWgBTEwBykB5T9zU0B1FTapQxf3q4FWjAdBgNVHQ4EFgQUxMAcpAeU/c1N
+AdRU2qUMX96uBVowDQYJKoZIhvcNAQEFBQADgYEAPy1q4yZDlX2Jl2X7deRy
+HUZXxGFraZ8SmyzVWujAovBDleMf6XbN3Ou8k6BlCsdNT1+nr6JGFLkM88y9
+am63nd4lQtBU/55oc2PcJOsiv6hy8l4A4Q1OOkNumU4/iXgDmMrzVcydro7B
+qkWY+o8aoI2II/EVQQ2lRj6RP4vr93E=
+-----END CERTIFICATE-----
diff --git a/gcr/tests/test-data/RSA_Security_2048_v3.pem b/gcr/tests/test-data/RSA_Security_2048_v3.pem
new file mode 100644
index 00000000..86c907eb
--- /dev/null
+++ b/gcr/tests/test-data/RSA_Security_2048_v3.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUF
+ADA6MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0Eg
+U2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5
+MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJT
+QSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37
+RqtBaB4Y6lXIL5F4iSj7Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E
+0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J
+6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iHKrtjEAMq
+s6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzD
+uvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2Mw
+YTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAW
+gBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NRMKSq6UWuNST6
+/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmYv/3V
+EhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5g
+EydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+
+f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJq
+aHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEk
+llgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA
+pKnXwiJPZ9d37CAFYd4=
+-----END CERTIFICATE-----
diff --git a/gcr/tests/test-data/Thawte_Personal_Basic_CA.pem b/gcr/tests/test-data/Thawte_Personal_Basic_CA.pem
new file mode 100644
index 00000000..22c2a8db
--- /dev/null
+++ b/gcr/tests/test-data/Thawte_Personal_Basic_CA.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDITCCAoqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFBl
+cnNvbmFsIEJhc2ljIENBMSgwJgYJKoZIhvcNAQkBFhlwZXJzb25hbC1iYXNp
+Y0B0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVow
+gcsxCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNV
+BAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAm
+BgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNV
+BAMTGFRoYXd0ZSBQZXJzb25hbCBCYXNpYyBDQTEoMCYGCSqGSIb3DQEJARYZ
+cGVyc29uYWwtYmFzaWNAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEAvLyTU23AUE+CFeZIlDWmWr5vQvoPR+53dXLdjUmbllegeNTK
+P1GzaQuRdhciB5dqxFGTS+CN7zeVoQxN2jSQHReJl+A1OFdKwPQIcOk8RHtQ
+fmGakOMj04gRRif1CwcOu93RfyAKiLlWCy4cgNrx454p7xS9CkT7G1sY0b8j
+kyECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOB
+gQAt4plrsD16iddZopQBHyvdEktTwq1/qqcAXJFAVyVKOKqEcLnZgA+le1z7
+c8a914phXAPjLSeoF+CEhULcXpvGt7Jtu3Sv5D/Lp7ew4F2+eIMllNLbgQ95
+B21P9DkVWlIBe94y1k049hJcBlDfBVu9FEuh3ym6O0GN92NWod8isQ==
+-----END CERTIFICATE-----
diff --git a/gcr/tests/test-data/Thawte_Personal_Freemail_CA.pem b/gcr/tests/test-data/Thawte_Personal_Freemail_CA.pem
new file mode 100644
index 00000000..565a4be1
--- /dev/null
+++ b/gcr/tests/test-data/Thawte_Personal_Freemail_CA.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBl
+cnNvbmFsIEZyZWVtYWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1m
+cmVlbWFpbEB0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIz
+NTk1OVowgdExCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUx
+EjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRp
+bmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x
+JDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBGcmVlbWFpbCBDQTErMCkGCSqG
+SIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhhd3RlLmNvbTCBnzANBgkq
+hkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfYDFG26nKRsIRefS0N
+j3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5ErHzmj+hND3Ef
+QDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVquzgkCGqY
+x7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjP
+MPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgC
+neSa/RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr
+5PjRzneigQ==
+-----END CERTIFICATE-----
diff --git a/gcr/tests/test-data/Thawte_Personal_Premium_CA.pem b/gcr/tests/test-data/Thawte_Personal_Premium_CA.pem
new file mode 100644
index 00000000..688c3ae1
--- /dev/null
+++ b/gcr/tests/test-data/Thawte_Personal_Premium_CA.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBzzELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEjMCEGA1UEAxMaVGhhd3RlIFBl
+cnNvbmFsIFByZW1pdW0gQ0ExKjAoBgkqhkiG9w0BCQEWG3BlcnNvbmFsLXBy
+ZW1pdW1AdGhhd3RlLmNvbTAeFw05NjAxMDEwMDAwMDBaFw0yMDEyMzEyMzU5
+NTlaMIHPMQswCQYDVQQGEwJaQTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIw
+EAYDVQQHEwlDYXBlIFRvd24xGjAYBgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5n
+MSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMSMw
+IQYDVQQDExpUaGF3dGUgUGVyc29uYWwgUHJlbWl1bSBDQTEqMCgGCSqGSIb3
+DQEJARYbcGVyc29uYWwtcHJlbWl1bUB0aGF3dGUuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDJZtn4B0TPuYwu8KHvE0VsBd/eJxZRNkERbGw7
+7f4QfRKe5ZtCmv5gMcNmt3M6SK5O0DI3lIi1DbbZ8/JE2dWIEt12TfIa/G8j
+Hnrx2JhFTgcQ7xZC0EN1bUre4qrJMf8fAHB8Zs8QJQi6+u4A6UYDZicRFTuq
+W/KY3TZCstqIdQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBBAUAA4GBAGk2ifc0KjNyL2071CKyuG+axTZmDhs8obF1Wub9NdP4qPIH
+b4Vnjt4rueIXsDqg8A6iAJrf8xQVbrvIhVqYgPn/vnQdPfP+MCXRNzRn+qVx
+eTBhKXLA4CxM+1bkOqhv5TJZUtt1KFBZDPgLGeSs2a+WjS9Q2wfD6h+rM+D1
+KzGJ
+-----END CERTIFICATE-----
diff --git a/gcr/tests/test-data/Thawte_Premium_Server_CA.pem b/gcr/tests/test-data/Thawte_Premium_Server_CA.pem
new file mode 100644
index 00000000..aa37cfc9
--- /dev/null
+++ b/gcr/tests/test-data/Thawte_Premium_Server_CA.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy
+dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3Rl
+IFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1
+OVowgc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQ
+BgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcg
+Y2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x
+ITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3
+DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhI
+NTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPL
+lyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/qgeN
+9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
+AQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
+hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZ
+a4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcU
+Qg==
+-----END CERTIFICATE-----
diff --git a/gcr/tests/test-data/Thawte_Server_CA.pem b/gcr/tests/test-data/Thawte_Server_CA.pem
new file mode 100644
index 00000000..ac4e25bf
--- /dev/null
+++ b/gcr/tests/test-data/Thawte_Server_CA.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy
+dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3Rl
+IFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0
+ZS5jb20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkG
+A1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2Fw
+ZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQ
+VGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRz
+QHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I
+/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC
+6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCXL+eQbcAoQpnX
+TEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzARMA8G
+A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWD
+TSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
+QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdni
+TCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc=
+-----END CERTIFICATE-----
diff --git a/gcr/tests/test-data/Thawte_Time_Stamping_CA.pem b/gcr/tests/test-data/Thawte_Time_Stamping_CA.pem
new file mode 100644
index 00000000..fecbd15e
--- /dev/null
+++ b/gcr/tests/test-data/Thawte_Time_Stamping_CA.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICoTCCAgqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmls
+bGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmlj
+YXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwHhcNOTcw
+MTAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBizELMAkGA1UEBhMCWkExFTAT
+BgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzAN
+BgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24x
+HzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBANYrWHhhRYZT6jR7UZztsOYuGA7+4F+oJ9O0yeB8
+WU4WDnNUYMF/9p8u6TqFJBU820cEY8OexJQaWt9MevPZQx08EHp5JduQ/vBR
+5zDWQQD9nyjfeb6Uu522FOMjhdepQeBMpHmwKxqL8vg7ij5FrHGSALSQQZj7
+X+36ty6K+Ig3AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN
+AQEEBQADgYEAZ9viwuaHPUCDhjc1fR/OmsMMZiCouqoEiYbC9RAIDb/LogWK
+0E02PvTX72nGXuSwlG9KuefeW4i2e9vjJ+V2w/A1wcu1J5szedyQpgCed/r8
+zSeUQhac0xxo7L9c3eWpexAKMnRUEzGLhQOEkbdYATAUOK8oyvyxUBkZCayJ
+SdM=
+-----END CERTIFICATE-----
diff --git a/gcr/tests/test-data/ca-certificates.crt b/gcr/tests/test-data/ca-certificates.crt
new file mode 100644
index 00000000..c30335f8
--- /dev/null
+++ b/gcr/tests/test-data/ca-certificates.crt
@@ -0,0 +1,2560 @@
+-----BEGIN CERTIFICATE-----
+MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy
+YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp
+Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wMTExMzAxMjU4MDBaFw0xMTExMzAy
+MzU5MDBaMIG0MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9MDsG
+A1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3Jt
+YWNhbyAtIElUSTERMA8GA1UEBxMIQnJhc2lsaWExCzAJBgNVBAgTAkRGMTEwLwYD
+VQQDEyhBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPMudwX/hvm+Uh2b/lQAcHVA
+isamaLkWdkwP9/S/tOKIgRrL6Oy+ZIGlOUdd6uYtk9Ma/3pUpgcfNAj0vYm5gsyj
+Qo9emsc+x6m4VWwk9iqMZSCK5EQkAq/Ut4n7KuLE1+gdftwdIgxfUsPt4CyNrY50
+QV57KM2UT8x5rrmzEjr7TICGpSUAl2gVqe6xaii+bmYR1QrmWaBSAG59LrkrjrYt
+bRhFboUDe1DK+6T8s5L6k8c8okpbHpa9veMztDVC9sPJ60MWXh6anVKo1UcLcbUR
+yEeNvZneVRKAAU6ouwdjDvwlsaKydFKwed0ToQ47bmUKgcm+wV3eTRk36UOnTwID
+AQABo4HSMIHPME4GA1UdIARHMEUwQwYFYEwBAQAwOjA4BggrBgEFBQcCARYsaHR0
+cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0RQQ2FjcmFpei5wZGYwPQYDVR0f
+BDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFj
+cmFpei5jcmwwHQYDVR0OBBYEFIr68VeEERM1kEL6V0lUaQ2kxPA3MA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAZA5c1
+U/hgIh6OcgLAfiJgFWpvmDZWqlV30/bHFpj8iBobJSm5uDpt7TirYh1Uxe3fQaGl
+YjJe+9zd+izPRbBqXPVQA34EXcwk4qpWuf1hHriWfdrx8AcqSqr6CuQFwSr75Fos
+SzlwDADa70mT7wZjAmQhnZx2xJ6wfWlT9VQfS//JYeIc7Fue2JNLd00UOSMMaiK/
+t79enKNHEA2fupH3vEigf5Eh4bVAN5VohrTm6MY53x7XQZZr1ME7a55lFEnSeT0u
+mlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5nmPb
+K+9A46sd33oqK8n8
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
+IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
+IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
+Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
+BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
+MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
+ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
+8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
+zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
+fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
+w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
+G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
+epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
+laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
+QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
+fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
+YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
+ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
+gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
+MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
+IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
+dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
+czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
+dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
+aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
+AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
+b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
+ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
+nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
+18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
+gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
+Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
+sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
+SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
+CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
+GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
+zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
+omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDtTCCAp2gAwIBAgIRANAeQJAAAEZSAAAAAQAAAAQwDQYJKoZIhvcNAQEF
+BQAwgYkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJEQzETMBEGA1UEBxMKV2Fz
+aGluZ3RvbjEXMBUGA1UEChMOQUJBLkVDT00sIElOQy4xGTAXBgNVBAMTEEFC
+QS5FQ09NIFJvb3QgQ0ExJDAiBgkqhkiG9w0BCQEWFWFkbWluQGRpZ3NpZ3Ry
+dXN0LmNvbTAeFw05OTA3MTIxNzMzNTNaFw0wOTA3MDkxNzMzNTNaMIGJMQsw
+CQYDVQQGEwJVUzELMAkGA1UECBMCREMxEzARBgNVBAcTCldhc2hpbmd0b24x
+FzAVBgNVBAoTDkFCQS5FQ09NLCBJTkMuMRkwFwYDVQQDExBBQkEuRUNPTSBS
+b290IENBMSQwIgYJKoZIhvcNAQkBFhVhZG1pbkBkaWdzaWd0cnVzdC5jb20w
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx0xHgeVVDBwhMywVC
+AOINg0Y95JO6tgbTDVm9PsHOQ2cBiiGo77zM0KLMsFWWU4RmBQDaREmA2FQK
+pSWGlO1jVv9wbKOhGdJ4vmgqRF4vz8wYXke8OrFGPR7wuSw0X4x8TAgpnUBV
+6zx9g9618PeKgw6hTLQ6pbNfWiKX7BmbwQVo/ea3qZGULOR4SCQaJRk665Wc
+OQqKz0Ky8BzVX/tr7WhWezkscjiw7pOp03t3POtxA6k4ShZsiSrK2jMTecJV
+jO2cu/LLWxD4LmE1xilMKtAqY9FlWbT4zfn0AIS2V0KFnTKo+SpU+/94Qby9
+cSj0u5C8/5Y0BONFnqFGKECBAgMBAAGjFjAUMBIGA1UdEwEB/wQIMAYBAf8C
+AQgwDQYJKoZIhvcNAQEFBQADggEBAARvJYbk5pYntNlCwNDJALF/VD6Hsm0k
+qS8Kfv2kRLD4VAe9G52dyntQJHsRW0mjpr8SdNWJt7cvmGQlFLdh6X9ggGvT
+ZOirvRrWUfrAtF13Gn9kCF55xgVM8XrdTX3O5kh7VNJhkoHWG9YA8A6eKHeg
+TYjHInYZw8eeG6Z3ePhfm1bR8PIXrI6dWeYf/le22V7hXZ9F7GFoGUHhsiAm
+/lowdiT/QHI8eZ98IkirRs3bs4Ysj78FQdPB4xTjQRcm0HyncUwZ6EoPclgx
+fexgeqMiKL0ZJGA/O4dzwGvky663qyVDslUte6sGDnVdNOVdc22esnVApVnJ
+TzFxiNmIf1Q=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC
+VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB
+bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg
+Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyOTA2MDAw
+MFoXDTM3MTEyMDE1MDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB
+T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg
+SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAJnej8Mlo2k06AX3dLm/WpcZuS+U0pPlLYnKhHw/EEMbjIt8hFj4JHxI
+zyr9wBXZGH6EGhfT257XyuTZ16pYUYfw8ItITuLCxFlpMGK2MKKMCxGZYTVt
+fu/FsRkGIBKOQuHfD5YQUqjPnF+VFNivO3ULMSAfRC+iYkGzuxgh28pxPIzs
+trkNn+9R7017EvILDOGsQI93f7DKeHEMXRZxcKLXwjqFzQ6axOAAsNUl6twr
+5JQtOJyJQVdkKGUZHLZEtMgxa44Be3ZZJX8VHIQIfHNlIAqhBC4aMqiaILGc
+LCFZ5/vP7nAtCMpjPiybkxlqpMKX/7eGV4iFbJ4VFitNLLMCAwEAAaNjMGEw
+DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUoTYwFsuGkABFgFOxj8jYPXy+
+XxIwHwYDVR0jBBgwFoAUoTYwFsuGkABFgFOxj8jYPXy+XxIwDgYDVR0PAQH/
+BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQCKIBilvrMvtKaEAEAwKfq0FHNM
+eUWn9nDg6H5kHgqVfGphwu9OH77/yZkfB2FK4V1Mza3u0FIy2VkyvNp5ctZ7
+CegCgTXTCt8RHcl5oIBN/lrXVtbtDyqvpxh1MwzqwWEFT2qaifKNuZ8u77Bf
+WgDrvq2g+EQFZ7zLBO+eZMXpyD8Fv8YvBxzDNnGGyjhmSs3WuEvGbKeXO/oT
+LW4jYYehY0KswsuXn2Fozy1MBJ3XJU8KDk2QixhWqJNIV9xvrr2eZ1d3iVCz
+vhGbRWeDhhmH05i9CBoWH1iCC+GWaQVLjuyDUTEH1dSf/1l7qG6Fz9NLqUmw
+X7A5KGgOc90lmt4S
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIF5jCCA86gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC
+VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB
+bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg
+Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyOTA2MDAw
+MFoXDTM3MDkyODIzNDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB
+T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg
+SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBALQ3WggWmRToVbEbJGv8x4vmh6mJ7ouZzU9AhqS2TcnZsdw8TQ2FTBVs
+RotSeJ/4I/1n9SQ6aF3Q92RhQVSji6UI0ilbm2BPJoPRYxJWSXakFsKlnUWs
+i4SVqBax7J/qJBrvuVdcmiQhLE0OcR+mrF1FdAOYxFSMFkpBd4aVdQxHAWZg
+/BXxD+r1FHjHDtdugRxev17nOirYlxcwfACtCJ0zr7iZYYCLqJV+FNwSbKTQ
+2O9ASQI2+W6p1h2WVgSysy0WVoaP2SBXgM1nEG2wTPDaRrbqJS5Gr42whTg0
+ixQmgiusrpkLjhTXUr2eacOGAgvqdnUxCc4zGSGFQ+aJLZ8lN2fxI2rSAG2X
++Z/nKcrdH9cG6rjJuQkhn8g/BsXS6RJGAE57COtCPStIbp1n3UsC5ETzkxml
+J85per5n0/xQpCyrw2u544BMzwVhSyvcG7mm0tCq9Stz+86QNZ8MUhy/XCFh
+EVsVS6kkUfykXPcXnbDS+gfpj1bkGoxoigTTfFrjnqKhynFbotSg5ymFXQNo
+Kk/SBtc9+cMDLz9l+WceR0DTYw/j1Y75hauXTLPXJuuWCpTehTacyH+BCQJJ
+Kg71ZDIMgtG6aoIbs0t0EfOMd9afv9w3pKdVBC/UMejTRrkDfNoSTllkt1Ex
+MVCgyhwn2RAurda9EGYrw7AiShJbAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMB
+Af8wHQYDVR0OBBYEFE9pbQN+nZ8HGEO8txBO1b+pxCAoMB8GA1UdIwQYMBaA
+FE9pbQN+nZ8HGEO8txBO1b+pxCAoMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
+9w0BAQUFAAOCAgEAO/Ouyuguh4X7ZVnnrREUpVe8WJ8kEle7+z802u6teio0
+cnAxa8cZmIDJgt43d15Ui47y6mdPyXSEkVYJ1eV6moG2gcKtNuTxVBFT8zRF
+ASbI5Rq8NEQh3q0l/HYWdyGQgJhXnU7q7C+qPBR7V8F+GBRn7iTGvboVsNIY
+vbdVgaxTwOjdaRITQrcCtQVBynlQboIOcXKTRuidDV29rs4prWPVVRaAMCf/
+drr3uNZK49m1+VLQTkCpx+XCMseqdiThawVQ68W/ClTluUI8JPu3B5wwn3la
+5uBAUhX0/Kr0VvlEl4ftDmVyXr4m+02kLQgH3thcoNyBM5kYJRF3p+v9WAks
+mWsbivNSPxpNSGDxoPYzAlOL7SUJuA0t7Zdz7NeWH45gDtoQmy8YJPamTQr5
+O8t1wswvziRpyQoijlmn94IM19drNZxDAGrElWe6nEXLuA4399xOAU++CrYD
+062KRffaJ00psUjf5BHklka9bAI+1lHIlRcBFanyqqryvy9lG2/QuRqT9Y41
+xICHPpQvZuTpqP9BnHAqTyo5GJUefvthATxRCC4oGKQWDzH9OmwjkyB24f0H
+hdFbP9IcczLd+rn4jM8Ch3qaluTtT4mNU0OrDhPAARW0eTjb/G49nlG2uBOL
+Z8/5fNkiHfZdxRwBL5joeiQYvITX+txyW/fBOmg=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4
+dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5h
+bCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzEL
+MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1B
+ZGRUcnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1
+c3QgRXh0ZXJuYWwgQ0EgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALf3GjPm8gAELTngTlvtH7xsD821+iO2zt6bETOXpClMfZOfvUq8
+k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfwTz/oMp50
+ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504
+B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDez
+eWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5
+aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0WicCAwEAAaOB
+3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0PBAQD
+AgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6
+xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
+cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdv
+cmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJ
+KoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl
+j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5R
+xNKWt9x+Tu5w/Rw56wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjT
+K3rMUUKhemPR5ruhxSvCNr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1
+n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHx
+REzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49O
+hgQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU
+UCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3Qw
+HhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU
+UCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3Qw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwze
+xODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY654eyNAbFvAWlA3yCyykQruGI
+gb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWroulpOj0O
+M3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1Lc
+sRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5
+mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG
+9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0OBBYEFJWxtPCU
+tr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTADAQH/
+MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQsw
+CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk
+ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAx
+IENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0
+MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph
+iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9
+tTEv2dB8Xfjea4MYeDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL
+/bscVjby/rK25Xa71SJlpz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlV
+g3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6
+tkD9xOQ14R0WHNC8K47Wcdk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU
+UCBOZXR3b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAe
+Fw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNF
+MRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ
+IE5ldHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+
+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c
++OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1id9NEHif2
+P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKX
+C1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8R
+s3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9
+BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQWBBSBPjfYkrAf
+d59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zCB
+jgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkG
+A1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU
+cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENB
+IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmu
+G7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL
++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbj
+PGsye/Kf8Lb93/AoGEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bY
+GozH7ZxOmuASu7VqTITh4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6
+NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9HEufOX1362Kqx
+My3ZdvJOOjMMK7MtkAY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU
+UCBOZXR3b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9v
+dDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYT
+AlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3Qg
+VFRQIE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBS
+b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoek
+n0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKk
+IhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3KP0q6p6z
+sLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1t
+UvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R
++Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvES
+a0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0GA1UdDgQWBBQ5
+lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw
+AwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkw
+ZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL
+ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVh
+bGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2Vh
+lRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG
+GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx9
+5dr6h+sNNVJn0J6XdgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKF
+Yqa0p9m9N5xotS1WfbC3P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVA
+wRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQw
+dOUeqN48Jzd/g66ed8/wMLH/S5noxqE=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJV
+UzEcMBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1l
+cmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4X
+DTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMx
+HDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJp
+Y2EgT25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCa
+xlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CGv2BlnEtUiMJIxUo5vxTjWVXl
+GbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44zDyL9Hy7n
+BzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145Lcx
+VR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiE
+mf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCu
+JKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
+HQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Zo/Z5
+9m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUA
+A4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF
+Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOM
+IOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTI
+dGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g
+Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j
+8uB9Gr784N/Xx6dssPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJV
+UzEcMBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1l
+cmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4X
+DTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkGA1UEBhMCVVMx
+HDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJp
+Y2EgT25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssN
+t79Hc9PwVU3dxgz6sWYFas14tNwC206B89enfHG8dWOgXeMHDEjsJcQDIPT/
+DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8f3SkWq7x
+uhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE
+18aO6lhOqKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxr
+kJB+FFk4u5QkE+XRnRTf04JNRvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMD
+bi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0gBe4lL8BPeraunzgWGcX
+uVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn6KVu
+Y8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9
+W6Wa6897GqidFEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZ
+o2C7HK2JNDJiuEMhBnIMoVxtRsX6Kc8w3onccVvdtjc+31D1uAclJuW8tf48
+ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnjB453cMor9H124Hhn
+AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op
+aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNee
+MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypL
+M7PmG2tZTiLMubekJcmnxPBUlgtk87FYT15R/LKXeydlwuXK5w0MJXti4/qf
+tIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p+DYtLHe/YUjR
+Ywu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R
++FXgJXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr
++CKAXEfOAuMRn0T//ZoyzH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVM
+nNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgOZtMADjMSW7yV5TKQqLPGbIOt
+d+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh1NolNscI
+WC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZ
+ZLF0KjhfGEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y
+3WRayhgoPmMEEf0cjQAPuDffZ4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz
+2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuPcX/9XhmgD0uRuMRUvAaw
+RY8mkaKO/qk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQG
+EwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0
+MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUx
+MjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNV
+BAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZ
+QmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+h
+Xe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gR
+QKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCP
+wBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1
+pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNT
+Px8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkC
+AwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1BE3wMBIGA1Ud
+EwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUA
+A4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkT
+I7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
+jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/
+oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67
+G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
+RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYT
+AlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNl
+cnR1bSBDQTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJ
+BgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNV
+BAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AM6xwS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYV
+M42sLQnFdvkrOYCJ5JdLkKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/Ox
+LjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE
+7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/KUz/iDsaW
+VhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu
+/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYD
+VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESS
+bLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQaTOs9qmdvLdTN
+/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvgGrZg
+FCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqT
+E5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x
+O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYV
+IZQs6GAqm4VKQPNriiTsBhYscw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJH
+QjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxm
+b3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFB
+IENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIz
+MTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu
+Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENB
+IExpbWl0ZWQxITAfBgNVBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL5AnfRu4ep2hxxNRUSO
+vkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhGC1Pqy0wk
+wLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfH
+dr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf04
+9vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULi
+mAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cmez6KJcfA3Z3m
+NWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEKIz6W
+8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB
+Af8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
+QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwu
+Y29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG
+9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm
+7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHC
+v8S5dIa2LX1rzNLzRt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdV
+CYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAV
+GI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C12yxow+ev+to
+51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJH
+QjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxm
+b3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2Vj
+dXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4
+MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIg
+TWFuY2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2Rv
+IENBIExpbWl0ZWQxJDAiBgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2
+aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMBxM4KK0HDr
+c4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP9nQ95IDC
++DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8
+j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWC
+iIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtG
+Cd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz6YiO/O1R65Nx
+Tq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4EFgQU
+PNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
+/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv
+Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNo
+dHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNl
+cy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm
+4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiF
+Gv45jN5bBAS0VPmjZ55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXG
+De+X3EyrEeFryzHRbPtIgKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsF
+Vy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfU
+a7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6s
+Cx1HRR3B7Hzs/Sk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJH
+QjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxm
+b3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1
+c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0y
+ODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVy
+IE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9k
+byBDQSBMaW1pdGVkMSUwIwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNl
+cnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhT
+WvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh73TkVvFVI
+xO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9
+C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/
+oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW1O24zG71++IsWL1/
+T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7kUlcsutT6vif
+R4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1UdDgQW
+BBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
+AQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v
+ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2
+hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2Vy
+dmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw
+uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdT
+mw7pSqBYaWcOrp32pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+C
+l5EfKNsYEYwq5GWDVxISjBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/g
+hhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVF
+wL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOj
+GM9O9y5Xt5hwXsjEeLBi
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQG
+EwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREw
+DwYDVQQLEwhEU1RDQSBFMTAeFw05ODEyMTAxODEwMjNaFw0xODEyMTAxODQw
+MjNaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVy
+ZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEB
+AQUAA4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlR
+EmlvMVW5SXIACH7TpWJENySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+Lth
+zfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2io74CTADKAqjuAQIxZA9SLRN0
+dja1erQtcQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBoBgNVHR8E
+YTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwg
+U2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTExDTALBgNV
+BAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMTAxODEwMjNagQ8yMDE4MTIx
+MDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFGp5fpFpRhgTCgJ3
+pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i+DAMBgNV
+HRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3
+DQEBBQUAA4GBACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lN
+QseSJqBcNJo4cvj9axY+IO6CizEqkzaFI4iKPANo08kJD038bKTaKHKTDomA
+sH3+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4RbyhkwS7hp86W0N6
+w4pl
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID2DCCAsACEQDQHkCLAAACfAAAAAIAAAABMA0GCSqGSIb3DQEBBQUAMIGp
+MQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBM
+YWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENv
+LjERMA8GA1UECxMIRFNUQ0EgWDExFjAUBgNVBAMTDURTVCBSb290Q0EgWDEx
+ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODEyMDEx
+ODE4NTVaFw0wODExMjgxODE4NTVaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UE
+CBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0Rp
+Z2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgWDEx
+FjAUBgNVBAMTDURTVCBSb290Q0EgWDExITAfBgkqhkiG9w0BCQEWEmNhQGRp
+Z3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANLGJrbnpT3BxGjVUG9TxW9JEwm4ryxIjRRqoxdfWvnTLnUv2Chi0ZMv/E3U
+q4flCMeZ55I/db3rJbQVwZsZPdJEjdd0IG03Ao9pk1uKxBmd9LIO/BZsubEF
+koPRhSxglD5FVaDZqwgh5mDoO3TymVBRaNADLbGAvqPYUrBEzUNKcI5YhZXh
+TizWLUFv1oTnyJhEykfbLCSlaSbPa7gnYsP0yXqSI+0TZ4KuRS5F5X5yP4Wd
+lGIQ5jyRoa13AOAV7POEgHJ6jm5gl8ckWRA0g1vhpaRptlc1HHhZxtMvOnNn
+7pTKBBMFYgZwI7P0fO5F2WQLW0mqpEPOJsREEmy43XkCAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAojeyP2n714Z5VEkxlTMr89EJFEliYIalsBHiUMIdBlc+
+LegzZL6bqq1fG03UmZWii5rJYnK1aerZWKs17RWiQ9a2vAd5ZWRzfdd5ynvV
+WlHG4VMElo04z6MXrDlxawHDi1M8Y+nuecDkvpIyZHqzH5eUYr3qsiAVlfuX
+8ngvYzZAOONGDx3drJXK50uQe7FLqdTF65raqtWjlBRGjS0f8zrWkzr2Pnn8
+6Oawde3uPclwx12qgUtGJRzHbBXjlU4PqjI3lAoXJJIThFjSY28r9+ZbYgsT
+F7ANUkz+/m9c4pFuHf2kYtdo+o56T9II2pPc8JIRetDccpMMc5NihWjQ9A==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQG
+EwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREw
+DwYDVQQLEwhEU1RDQSBFMjAeFw05ODEyMDkxOTE3MjZaFw0xODEyMDkxOTQ3
+MjZaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVy
+ZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEB
+AQUAA4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fB
+w18DW9Fvrn5C6mYjuGODVvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87e
+ZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd
+55FbgM2UnQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBoBgNVHR8E
+YTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwg
+U2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTIxDTALBgNV
+BAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxOTE3MjZagQ8yMDE4MTIw
+OTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFB6CTShlgDzJQW6s
+NS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5WzAMBgNV
+HRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3
+DQEBBQUAA4GBAEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHR
+xdf0CiUPPXiBng+xZ8SQTGPdXqfiup/1902lMXucKS1M/mQ+7LZT/uqb7YLb
+dHVLB3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1mPnHfxsb1gYgAlih
+w6ID
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID2DCCAsACEQDQHkCLAAB3bQAAAAEAAAAEMA0GCSqGSIb3DQEBBQUAMIGp
+MQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBM
+YWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENv
+LjERMA8GA1UECxMIRFNUQ0EgWDIxFjAUBgNVBAMTDURTVCBSb290Q0EgWDIx
+ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODExMzAy
+MjQ2MTZaFw0wODExMjcyMjQ2MTZaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UE
+CBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0Rp
+Z2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgWDIx
+FjAUBgNVBAMTDURTVCBSb290Q0EgWDIxITAfBgkqhkiG9w0BCQEWEmNhQGRp
+Z3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANx18IzAdZaawGIfJvfE4Zrq4FZzW5nNAUSoCLbVp9oaBBg5kkp4o4HC9Xd6
+ULRw/5qrxsfKboNPQpj7Jgva3G3WqZlVUmfpKAOS3OWwBZoPFflrWXJW8vo5
+/Kpo7g8fEIMv/J36F5bdguPmRX3AS4BEH+0s4IT9kVySVGkl5WJp3OXuAFK9
+MwutdQKFp2RQLcUZGTDAJtvJ0/0uma1ZtQtN1EGuhUhDWdy3qOKi3sOP17ih
+YqZoUFLkzzGnlIXan0YyF1bl8utmPRL/Q9uY73fPy4GNNLHGUEom0eQ+QVCv
+bK4iNC7Va26Dunm4dmVI2gkpZGMiuftHdoWMhkTLCdsCAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAtTYOXeFhKFoRZcA/gwN5Tb4opgsHAlKFzfiR0BBstWog
+WxyQ2TA8xkieil5k+aFxd+8EJx8H6+Qm93N0yUQYGmbT4EOvkTvRyyzYdFQ6
+HE3K1GjNI3wdEJ5F6fYAbqbNGf9PLCmPV03Ed5K+4EwJ+11EhmYhqLkyolbV
+6YyDfFk/xPEL553snr2cGA4+wjl5KLcDDQjLxufZATdQEOzMYRZA1K8xdHv8
+PzGn0EdzMzkbzE5q10mDEQb+64JYMzJM8FasHpwvVpp7wUocpf1VNs78lk30
+sPDst2yC7S8xmUJMqbINuBVd8d+6ybVK1GSYsyapMMj9puyrliGtf8J4tg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEgzCCA+ygAwIBAgIEOJ725DANBgkqhkiG9w0BAQQFADCBtDEUMBIGA1UE
+ChMLRW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9HQ0NB
+X0NQUyBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsT
+HChjKSAyMDAwIEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1
+c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMDAy
+MDcxNjE2NDBaFw0yMDAyMDcxNjQ2NDBaMIG0MRQwEgYDVQQKEwtFbnRydXN0
+Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0dDQ0FfQ1BTIGluY29y
+cC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDIwMDAg
+RW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xp
+ZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQCTdLS25MVL1qFof2LV7PdRV7NySpj10InJrWPNTTVRaoTU
+rcloeW+46xHbh65cJFET8VQlhK8pK5/jgOLZy93GRUk0iJBeAZfv6lOm3fzB
+3ksqJeTpNfpVBQbliXrqpBFXO/x8PTbNZzVtpKklWb1m9fkn5JVn1j+SgF7y
+NH0rhQIDAQABo4IBnjCCAZowEQYJYIZIAYb4QgEBBAQDAgAHMIHdBgNVHR8E
+gdUwgdIwgc+ggcyggcmkgcYwgcMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUAw
+PgYDVQQLFDd3d3cuZW50cnVzdC5uZXQvR0NDQV9DUFMgaW5jb3JwLiBieSBy
+ZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMjAwMCBFbnRydXN0
+Lm5ldCBMaW1pdGVkMTMwMQYDVQQDEypFbnRydXN0Lm5ldCBDbGllbnQgQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw
+IoAPMjAwMDAyMDcxNjE2NDBagQ8yMDIwMDIwNzE2NDY0MFowCwYDVR0PBAQD
+AgEGMB8GA1UdIwQYMBaAFISLdP3FjcD/J20gN0V8/i3OutN9MB0GA1UdDgQW
+BBSEi3T9xY3A/ydtIDdFfP4tzrrTfTAMBgNVHRMEBTADAQH/MB0GCSqGSIb2
+fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQQFAAOBgQBObzWA
+O9GK9Q6nIMstZVXQkvTnhLUGJoMShAusO7JE7r3PQNsgDrpuFOow4DtifH+L
+a3xKp9U1PL6oXOpLu5OOgGarDyn9TS2/GpsKkMWr2tGzhtQvJFJcem3G8v7l
+TRowjJDyutdKPkN+1MhQGof4T4HHdguEOnKdzmVml64mXg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIElTCCA/6gAwIBAgIEOJsRPDANBgkqhkiG9w0BAQQFADCBujEUMBIGA1UE
+ChMLRW50cnVzdC5uZXQxPzA9BgNVBAsUNnd3dy5lbnRydXN0Lm5ldC9TU0xf
+Q1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
+KGMpIDIwMDAgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVz
+dC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
+Fw0wMDAyMDQxNzIwMDBaFw0yMDAyMDQxNzUwMDBaMIG6MRQwEgYDVQQKEwtF
+bnRydXN0Lm5ldDE/MD0GA1UECxQ2d3d3LmVudHJ1c3QubmV0L1NTTF9DUFMg
+aW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykg
+MjAwMCBFbnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5l
+dCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0G
+CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHwV9OcfHO8GCGD9JYf9Mzly0XonUw
+tZZkJi9ow0SrqHXmAGc0V55lxyKbc+bT3QgON1WqJUaBbL3+qPZ1V1eMkGxK
+wz6LS0MKyRFWmponIpnPVZ5h2QLifLZ8OAfc439PmrkDQYC2dWcTC5/oVzbI
+XQA23mYU2m52H083jIITiQIDAQABo4IBpDCCAaAwEQYJYIZIAYb4QgEBBAQD
+AgAHMIHjBgNVHR8EgdswgdgwgdWggdKggc+kgcwwgckxFDASBgNVBAoTC0Vu
+dHJ1c3QubmV0MT8wPQYDVQQLFDZ3d3cuZW50cnVzdC5uZXQvU1NMX0NQUyBp
+bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAy
+MDAwIEVudHJ1c3QubmV0IExpbWl0ZWQxOjA4BgNVBAMTMUVudHJ1c3QubmV0
+IFNlY3VyZSBTZXJ2ZXIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxDTALBgNV
+BAMTBENSTDEwKwYDVR0QBCQwIoAPMjAwMDAyMDQxNzIwMDBagQ8yMDIwMDIw
+NDE3NTAwMFowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFMtswGvjuz7L/CKc
+/vuLkpyw8m4iMB0GA1UdDgQWBBTLbMBr47s+y/winP77i5KcsPJuIjAMBgNV
+HRMEBTADAQH/MB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkq
+hkiG9w0BAQQFAAOBgQBi24GRzsiad0Iv7L0no1MPUBvqTpLwqa+poLpIYcvv
+yQbvH9X07t9WLebKahlzqlO+krNQAraFJnJj2HVQYnUUt7NQGj/KEQALhUVp
+bbalrlHhStyCP2yMNLJ3a9kC9n8O6mUE8c1UyrrJzOCE98g+EZfTYAkYvAX/
+bIkz8OwVDw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UE
+ChMLRW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNf
+MjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsT
+HChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1
+c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEy
+MjQxNzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0
+Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29y
+cC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkg
+RW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4
+QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/EC
+DNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuXMlBvPci6Zgzj
+/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzWnLLP
+KQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZd
+enoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
+4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB
+0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJ
+FrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0B
+AQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFh
+fGPjK50xA3B20qMooPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVU
+KcgF7bISKo30Axv/55IQh7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaoho
+wXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2
++z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof888
+6ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE7TCCBFagAwIBAgIEOAOR7jANBgkqhkiG9w0BAQQFADCByTELMAkGA1UE
+BhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUgwRgYDVQQLFD93d3cuZW50
+cnVzdC5uZXQvQ2xpZW50X0NBX0luZm8vQ1BTIGluY29ycC4gYnkgcmVmLiBs
+aW1pdHMgbGlhYi4xJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExp
+bWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0
+aW9uIEF1dGhvcml0eTAeFw05OTEwMTIxOTI0MzBaFw0xOTEwMTIxOTU0MzBa
+MIHJMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxSDBGBgNV
+BAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0FfSW5mby9DUFMgaW5jb3Jw
+LiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UECxMcKGMpIDE5OTkgRW50
+cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xpZW50
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GL
+ADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/Bo6oT9n3V5z8GKUZSv
+x1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux5zDeg7K6PvHV
+iTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zmAqTmT173
+iwIBA6OCAeAwggHcMBEGCWCGSAGG+EIBAQQEAwIABzCCASIGA1UdHwSCARkw
+ggEVMIHkoIHhoIHepIHbMIHYMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50
+cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0Ff
+SW5mby9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UE
+CxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50
+cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYD
+VQQDEwRDUkwxMCygKqAohiZodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9D
+bGllbnQxLmNybDArBgNVHRAEJDAigA8xOTk5MTAxMjE5MjQzMFqBDzIwMTkx
+MDEyMTkyNDMwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUxPucKXuXzUyW
+/O5bs8qZdIuV6kwwHQYDVR0OBBYEFMT7nCl7l81MlvzuW7PKmXSLlepMMAwG
+A1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
+hvcNAQEEBQADgYEAP66K8ddmAwWePvrqHEa7pFuPeJoSSJn59DXeDDYHAmsQ
+OokUgZwxpnyyQbJq5wcBoUv5nyU7lsqZwz6hURzzwy5E97BnRqqS5TvaHBkU
+ODDV4qIxJS7x7EU47fgGWANzYrAQMY9Av2TgXD7FTx/aEkP/TOYGJqibGapE
+PHayXOw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UE
+BhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50
+cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl
+MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UE
+AxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQsw
+CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3
+dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlh
+Yi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVkMTow
+OAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
+b24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0
+VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHIN
+iC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3wkrYKZImZNHk
+mGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcwggHT
+MBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHY
+pIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
+BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChs
+aW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBM
+aW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNo
+dHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAi
+gA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMC
+AQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYE
+FPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9
+B0EABAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKn
+CqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2Zcgx
+xufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd2cNgQ4xYDiKWL2KjLB+6
+rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQG
+EwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1
+cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4
+MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgx
+LTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0
+eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2R
+FGiYCh7+2gRvE4RiIcPRfM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO
+/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuv
+K9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAGA1UdHwRp
+MGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEt
+MCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
+MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjAL
+BgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gjIBBPM5iQn9Qw
+HQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMBAf8w
+GgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GB
+AFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
+7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2u
+FHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJV
+UzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1
+aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0
+MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoT
+E0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJl
+IEdsb2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAuucXkAJlsTRVPEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQy
+td4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORR
+OhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxnhcXIw2EC
+AwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8w
+HwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6o
+oHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf
+2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkAZ70Br83gcfxa
+z2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIYNMR1
+pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJV
+UzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1
+aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcN
+MjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZh
+eCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2lu
+ZXNzIENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fe
+k6lfWg0XTzQaDJj0ItlZ1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5
+/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4aIZX5UkxVWsUPOE9G+m34LjXW
+HXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBkMBEGCWCG
+SAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4
+MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBq
+R3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnm
+JXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+WB5Hh1Q+WKG1
+tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+KpYr
+tWKmpj29f5JZzVoqgrI3eQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQG
+EwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlm
+YXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMB4XDTk5MDYyMzEyMTQ0NVoXDTE5
+MDYyMzEyMTQ0NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkVxdWlmYXgg
+U2VjdXJlMSYwJAYDVQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0Et
+MjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF
+7Y6yEb3+6+e0dMKP/wXn2Z0GvxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKD
+pkWNYmi7hRsgcDKqQM2mll/EcTc/BPO3QSQ5BxoeLmFYoBIL5aXfxavqN3HM
+HMg3OrmXUqesxWoklE6ce8/AatbfIb0CAwEAAaOCAQkwggEFMHAGA1UdHwRp
+MGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBT
+ZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0y
+MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTkwNjIzMTIxNDQ1WjAL
+BgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9euSBIplBqy/3YIHqngnYw
+HQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQFMAMBAf8w
+GgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GB
+AAyGgq3oThr1jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy
+0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkt
+y3D1E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUmV+GRMOrN
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgw
+FgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRy
+dXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3Qg
+R2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1
+MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYD
+VQQLEx5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMT
+GkdURSBDeWJlclRydXN0IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4usJTQGz0O9pTAipTHBsiQl8i4
+ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcqlHHK6XALn
+ZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8F
+LztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh3
+46B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq
+81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0PlZPvy5TYnh+d
+XIVtx6quTx8itc2VrbqnzPmrC3p/
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB+jCCAWMCAgGjMA0GCSqGSIb3DQEBBAUAMEUxCzAJBgNVBAYTAlVTMRgw
+FgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xHDAaBgNVBAMTE0dURSBDeWJlclRy
+dXN0IFJvb3QwHhcNOTYwMjIzMjMwMTAwWhcNMDYwMjIzMjM1OTAwWjBFMQsw
+CQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMRwwGgYDVQQD
+ExNHVEUgQ3liZXJUcnVzdCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
+iQKBgQC45k+625h8cXyvRLfTD0bZZOWTwUKOx7pJjTUteueLveUFMVnGsS8K
+DPufpz+iCWaEVh43KRuH6X4MypqfpX/1FZSj1aJGgthoTNE3FQZor734sLPw
+KfWVWgkWYXcKIiXUT0Wqx73llt/51KiOQswkwB6RJ0q1bQaAYznEol44AwID
+AQABMA0GCSqGSIb3DQEBBAUAA4GBABKzdcZfHeFhVYAA1IFLezEPI2PnPfMD
++fQ2qLvZ46WXTeorKeDWanOB5sCJo9Px4KWlIjeaY8JIILTbcuPI9tl8vrGv
+U9oUtCG41tWW4/5ODFlitppK+ULdjG+BqXH/9ApybW1EDp3zdHSo1TRJ6V6e
+6bR64eVaH4QwnNOfpSXY
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYT
+AlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVz
+dCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBC
+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE
+AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEH
+CIjaWC9mOSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlC
+GDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7
+csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAj
+Nvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdRe
+JivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQAB
+o1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9
+qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1luMrMTjANBgkq
+hkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Qzxpe
+R+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWV
+Yrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
+PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot
+2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeX
+xx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm
+Mw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILAgAAAAAA1ni3lAUwDQYJKoZIhvcNAQEEBQAwVzEL
+MAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNV
+BAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05
+ODA5MDExMjAwMDBaFw0xNDAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkw
+FwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRsw
+GQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDaDuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR
+4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc
+71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4
+bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgK
+OOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMW
+ea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DP
+AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQUYHtmGkUNl8qJ
+UC99BM00qP/8/UswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOC
+AQEArqqf/LfSyx9fOSkoGJ40yWxPbxrwZKJwSk8ThptgKJ7ogUmYfQq75bCd
+PTbbjwVR/wkxKh/diXeeDy5slQTthsu0AD+EAk2AaioteAuubyuig0SDH81Q
+gkwkr733pbTIWg/050deSY43lv6aiAU62cDbKYfmGZZHpzqmjIs8d/5GY6dT
+2iHRrH5Jokvmw2dZL7OKDrssvamqQnw1wdh/1acxOk5jQzmvCLBhNIzTmKlD
+NPYPhyk7ncJWWJh3w/cbrPad+D6qp1RF8PX51TFl/mtYnHGzHtdS6jIX/EBg
+Hcl5JLL2bP2oZg6C3ZjL2sJETy6ge/L3ayx2EYRGinij4w==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIH6jCCB1OgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARIxCzAJBgNVBAYT
+AkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEu
+MCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5s
+LjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1
+MjEuMCwGA1UECxMlSVBTIENBIENMQVNFMSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eTEuMCwGA1UEAxMlSVBTIENBIENMQVNFMSBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTEeMBwGCSqGSIb3DQEJARYPaXBzQG1haWwuaXBzLmVzMB4XDTAx
+MTIyOTAwNTkzOFoXDTI1MTIyNzAwNTkzOFowggESMQswCQYDVQQGEwJFUzES
+MBAGA1UECBMJQmFyY2Vsb25hMRIwEAYDVQQHEwlCYXJjZWxvbmExLjAsBgNV
+BAoTJUlQUyBJbnRlcm5ldCBwdWJsaXNoaW5nIFNlcnZpY2VzIHMubC4xKzAp
+BgNVBAoUImlwc0BtYWlsLmlwcy5lcyBDLkkuRi4gIEItNjA5Mjk0NTIxLjAs
+BgNVBAsTJUlQUyBDQSBDTEFTRTEgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx
+LjAsBgNVBAMTJUlQUyBDQSBDTEFTRTEgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkxHjAcBgkqhkiG9w0BCQEWD2lwc0BtYWlsLmlwcy5lczCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEA4FEnpwvdr9G5Q1uCN0VWcu+atsIS7ywSzHb5
+BlmvXSHU0lq4oNTzav3KaY1mSPd05u42veiWkXWmcSjK5yISMmmwPh5r9FBS
+YmL9Yzt9fuzuOOpi9GyocY3h6YvJP8a1zZRCb92CRTzo3wno7wpVqVZHYUxJ
+ZHMQKD/Kvwn/xi8CAwEAAaOCBEowggRGMB0GA1UdDgQWBBTrsxl588GlHKzc
+uh9morKbadB4CDCCAUQGA1UdIwSCATswggE3gBTrsxl588GlHKzcuh9morKb
+adB4CKGCARqkggEWMIIBEjELMAkGA1UEBhMCRVMxEjAQBgNVBAgTCUJhcmNl
+bG9uYTESMBAGA1UEBxMJQmFyY2Vsb25hMS4wLAYDVQQKEyVJUFMgSW50ZXJu
+ZXQgcHVibGlzaGluZyBTZXJ2aWNlcyBzLmwuMSswKQYDVQQKFCJpcHNAbWFp
+bC5pcHMuZXMgQy5JLkYuICBCLTYwOTI5NDUyMS4wLAYDVQQLEyVJUFMgQ0Eg
+Q0xBU0UxIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVJUFMg
+Q0EgQ0xBU0UxIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJKoZIhvcN
+AQkBFg9pcHNAbWFpbC5pcHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAMBgNVHQ8E
+BQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUH
+AwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYBBAGCNwIB
+FgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhCAQEEBAMCAAcw
+GgYDVR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGBD2lwc0Bt
+YWlsLmlwcy5lczBBBglghkgBhvhCAQ0ENBYyQ0xBU0UxIENBIENlcnRpZmlj
+YXRlIGlzc3VlZCBieSBodHRwOi8vd3d3Lmlwcy5lcy8wKQYJYIZIAYb4QgEC
+BBwWGmh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvMDoGCWCGSAGG+EIBBAQt
+FitodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJDTEFTRTEuY3Js
+MD8GCWCGSAGG+EIBAwQyFjBodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL3Jl
+dm9jYXRpb25DTEFTRTEuaHRtbD8wPAYJYIZIAYb4QgEHBC8WLWh0dHA6Ly93
+d3cuaXBzLmVzL2lwczIwMDIvcmVuZXdhbENMQVNFMS5odG1sPzA6BglghkgB
+hvhCAQgELRYraHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9wb2xpY3lDTEFT
+RTEuaHRtbDBzBgNVHR8EbDBqMDGgL6AthitodHRwOi8vd3d3Lmlwcy5lcy9p
+cHMyMDAyL2lwczIwMDJDTEFTRTEuY3JsMDWgM6Axhi9odHRwOi8vd3d3YmFj
+ay5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xBU0UxLmNybDAvBggrBgEFBQcB
+AQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9vY3NwLmlwcy5lcy8wDQYJKoZI
+hvcNAQEFBQADgYEAK9Dr/drIyllq2tPMMi7JVBuKYn4VLenZMdMu9Ccj/1ur
+xUq2ckCuU3T0vAW0xtnIyXf7t/k0f3gA+Nak5FI/LEpjV4F1Wo7ojPsCwJTG
+Kbqz3Bzosq/SLmJbGqmODszFV0VRFOlOHIilkfSj945RyKm+hjM+5i9Ibq9U
+kE6tsSU=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIH6jCCB1OgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARIxCzAJBgNVBAYT
+AkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEu
+MCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5s
+LjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1
+MjEuMCwGA1UECxMlSVBTIENBIENMQVNFMyBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eTEuMCwGA1UEAxMlSVBTIENBIENMQVNFMyBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTEeMBwGCSqGSIb3DQEJARYPaXBzQG1haWwuaXBzLmVzMB4XDTAx
+MTIyOTAxMDE0NFoXDTI1MTIyNzAxMDE0NFowggESMQswCQYDVQQGEwJFUzES
+MBAGA1UECBMJQmFyY2Vsb25hMRIwEAYDVQQHEwlCYXJjZWxvbmExLjAsBgNV
+BAoTJUlQUyBJbnRlcm5ldCBwdWJsaXNoaW5nIFNlcnZpY2VzIHMubC4xKzAp
+BgNVBAoUImlwc0BtYWlsLmlwcy5lcyBDLkkuRi4gIEItNjA5Mjk0NTIxLjAs
+BgNVBAsTJUlQUyBDQSBDTEFTRTMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx
+LjAsBgNVBAMTJUlQUyBDQSBDTEFTRTMgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkxHjAcBgkqhkiG9w0BCQEWD2lwc0BtYWlsLmlwcy5lczCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEAqxf+DrDGaBtT8FK+n/ra+osTBLsBjzLZH49N
+zjaY2uQARIwo2BNEKqRrThckQpzTiKRBgtYj+4vJhuW5qYIF3PHeH+AMmVWY
+8jjsbJ0gA8DvqqPGZARRLXgNo9KoOtYkTOmWehisEyMiG3zoMRGzXwmqMHBx
+RiVrSXGAK5UBsh8CAwEAAaOCBEowggRGMB0GA1UdDgQWBBS4k/8uy9wsjqLn
+ev42USGjmFsMNDCCAUQGA1UdIwSCATswggE3gBS4k/8uy9wsjqLnev42USGj
+mFsMNKGCARqkggEWMIIBEjELMAkGA1UEBhMCRVMxEjAQBgNVBAgTCUJhcmNl
+bG9uYTESMBAGA1UEBxMJQmFyY2Vsb25hMS4wLAYDVQQKEyVJUFMgSW50ZXJu
+ZXQgcHVibGlzaGluZyBTZXJ2aWNlcyBzLmwuMSswKQYDVQQKFCJpcHNAbWFp
+bC5pcHMuZXMgQy5JLkYuICBCLTYwOTI5NDUyMS4wLAYDVQQLEyVJUFMgQ0Eg
+Q0xBU0UzIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVJUFMg
+Q0EgQ0xBU0UzIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJKoZIhvcN
+AQkBFg9pcHNAbWFpbC5pcHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAMBgNVHQ8E
+BQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUH
+AwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYBBAGCNwIB
+FgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhCAQEEBAMCAAcw
+GgYDVR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGBD2lwc0Bt
+YWlsLmlwcy5lczBBBglghkgBhvhCAQ0ENBYyQ0xBU0UzIENBIENlcnRpZmlj
+YXRlIGlzc3VlZCBieSBodHRwOi8vd3d3Lmlwcy5lcy8wKQYJYIZIAYb4QgEC
+BBwWGmh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvMDoGCWCGSAGG+EIBBAQt
+FitodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJDTEFTRTMuY3Js
+MD8GCWCGSAGG+EIBAwQyFjBodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL3Jl
+dm9jYXRpb25DTEFTRTMuaHRtbD8wPAYJYIZIAYb4QgEHBC8WLWh0dHA6Ly93
+d3cuaXBzLmVzL2lwczIwMDIvcmVuZXdhbENMQVNFMy5odG1sPzA6BglghkgB
+hvhCAQgELRYraHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9wb2xpY3lDTEFT
+RTMuaHRtbDBzBgNVHR8EbDBqMDGgL6AthitodHRwOi8vd3d3Lmlwcy5lcy9p
+cHMyMDAyL2lwczIwMDJDTEFTRTMuY3JsMDWgM6Axhi9odHRwOi8vd3d3YmFj
+ay5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xBU0UzLmNybDAvBggrBgEFBQcB
+AQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9vY3NwLmlwcy5lcy8wDQYJKoZI
+hvcNAQEFBQADgYEAF2VcmZVDAyevJuXr0LMXI/dDqsfwfewPxqmurpYPdikc
+4gYtfibFPPqhwYHOU7BC0ZdXGhd+pFFhxu7pXu8Fuuu9D6eSb9ijBmgpjnn1
+/7/5p6/ksc7C0YBCJwUENPjDfxZ4IwwHJPJGR607VNCv1TGyr33I6unUVtkO
+E7LFRVA=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIH9zCCB2CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARQxCzAJBgNVBAYT
+AkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEu
+MCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5s
+LjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1
+MjEvMC0GA1UECxMmSVBTIENBIENMQVNFQTEgQ2VydGlmaWNhdGlvbiBBdXRo
+b3JpdHkxLzAtBgNVBAMTJklQUyBDQSBDTEFTRUExIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXMwHhcN
+MDExMjI5MDEwNTMyWhcNMjUxMjI3MDEwNTMyWjCCARQxCzAJBgNVBAYTAkVT
+MRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwG
+A1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjEr
+MCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEv
+MC0GA1UECxMmSVBTIENBIENMQVNFQTEgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkxLzAtBgNVBAMTJklQUyBDQSBDTEFTRUExIENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXMwgZ8wDQYJ
+KoZIhvcNAQEBBQADgY0AMIGJAoGBALsw19zQVL01Tp/FTILq0VA8R5j8m2md
+d81u4D/u6zJfX5/S0HnllXNEITLgCtud186Nq1KLK3jgm1t99P1tCeWu4Wwd
+ByOgF9H5fahGRpEiqLJpxq339fWUoTCUvQDMRH/uxJ7JweaPCjbB/SQ9AaD1
+e+J8eGZDi09Z8pvZ+kmzAgMBAAGjggRTMIIETzAdBgNVHQ4EFgQUZyaW56G/
+2LUDnf473P7yiuYV3TAwggFGBgNVHSMEggE9MIIBOYAUZyaW56G/2LUDnf47
+3P7yiuYV3TChggEcpIIBGDCCARQxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlC
+YXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UEChMlSVBTIElu
+dGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjErMCkGA1UEChQiaXBz
+QG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEvMC0GA1UECxMmSVBT
+IENBIENMQVNFQTEgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxLzAtBgNVBAMT
+JklQUyBDQSBDTEFTRUExIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJ
+KoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAM
+BgNVHQ8EBQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUFBwMBBggrBgEFBQcDAgYI
+KwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYB
+BAGCNwIBFgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhCAQEE
+BAMCAAcwGgYDVR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGB
+D2lwc0BtYWlsLmlwcy5lczBCBglghkgBhvhCAQ0ENRYzQ0xBU0VBMSBDQSBD
+ZXJ0aWZpY2F0ZSBpc3N1ZWQgYnkgaHR0cDovL3d3dy5pcHMuZXMvMCkGCWCG
+SAGG+EIBAgQcFhpodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyLzA7BglghkgB
+hvhCAQQELhYsaHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xB
+U0VBMS5jcmwwQAYJYIZIAYb4QgEDBDMWMWh0dHA6Ly93d3cuaXBzLmVzL2lw
+czIwMDIvcmV2b2NhdGlvbkNMQVNFQTEuaHRtbD8wPQYJYIZIAYb4QgEHBDAW
+Lmh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvcmVuZXdhbENMQVNFQTEuaHRt
+bD8wOwYJYIZIAYb4QgEIBC4WLGh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIv
+cG9saWN5Q0xBU0VBMS5odG1sMHUGA1UdHwRuMGwwMqAwoC6GLGh0dHA6Ly93
+d3cuaXBzLmVzL2lwczIwMDIvaXBzMjAwMkNMQVNFQTEuY3JsMDagNKAyhjBo
+dHRwOi8vd3d3YmFjay5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xBU0VBMS5j
+cmwwLwYIKwYBBQUHAQEEIzAhMB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5p
+cHMuZXMvMA0GCSqGSIb3DQEBBQUAA4GBAH66iqyAAIQVCtWYUQxkxZwCWINm
+yq0eB81+atqAB98DNEock8RLWCA1NnHtogo1EqWmZaeFaQoO42Hu6r4okzPV
+7Oi+xNtff6j5YzHIa5biKcJboOeXNp13XjFr/tOn2yrb25aLH2betgPAK7N4
+1lUH5Y85UN4HI3LmvSAUS7SG
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIH9zCCB2CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARQxCzAJBgNVBAYT
+AkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEu
+MCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5s
+LjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1
+MjEvMC0GA1UECxMmSVBTIENBIENMQVNFQTMgQ2VydGlmaWNhdGlvbiBBdXRo
+b3JpdHkxLzAtBgNVBAMTJklQUyBDQSBDTEFTRUEzIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXMwHhcN
+MDExMjI5MDEwNzUwWhcNMjUxMjI3MDEwNzUwWjCCARQxCzAJBgNVBAYTAkVT
+MRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwG
+A1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjEr
+MCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEv
+MC0GA1UECxMmSVBTIENBIENMQVNFQTMgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
+dHkxLzAtBgNVBAMTJklQUyBDQSBDTEFTRUEzIENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXMwgZ8wDQYJ
+KoZIhvcNAQEBBQADgY0AMIGJAoGBAO6AAPYaZC6tasiDsYun7o/ZttvNG7uG
+BiJ2MwwSbUhWYdLcgiViL5/SaTBlA0IjWLxH3GvWdV0XPOH/8lhneaDBgbHU
+VqLyjRGZ/fZ98cfEXgIqmuJKtROKAP2Md4bm15T1IHUuDky/dMQ/gT6DtKM4
+Ninn6Cr1jIhBqoCm42zvAgMBAAGjggRTMIIETzAdBgNVHQ4EFgQUHp9XUEe2
+YZM50yz82l09BXW3mQIwggFGBgNVHSMEggE9MIIBOYAUHp9XUEe2YZM50yz8
+2l09BXW3mQKhggEcpIIBGDCCARQxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlC
+YXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEuMCwGA1UEChMlSVBTIElu
+dGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5sLjErMCkGA1UEChQiaXBz
+QG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1MjEvMC0GA1UECxMmSVBT
+IENBIENMQVNFQTMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxLzAtBgNVBAMT
+JklQUyBDQSBDTEFTRUEzIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJ
+KoZIhvcNAQkBFg9pcHNAbWFpbC5pcHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAM
+BgNVHQ8EBQMDB/+AMGsGA1UdJQRkMGIGCCsGAQUFBwMBBggrBgEFBQcDAgYI
+KwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYB
+BAGCNwIBFgYKKwYBBAGCNwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhCAQEE
+BAMCAAcwGgYDVR0RBBMwEYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGB
+D2lwc0BtYWlsLmlwcy5lczBCBglghkgBhvhCAQ0ENRYzQ0xBU0VBMyBDQSBD
+ZXJ0aWZpY2F0ZSBpc3N1ZWQgYnkgaHR0cDovL3d3dy5pcHMuZXMvMCkGCWCG
+SAGG+EIBAgQcFhpodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyLzA7BglghkgB
+hvhCAQQELhYsaHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xB
+U0VBMy5jcmwwQAYJYIZIAYb4QgEDBDMWMWh0dHA6Ly93d3cuaXBzLmVzL2lw
+czIwMDIvcmV2b2NhdGlvbkNMQVNFQTMuaHRtbD8wPQYJYIZIAYb4QgEHBDAW
+Lmh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvcmVuZXdhbENMQVNFQTMuaHRt
+bD8wOwYJYIZIAYb4QgEIBC4WLGh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIv
+cG9saWN5Q0xBU0VBMy5odG1sMHUGA1UdHwRuMGwwMqAwoC6GLGh0dHA6Ly93
+d3cuaXBzLmVzL2lwczIwMDIvaXBzMjAwMkNMQVNFQTMuY3JsMDagNKAyhjBo
+dHRwOi8vd3d3YmFjay5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0xBU0VBMy5j
+cmwwLwYIKwYBBQUHAQEEIzAhMB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5p
+cHMuZXMvMA0GCSqGSIb3DQEBBQUAA4GBAEo9IEca2on0eisxeewBwMwB9dbB
+/MjD81ACUZBYKp/nNQlbMAqBACVHr9QPDp5gJqiVp4MI3y2s6Q73nMify5NF
+8bpqxmdRSmlPa/59Cy9SKcJQrSRE7SOzSMtEQMEDlQwKeAYSAfWRMS1Jjbs/
+RU4s4OjNtckUFQzjB4ObJnXv
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIH9zCCB2CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCARwxCzAJBgNVBAYT
+AkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEu
+MCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5s
+LjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1
+MjEzMDEGA1UECxMqSVBTIENBIENoYWluZWQgQ0FzIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5MTMwMQYDVQQDEypJUFMgQ0EgQ2hhaW5lZCBDQXMgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkxHjAcBgkqhkiG9w0BCQEWD2lwc0BtYWlsLmlw
+cy5lczAeFw0wMTEyMjkwMDUzNThaFw0yNTEyMjcwMDUzNThaMIIBHDELMAkG
+A1UEBhMCRVMxEjAQBgNVBAgTCUJhcmNlbG9uYTESMBAGA1UEBxMJQmFyY2Vs
+b25hMS4wLAYDVQQKEyVJUFMgSW50ZXJuZXQgcHVibGlzaGluZyBTZXJ2aWNl
+cyBzLmwuMSswKQYDVQQKFCJpcHNAbWFpbC5pcHMuZXMgQy5JLkYuICBCLTYw
+OTI5NDUyMTMwMQYDVQQLEypJUFMgQ0EgQ2hhaW5lZCBDQXMgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkxMzAxBgNVBAMTKklQUyBDQSBDaGFpbmVkIENBcyBD
+ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEeMBwGCSqGSIb3DQEJARYPaXBzQG1h
+aWwuaXBzLmVzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcVpJJspQg
+vJhPUOtopKdJC7/SMejHT8KGC/po/UNaivNgkjWZOLtNA1IhW/A3mTXhQSCB
+hYEFcYGdtJUZqV92NC5jNzVXjrQfQj8VXOF6wV8TGDIxya2+o8eDZh65nAQT
+y2nBBt4wBrszo7Uf8I9vzv+W6FS+ZoCua9tBhDaiPQIDAQABo4IEQzCCBD8w
+HQYDVR0OBBYEFKGtMbH5PuEXpsirNPxShwkeYlJBMIIBTgYDVR0jBIIBRTCC
+AUGAFKGtMbH5PuEXpsirNPxShwkeYlJBoYIBJKSCASAwggEcMQswCQYDVQQG
+EwJFUzESMBAGA1UECBMJQmFyY2Vsb25hMRIwEAYDVQQHEwlCYXJjZWxvbmEx
+LjAsBgNVBAoTJUlQUyBJbnRlcm5ldCBwdWJsaXNoaW5nIFNlcnZpY2VzIHMu
+bC4xKzApBgNVBAoUImlwc0BtYWlsLmlwcy5lcyBDLkkuRi4gIEItNjA5Mjk0
+NTIxMzAxBgNVBAsTKklQUyBDQSBDaGFpbmVkIENBcyBDZXJ0aWZpY2F0aW9u
+IEF1dGhvcml0eTEzMDEGA1UEAxMqSVBTIENBIENoYWluZWQgQ0FzIENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNAbWFpbC5p
+cHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAMBgNVHQ8EBQMDB/+AMGsGA1UdJQRk
+MGIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggr
+BgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYBBAGCNwIBFgYKKwYBBAGCNwoDAQYK
+KwYBBAGCNwoDBDARBglghkgBhvhCAQEEBAMCAAcwGgYDVR0RBBMwEYEPaXBz
+QG1haWwuaXBzLmVzMBoGA1UdEgQTMBGBD2lwc0BtYWlsLmlwcy5lczBCBglg
+hkgBhvhCAQ0ENRYzQ2hhaW5lZCBDQSBDZXJ0aWZpY2F0ZSBpc3N1ZWQgYnkg
+aHR0cDovL3d3dy5pcHMuZXMvMCkGCWCGSAGG+EIBAgQcFhpodHRwOi8vd3d3
+Lmlwcy5lcy9pcHMyMDAyLzA3BglghkgBhvhCAQQEKhYoaHR0cDovL3d3dy5p
+cHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0FDLmNybDA8BglghkgBhvhCAQMELxYt
+aHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9yZXZvY2F0aW9uQ0FDLmh0bWw/
+MDkGCWCGSAGG+EIBBwQsFipodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL3Jl
+bmV3YWxDQUMuaHRtbD8wNwYJYIZIAYb4QgEIBCoWKGh0dHA6Ly93d3cuaXBz
+LmVzL2lwczIwMDIvcG9saWN5Q0FDLmh0bWwwbQYDVR0fBGYwZDAuoCygKoYo
+aHR0cDovL3d3dy5pcHMuZXMvaXBzMjAwMi9pcHMyMDAyQ0FDLmNybDAyoDCg
+LoYsaHR0cDovL3d3d2JhY2suaXBzLmVzL2lwczIwMDIvaXBzMjAwMkNBQy5j
+cmwwLwYIKwYBBQUHAQEEIzAhMB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5p
+cHMuZXMvMA0GCSqGSIb3DQEBBQUAA4GBAERyMJ1WWKJBGyi3leGmGpVfp3hA
+K+/blkr8THFj2XOVvQLiogbHvpcqk4A0hgP63Ng9HgfNHnNDJGD1HWHc3Jag
+vPsd4+cSACczAsDAK1M92GsDgaPb1pOVIO/Tln4mkImcJpvNb2ar7QMiRDjM
+Wb2f2/YHogF/JsRj9SVCXmK9
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICtzCCAiACAQAwDQYJKoZIhvcNAQEEBQAwgaMxCzAJBgNVBAYTAkVTMRIw
+EAYDVQQIEwlCQVJDRUxPTkExEjAQBgNVBAcTCUJBUkNFTE9OQTEZMBcGA1UE
+ChMQSVBTIFNlZ3VyaWRhZCBDQTEYMBYGA1UECxMPQ2VydGlmaWNhY2lvbmVz
+MRcwFQYDVQQDEw5JUFMgU0VSVklET1JFUzEeMBwGCSqGSIb3DQEJARYPaXBz
+QG1haWwuaXBzLmVzMB4XDTk4MDEwMTIzMjEwN1oXDTA5MTIyOTIzMjEwN1ow
+gaMxCzAJBgNVBAYTAkVTMRIwEAYDVQQIEwlCQVJDRUxPTkExEjAQBgNVBAcT
+CUJBUkNFTE9OQTEZMBcGA1UEChMQSVBTIFNlZ3VyaWRhZCBDQTEYMBYGA1UE
+CxMPQ2VydGlmaWNhY2lvbmVzMRcwFQYDVQQDEw5JUFMgU0VSVklET1JFUzEe
+MBwGCSqGSIb3DQEJARYPaXBzQG1haWwuaXBzLmVzMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQCsT1J0nznqjtwlxLyYXZhkJAk8IbPMGbWOlI6H0fg3
+PqHILVikgDVboXVsHUUMH2Fjal5vmwpMwci4YSM1gf/+rHhwLWjhOgeYlQJU
+3c0jt4BT18g3RXIGJBK6E2Ehim51KODFDzT9NthFf+G4Nu+z4cYgjui0OLzh
+PvYR3oydAQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBACzzw3lYJN7GO9HgQmm4
+7mSzPWIBubOE3yN93ZjPEKn+ANgilgUTB1RXxafey9m4iEL2mdsUdx+2/iU9
+4aI+A6mB0i1sR/WWRowiq8jMDQ6XXotBtDvECgZAHd1G9AHduoIuPD14cJ58
+GNCr+Lh3B0Zx8coLY1xq+XKU1QFPoNtC
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIIODCCB6GgAwIBAgIBADANBgkqhkiG9w0BAQUFADCCAR4xCzAJBgNVBAYT
+AkVTMRIwEAYDVQQIEwlCYXJjZWxvbmExEjAQBgNVBAcTCUJhcmNlbG9uYTEu
+MCwGA1UEChMlSVBTIEludGVybmV0IHB1Ymxpc2hpbmcgU2VydmljZXMgcy5s
+LjErMCkGA1UEChQiaXBzQG1haWwuaXBzLmVzIEMuSS5GLiAgQi02MDkyOTQ1
+MjE0MDIGA1UECxMrSVBTIENBIFRpbWVzdGFtcGluZyBDZXJ0aWZpY2F0aW9u
+IEF1dGhvcml0eTE0MDIGA1UEAxMrSVBTIENBIFRpbWVzdGFtcGluZyBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTEeMBwGCSqGSIb3DQEJARYPaXBzQG1haWwu
+aXBzLmVzMB4XDTAxMTIyOTAxMTAxOFoXDTI1MTIyNzAxMTAxOFowggEeMQsw
+CQYDVQQGEwJFUzESMBAGA1UECBMJQmFyY2Vsb25hMRIwEAYDVQQHEwlCYXJj
+ZWxvbmExLjAsBgNVBAoTJUlQUyBJbnRlcm5ldCBwdWJsaXNoaW5nIFNlcnZp
+Y2VzIHMubC4xKzApBgNVBAoUImlwc0BtYWlsLmlwcy5lcyBDLkkuRi4gIEIt
+NjA5Mjk0NTIxNDAyBgNVBAsTK0lQUyBDQSBUaW1lc3RhbXBpbmcgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkxNDAyBgNVBAMTK0lQUyBDQSBUaW1lc3RhbXBp
+bmcgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHjAcBgkqhkiG9w0BCQEWD2lw
+c0BtYWlsLmlwcy5lczCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvLju
+VqWajOY2ycJioGaBjRrVetJznw6EZLqVtJCneK/K/lRhW86yIFcBrkSSQxA4
+Efdo/BdApWgnMjvEp+ZCccWZ73b/K5Uk9UmSGGjKALWkWi9uy9YbLA1UZ2t6
+KaFYq6JaANZbuxjC3/YeE1Z2m6Vo4pjOxgOKNNtMg0GmqaMCAwEAAaOCBIAw
+ggR8MB0GA1UdDgQWBBSL0BBQCYHynQnVDmB4AyKiP8jKZjCCAVAGA1UdIwSC
+AUcwggFDgBSL0BBQCYHynQnVDmB4AyKiP8jKZqGCASakggEiMIIBHjELMAkG
+A1UEBhMCRVMxEjAQBgNVBAgTCUJhcmNlbG9uYTESMBAGA1UEBxMJQmFyY2Vs
+b25hMS4wLAYDVQQKEyVJUFMgSW50ZXJuZXQgcHVibGlzaGluZyBTZXJ2aWNl
+cyBzLmwuMSswKQYDVQQKFCJpcHNAbWFpbC5pcHMuZXMgQy5JLkYuICBCLTYw
+OTI5NDUyMTQwMgYDVQQLEytJUFMgQ0EgVGltZXN0YW1waW5nIENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5MTQwMgYDVQQDEytJUFMgQ0EgVGltZXN0YW1waW5n
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9pcHNA
+bWFpbC5pcHMuZXOCAQAwDAYDVR0TBAUwAwEB/zAMBgNVHQ8EBQMDB/+AMGsG
+A1UdJQRkMGIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUF
+BwMEBggrBgEFBQcDCAYKKwYBBAGCNwIBFQYKKwYBBAGCNwIBFgYKKwYBBAGC
+NwoDAQYKKwYBBAGCNwoDBDARBglghkgBhvhCAQEEBAMCAAcwGgYDVR0RBBMw
+EYEPaXBzQG1haWwuaXBzLmVzMBoGA1UdEgQTMBGBD2lwc0BtYWlsLmlwcy5l
+czBHBglghkgBhvhCAQ0EOhY4VGltZXN0YW1waW5nIENBIENlcnRpZmljYXRl
+IGlzc3VlZCBieSBodHRwOi8vd3d3Lmlwcy5lcy8wKQYJYIZIAYb4QgECBBwW
+Gmh0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvMEAGCWCGSAGG+EIBBAQzFjFo
+dHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJUaW1lc3RhbXBpbmcu
+Y3JsMEUGCWCGSAGG+EIBAwQ4FjZodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAy
+L3Jldm9jYXRpb25UaW1lc3RhbXBpbmcuaHRtbD8wQgYJYIZIAYb4QgEHBDUW
+M2h0dHA6Ly93d3cuaXBzLmVzL2lwczIwMDIvcmVuZXdhbFRpbWVzdGFtcGlu
+Zy5odG1sPzBABglghkgBhvhCAQgEMxYxaHR0cDovL3d3dy5pcHMuZXMvaXBz
+MjAwMi9wb2xpY3lUaW1lc3RhbXBpbmcuaHRtbDB/BgNVHR8EeDB2MDegNaAz
+hjFodHRwOi8vd3d3Lmlwcy5lcy9pcHMyMDAyL2lwczIwMDJUaW1lc3RhbXBp
+bmcuY3JsMDugOaA3hjVodHRwOi8vd3d3YmFjay5pcHMuZXMvaXBzMjAwMi9p
+cHMyMDAyVGltZXN0YW1waW5nLmNybDAvBggrBgEFBQcBAQQjMCEwHwYIKwYB
+BQUHMAGGE2h0dHA6Ly9vY3NwLmlwcy5lcy8wDQYJKoZIhvcNAQEFBQADgYEA
+ZbrBzAAalZHK6Ww6vzoeFAh8+4Pua2JR0zORtWB5fgTYXXk36MNbsMRnLWha
+sl8OCvrNPzpFoeo2zyYepxEoxZSPhExTCMWTs/zif/WN87GphV+I3pGW7hdb
+rqXqcGV4LCFkAZXOzkw+UPS2Wctjjba9GNSHSl/c7+lW8AoM6HU=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQG
+EwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9v
+dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMg
+Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNa
+Fw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9W
+YWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1
+lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMukJ0KX0J+D
+isPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj18
+2d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Sp
+x2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZ
+yH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospUxbF6lR1xHkop
+igPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4wPQYI
+KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFk
+aXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
+ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlh
+bmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBw
+YXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs
+ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRp
+ZmljYXRpb24gcHJhY3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmlj
+YXRlIFBvbGljeS4wIgYIKwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMu
+Ym0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYw
+gaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJN
+MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCLMA4GA1UdDwEB/wQEAwIB
+BjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lofFIk3Wdv
+OXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10
+buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe
+/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6
+isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
+xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQ
+NiOKSnQ2+Q==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD
+ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRp
+b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv
+bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy
+NjAwMjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x
+NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x
+IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2f
+NUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChM
+MFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqYJJgpp0lZpd34
+t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs3x/b
+e0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0Wu
+PIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A
+PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICXDCCAcWgAwIBAgIQCgEBAQAAAnwAAAALAAAAAjANBgkqhkiG9w0BAQUF
+ADA6MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0Eg
+U2VjdXJpdHkgMTAyNCBWMzAeFw0wMTAyMjIyMTAxNDlaFw0yNjAyMjIyMDAx
+NDlaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJT
+QSBTZWN1cml0eSAxMDI0IFYzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQDV3f5mCc8kPD6ugU5OisRpgFtZO9+5TUzKtS3DJy08rwBCbbwoppbPf9dY
+rIMKo1W1exeQFYRMiu4mmdxY78c4pqqv0I5CyGLXq6yp+0p9v+r+Ek3d/yYt
+bzZUaMjShFbuklNhCbM/OZuoyZu9zp9+1BlqFikYvtc6adwlWzMaUQIDAQAB
+o2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSME
+GDAWgBTEwBykB5T9zU0B1FTapQxf3q4FWjAdBgNVHQ4EFgQUxMAcpAeU/c1N
+AdRU2qUMX96uBVowDQYJKoZIhvcNAQEFBQADgYEAPy1q4yZDlX2Jl2X7deRy
+HUZXxGFraZ8SmyzVWujAovBDleMf6XbN3Ou8k6BlCsdNT1+nr6JGFLkM88y9
+am63nd4lQtBU/55oc2PcJOsiv6hy8l4A4Q1OOkNumU4/iXgDmMrzVcydro7B
+qkWY+o8aoI2II/EVQQ2lRj6RP4vr93E=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUF
+ADA6MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0Eg
+U2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5
+MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJT
+QSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37
+RqtBaB4Y6lXIL5F4iSj7Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E
+0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J
+6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iHKrtjEAMq
+s6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzD
+uvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2Mw
+YTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAW
+gBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NRMKSq6UWuNST6
+/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmYv/3V
+EhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5g
+EydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+
+f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJq
+aHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEk
+llgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA
+pKnXwiJPZ9d37CAFYd4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJK
+UDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0
+eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMw
+OTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1
+c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RD
+QTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8
+V6UMbXaKL0u/ZPtM7orw8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpx
+xpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uMDPpVmDvY6CKhS3E4eayXkmmz
+iX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX5HA49LY6
+tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819
+uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/L
+TX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZ
+aNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g0dNq
+/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94
+nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5
+Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNn
+PaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfci
+oU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi
+FL39vmwLAw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJG
+STEPMA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENB
+MB4XDTAxMDQwNjEwNDkxM1oXDTIxMDQwNjEwNDkxM1owOTELMAkGA1UEBhMC
+RkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMSBD
+QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue
++H887dF+2rDNbS82rDTG29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mX
+y47vPxVnqIJyY1MPQYx9EJUkoVqlBvqSV536pQHydekfvFYmUk54GWVYVQNY
+wBSujHxVX3BbdyMGNpfzJLWaRpXk3w0LBUXl0fIdgrvGE+D+qnr9aTCU89JF
+hfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBLqdReLjVQCfOAl/QMF6452F/NM8Ec
+yonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIINnvmLVz5MxxftLItyM19yejhW
+1ebZrgUaHXVFsculJRwSVzb9IjcCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB
+/zARBgNVHQ4ECgQIR+IMi/ZTiFIwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEB
+BQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE928Jj2VuXZfsSZ9gqXLar
+5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0HDjxVyhbM
+p6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VOTzF2
+nBBhjrZTOqMRvq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2Uv
+kVrCqIexVmiUefkl98HVrhq4uz2PqYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2y
+Ix4wzMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9ZIRlXvVWa
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJG
+STEPMA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENB
+MB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMC
+RkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBD
+QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE
++hY3/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gX
+GM2RX/uJ4+q/Tl18GybTdXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQ
+TiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMGf+dJQMjFAbJUWmYdPfz56TwK
+noG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8PtOFCx4j1
+P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURr
+BGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB
+/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEB
+BQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zilzqsWuasvfDXL
+rNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEIcbCd
+jdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr4
+50kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6
+Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkeja
+nZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQG
+EwJOTDEeMBwGA1UEChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQD
+Ex1TdGFhdCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQTAeFw0wMjEyMTcwOTIz
+NDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVT
+dGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRl
+cmxhbmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAmNK1URF6gaYUmHFtvsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rF
+DBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02PjLwYdjeFnejKScfST5gTCaI+
+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGcaC1Hoi6Ce
+UJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7l
+r7HcsBthvJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4Zl
+kuxEK7COudxwC0barbxjiDn622r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGO
+MAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRVHSAAMDwwOgYIKwYBBQUH
+AgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9vdC1w
+b2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg
+0zTBLL9s+DANBgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k
+/rvuFbQvBgwp8qiSpGEN/KtcCFtREytNwiphyPgJWPwtArI5fZlmgb9uXJVF
+IGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbwMVcoEoJz6TMvplW0
+C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y
+nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBp
+IzlWYGeQiy52OfsRiJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYV
+wSR8MnwDHTuhWEUykw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAsWgAwIBAgICA+owDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYT
+AkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYD
+VQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3
+b3JrcyBHbWJIMSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAyIENB
+MSkwJwYJKoZIhvcNAQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAe
+Fw05ODAzMDkxMTU5NTlaFw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJE
+RTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UE
+ChMxVEMgVHJ1c3RDZW50ZXIgZm9yIFNlY3VyaXR5IGluIERhdGEgTmV0d29y
+a3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTEp
+MCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVAdHJ1c3RjZW50ZXIuZGUwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANo46O0yAClxgwENv4wB3NrGrTmk
+qYov1YtcaF9QxmL1Zr3KkSLsqh1R1z2zUbKDTl3LSbDwTFXlay3HhQswHJJO
+gtTKAu33b77c4OMUuAVT8pr0VotanoWT0bSCVq5Nu6hLVxa8/vhYnvgpjbB7
+zXjJT6yLZwzxnPv8V5tXXE8NAgMBAAGjazBpMA8GA1UdEwEB/wQFMAMBAf8w
+DgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRy
+dXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G
+CSqGSIb3DQEBBAUAA4GBAIRS+yjf/x91AbwBvgRWl2p0QiQxg/lGsQaKic+W
+LDO/jLVfenKhhQbOhvgFjuj5Jcrag4wGrOs2bYWRNAQ29ELw+HkuCkhcq8xR
+T3h2oNmsGb0q0WkEKJHKNhAngFdb0lz1wlurZIFjdFH0l7/NEij3TWZ/p/Ac
+ASZ4smZHcFFk
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAsWgAwIBAgICA+swDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYT
+AkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYD
+VQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3
+b3JrcyBHbWJIMSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAzIENB
+MSkwJwYJKoZIhvcNAQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAe
+Fw05ODAzMDkxMTU5NTlaFw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJE
+RTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UE
+ChMxVEMgVHJ1c3RDZW50ZXIgZm9yIFNlY3VyaXR5IGluIERhdGEgTmV0d29y
+a3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTEp
+MCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVAdHJ1c3RjZW50ZXIuZGUwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALa0wTUFLg2N7KBAahwOJ6ZQkmtQ
+GwfeLud2zODa/ISoXoxjaitN2U4CdhHBC/KNecoAtvGwDtf7pBc9r6tpepYn
+v68zoZoqWarEtTcI8hKlMbZD9TKWcSgoq40oht+77uMMfTDWw1Krj10nnGvA
+o+cFa1dJRLNu6mTP0o56UHd3AgMBAAGjazBpMA8GA1UdEwEB/wQFMAMBAf8w
+DgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRy
+dXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G
+CSqGSIb3DQEBBAUAA4GBABY9xs3Bu4VxhUafPiCPUSiZ7C1FIWMjWwS7TJC4
+iJIETb19AaM/9uzO8d7+feXhPrvGq14L3T2WxMup1Pkm5gZOngylerpuw3yC
+GdHHsbHD2w2Om0B8NwvxXej9H5CIpQ5ON2QhqE6NtJ/x3kit1VYYUimLRzQS
+CdS7kjXvD9s0
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQG
+EwJESzEVMBMGA1UEChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50
+ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUxNjMzMTdaFw0yMTA0MDUxNzAzMTda
+MEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJuZXQxHTAbBgNV
+BAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4Nr
+XceO+YQwzho7+vvOi20jxsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaq
+HF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvLznWv3Hv6gXPU/Lq9QYjUdLP5
+Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc5IogCSEW
+Vmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8n
+mHj9eQY6otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwID
+AQABo4IBJTCCASEwEQYJYIZIAYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBY
+oFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMMVERDIEludGVybmV0MR0w
+GwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JMMTAr
+BgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjAL
+BgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAw
+HQYDVR0OBBYEFGxkAcf9hW2syNqeUAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8w
+HQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
+A4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m
+gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKs
+LtB9KOy282A4aW8+2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7T
+mHnaCB4Mb7j4Fifvwm899qNLPg7kbWzbO0ESm70NRyN/PErQr8Cv9u8btRXE
+64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrUCbz0vGbMPVjQ
+V0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFGTCCBAGgAwIBAgIEPki9xDANBgkqhkiG9w0BAQUFADAxMQswCQYDVQQG
+EwJESzEMMAoGA1UEChMDVERDMRQwEgYDVQQDEwtUREMgT0NFUyBDQTAeFw0w
+MzAyMTEwODM5MzBaFw0zNzAyMTEwOTA5MzBaMDExCzAJBgNVBAYTAkRLMQww
+CgYDVQQKEwNUREMxFDASBgNVBAMTC1REQyBPQ0VTIENBMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEArGL2YSCyz8DGhdfjeebM7fI5kqSXLmSj
+hFuHnEz9pPPEXyG9VhDr2y5h7JNp46PMvZnDBfwGuMo2HP6QjklMxFaaL1a8
+z3sM8W9Hpg1DTeLpHTk0zY0s2RKY+ePhwUp8hjjEqcRhiNJerxomTdXkoCJH
+hNlktxmW/OwZ5LKXJk5KTMuPJItUGBxIYXvViGjaXbXqzRowwYCDdlCqT9HU
+3Tjw7xb04QxQBr/q+3pJoSgrHPb8FTKjdGqPqcNiKXEx5TukYBdedObaE+3p
+Hx8b0bJoc8YQNHVGEBDjkAB2QMuLt0MJIf+rTpPGWOmlgtt3xDqZsXKVSQTw
+tyv6e1mO3QIDAQABo4ICNzCCAjMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
+Af8EBAMCAQYwgewGA1UdIASB5DCB4TCB3gYIKoFQgSkBAQEwgdEwLwYIKwYB
+BQUHAgEWI2h0dHA6Ly93d3cuY2VydGlmaWthdC5kay9yZXBvc2l0b3J5MIGd
+BggrBgEFBQcCAjCBkDAKFgNUREMwAwIBARqBgUNlcnRpZmlrYXRlciBmcmEg
+ZGVubmUgQ0EgdWRzdGVkZXMgdW5kZXIgT0lEIDEuMi4yMDguMTY5LjEuMS4x
+LiBDZXJ0aWZpY2F0ZXMgZnJvbSB0aGlzIENBIGFyZSBpc3N1ZWQgdW5kZXIg
+T0lEIDEuMi4yMDguMTY5LjEuMS4xLjARBglghkgBhvhCAQEEBAMCAAcwgYEG
+A1UdHwR6MHgwSKBGoESkQjBAMQswCQYDVQQGEwJESzEMMAoGA1UEChMDVERD
+MRQwEgYDVQQDEwtUREMgT0NFUyBDQTENMAsGA1UEAxMEQ1JMMTAsoCqgKIYm
+aHR0cDovL2NybC5vY2VzLmNlcnRpZmlrYXQuZGsvb2Nlcy5jcmwwKwYDVR0Q
+BCQwIoAPMjAwMzAyMTEwODM5MzBagQ8yMDM3MDIxMTA5MDkzMFowHwYDVR0j
+BBgwFoAUYLWF7FZkfhIZJ2cdUBVLc647+RIwHQYDVR0OBBYEFGC1hexWZH4S
+GSdnHVAVS3OuO/kSMB0GCSqGSIb2fQdBAAQQMA4bCFY2LjA6NC4wAwIEkDAN
+BgkqhkiG9w0BAQUFAAOCAQEACromJkbTc6gJ82sLMJn9iuFXehHTuJTXCRBu
+o7E4A9G28kNBKWKnctj7fAXmMXAnVBhOinxO5dHKjHiIzxvTkIvmI/gLDjND
+fZziChmPyQE+dF10yYscA+UYyAFMP8uXBV2YcaaYb7Z8vTd/vuGTJW1v8Aqt
+FxjhA7wHKcitJuj4YfD9IQl+mo6paH1IYnK9AOoBmbgGglGBTvH1tJFUuSN6
+AJqfXY3gPGS5GhKSKseCRHI53OI8xthV9RVOyAUO28bQYqbsFbS1AoLbrIyi
+gfCbmTH1ICCoiGEKB5+U/NDXG8wuF/MEJ3Zn61SD/aSQfgY9BKNDLdr8C2Lq
+L19iUw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDITCCAoqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFBl
+cnNvbmFsIEJhc2ljIENBMSgwJgYJKoZIhvcNAQkBFhlwZXJzb25hbC1iYXNp
+Y0B0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVow
+gcsxCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNV
+BAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAm
+BgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNV
+BAMTGFRoYXd0ZSBQZXJzb25hbCBCYXNpYyBDQTEoMCYGCSqGSIb3DQEJARYZ
+cGVyc29uYWwtYmFzaWNAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEAvLyTU23AUE+CFeZIlDWmWr5vQvoPR+53dXLdjUmbllegeNTK
+P1GzaQuRdhciB5dqxFGTS+CN7zeVoQxN2jSQHReJl+A1OFdKwPQIcOk8RHtQ
+fmGakOMj04gRRif1CwcOu93RfyAKiLlWCy4cgNrx454p7xS9CkT7G1sY0b8j
+kyECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOB
+gQAt4plrsD16iddZopQBHyvdEktTwq1/qqcAXJFAVyVKOKqEcLnZgA+le1z7
+c8a914phXAPjLSeoF+CEhULcXpvGt7Jtu3Sv5D/Lp7ew4F2+eIMllNLbgQ95
+B21P9DkVWlIBe94y1k049hJcBlDfBVu9FEuh3ym6O0GN92NWod8isQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBl
+cnNvbmFsIEZyZWVtYWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1m
+cmVlbWFpbEB0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIz
+NTk1OVowgdExCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUx
+EjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRp
+bmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x
+JDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBGcmVlbWFpbCBDQTErMCkGCSqG
+SIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhhd3RlLmNvbTCBnzANBgkq
+hkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfYDFG26nKRsIRefS0N
+j3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5ErHzmj+hND3Ef
+QDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVquzgkCGqY
+x7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjP
+MPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgC
+neSa/RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr
+5PjRzneigQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBzzELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEjMCEGA1UEAxMaVGhhd3RlIFBl
+cnNvbmFsIFByZW1pdW0gQ0ExKjAoBgkqhkiG9w0BCQEWG3BlcnNvbmFsLXBy
+ZW1pdW1AdGhhd3RlLmNvbTAeFw05NjAxMDEwMDAwMDBaFw0yMDEyMzEyMzU5
+NTlaMIHPMQswCQYDVQQGEwJaQTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIw
+EAYDVQQHEwlDYXBlIFRvd24xGjAYBgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5n
+MSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMSMw
+IQYDVQQDExpUaGF3dGUgUGVyc29uYWwgUHJlbWl1bSBDQTEqMCgGCSqGSIb3
+DQEJARYbcGVyc29uYWwtcHJlbWl1bUB0aGF3dGUuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDJZtn4B0TPuYwu8KHvE0VsBd/eJxZRNkERbGw7
+7f4QfRKe5ZtCmv5gMcNmt3M6SK5O0DI3lIi1DbbZ8/JE2dWIEt12TfIa/G8j
+Hnrx2JhFTgcQ7xZC0EN1bUre4qrJMf8fAHB8Zs8QJQi6+u4A6UYDZicRFTuq
+W/KY3TZCstqIdQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBBAUAA4GBAGk2ifc0KjNyL2071CKyuG+axTZmDhs8obF1Wub9NdP4qPIH
+b4Vnjt4rueIXsDqg8A6iAJrf8xQVbrvIhVqYgPn/vnQdPfP+MCXRNzRn+qVx
+eTBhKXLA4CxM+1bkOqhv5TJZUtt1KFBZDPgLGeSs2a+WjS9Q2wfD6h+rM+D1
+KzGJ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy
+dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3Rl
+IFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1
+OVowgc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQ
+BgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcg
+Y2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x
+ITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3
+DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhI
+NTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPL
+lyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/qgeN
+9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
+AQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
+hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZ
+a4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcU
+Qg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy
+dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3Rl
+IFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0
+ZS5jb20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkG
+A1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2Fw
+ZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQ
+VGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRz
+QHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I
+/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC
+6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCXL+eQbcAoQpnX
+TEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzARMA8G
+A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWD
+TSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
+QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdni
+TCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICoTCCAgqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmls
+bGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmlj
+YXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwHhcNOTcw
+MTAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBizELMAkGA1UEBhMCWkExFTAT
+BgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzAN
+BgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24x
+HzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBANYrWHhhRYZT6jR7UZztsOYuGA7+4F+oJ9O0yeB8
+WU4WDnNUYMF/9p8u6TqFJBU820cEY8OexJQaWt9MevPZQx08EHp5JduQ/vBR
+5zDWQQD9nyjfeb6Uu522FOMjhdepQeBMpHmwKxqL8vg7ij5FrHGSALSQQZj7
+X+36ty6K+Ig3AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN
+AQEEBQADgYEAZ9viwuaHPUCDhjc1fR/OmsMMZiCouqoEiYbC9RAIDb/LogWK
+0E02PvTX72nGXuSwlG9KuefeW4i2e9vjJ+V2w/A1wcu1J5szedyQpgCed/r8
+zSeUQhac0xxo7L9c3eWpexAKMnRUEzGLhQOEkbdYATAUOK8oyvyxUBkZCayJ
+SdM=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEZDCCA0ygAwIBAgIQRL4Mi1AAJLQR0zYwS8AzdzANBgkqhkiG9w0BAQUF
+ADCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0
+IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw
+HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVU
+Ti1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwHhcNOTkwNzA5MTg0
+ODM5WhcNMTkwNzA5MTg1NzQ5WjCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgT
+AlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVT
+RVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVz
+dC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNh
+dGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz+5Gh5DZV
+hawGNFugmliy+LUPBXeDrjKxdpJo7CNKyXY/45y2N3kDuatpjQclthln5LAb
+GHNhSuh+zdMvZOOmfAz6F4CjDUeJT1FxL+78P/m4FoCHiZMlIJpDgmkkdihZ
+NaEdwH+DBmQWICzTSaSFtMBhf1EI+GgVkYDLpdXuOzr0hAReYFmnjDRy7rh4
+xdE7EkpvfmUnuaRVxblvQ6TFHSyZwFKkeEwVs0CYCGtDxgGwenv1axwiP8vv
+/6jQOkt2FZ7S0cYu49tXGzKiuG/ohqY/cKvlcJKrRB5AUPuco2LkbG6gyN7i
+gEL66S/ozjIEj3yNtxyjNTwV3Z7DrpelAgMBAAGjgZEwgY4wCwYDVR0PBAQD
+AgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPqGydvguul49Uuo1hXf
+8NPhahQ8ME8GA1UdHwRIMEYwRKBCoECGPmh0dHA6Ly9jcmwudXNlcnRydXN0
+LmNvbS9VVE4tVVNFUkZpcnN0LU5ldHdvcmtBcHBsaWNhdGlvbnMuY3JsMA0G
+CSqGSIb3DQEBBQUAA4IBAQCk8yXM0dSRgyLQzDKrm5ZONJFUICU0YV8qAhXh
+i6r/fWRRzwr/vH3YIWp4yy9Rb/hCHTO967V7lMPDqaAt39EpHx3+jz+7qEUq
+f9FuVSTiuwL7MT++6LzsQCv4AdRWOOTKRIK1YSAhZ2X28AvnNPilwpyjXEAf
+hZOVBt5P1CeptqX8Fs1zMT+4ZSfP1FMa8Kxun08FDAOBp4QpxFq9ZFdyrTvP
+NximmMatBrTcCKME1SmklpoSZ0qMYEWd8SOasACcaLWYUNPvji6SZbFIPiG+
+FTAqDbUMo2s/rn9X9R+WfN9v3YIwLGUbQErNaLly7HF27FSOH4UMAWr6pjis
+H8SE
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUF
+ADCBkzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0
+IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw
+HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVU
+TiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2
+MzBaMIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNh
+bHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsx
+ITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTEbMBkGA1UEAxMS
+VVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+O
+GQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrr
+U0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrL
+Z9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykqlXvY8qdOD1R8
+oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv33i+
+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4Gr
+MIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT
+MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8v
+Y3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUE
+IzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3
+DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHc
+rpY6CiM+iVnJowftGzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuM
+FrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1
++XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdO
+jtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jF
+VkwPDPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUF
+ADCBrjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0
+IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw
+HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVU
+Ti1VU0VSRmlyc3QtQ2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAe
+Fw05OTA3MDkxNzI4NTBaFw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJV
+UzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD
+VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93
+d3cudXNlcnRydXN0LmNvbTE2MDQGA1UEAxMtVVROLVVTRVJGaXJzdC1DbGll
+bnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWlsMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3BYHW8OWX5ShpHornMSMxq
+mNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9hVE1eaROaJB7HHqk
+kqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6lL8/K2m2qL+us
+obNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLmSGHGTPNp
+saguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM1tZU
+Ot4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws
+6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0G
+A1UdDgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJ
+hkdodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGll
+bnRBdXRoZW50aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEF
+BQcDAgYIKwYBBQUHAwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rk
+MPxTbyUkxsrt4jFcKw7u7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK
+2lYcYJeA3IKirUq9iiv/Cwm0xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/
+bDCVNy8zEqx/3cfREYxRmLLQo5HQrfafnoOTHh1CuEava2bwm3/q4wMC5QJR
+warVNZ1yQAOJujEdxRBoUp7fooXFXAimeOZTT7Hot9MUnpOmw2TjrH5xzbyf
+6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gkUSeh1YdV8nuPmD0Wnu51tvjQ
+jvLzxq4oW6fw8zYX/MMF08oDSlQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUF
+ADCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0
+IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw
+HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVU
+Ti1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5
+MTgxOTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQH
+Ew5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3
+b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNV
+BAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZ
+FvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6N
+q9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEH
+OG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNdoI6yqqr2jmmI
+BsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjfPe58
+BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhb
+AgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD
+VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWG
+M2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3
+YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF
+BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0o
+XnWO6y1n7k57K9cM//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjA
+bPLPSbtNk28GpgoiskliCE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59
+Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4f
+Fm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchq
+J/kniCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0q
+UZ6B+dQ7XnASfxAynB67nfhmqA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUF
+ADCBlTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0
+IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw
+HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVU
+Ti1VU0VSRmlyc3QtT2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4
+NDAzNlowgZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMO
+U2FsdCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29y
+azEhMB8GA1UECxMYaHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0wGwYDVQQD
+ExRVVE4tVVNFUkZpcnN0LU9iamVjdDCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicPHxzfOpuCaDDASmEd8S8O+r55
+96Uj71VRloTN2+O5bj4x2AogZ8f02b+U60cEPgLOKqJdhwQJ9jCdGIqXsqoc
+/EHSoTbL+z2RuufZcDX65OeQw5ujm9M89RKZd7G3CeBo5hy485RjiGpq/gt2
+yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vulBe3/IW+pKvEHDHd17bR
+5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehbkkj7RwvC
+bNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUCAwEA
+AaOBrzCBrDALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQU2u1kdBScFDyr3ZmpvVsoTYs8ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0
+cDovL2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNy
+bDApBgNVHSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQw
+DQYJKoZIhvcNAQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXF
+wfNfLEzIR1pp6ujwNTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T
+7/yxSPlrJSUtUbYsbUXBmMiKVl0+7kNOPmsnjtA6S4ULX9Ptaqd1y9Fahy85
+dRNacrACgZ++8A+EVCBibGnU4U3GDZlDAQ0Slox4nb9QorFEqmrPF3rPbw/U
++CRVX/A0FklmPlBGyWNxODFiuGK581OtbLUrohKqGU8J2l7nk8aOFAj+8DCA
+GKCGhU3IfdeLA/5u1fedFqySLKAj5ZyRUh+U3xeUc8OzwcFxBSAAeL0TUh2o
+Ps0AH8g=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD
+ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRp
+b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv
+bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy
+NTIyMjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x
+NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x
+IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw
+8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m
++FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8YTfwggtFzVXSN
+dnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwGlN+V
+YH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8so
+gTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw
+nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD
+ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRp
+b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv
+bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy
+NjAwMTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x
+NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x
+IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc
+65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQ
+b7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QSv4dk+NoS/zcn
+wbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZSWI4
+OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZ
+oDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
+W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPTCCAaYCEQDNun9W8N/kvFT+IqyzcqpVMA0GCSqGSIb3DQEBAgUAMF8x
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UE
+CxMuQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eTAeFw05NjAxMjkwMDAwMDBaFw0yODA4MDEyMzU5NTlaMF8xCzAJBgNV
+BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xh
+c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCB
+nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Rm/baNWYS2ZSHH2Z965jeu3
+noaACpEO+jglr0aIguVzqKCbJF0NH8xlbgyw0FaEGIeaBpsQoXPftFg5a27B
+9hXVqKg/qhIGjTGsf7A01480Z4gJzRQR4k5FVmkfeAKA2txHkSm7NsljXMXg
+1y2He6G3MrB7MLoqLzGq7qNn2tsCAwEAATANBgkqhkiG9w0BAQIFAAOBgQBM
+P7iLxmjf7kMzDl3ppssHhE16M/+SG/Q2rdiVIjZoEWx8QszznC7EBz8UsA9P
+/5CSdvnivErpj82ggAr3xSnxgiJduLHdgSOjeyUVRjB5FvjqBUuUfx3CHMjj
+t/QQQDwTw18fU+hI5Ia0e6E1sHslurjTjqs/OJ0ANACY89FxlA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcEx
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UE
+CxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAt
+IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVow
+gcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoG
+A1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5j
+LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq
+0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYKVdPfQ4chEWWKfo+9
+Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSmFc/IReumXY6c
+PvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQIDAQABMA0G
+CSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0Jh9Zr
+bWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2ul
+uIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4i
+P/68DzFc6PLZ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHK
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV
+BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5
+IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBD
+BgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3
+MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s
+IEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNV
+BAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQg
+dXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFBy
+aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4nN493GwTFtl63SRR
+ZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO8ESlV8dAWB6j
+Rx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjVojYJrKsh
+JlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjbPG7P
+oBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2
+6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHh
+v2Vrn5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQ
+BfGfMY1aqtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/N
+y9Sn2WCVhDr4wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUf
+xJM8/XmPBNQ+T+r3ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFM
+DSZl4kSAHsef493oCtrspSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5
+SCJ5ShkPshw+IHTZasO+8ih4E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXV
+OBRgmaNL3gaWcSzy27YfpO8/7g==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDnjCCAwegAwIBAgIQK2jUo0aexTsoCas4XX8nIDANBgkqhkiG9w0BAQUF
+ADBfMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1
+BgNVBAsTLkNsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDAwODA0MDAwMDAwWhcNMDQwODAzMjM1OTU5WjCBpzEX
+MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov
+L3d3dy52ZXJpc2lnbi5jb20vUlBBIChjKTAwMS4wLAYDVQQDEyVDbGFzcyAx
+IFB1YmxpYyBQcmltYXJ5IE9DU1AgUmVzcG9uZGVyMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQC57V56Ondfzl86UvzNZPdxtW9qlsZZklWUXS9bLsER
+6iaKy6eBPPZaRN56Ey/9WlHZezcmSsAnPwQDalbBgyzhb1upVFAkSsYuekyh
+WzdUJCExH6F4GHansXDaItBq/gdiQMb39pt9DAa4S8co5GYjhFHvRreT2IEz
+y+U2rMboBQIDAQABo4IBEDCCAQwwIAYDVR0RBBkwF6QVMBMxETAPBgNVBAMT
+CE9DU1AgMS0xMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNp
+Z24uY29tL3BjYTEuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMJMEIGCCsGAQUF
+BwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJpc2lnbi5j
+b20vb2NzcC9zdGF0dXMwRAYDVR0gBD0wOzA5BgtghkgBhvhFAQcBATAqMCgG
+CCsGAQUFBwIBFhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vUlBBMAkGA1Ud
+EwQCMAAwCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBBQUAA4GBAHCQ3bjkvlMX
+fH8C6dX3i5mTMWCNfuZgayTvYKzSzpHegG0JpNO4OOVEynJeDS3Bd5y9LAN4
+KY2kpXeH9fErJq3MB2w6VFoo4AnzTQoEytRYaQuns/XdAaXn3PAfusFdkI2z
+6k/BEVmXarIrE7HarZehs7GgIFvKMquNzxPwHynD
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEC0b/EoXjaOR6+f/9YtFvgswDQYJKoZIhvcNAQECBQAwXzEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQL
+Ey5DbGFzcyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2WoujDWojg4BrzzmH9CETMwZM
+JaLtVRKXxaeAufqDwSCg+i8VDXyhYGt+eSz6Bg86rvYbb7HS/y8oUl+DfUvE
+erf4Zh+AVPy3wo5ZShRXRtGak75BkQO7FYCTXOvnzAhsPz6zSvz/S2wj1VCC
+JkQZjiPDceoZJEcEnnW/yKYAHwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBAIob
+K/o5wXTXXtgZZKJYSi034DNHD6zt96rbHuSLBlxgJ8pFUs4W7z8GZOeUaHxg
+MxURaa+dYo2jA1Rrpr7l7gUYYAS/QoD90KioHgE796Ncr6Pc5iaAIzy4RHT3
+Cq5Ji2F4zCS/iIqnDupzGUH9TQPwiNHleI2lKk/2lw0Xd8rY
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHB
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNV
+BAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRo
+b3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4g
+LSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24g
+VHJ1c3QgTmV0d29yazAeFw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTla
+MIHBMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6
+BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIElu
+Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNp
+Z24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
+p4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjMHiwSViy4AWkszJkf
+rbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjwDqL7MWzJ5m+Z
+Jwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cCAwEAATAN
+BgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9jinb3/
+7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX
+rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6x
+RnInjBJ7xUS0rg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcox
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UE
+CxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkg
+VmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMG
+A1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZp
+Y2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcx
+NjIzNTk1OVowgcoxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwg
+SW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UE
+CxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1
+c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJp
+bWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWUJ92lvuCwTY+zYVY8
+1nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDOJxOeBUebMXoT
+2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUYwZF7C9UT
+AJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9okoqQ
+HgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN
+qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVC
+YQ/ESrg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekh
+ktdmnLfexbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf
+0xwLRtxyID+u7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydE
+p85EXdQbkJgNHkKUsQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377B
+MnMiIYtYgXsVkXq642RIsH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab
+5iXiQkWquJCtvgiPqQtCGJTPcjnhsUPgKM+351psE2tJs//jGHyJizNdrDPX
+p/naOlXJWBD5qu9ats9LS98q
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDnjCCAwegAwIBAgIQCUYX5h3Y1BygDKBi6HmKpzANBgkqhkiG9w0BAQUF
+ADBfMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1
+BgNVBAsTLkNsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDAwODAxMDAwMDAwWhcNMDQwNzMxMjM1OTU5WjCBpzEX
+MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov
+L3d3dy52ZXJpc2lnbi5jb20vUlBBIChjKTAwMS4wLAYDVQQDEyVDbGFzcyAy
+IFB1YmxpYyBQcmltYXJ5IE9DU1AgUmVzcG9uZGVyMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDQymMxYX9ENHwFfQs9apDLeUt3Cj9LxyPlwGItfpx+
+PoiHkdCs6E1Jh6KWkIrdBKUCP4yb6Yn+YqDiWr3I3bR45qVCkwhnAcAgTddc
+9F3as+M3plIaLExlTYqH2aij8UlUuzxcgFFoxvtJ/wtVqxXd+5rBuR10DbKM
+RF2J/J/5gwIDAQABo4IBEDCCAQwwIAYDVR0RBBkwF6QVMBMxETAPBgNVBAMT
+CE9DU1AgMS0yMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNp
+Z24uY29tL3BjYTIuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMJMEIGCCsGAQUF
+BwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJpc2lnbi5j
+b20vb2NzcC9zdGF0dXMwRAYDVR0gBD0wOzA5BgtghkgBhvhFAQcBATAqMCgG
+CCsGAQUFBwIBFhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vUlBBMAkGA1Ud
+EwQCMAAwCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBBQUAA4GBAB99CW4kRnUE
+nPMmm+M5bhfvvL2iG9IChIar0ECXLMRDiDcZayKoA3FQnSDcNmAgmnMtc1Vs
+WJsswrQ0LHozQsqR2elDr88e4PXEeqs/cmMeqTfhWzuIsxOGgpBXy1f/9Fa+
+It3jl6jhvCJDwt1N2/aBnpIUnjkPE1TegtjAXjSN
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQL
+Ey5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69q
+RUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94f56TuZoAqiN91qyFomNFx3In
+zPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Olhec9vn2a
+/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtM
+EivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPw
+TtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzk
+uxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcEx
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UE
+CxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAt
+IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVow
+gcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoG
+A1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5j
+LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDM
+XtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXX
+wc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg013gfqLptQ5GV
+j0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQABMA0G
+CSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01U
+bSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
+F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo
+1KpYoJ2daZH9
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHK
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV
+BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5
+IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBD
+BgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3
+MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s
+IEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNV
+BAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQg
+dXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFBy
+aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2
+R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2tKmFZpGcmTNDo
+vFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUccLwg
+TS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+V
+k7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
+Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJ
+OxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my
+/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f
+j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoA
+Wii/gt/4uhMdUIaC/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8S
+GhJouPtmmRQURVyu565pF4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbb
+o27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh
+/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDojCCAwugAwIBAgIQLpaev7ZibOx76XPM42zBhDANBgkqhkiG9w0BAQUF
+ADBfMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1
+BgNVBAsTLkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDAwODA0MDAwMDAwWhcNMDQwODAzMjM1OTU5WjCBpzEX
+MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov
+L3d3dy52ZXJpc2lnbi5jb20vUlBBIChjKTAwMS4wLAYDVQQDEyVDbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IE9DU1AgUmVzcG9uZGVyMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDx5AgOg7t140jluNum8Lmr6Txix141W9ACVBHYydFW
+uXZLuat65s269gwE1n7WsAplrE454/H3LaMlOe+wi8++2wxdbnD0B81w9zrA
+PjUW7XiMQ8/CJi5H1oZ9nPG+1mcMIiWkymXmH3p4KC8/BdsEIb/hRWb+PLeC
+7Vq4FhW5VQIDAQABo4IBFDCCARAwIAYDVR0RBBkwF6QVMBMxETAPBgNVBAMT
+CE9DU1AgMS0zMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwudmVyaXNp
+Z24uY29tL3BjYTMuMS4xLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCTBCBggr
+BgEFBQcBAQQ2MDQwMgYIKwYBBQUHMAGmJhYkaHR0cDovL29jc3AudmVyaXNp
+Z24uY29tL29jc3Avc3RhdHVzMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHAQEw
+KjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL1JQQTAJ
+BgNVHRMEAjAAMAsGA1UdDwQEAwIHgDANBgkqhkiG9w0BAQUFAAOBgQAC9lNj
+wKke8tCLMzCPSJtMsFa0g3FKvtxQ2PW24AvbvXhP6c8JNNopSZ0Bc1qRkYJU
+LBMK03cjzzf8Y96n4/a3tWlFKEnDkdyqRxypiJksBSqNjYr6YuJatwAgXTnE
+KMLL/J6oia5bPY4S6jKy/OsU1wkVGsDNG9W1FU5B1ZbjTg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEDKIjprS9esTR/h/xCA3JfgwDQYJKoZIhvcNAQEFBQAwgcEx
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UE
+CxMzQ2xhc3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAt
+IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVow
+gcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoG
+A1UECxMzQ2xhc3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5j
+LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6
+8OTP+cSuhVS5B1f5j8V/aBH4xBewRNzjMHPVKmIquNDMHO0oW369atyzkSTK
+QWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDHqGKB3FtKqsGgtG7rL+VX
+xbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHwIDAQABMA0G
+CSqGSIb3DQEBBQUAA4GBAIWMEsGnuVAVess+rLhDityq3RS6iYF+ATwjcSGI
+L4LcY/oCRaxFWdcqWERbt5+BO5JoPeI3JPV7bI92NZYJqFmduc4jq3TWg/0y
+cyfYaT5DdPauxYma51N86Xv2S/PBZYPejYqcPIiNOVn8qj8ijaHBZlCBckzt
+ImRPT8qAkbYp
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHK
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV
+BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5
+IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBD
+BgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3
+MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s
+IEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNV
+BAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQg
+dXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFBy
+aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYl
+S+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ+mGuqPKljYXC
+KtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM8BDc
+VHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdL
+MEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY
+ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDD
+Zq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1Wr
+IhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt
+mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csK
+vE+MW8VLADsfKoKmfjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluP
+QSjA1egtTaRezarZ7c7c2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kP
+mF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr
+9Xgn2uf3ZkPznoM+IKrDNWCRzg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzEL
+MAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMu
+MS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5MB4XDTk0MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UE
+BhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD
+VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGb
+MA0GCSqGSIb3DQEBAQUAA4GJADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6O
+LDfO6zV4ZFQD5YRAUcm/jwjiioII0haGN1XpsSECrXZogZoFokvJSyVmIlZs
+iAeP94FZbYQHZXATcXY+m3dM41CJVphIuR2nKRoTLkoRWZweFdVJVCxzOmmC
+sZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZIhvcNAQECBQADfgBl3X7hsuyw
+4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3YQO2WxZpO8ZECAyIUwxr
+l0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc1/p3yjkWWW8O6tO1
+g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDnzCCAwygAwIBAgIRAP9F1SddJPuzwjkkU1fhT94wDQYJKoZIhvcNAQEF
+BQAwXzELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5
+LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5MB4XDTAwMDgwNDAwMDAwMFoXDTA0MDgwMzIzNTk1OVowgZ4x
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQgaHR0cHM6
+Ly93d3cudmVyaXNpZ24uY29tL1JQQSAoYykwMDElMCMGA1UEAxMcU2VjdXJl
+IFNlcnZlciBPQ1NQIFJlc3BvbmRlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAuFGZZIUO7rMKaPC/Y3YdU/X8oXiMM+6f9L452psPTUepjyDoS0S9
+zs17kNEw6JDEJXuJKN699pMd/7n/krWpjeSuzOLDB4Nqo3IQASdiIqY1Jjkt
+ns9gDPxHpNfQQninHWzQy08VpykKtJVFxLHnWgnXOZXYHTWewr2zXcEMSx8C
+AwEAAaOCAR0wggEZMCAGA1UdEQQZMBekFTATMREwDwYDVQQDEwhPQ1NQIDEt
+NDA+BgNVHR8ENzA1MDOgMaAvhi1odHRwOi8vY3JsLnZlcmlzaWduLmNvbS9S
+U0FTZWN1cmVTZXJ2ZXItcC5jcmwwEwYDVR0lBAwwCgYIKwYBBQUHAwkwQgYI
+KwYBBQUHAQEENjA0MDIGCCsGAQUFBzABpiYWJGh0dHA6Ly9vY3NwLnZlcmlz
+aWduLmNvbS9vY3NwL3N0YXR1czBEBgNVHSAEPTA7MDkGC2CGSAGG+EUBBwEB
+MCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZlcmlzaWduLmNvbS9SUEEw
+CQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQEFBQADfgAAsxBT
+ZpxJky4xoAJC0lhXfmah/huKYRhQQCweK0Gl1tv/rAgcWgVtAlwqtpZPR9u+
+TtvOzLqGuBjOsRKRX2P380g+zPFNE+RtCZR4AJLLoyCdBgtqoEMHztEZbI8Y
+dZqfFzP9qSa44+LewqjEWop/mNYHBmvMVp6GcM7U7w==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDzTCCAzagAwIBAgIQU2GyYK7bcY6nlLMTM/QHCTANBgkqhkiG9w0BAQUF
+ADCBwTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTww
+OgYDVQQLEzNDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5IC0gRzIxOjA4BgNVBAsTMShjKSAxOTk4IFZlcmlTaWduLCBJ
+bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAsTFlZlcmlT
+aWduIFRydXN0IE5ldHdvcmswHhcNMDAwOTI2MDAwMDAwWhcNMTAwOTI1MjM1
+OTU5WjCBpTEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
+cmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBh
+dCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTAwMSwwKgYDVQQD
+EyNWZXJpU2lnbiBUaW1lIFN0YW1waW5nIEF1dGhvcml0eSBDQTCBnzANBgkq
+hkiG9w0BAQEFAAOBjQAwgYkCgYEA0hmdZ8IAIVlizrQJIkRpivglWtvtDbc2
+fk7gu5Q+kCWHwmFHKdm9VLhjzCx9abQzNvQ3B5rB3UBU/OB4naCTuQk9I1F/
+RMIUdNsKvsvJMDRAmD7Q1yUQgZS9B0+c1lQn3y6ov8uQjI11S7zi6ESHzeZB
+CiVu6PQkAsVSD27smHUCAwEAAaOB3zCB3DAPBgNVHRMECDAGAQH/AgEAMEUG
+A1UdIAQ+MDwwOgYMYIZIAYb4RQEHFwEDMCowKAYIKwYBBQUHAgEWHGh0dHBz
+Oi8vd3d3LnZlcmlzaWduLmNvbS9ycGEwMQYDVR0fBCowKDAmoCSgIoYgaHR0
+cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwCwYDVR0PBAQDAgEGMEIG
+CCsGAQUFBwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJp
+c2lnbi5jb20vb2NzcC9zdGF0dXMwDQYJKoZIhvcNAQEFBQADgYEAgnBold+2
+DcIBcBlK0lRWHqzyRUyHuPU163hLBanInTsZIS5wNEqi9YngFXVF5yg3ADQn
+Keg3S/LvRJdrF1Eaw1adPBqK9kpGRjeM+sv1ZFo4aC4cw+9wzrhGBha/937n
+tag+RaypJXUie28/sJyU58dzq6wf7iWbwBbtt8pb8BQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDgDCCAmigAwIBAgICAx4wDQYJKoZIhvcNAQEFBQAwYTELMAkGA1UEBhMC
+VVMxDTALBgNVBAoTBFZJU0ExLzAtBgNVBAsTJlZpc2EgSW50ZXJuYXRpb25h
+bCBTZXJ2aWNlIEFzc29jaWF0aW9uMRIwEAYDVQQDEwlHUCBSb290IDIwHhcN
+MDAwODE2MjI1MTAwWhcNMjAwODE1MjM1OTAwWjBhMQswCQYDVQQGEwJVUzEN
+MAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNl
+cnZpY2UgQXNzb2NpYXRpb24xEjAQBgNVBAMTCUdQIFJvb3QgMjCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkBcLWqxEDwq2omYXkZAPy/mzdZ
+DK9vZBv42pWUJGkzEXDK41Z0ohdXZFwgBuHW73G3O/erwWnQSaSxBNf0V2KJ
+XLB1LRckaeNCYOTudNargFbYiCjh+20i/SN8RnNPflRzHqgsVVh1t0zzWkWl
+Ahr62p3DRcMiXvOL8WAp0sdftAw6UYPvMPjU58fy+pmjIlC++QU3o63tmsPm
+7IgbthknGziLgE3sucfFicv8GjLtI/C1AVj59o/ghalMCXI5Etuz9c9OYmTa
+xhkVOmMd6RdVoUwiPDQyRvhlV7or7zaMavrZ2UT0qt2E1w0cslSsMoW0ZA3e
+QbuxNMYBhjJk1Z8CAwEAAaNCMEAwHQYDVR0OBBYEFJ59SzS/ca3CBfYDdYDO
+qU8axCRMMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqG
+SIb3DQEBBQUAA4IBAQAhpXYUVfmtJ3CPPPTVbMjMCqujmAuKBiPFyWHbmQdp
+NSYx/scuhMKZYdQN6X0uEyt8joW2hcdLzzW2LEc9zikv2G+fiRxkk78IvXbQ
+kIqUs38oW26sTTMs7WXcFsziza6kPWKSBpUmv9+55CCmc2rBvveURNZNbyoL
+axhNdBA2aGpawWqn3TYpjLgwi08hPwAuVDAHOrqK5MOeyti12HvOdUVmB/Rt
+Ldh6yumJivIj2C/LbgA2T/vwLwHMD8AiZfSr4k5hLQOCfZEWtTDVFN5ex5D8
+ofyrEK9ca3CnB+8phuiyJccg/ybdd+95RBTEvd07xQObdyPsoOy7Wjm1zK0G
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUF
+ADBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlz
+YSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMT
+E1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0
+MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UE
+CxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAa
+BgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh
+28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8bRaVK7362
+rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81
+q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtF
+Wsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0
+lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaLdXe6YJ2E5/4t
+AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
+A1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOC
+AQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR
+zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKht
+cbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGI
+xHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu
+YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/
+hC3euiInlhBx6yLt398znM/jra6O1I7mT1GvFpLgXPYHDw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFajCCBFKgAwIBAgIEPLU9RjANBgkqhkiG9w0BAQUFADBmMRIwEAYDVQQK
+EwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290IENBczEzMDEG
+A1UEAxMqYmVUUlVTVGVkIFJvb3QgQ0EtQmFsdGltb3JlIEltcGxlbWVudGF0
+aW9uMB4XDTAyMDQxMTA3Mzg1MVoXDTIyMDQxMTA3Mzg1MVowZjESMBAGA1UE
+ChMJYmVUUlVTVGVkMRswGQYDVQQLExJiZVRSVVNUZWQgUm9vdCBDQXMxMzAx
+BgNVBAMTKmJlVFJVU1RlZCBSb290IENBLUJhbHRpbW9yZSBJbXBsZW1lbnRh
+dGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALx+xDmcjOPW
+HIb/ymKt4H8wRXqOGrO4x/nRNv8i805qX4QQ+2aBw5R5MdKR4XeOGCrDFN5R
+9U+jK7wYFuK13XneIviCfsuBH/0nLI/6l2Qijvj/YaOcGx6Sj8CoCd8JEey3
+fTGaGuqDIQY8n7pc/5TqarjDa1U0Tz0yH92BFODEPM2dMPgwqZfT7syj0B9f
+HBOB1BirlNFjw55/NZKeX0Tq7PQiXLfoPX2k+YmpkbIq2eszh+6l/ePazIjm
+iSZuxyuC0F6dWdsU7JGDBcNeDsYq0ATdcT0gTlgn/FP7eHgZFLL8kFKJOGJg
+B7Sg7KxrUNb9uShr71ItOrL/8QFArDcCAwEAAaOCAh4wggIaMA8GA1UdEwEB
+/wQFMAMBAf8wggG1BgNVHSAEggGsMIIBqDCCAaQGDysGAQQBsT4AAAEJKIOR
+MTCCAY8wggFIBggrBgEFBQcCAjCCAToaggE2UmVsaWFuY2Ugb24gb3IgdXNl
+IG9mIHRoaXMgQ2VydGlmaWNhdGUgY3JlYXRlcyBhbiBhY2tub3dsZWRnbWVu
+dCBhbmQgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5k
+YXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgdGhlIENlcnRpZmlj
+YXRpb24gUHJhY3RpY2UgU3RhdGVtZW50IGFuZCB0aGUgUmVseWluZyBQYXJ0
+eSBBZ3JlZW1lbnQsIHdoaWNoIGNhbiBiZSBmb3VuZCBhdCB0aGUgYmVUUlVT
+VGVkIHdlYiBzaXRlLCBodHRwOi8vd3d3LmJldHJ1c3RlZC5jb20vcHJvZHVj
+dHNfc2VydmljZXMvaW5kZXguaHRtbDBBBggrBgEFBQcCARY1aHR0cDovL3d3
+dy5iZXRydXN0ZWQuY29tL3Byb2R1Y3RzX3NlcnZpY2VzL2luZGV4Lmh0bWww
+HQYDVR0OBBYEFEU9w6nR3D8kVpgccxiIav+DR+22MB8GA1UdIwQYMBaAFEU9
+w6nR3D8kVpgccxiIav+DR+22MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0B
+AQUFAAOCAQEASZK8o+6svfoNyYt5hhwjdrCAWXf82n+0S9/DZEtqTg6t8n1Z
+dwWtColzsPq8y9yNAIiPpqCy6qxSJ7+hSHyXEHu67RMdmgduyzFiEuhjA6p9
+beP4G3YheBufS0OM00mG9htc9i5gFdPp43t1P9ACg9AYgkHNZTfqjjJ+vWuZ
+XTARyNtIVBw74acT02pIk/c9jH8F6M7ziCpjBLjqflh8AXtb4cV97yHgjQ5d
+UX2xZ/2jvTg2xvI4hocalmhgRvsoFEdV4aeADGvi6t9NfJBIoDa9CReJf8Py
+05yc493EG931t3GzUwWJBtDLSoDByFOQtTwxiBdQn8nEDovYqAJjDQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFLDCCBBSgAwIBAgIEOU99hzANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQG
+EwJXVzESMBAGA1UEChMJYmVUUlVTVGVkMRswGQYDVQQDExJiZVRSVVNUZWQg
+Um9vdCBDQXMxGjAYBgNVBAMTEWJlVFJVU1RlZCBSb290IENBMB4XDTAwMDYy
+MDE0MjEwNFoXDTEwMDYyMDEzMjEwNFowWjELMAkGA1UEBhMCV1cxEjAQBgNV
+BAoTCWJlVFJVU1RlZDEbMBkGA1UEAxMSYmVUUlVTVGVkIFJvb3QgQ0FzMRow
+GAYDVQQDExFiZVRSVVNUZWQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBANS0c3oTCjhVAb6JVuGUntS+WutKNHUbYSnE4a0IYCF4
+SP+00PpeQY1hRIfo7clY+vyTmt9P6j41ffgzeubx181vSUs9Ty1uDoM6GHh3
+o8/n9E1z2Jo7Gh2+lVPPIJfCzz4kUmwMjmVZxXH/YgmPqsWPzGCgc0rXOD8V
+cr+il7dw6K/ifhYGTPWqZCZyByWtNfwYsSbX2P8ZDoMbjNx4RWc0PfSvHI3k
+bWvtILNnmrRhyxdviTX/507AMhLn7uzf/5cwdO2NR47rtMNE5qdMf1ZD6Li8
+tr76g5fmu/vEtpO+GRg+jIG5c4gW9JZDnGdzF5DYCW5jrEq2I8QBoa2k5MUC
+AwEAAaOCAfgwggH0MA8GA1UdEwEB/wQFMAMBAf8wggFZBgNVHSAEggFQMIIB
+TDCCAUgGCisGAQQBsT4BAAAwggE4MIIBAQYIKwYBBQUHAgIwgfQagfFSZWxp
+YW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVz
+IGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0
+ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGFuZCBjZXJ0aWZpY2F0aW9u
+IHByYWN0aWNlIHN0YXRlbWVudCwgd2hpY2ggY2FuIGJlIGZvdW5kIGF0IGJl
+VFJVU1RlZCdzIHdlYiBzaXRlLCBodHRwczovL3d3dy5iZVRSVVNUZWQuY29t
+L3ZhdWx0L3Rlcm1zMDEGCCsGAQUFBwIBFiVodHRwczovL3d3dy5iZVRSVVNU
+ZWQuY29tL3ZhdWx0L3Rlcm1zMDQGA1UdHwQtMCswKaAnoCWkIzAhMRIwEAYD
+VQQKEwliZVRSVVNUZWQxCzAJBgNVBAYTAldXMB0GA1UdDgQWBBQquZtpLjub
+2M3eKjEENGvKBxirZzAfBgNVHSMEGDAWgBQquZtpLjub2M3eKjEENGvKBxir
+ZzAOBgNVHQ8BAf8EBAMCAf4wDQYJKoZIhvcNAQEFBQADggEBAHlh26Nebhax
+6nZR+csVm8tpvuaBa58oH2U+3RGFktToQb9+M70j5/Egv6S0phkBxoyNNXxl
+pE8JpNbYIxUFE6dDea/bow6be3ga8wSGWsb2jCBHOElQBp1yZzrwmAOtlmdE
+/D8QDYZN5AA7KXvOOzuZhmElQITcE2K3+spZ1gMe1lMBzW1MaFVA4e5rxyoA
+AEiCswoBw2AqDPeCNe5IhpbkdNQ96gFxugR1QKepfzk5mlWXKWWuGVUlBXJH
+0+gY3Ljpr0NzARJ0o+FcXxVdJPP55PS2Z2cS52QiivalQaYctmBjRYoQtLpG
+EK5BV2VsPyMQPyEQWbfkQN0mDCP2qq4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGUTCCBTmgAwIBAgIEPLVPQDANBgkqhkiG9w0BAQUFADBmMRIwEAYDVQQK
+EwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290IENBczEzMDEG
+A1UEAxMqYmVUUlVTVGVkIFJvb3QgQ0EgLSBFbnRydXN0IEltcGxlbWVudGF0
+aW9uMB4XDTAyMDQxMTA4MjQyN1oXDTIyMDQxMTA4NTQyN1owZjESMBAGA1UE
+ChMJYmVUUlVTVGVkMRswGQYDVQQLExJiZVRSVVNUZWQgUm9vdCBDQXMxMzAx
+BgNVBAMTKmJlVFJVU1RlZCBSb290IENBIC0gRW50cnVzdCBJbXBsZW1lbnRh
+dGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALr0RAOqEmq1
+Q+xVkrYwfTVXDNvzDSduTPdQqJtOK2/b9a0cS12zqcH+e0TrW6MFDR/FNCsw
+ACnxeECypP869AGIF37m1CbTukzqMvtDd5eHI8XbQ6P1KqNRXuE70mVpflUV
+m3rnafdE4Fe1FehmYA8NA/uCjqPoEXtsvsdjDheT389Lrm5zdeDzqrmkwAkb
+hepxKYhBMvnwKg5sCfJ0a2ZsUhMfGLzUPvfYbiCeyv78IZTuEyhL11xeDGbu
+6bsPwTSxfwh28z0mcMmLJR1iJAzqHHVOwBLkuhMdMCktVjMFu5dZfsZJT4nX
+LySotohAtWSSU1Yk5KKghbNekLQSM80CAwEAAaOCAwUwggMBMIIBtwYDVR0g
+BIIBrjCCAaowggGmBg8rBgEEAbE+AAACCSiDkTEwggGRMIIBSQYIKwYBBQUH
+AgIwggE7GoIBN1JlbGlhbmNlIG9uIG9yIHVzZSBvZiB0aGlzIENlcnRpZmlj
+YXRlIGNyZWF0ZXMgYW4gYWNrbm93bGVkZ21lbnQgYW5kIGFjY2VwdGFuY2Ug
+b2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29u
+ZGl0aW9ucyBvZiB1c2UsIHRoZSBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0
+YXRlbWVudCBhbmQgdGhlIFJlbHlpbmcgUGFydHkgQWdyZWVtZW50LCB3aGlj
+aCBjYW4gYmUgZm91bmQgYXQgdGhlIGJlVFJVU1RlZCB3ZWIgc2l0ZSwgaHR0
+cHM6Ly93d3cuYmV0cnVzdGVkLmNvbS9wcm9kdWN0c19zZXJ2aWNlcy9pbmRl
+eC5odG1sMEIGCCsGAQUFBwIBFjZodHRwczovL3d3dy5iZXRydXN0ZWQuY29t
+L3Byb2R1Y3RzX3NlcnZpY2VzL2luZGV4Lmh0bWwwEQYJYIZIAYb4QgEBBAQD
+AgAHMIGJBgNVHR8EgYEwfzB9oHugeaR3MHUxEjAQBgNVBAoTCWJlVFJVU1Rl
+ZDEbMBkGA1UECxMSYmVUUlVTVGVkIFJvb3QgQ0FzMTMwMQYDVQQDEypiZVRS
+VVNUZWQgUm9vdCBDQSAtIEVudHJ1c3QgSW1wbGVtZW50YXRpb24xDTALBgNV
+BAMTBENSTDEwKwYDVR0QBCQwIoAPMjAwMjA0MTEwODI0MjdagQ8yMDIyMDQx
+MTA4NTQyN1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFH1w5a44iwY/qhwa
+j/nPJDCqhIQWMB0GA1UdDgQWBBR9cOWuOIsGP6ocGo/5zyQwqoSEFjAMBgNV
+HRMEBTADAQH/MB0GCSqGSIb2fQdBAAQQMA4bCFY2LjA6NC4wAwIEkDANBgkq
+hkiG9w0BAQUFAAOCAQEAKrgXzh8QlOu4mre5X+za95IkrNySO8cgjfKZ5V04
+ocI07cUTWVwFtStPYZuR+0H8/NU8TZh2BvWBfevdkObRVlTa4y0MnxEylCIB
+evZsLHRnBMylj44ss0O1lKLQfelifwa+JwGDnjr9iu6YQ0pr17WXOzq/T220
+Y/ozADQuLW2WyXvKmWO6vvT2MKAtmJbpVkQFqUSjYRDrgqFnXbxdJ3Wqiig2
+KjiS2d2kXgClzMx8KSreKJCrt+G2/30lC0DYqjSjLd4H61/OCt3Kfjp9JsFi
+aDrmLzfzgYYhxKlkqu9FNtEaZnz46TfW1mG+oq1I59/mdP7TbX3SJdysYlep
+9w==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFaDCCBFCgAwIBAgIQO1nHe81bV569N1KsdrSqGjANBgkqhkiG9w0BAQUF
+ADBiMRIwEAYDVQQKEwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBS
+b290IENBczEvMC0GA1UEAxMmYmVUUlVTVGVkIFJvb3QgQ0EgLSBSU0EgSW1w
+bGVtZW50YXRpb24wHhcNMDIwNDExMTExODEzWhcNMjIwNDEyMTEwNzI1WjBi
+MRIwEAYDVQQKEwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290
+IENBczEvMC0GA1UEAxMmYmVUUlVTVGVkIFJvb3QgQ0EgLSBSU0EgSW1wbGVt
+ZW50YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkujQw
+CY5X0LkGLG9uJIAiv11DpvpPrILnHGhwhRujbrWqeNluB0s/6d/16uhUoWGK
+Di9pdRi3DOUUjXFumLhV/AyV0Jtu4S2I1DpAa5LxmZZk3tv/ePTulh1HiXzU
+vrmIdyM6CeYEnm2qXtLIvZpOGd+J6lsOfsPktPDgaTuID0GQ+NRxQyTBjyZL
+O1bp/4xsN+lFrYWMU8NghpBKlsmzVLC7F/AcRdnUGxlkVgoZ98zh/4avflhe
+rHqQH8koOUV7orbHnB/ahdQhhlkwk75TMzf270HPM8ercmsl9fNTGwxMLvF1
+S++gh/f+ihXQbNXL+WhTuXAVE8L1LvtDNXUtAgMBAAGjggIYMIICFDAMBgNV
+HRMEBTADAQH/MIIBtQYDVR0gBIIBrDCCAagwggGkBg8rBgEEAbE+AAADCSiD
+kTEwggGPMEEGCCsGAQUFBwIBFjVodHRwOi8vd3d3LmJldHJ1c3RlZC5jb20v
+cHJvZHVjdHNfc2VydmljZXMvaW5kZXguaHRtbDCCAUgGCCsGAQUFBwICMIIB
+OhqCATZSZWxpYW5jZSBvbiBvciB1c2Ugb2YgdGhpcyBDZXJ0aWZpY2F0ZSBj
+cmVhdGVzIGFuIGFja25vd2xlZGdtZW50IGFuZCBhY2NlcHRhbmNlIG9mIHRo
+ZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlv
+bnMgb2YgdXNlLCB0aGUgQ2VydGlmaWNhdGlvbiBQcmFjdGljZSBTdGF0ZW1l
+bnQgYW5kIHRoZSBSZWx5aW5nIFBhcnR5IEFncmVlbWVudCwgd2hpY2ggY2Fu
+IGJlIGZvdW5kIGF0IHRoZSBiZVRSVVNUZWQgd2ViIHNpdGUsIGh0dHA6Ly93
+d3cuYmV0cnVzdGVkLmNvbS9wcm9kdWN0c19zZXJ2aWNlcy9pbmRleC5odG1s
+MAsGA1UdDwQEAwIBBjAfBgNVHSMEGDAWgBSp7BR++dlDzFMrFK3P9/BZiUHN
+GTAdBgNVHQ4EFgQUqewUfvnZQ8xTKxStz/fwWYlBzRkwDQYJKoZIhvcNAQEF
+BQADggEBANuXsHXqDMTBmMpWBcCorSZIry0g6IHHtt9DwSwddUvUQo3neqh0
+3GZCWYez9Wlt2ames30cMcH1VOJZJEnl7r05pmuKmET7m9cqg5c0Lcd9NUwt
+NLg+DcTsiCevnpL9UGGCqGAHFFPMZRPB9kdEadIxyKbdLrML3kqNWz2rDcI1
+UqJWN8wyiyiFQpyRQHpwKzg21eFzGh/l+n5f3NacOzDq28BbJ1zTcwfBwvNM
+m2+fG8oeqqg4MwlYsq78B+g23FW6L09A/nq9BqaBwZMifIYRCgZ3SK41ty8y
+mmFei74pnykkiFY5LKjSq5YDWtRIn7lAhAuYaPsBQ9Yb4gmxlxw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
+MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
+IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
+dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
+li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
+rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
+WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
+F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
+xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
+Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
+dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
+ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
+IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
+c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
+ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
+Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
+KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
+KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
+y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
+dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
+VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
+MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
+fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
+7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
+cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
+mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
+xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
+SnQ2+Q==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIETzCCAzegAwIBAgIEO63vKTANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDEwOTIzMTQxODE3WhcNMTEw
+OTIzMTMxODE3WjB1MQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MR8wHQYDVQQDExZDQyBTaWdu
+ZXQgLSBDQSBLbGFzYSAxMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4SRW9Q58g5DY1Hw7h
+gCRKBEdPdGn0MFHsfw7rlu/oQm7IChI/uWd9q5wwo77YojtTDjRnpgZsjqBeynX8T90vFILqsY2K
+5CF1OESalwvVr3sZiQX79lisuFKat92u6hBFikFIVxfHHB67Af+g7u0dEHdDW7lwy81MwFYxBTRy
+9wIDAQABo4IBbTCCAWkwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwggEEBgNVHSAE
+gfwwgfkwgfYGDSsGAQQBvj8CAQoBAQAwgeQwgZoGCCsGAQUFBwICMIGNGoGKQ2VydHlmaWthdCB3
+eXN0YXdpb255IHpnb2RuaWUgeiBkb2t1bWVudGVtOiAiUG9saXR5a2EgQ2VydHlmaWthY2ppIGRs
+YSBSb290Q0EiLiBDZXJ0eWZpa2F0IHd5c3Rhd2lvbnkgcHJ6ZXogUm9vdENBIHcgaGllcmFyY2hp
+aSBDQyBTaWduZXQuMEUGCCsGAQUFBwIBFjlodHRwOi8vd3d3LnNpZ25ldC5wbC9yZXBvenl0b3Jp
+dW0vZG9rdW1lbnR5L3BjX3Jvb3RjYS50eHQwHwYDVR0jBBgwFoAUwJvFIw0C4aZOSGsfAOnjmhQb
+sa8wHQYDVR0OBBYEFMODHtVZd1T7TftXR/nEI1zR54njMA0GCSqGSIb3DQEBBQUAA4IBAQBRIHQB
+FIGh8Jpxt87AgSLwIEEk4+oGy769u3NtoaR0R3WNMdmt7fXTi0tyTQ9V4AIszxVjhnUPaKnF1KYy
+f8Tl+YTzk9ZfFkZ3kCdSaILZAOIrmqWNLPmjUQ5/JiMGho0e1YmWUcMci84+pIisTsytFzVP32/W
++sz2H4FQAvOIMmxB7EJX9AdbnXn9EXZ+4nCqi0ft5z96ZqOJJiCB3vSaoYg+wdkcvb6souMJzuc2
+uptXtR1Xf3ihlHaGW+hmnpcwFA6AoNrom6Vgzk6U1ienx0Cw28BhRSKqzKkyXkuK8gRflZUx84uf
+tXncwKJrMiE3lvgOOBITRzcahirLer4c
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE9zCCA9+gAwIBAgIEPL/xoTANBgkqhkiG9w0BAQUFADB2MQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MSAwHgYDVQQDExdDQyBTaWduZXQgLSBQQ0EgS2xhc2EgMjAeFw0wMjA0MTkxMDI5NTNa
+Fw0xNzA0MTgxMjUzMDdaMHUxCzAJBgNVBAYTAlBMMR8wHQYDVQQKExZUUCBJbnRlcm5ldCBTcC4g
+eiBvLm8uMSQwIgYDVQQLExtDZW50cnVtIENlcnR5ZmlrYWNqaSBTaWduZXQxHzAdBgNVBAMTFkND
+IFNpZ25ldCAtIENBIEtsYXNhIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqgLJu
+QqY4yavbSgHg8CyfKTx4BokNSDOVz4eD9vptUr11Kqd06ED1hlH7Sg0goBFAfntNU/QTKwSBaNui
+me7C4sSEdgsKrPoAhGb4Mq8y7Ty7RqZz7mkzNMqzL2L2U4yQ2QjvpH8MH0IBqOWEcpSkpwnrCDIm
+RoTfd+YlZWKi2JceQixUUYIQ45Ox8+x8hHbvvZdgqtcvo8PW27qoHkp/7hMuJ44kDAGrmxffBXl/
+OBRZp0uO1CSLcMcVJzyr2phKhy406MYdWrtNPEluGs0GFDzd0nrIctiWAO4cmct4S72S9Q6e//0G
+O9f3/Ca5Kb2I1xYLj/xE+HgjHX9aD2MhAgMBAAGjggGMMIIBiDAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBBjCB4wYDVR0gBIHbMIHYMIHVBg0rBgEEAb4/AhQKAQEAMIHDMHUGCCsGAQUF
+BwICMGkaZ0NlcnR5ZmlrYXQgd3lzdGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0
+eWthIENlcnR5ZmlrYWNqaSBQQ0EyIC0gQ2VydHlmaWthdHkgVXJ6ZWRvdyBLbGFzeSAyIi4wSgYI
+KwYBBQUHAgEWPmh0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9kb2t1bWVudHkva2xh
+c2EyL3BjX3BjYTIudHh0MD8GA1UdHwQ4MDYwNKAyoDCGLmh0dHA6Ly93d3cuc2lnbmV0LnBsL3Jl
+cG96eXRvcml1bS9jcmwvcGNhMi5jcmwwHwYDVR0jBBgwFoAUwGxGyl2CfpYHRonE82AVXO08kMIw
+HQYDVR0OBBYEFLtFBlILy4HNKVSzvHxBTM0HDowlMA0GCSqGSIb3DQEBBQUAA4IBAQBWTsCbqXrX
+hBBev5v5cIuc6gJM8ww7oR0uMQRZoFSqvQUPWBYM2/TLI/f8UM9hSShUVj3zEsSj/vFHagUVmzuV
+Xo5u0WK8iaqATSyEVBhADHrPG6wYcLKJlagge/ILA0m+SieyP2sjYD9MUB9KZIEyBKv0429UuDTw
+6P7pslxMWJBSNyQxaLIs0SRKsqZZWkc7ZYAj2apSkBMX2Is1oHA+PwkF6jQMwCao/+CndXPUzfCF
+6caa9WwW31W26MlXCvSmJgfiTPwGvm4PkPmOnmWZ3CczzhHl4q7ztHFzshJH3sZWDnrWwBFjzz5e
+Pr3WHV1wA7EY6oT4zBx+2gT9XBTB
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEUzCCAzugAwIBAgIEPq+qjzANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQGEwJQTDE3MDUGA1UE
+ChMuQ1ppQyBDZW50cmFzdCBTQSB3IGltaWVuaXUgTWluaXN0cmEgR29zcG9kYXJraTEZMBcGA1UE
+AxMQQ1ppQyBDZW50cmFzdCBTQTAeFw0wMzA0MzAxMDUwNTVaFw0wODA0MjgxMDUwNTVaMGgxCzAJ
+BgNVBAYTAlBMMR8wHQYDVQQKExZUUCBJbnRlcm5ldCBTcC4geiBvLm8uMR8wHQYDVQQDExZDQyBT
+aWduZXQgLSBDQSBLbGFzYSAzMRcwFQYDVQQFEw5OdW1lciB3cGlzdTogNDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALVdeOM62cPH2NERFxbS5FIp/HSv3fgesdVsTUFxZbGtE+/E0RMl
+KZQJHH9emx7vRYubsi4EOLCjYsCOTFvgGRIpZzx7R7T5c0Di5XFkRU4gjBl7aHJoKb5SLzGlWdoX
+GsekVtl6keEACrizV2EafqjI8cnBWY7OxQ1ooLQp5AeFjXg+5PT0lO6TUZAubqjFbhVbxSWjqvdj
+93RGfyYE76MnNn4c2xWySD07n7uno06TC0IJe6+3WSX1h+76VsIFouWBXOoM7cxxiLjoqdBVu24+
+P8e81SukE7qEvOwDPmk9ZJFtt1nBNg8a1kaixcljrA/43XwOPz6qnJ+cIj/xywECAwEAAaOCAQow
+ggEGMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMDMGA1UdIAEB/wQpMCcwJQYEVR0g
+ADAdMBsGCCsGAQUFBwIBFg93d3cuY2VudHJhc3QucGwwgY4GA1UdIwSBhjCBg4AU2a7r85Cp1iJN
+W0Ca1LR6VG3996ShZaRjMGExCzAJBgNVBAYTAlBMMTcwNQYDVQQKEy5DWmlDIENlbnRyYXN0IFNB
+IHcgaW1pZW5pdSBNaW5pc3RyYSBHb3Nwb2RhcmtpMRkwFwYDVQQDExBDWmlDIENlbnRyYXN0IFNB
+ggQ9/0sQMB0GA1UdDgQWBBR7Y8wZkHq0zrY7nn1tFSdQ0PlJuTANBgkqhkiG9w0BAQUFAAOCAQEA
+ldt/svO5c1MU08FKgrOXCGEbEPbQxhpM0xcd6Iv3dCo6qugEgjEs9Qm5CwUNKMnFsvR27cJWUvZb
+MVcvwlwCwclOdwF6u/QRS8bC2HYErhYo9bp9yuxxzuow2A94c5fPqfVrjXy+vDouchAm6+A5Wjzv
+J8wxVFDCs+9iGACmyUWr/JGXCYiQIbQkwlkRKHHlan9ymKf1NvIej/3EpeT8fKr6ywxGuhAfqofW
+pg3WJY/RCB4lTzD8vZGNwfMFGkWhJkypad3i9w3lGmDVpsHaWtCgGfd0H7tUtWPkP+t7EjIRCD9J
+HYnTR+wbbewc5vOI+UobR15ynGfFIaSIiMTVtQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEejCCA2KgAwIBAgIEP4vk6TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJQ
+TDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2Vu
+dHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MR8wHQYDVQQDExZDQyBTaWduZXQgLSBD
+QSBLbGFzYSAyMB4XDTAzMTAxNDExNTgyMloXDTE3MDQxODEyNTMwN1owdzELMAkG
+A1UEBhMCUEwxHzAdBgNVBAoTFlRQIEludGVybmV0IFNwLiB6IG8uby4xJDAiBgNV
+BAsTG0NlbnRydW0gQ2VydHlmaWthY2ppIFNpZ25ldDEhMB8GA1UEAxMYQ0MgU2ln
+bmV0IC0gT0NTUCBLbGFzYSAyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo
+VCsaBStblXQYVNthe3dvaCrfvKpPXngh4almm988iIlEv9CVTaAdCfaJNihvA+Vs
+Qw8++ix1VqteMQE474/MV/YaXigP0Zr0QB+g+/7PWVlv+5U9Gzp9+Xx4DJay8AoI
+iB7Iy5Qf9iZiHm5BiPRIuUXT4ZRbZRYPh0/76vgRsQIDAQABo4IBkjCCAY4wDgYD
+VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMJMEEGA1UdHwQ6MDgwNqA0
+oDKGMGh0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9jcmwva2xhc2Ey
+LmNybDCB2AYDVR0gBIHQMIHNMIHKBg4rBgEEAb4/AoFICgwBADCBtzBsBggrBgEF
+BQcCAjBgGl5DZXJ0eWZpa2F0IHd5ZGFueSB6Z29kbmllIHogZG9rdW1lbnRlbSAi
+UG9saXR5a2EgQ2VydHlmaWthY2ppIC0gQ2VydHlmaWthdHkgcmVzcG9uZGVyb3cg
+T0NTUCIuMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnNpZ25ldC5wbC9yZXBvenl0
+b3JpdW0vZG9rdW1lbnR5L3BjX29jc3BfMV8wLnBkZjAfBgNVHSMEGDAWgBS7RQZS
+C8uBzSlUs7x8QUzNBw6MJTAdBgNVHQ4EFgQUKEVrOY7cEHvsVgvoyZdytlbtgwEw
+CQYDVR0TBAIwADANBgkqhkiG9w0BAQUFAAOCAQEAQrRg5MV6dxr0HU2IsLInxhvt
+iUVmSFkIUsBCjzLoewOXA16d2oDyHhI/eE+VgAsp+2ANjZu4xRteHIHoYMsN218M
+eD2MLRsYS0U9xxAFK9gDj/KscPbrrdoqLvtPSMhUb4adJS9HLhvUe6BicvBf3A71
+iCNe431axGNDWKnpuj2KUpj4CFHYsWCXky847YtTXDjri9NIwJJauazsrSjK+oXp
+ngRS506mdQ7vWrtApkh8zhhWp7duCkjcCo1O8JxqYr2qEW1fXmgOISe010v2mmuv
+hHxPyVwoAU4KkOw0nbXZn53yak0is5+XmAjh0wWue44AssHrjC9nUh3mkLt6eQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEezCCA2OgAwIBAgIEP4vnLzANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJQ
+TDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEfMB0GA1UEAxMWQ0Mg
+U2lnbmV0IC0gQ0EgS2xhc2EgMzEXMBUGA1UEBRMOTnVtZXIgd3Bpc3U6IDQwHhcN
+MDMxMDE0MTIwODAwWhcNMDgwNDI4MTA1MDU1WjB3MQswCQYDVQQGEwJQTDEfMB0G
+A1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBD
+ZXJ0eWZpa2FjamkgU2lnbmV0MSEwHwYDVQQDExhDQyBTaWduZXQgLSBPQ1NQIEts
+YXNhIDMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM/9GwvARNuCVN+PqZmO
+4FqH8vTqhenUyqRkmAVT4YhLu0a9AXeLAYVDu+NTkYzsAUMAfu55rIKHNLlm6WbF
+KvLiKKz4p4pbUr+ToPcwl/TDotidloUdBAxDg0SL+PmQqACZDe3seJho2IYf2vDL
+/G4TLMbKmNB0mlWFuN0f4fJNAgMBAAGjggGgMIIBnDAOBgNVHQ8BAf8EBAMCB4Aw
+EwYDVR0lBAwwCgYIKwYBBQUHAwkwTwYDVR0fBEgwRjBEoEKgQIY+aHR0cDovL3d3
+dy5zaWduZXQucGwva3dhbGlmaWtvd2FuZS9yZXBvenl0b3JpdW0vY3JsL2tsYXNh
+My5jcmwwgdgGA1UdIASB0DCBzTCBygYOKwYBBAG+PwKCLAoCAQAwgbcwbAYIKwYB
+BQUHAgIwYBpeQ2VydHlmaWthdCB3eWRhbnkgemdvZG5pZSB6IGRva3VtZW50ZW0g
+IlBvbGl0eWthIENlcnR5ZmlrYWNqaSAtIENlcnR5ZmlrYXR5IHJlc3BvbmRlcm93
+IE9DU1AiLjBHBggrBgEFBQcCARY7aHR0cDovL3d3dy5zaWduZXQucGwvcmVwb3p5
+dG9yaXVtL2Rva3VtZW50eS9wY19vY3NwXzFfMC5wZGYwHwYDVR0jBBgwFoAUe2PM
+GZB6tM62O559bRUnUND5SbkwHQYDVR0OBBYEFG4jnCMvBALRQXtmDn9TyXQ/EKP+
+MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADggEBACXrKG5Def5lpRwmZom3UEDq
+bl7y4U3qomG4B+ok2FVZGgPZti+ZgvrenPj7PtbYCUBPsCSTNrznKinoT3gD9lQQ
+xkEHwdc6VD1GlFp+qI64u0+wS9Epatrdf7aBnizrOIB4LJd4E2TWQ6trspetjMIU
+upyWls1BmYUxB91R7QkTiAUSNZ87s3auhZuG4f0V0JLVCcg2rn7AN1rfMkgxCbHk
+GxiQbYWFljl6aatxR3odnnzVUe1I8uoY2JXpmmUcOG4dNGuQYziyKG3mtXCQWvug
+5qi9Mf3KUh1oSTKx6HfLjjNl1+wMB5Mdb8LF0XyZLdJM9yIZh7SBRsYm9QiXevY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFGjCCBAKgAwIBAgIEPL7eEDANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDIwNDE4MTQ1NDA4WhcNMjYw
+OTIxMTU0MjE5WjB2MQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MSAwHgYDVQQDExdDQyBTaWdu
+ZXQgLSBQQ0EgS2xhc2EgMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM7BrBlbN5ma
+M5eg0BOTqoZ+9NBDvU8Lm5rTdrMswFTCathzpVVLK/JD4K3+4oCZ9SRAspEXE4gvwb08ASY6w5s+
+HpRkeJw8YzMFR5kDZD5adgnCAy4vDfIXYZgppXPaTQ8wnfUZ7BZ7Zfa7QBemUIcJIzJBB0UqgtxW
+Ceol9IekpBRVmuuSA6QG0Jkm+pGDJ05yj2eQG8jTcBENM7sVA8rGRMyFA4skSZ+D0OG6FS2xC1i9
+JyN0ag1yII/LPx8HK5J4W9MaPRNjAEeaa2qI9EpchwrOxnyVbQfSedCG1VRJfAsE/9tT9CMUPZ3x
+W20QjQcSZJqVcmGW9gVsXKQOVLsCAwEAAaOCAbMwggGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMIIBBAYDVR0gBIH8MIH5MIH2Bg0rBgEEAb4/AgEKAQEBMIHkMIGaBggrBgEFBQcC
+AjCBjRqBikNlcnR5ZmlrYXQgd3lzdGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0
+eWthIENlcnR5ZmlrYWNqaSBkbGEgUm9vdENBIi4gQ2VydHlmaWthdCB3eXN0YXdpb255IHByemV6
+IFJvb3RDQSB3IGhpZXJhcmNoaWkgQ0MgU2lnbmV0LjBFBggrBgEFBQcCARY5aHR0cDovL3d3dy5z
+aWduZXQucGwvcmVwb3p5dG9yaXVtL2Rva3VtZW50eS9wY19yb290Y2EudHh0MEQGA1UdHwQ9MDsw
+OaA3oDWGM2h0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9yb290Y2Evcm9vdGNhLmNy
+bDAfBgNVHSMEGDAWgBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAdBgNVHQ4EFgQUwGxGyl2CfpYHRonE
+82AVXO08kMIwDQYJKoZIhvcNAQEFBQADggEBABp1TAUsa+BeVWg4cjowc8yTJ5XN3GvN96GObMkx
+UGY7U9kVrLI71xBgoNVyzXTiMNDBvjh7vdPWjpl5SDiRpnnKiOFXA43HvNWzUaOkTu1mxjJsZsan
+ot1Xt6j0ZDC+03FjLHdYMyM9kSWp6afb4980EPYZCcSzgM5TOGfJmNii5Tq468VFKrX+52Aou1G2
+2Ohu+EEOlOrG7ylKv1hHUJJCjwN0ZVEIn1nDbrU9FeGCz8J9ihVUvnENEBbBkU37PWqWuHitKQDV
+tcwTwJJdR8cmKq3NmkwAm9fPacidQLpaw0WkuGrS+fEDhu1Nhy9xELP6NA9GRTCNxm/dXlcwnmY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFGjCCBAKgAwIBAgIEPV0tNDANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDIwODE2MTY0OTU2WhcNMjYw
+OTIxMTU0MjE5WjB2MQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MSAwHgYDVQQDExdDQyBTaWdu
+ZXQgLSBQQ0EgS2xhc2EgMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALN3LanJtdue
+Ne6geWUTFENa+lEuzqELcoqhYB+a/tJcPEkc6TX/bYPzalRRjqs+quMP6KZTU0DixOrV+K7iWaqA
+iQ913HX5IBLmKDCrTVW/ZvSDpiBKbxlHfSNuJxAuVT6HdbzK7yAW38ssX+yS2tZYHZ5FhZcfqzPE
+OpO94mAKcBUhk6T/ki0evXX/ZvvktwmF3hKattzwtM4JMLurAEl8SInyEYULw5JdlfcBez2Tg6Db
+w34hA1A+ckTwhxzecrB8TUe2BnQKOs9vr2cCACpFFcOmPkM0Drtjctr1QHm1tYSqRFRf9VcV5tfC
+3P8QqoK4ONjtLPHc9x5NE1uK/FMCAwEAAaOCAbMwggGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMIIBBAYDVR0gBIH8MIH5MIH2Bg0rBgEEAb4/AgEKAQECMIHkMIGaBggrBgEFBQcC
+AjCBjRqBikNlcnR5ZmlrYXQgd3lzdGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0
+eWthIENlcnR5ZmlrYWNqaSBkbGEgUm9vdENBIi4gQ2VydHlmaWthdCB3eXN0YXdpb255IHByemV6
+IFJvb3RDQSB3IGhpZXJhcmNoaWkgQ0MgU2lnbmV0LjBFBggrBgEFBQcCARY5aHR0cDovL3d3dy5z
+aWduZXQucGwvcmVwb3p5dG9yaXVtL2Rva3VtZW50eS9wY19yb290Y2EudHh0MEQGA1UdHwQ9MDsw
+OaA3oDWGM2h0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9yb290Y2Evcm9vdGNhLmNy
+bDAfBgNVHSMEGDAWgBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAdBgNVHQ4EFgQUXvthcPHlH5BgGhlM
+ErJNXWlhlgAwDQYJKoZIhvcNAQEFBQADggEBACIce95Mvn710KCAISA0CuHD4aznTU6pLoCDShW4
+7OR+GTpJUm1coTcUqlBHV9mra4VFrBcBuOkHZoBLq/jmE0QJWnpSEULDcH9J3mF0nqO9SM+mWyJG
+dsJF/XU/7smummgjMNQXwzQTtWORF+6v5KUbWX85anO2wR+M6YTBWC55zWpWi4RG3vkHFs5Ze2oF
+JTlpuxw9ZgxTnWlwI9QR2MvEhYIUMKMOWxw1nt0kKj+5TCNQQGh/VJJ1dsiroGh/io1DOcePEhKz
+1Ag52y6Wf0nJJB9yk0sFakqZH18F7eQecQImgZyyeRtsG95leNugB3BXWCW+KxwiBrtQTXv4dTE=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEzzCCA7egAwIBAgIEO6ocGTANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDEwOTIwMTY0MjE5WhcNMjYw
+OTIxMTU0MjE5WjBxMQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MRswGQYDVQQDExJDQyBTaWdu
+ZXQgLSBSb290Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrr2vydnNpELfGW3Ks
+ARiDhJvwDtUe4AbWev+OfMc3+vA29nX8ZmIwno3gmItjo5DbUCCRiCMq5c9epcGu+kg4a3BJChVX
+REl8gVh0ST15rr3RKrSc4VgsvQzl0ZUraeQLl8JoRT5PLsUj3qwF78jUCQVckiiLVcnGfZtFCm+D
+CJXliQBDMB9XFAUEiO/DtEBs0B7wJGx7lgJeJpQUcGiaOPjcJDYOk7rNAYmmD2gWeSlepufO8luU
+YG/YDxTC4mqhRqfa4MnVO5dqy+ICj2UvUpHbZDB0KfGRibgBYeQP1kuqgIzJN4UqknVAJb0aMBSP
+l+9k2fAUdchx1njlbdcbAgMBAAGjggFtMIIBaTAPBgNVHRMBAf8EBTADAQH/MIIBBAYDVR0gBIH8
+MIH5MIH2Bg0rBgEEAb4/AgEKAQEAMIHkMIGaBggrBgEFBQcCAjCBjRqBikNlcnR5ZmlrYXQgd3lz
+dGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0eWthIENlcnR5ZmlrYWNqaSBkbGEg
+Um9vdENBIi4gQ2VydHlmaWthdCB3eXN0YXdpb255IHByemV6IFJvb3RDQSB3IGhpZXJhcmNoaWkg
+Q0MgU2lnbmV0LjBFBggrBgEFBQcCARY5aHR0cDovL3d3dy5zaWduZXQucGwvcmVwb3p5dG9yaXVt
+L2Rva3VtZW50eS9wY19yb290Y2EudHh0MB0GA1UdDgQWBBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAf
+BgNVHSMEGDAWgBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcN
+AQEFBQADggEBAGnY5QmYqnnO9OqFOWZxxb25UHRnaRF6IV9aaGit5BZufZj2Tq3v8L3SgE34GOoI
+cdRMMG5JEpEU4mN/Ef3oY6Eo+7HfqaPHI4KFmbDSPiK5s+wmf+bQSm0Yq5/h4ZOdcAESlLQeLSt1
+CQk2JoKQJ6pyAf6xJBgWEIlm4RXE4J3324PUiOp83kW6MDvaa1xY976WyInr4rwoLgxVl11LZeKW
+ha0RJJxJgw/NyWpKG7LWCm1fglF8JH51vZNndGYq1iKtfnrIOvLZq6bzaCiZm1EurD8HE6P7pmAB
+KK6o3C2OXlNfNIgwkDN/cDqk5TYsTkrpfriJPdxXBH8hQOkW89g=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/TCCA2agAwIBAgIEP4/gkTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MR8wHQYDVQQDExZDQyBTaWduZXQgLSBDQSBLbGFzYSAxMB4XDTAzMTAxNzEyMjkwMloX
+DTExMDkyMzExMTgxN1owdjELMAkGA1UEBhMCUEwxHzAdBgNVBAoTFlRQIEludGVybmV0IFNwLiB6
+IG8uby4xJDAiBgNVBAsTG0NlbnRydW0gQ2VydHlmaWthY2ppIFNpZ25ldDEgMB4GA1UEAxMXQ0Mg
+U2lnbmV0IC0gVFNBIEtsYXNhIDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOJYrISEtSsd
+uHajROh5/n7NGrkpYTT9NEaPe9+ucuQ37KxIbfJwXJjgUc1dw4wCkcQ12FJarD1X6mSQ4cfN/60v
+LfKI5ZD4nhJTMKlAj1pX9ScQ/MuyvKStCbn5WTkjPhjRAM0tdwXSnzuTEunfw0Oup559y3Iqxg1c
+ExflB6cfAgMBAAGjggGXMIIBkzBBBgNVHR8EOjA4MDagNKAyhjBodHRwOi8vd3d3LnNpZ25ldC5w
+bC9yZXBvenl0b3JpdW0vY3JsL2tsYXNhMS5jcmwwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQM
+MAoGCCsGAQUFBwMIMIHaBgNVHSAEgdIwgc8wgcwGDSsGAQQBvj8CZAoRAgEwgbowbwYIKwYBBQUH
+AgIwYxphQ2VydHlmaWthdCB3eXN0YXdpb255IHpnb2RuaWUgeiBkb2t1bWVudGVtICJQb2xpdHlr
+YSBDZXJ0eWZpa2FjamkgQ0MgU2lnbmV0IC0gWm5ha293YW5pZSBjemFzZW0iLjBHBggrBgEFBQcC
+ARY7aHR0cDovL3d3dy5zaWduZXQucGwvcmVwb3p5dG9yaXVtL2Rva3VtZW50eS9wY190c2ExXzJf
+MS5wZGYwHwYDVR0jBBgwFoAUw4Me1Vl3VPtN+1dH+cQjXNHnieMwHQYDVR0OBBYEFJdDwEqtcavO
+Yd9u9tej53vWXwNBMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADgYEAnpiQkqLCJQYXUrqMHUEz
++z3rOqS0XzSFnVVLhkVssvXc8S3FkJIiQTUrkScjI4CToCzujj3EyfNxH6yiLlMbskF8I31JxIeB
+vueqV+s+o76CZm3ycu9hb0I4lswuxoT+q5ZzPR8Irrb51rZXlolR+7KtwMg4sFDJZ8RNgOf7tbA=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGFTCCBX6gAwIBAgIBEDANBgkqhkiG9w0BAQUFADCBvjELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0luZGlhbmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UE
+ChMfU29mdHdhcmUgaW4gdGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKaG9z
+dG1hc3RlcjEgMB4GA1UEAxMXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxJTAjBgkq
+hkiG9w0BCQEWFmhvc3RtYXN0ZXJAc3BpLWluYy5vcmcwHhcNMDYwODE2MjA0NjUy
+WhcNMTYwODEzMjA0NjUyWjCBvDELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0luZGlh
+bmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UEChMfU29mdHdhcmUgaW4g
+dGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKSG9zdG1hc3RlcjEeMBwGA1UE
+AxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MSUwIwYJKoZIhvcNAQkBFhZob3N0bWFz
+dGVyQHNwaS1pbmMub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
+uTlCOrOYvCe0Qszlz90rVIQkS2UDa7R+qCTkRE5ipKC+3OZXGgv2EOYk/IOuCE3D
+8+tlTAl8MwvmwOHgL0GWnjoluR95narhNiV9Px8xyL2Z8kctwrWXbEii0Ot48VcN
+IQMDW5GK8Ww5puMKZr8+bXF2HFzkt8gwaCtrojcF4fLGcm3//bR13CKWbOzacANZ
+gAi00pD70ppjJU/uNVOe5HX6x/Id+50G6jwasanEzhteYJdbbHaMqrL4J6XbTwgJ
+ZciB0ifOZkwTBvQiRJpTkTWPyn/HptMbJGOwwiNbcgtWV7QVLW1GB3HYVum47Zwp
+XcXVM4Fs4XLwfx1Ti87LGdAJJYDS5K1F2prHKW5MsmFLJg61Nd6dZPRDLJLaOPEq
+0Jxa1NdfDjJ8rXOIcuLRAozjBui23vUhfOW9z4kpVIg+c+ylV2Oh/7f+fO731P7E
+AqsTFmiU7svcvkm2OKslolpnQ+pqGFLM4laQE/Q0kjogm7LiOpRssBW/ZfZ7pPLZ
+Ru0/AXrgKwQIM8mLazwlD3wHKPRElhIkxSv4qZ95zgJo6ky/7BpzZ/OAlz3BICbN
+WQhW/MBQVFiC6ivhjDq3AzgyykLShB0eIMRN0SBfTy3/aNfIJbqLmTNookXPoSP7
++mg5OVzVBvDv5ZskWSl8kJB1DzJbN+DOr/i8V3Ugl90CAwEAAaOCAZ0wggGZMB0G
+A1UdDgQWBBTON+buqfoMXkWdtvObUS11tnY77DCB6wYDVR0jBIHjMIHggBQHrehB
+HX+91r8bgXo/jEuI3gTS+qGBxKSBwTCBvjELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
+B0luZGlhbmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UEChMfU29mdHdh
+cmUgaW4gdGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKaG9zdG1hc3RlcjEg
+MB4GA1UEAxMXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxJTAjBgkqhkiG9w0BCQEW
+Fmhvc3RtYXN0ZXJAc3BpLWluYy5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zARBglg
+hkgBhvhCAQEEBAMCAQYwCQYDVR0SBAIwADArBglghkgBhvhCAQ0EHhYcVGlueUNB
+IEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAhBgNVHREEGjAYgRZob3N0bWFzdGVyQHNw
+aS1pbmMub3JnMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQUFAAOBgQA5HQcqwPE/
+RaN8cb8H1G34rkvSEsj4l8UIivMFEWIKnF3SQT8KVcD7j/eJuhMazwRlTs8Rnu6V
+/uTC10w7SS6gELwDqAzR4PiXTzfkW8OJemyQn6JWXKfq2pR1n4fvwEn7htjeNS69
+iFKlFXyE9j2jhGaps1CKHfe1YX0MuwO4Jw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEFTCCA36gAwIBAgIBADANBgkqhkiG9w0BAQQFADCBvjELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0luZGlhbmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UE
+ChMfU29mdHdhcmUgaW4gdGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKaG9z
+dG1hc3RlcjEgMB4GA1UEAxMXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxJTAjBgkq
+hkiG9w0BCQEWFmhvc3RtYXN0ZXJAc3BpLWluYy5vcmcwHhcNMDMwMTE1MTYyOTE3
+WhcNMDcwMTE0MTYyOTE3WjCBvjELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0luZGlh
+bmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UEChMfU29mdHdhcmUgaW4g
+dGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKaG9zdG1hc3RlcjEgMB4GA1UE
+AxMXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxJTAjBgkqhkiG9w0BCQEWFmhvc3Rt
+YXN0ZXJAc3BpLWluYy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPB6
+rdoiLR3RodtM22LMcfwfqb5OrJNl7fwmvskgF7yP6sdD2bOfDIXhg9852jhY8/kL
+VOFe1ELAL2OyN4RAxk0rliZQVgeTgqvgkOVIBbNwgnjN6mqtuWzFiPL+NXQExq40
+I3whM+4lEiwSHaV+MYxWanMdhc+kImT50LKfkxcdAgMBAAGjggEfMIIBGzAdBgNV
+HQ4EFgQUB63oQR1/vda/G4F6P4xLiN4E0vowgesGA1UdIwSB4zCB4IAUB63oQR1/
+vda/G4F6P4xLiN4E0vqhgcSkgcEwgb4xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdJ
+bmRpYW5hMRUwEwYDVQQHEwxJbmRpYW5hcG9saXMxKDAmBgNVBAoTH1NvZnR3YXJl
+IGluIHRoZSBQdWJsaWMgSW50ZXJlc3QxEzARBgNVBAsTCmhvc3RtYXN0ZXIxIDAe
+BgNVBAMTF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MSUwIwYJKoZIhvcNAQkBFhZo
+b3N0bWFzdGVyQHNwaS1pbmMub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
+AQEEBQADgYEAm/Abn8c2y1nO3fgpAIslxvi9iNBZDhQtJ0VQZY6wgSfANyDOR4DW
+iexO/AlorB49KnkFS7TjCAoLOZhcg5FaNiKnlstMI5krQmau1Qnb/vGSNsE/UGms
+1ts+QYPUs0KmGEAFUri2XzLy+aQo9Kw74VBvqnxvaaMeY5yMcKNOieY=
+-----END CERTIFICATE-----
diff --git a/gcr/tests/test-data/cacert.org.pem b/gcr/tests/test-data/cacert.org.pem
new file mode 100644
index 00000000..e7dfc829
--- /dev/null
+++ b/gcr/tests/test-data/cacert.org.pem
@@ -0,0 +1,41 @@
+-----BEGIN CERTIFICATE-----
+MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290
+IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB
+IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA
+Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO
+BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi
+MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ
+ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ
+8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6
+zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y
+fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7
+w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc
+G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k
+epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q
+laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ
+QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU
+fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826
+YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w
+ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY
+gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe
+MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0
+IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy
+dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw
+czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0
+dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl
+aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC
+AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg
+b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB
+ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc
+nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg
+18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c
+gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl
+Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY
+sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T
+SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF
+CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum
+GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk
+zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW
+omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD
+-----END CERTIFICATE-----
diff --git a/gcr/tests/test-data/der-certificate.crt b/gcr/tests/test-data/der-certificate.crt
new file mode 100644
index 00000000..56fa8491
--- /dev/null
+++ b/gcr/tests/test-data/der-certificate.crt
Binary files differ
diff --git a/gcr/tests/test-data/der-dsa-1024.key b/gcr/tests/test-data/der-dsa-1024.key
new file mode 100644
index 00000000..61af5251
--- /dev/null
+++ b/gcr/tests/test-data/der-dsa-1024.key
Binary files differ
diff --git a/gcr/tests/test-data/der-pkcs8-PBE-MD5-DES.key b/gcr/tests/test-data/der-pkcs8-PBE-MD5-DES.key
new file mode 100644
index 00000000..71b7d61b
--- /dev/null
+++ b/gcr/tests/test-data/der-pkcs8-PBE-MD5-DES.key
Binary files differ
diff --git a/gcr/tests/test-data/der-pkcs8-PBE-SHA1-3DES.key b/gcr/tests/test-data/der-pkcs8-PBE-SHA1-3DES.key
new file mode 100644
index 00000000..1c37fe10
--- /dev/null
+++ b/gcr/tests/test-data/der-pkcs8-PBE-SHA1-3DES.key
Binary files differ
diff --git a/gcr/tests/test-data/der-pkcs8-PBE-SHA1-DES.key b/gcr/tests/test-data/der-pkcs8-PBE-SHA1-DES.key
new file mode 100644
index 00000000..99896139
--- /dev/null
+++ b/gcr/tests/test-data/der-pkcs8-PBE-SHA1-DES.key
Binary files differ
diff --git a/gcr/tests/test-data/der-pkcs8-PBE-SHA1-RC2-40.key b/gcr/tests/test-data/der-pkcs8-PBE-SHA1-RC2-40.key
new file mode 100644
index 00000000..820926cc
--- /dev/null
+++ b/gcr/tests/test-data/der-pkcs8-PBE-SHA1-RC2-40.key
Binary files differ
diff --git a/gcr/tests/test-data/der-pkcs8-PBE-SHA1-RC4-128.key b/gcr/tests/test-data/der-pkcs8-PBE-SHA1-RC4-128.key
new file mode 100644
index 00000000..2888450c
--- /dev/null
+++ b/gcr/tests/test-data/der-pkcs8-PBE-SHA1-RC4-128.key
Binary files differ
diff --git a/gcr/tests/test-data/der-pkcs8-dsa.key b/gcr/tests/test-data/der-pkcs8-dsa.key
new file mode 100644
index 00000000..8b61684e
--- /dev/null
+++ b/gcr/tests/test-data/der-pkcs8-dsa.key
Binary files differ
diff --git a/gcr/tests/test-data/der-pkcs8-encrypted-pkcs5.key b/gcr/tests/test-data/der-pkcs8-encrypted-pkcs5.key
new file mode 100644
index 00000000..68c6b4a5
--- /dev/null
+++ b/gcr/tests/test-data/der-pkcs8-encrypted-pkcs5.key
Binary files differ
diff --git a/gcr/tests/test-data/der-pkcs8-v2-des.key b/gcr/tests/test-data/der-pkcs8-v2-des.key
new file mode 100644
index 00000000..4cd80348
--- /dev/null
+++ b/gcr/tests/test-data/der-pkcs8-v2-des.key
Binary files differ
diff --git a/gcr/tests/test-data/der-pkcs8-v2-des3.key b/gcr/tests/test-data/der-pkcs8-v2-des3.key
new file mode 100644
index 00000000..9e2999c8
--- /dev/null
+++ b/gcr/tests/test-data/der-pkcs8-v2-des3.key
Binary files differ
diff --git a/gcr/tests/test-data/der-pkcs8.key b/gcr/tests/test-data/der-pkcs8.key
new file mode 100644
index 00000000..16505632
--- /dev/null
+++ b/gcr/tests/test-data/der-pkcs8.key
Binary files differ
diff --git a/gcr/tests/test-data/der-rsa-1024.key b/gcr/tests/test-data/der-rsa-1024.key
new file mode 100644
index 00000000..878fda5b
--- /dev/null
+++ b/gcr/tests/test-data/der-rsa-1024.key
Binary files differ
diff --git a/gcr/tests/test-data/email.p12 b/gcr/tests/test-data/email.p12
new file mode 100644
index 00000000..d45d0f8d
--- /dev/null
+++ b/gcr/tests/test-data/email.p12
Binary files differ
diff --git a/gcr/tests/test-data/pem-dsa-1024.key b/gcr/tests/test-data/pem-dsa-1024.key
new file mode 100644
index 00000000..ccb1e7b9
--- /dev/null
+++ b/gcr/tests/test-data/pem-dsa-1024.key
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBuwIBAAKBgQCQ7Atgc1g5x1Tq+PZLsD/DU5jWl3K/rlQAed6i06Yfr/snYwoD
+igGj0M1ioQdFpXSifstGL08IhbecYbvpVKYKKWaK1Uu6XAenL9ixEFJJZwsznfLF
+nmSkcGTvzwtyNsXHLNVc6zKRdDC+yaAD1OSE+6qE15Vxs41rWslbtz4/ewIVAPoh
+ShOFwhv+uq2rJAokMMYH1WJxAoGALeBXUfXa7pfz1DxUWVo+lKCAco8MZsmK6+1X
+YvarFVgC2DWerR3h7DakWfvu6kjlm55qjLT1KVk2s8yIGl2VfHM5F14s/+DzDTcR
+5DDbZkjC60dKoQpKMpdFBTH/LHxpUSIMnURra2sPACYuHr6zzIYUdqpRjMVVyav5
+5fOQI/wCgYBUc0RR23nU7t8LvOvUO7bLt7hYRgO5VwgAdd0xjrWwJm1LINxe/zdr
+38TqKYOx9/AqOe1MYZ7WhxJyn/87fGlq3RttdI9WpLS+xcQ4XlKEI6O4iuZebVUA
++Xg556SGJVmCGJw7T6jZQzjHbw5cr8mjCh7XKLufIJHVlOMlCgnqAAIVAIdvhPcJ
+1REI37DL+h8cVpwJxBPs
+-----END DSA PRIVATE KEY-----
diff --git a/gcr/tests/test-data/pem-pkcs8.key b/gcr/tests/test-data/pem-pkcs8.key
new file mode 100644
index 00000000..1f0d5edb
--- /dev/null
+++ b/gcr/tests/test-data/pem-pkcs8.key
@@ -0,0 +1,17 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIf4lXzcHXqAICAggA
+MBQGCCqGSIb3DQMHBAhV8iKHmoUJDQSCAoDK4fY/dfIzOof1QlYV8mhr0OmhaIAe
+aaDz4rER6+OGwq7WQ32ErmHBiJRI8oQRrxy4oHTXx8TR+JxQT1io9V9jjXZCS8HT
+Ek0J69pgjPdIUnlZYV1GdJUJq7tWxTKJJFr1tsB0XzgugQLYcui4ikrZ6rHE9PVh
+SXFIEbns42xHIvtQ/j3c6ScS63UNpSsKe5pWRNgHUHpjSGPAaZogOdMfStmj3tdN
+nIH3mjIM8uisN9nyPV42aziZi5mO9F5JHlNp1H5pDAUWOKkKoXw27MgXnxvSkLAX
+HSgpPuK5ejqXu4nkciwHnr4eULV0biwqpmYEWxC4aNuog/t3DoL1gkC74J07jjUU
+3gZwxjKsAWfIKnNURsHlBAcYClug5ZWs9ru/4sc27bNV9qRKbHcgGD+nzfTXxZZ2
+BG5eSBVpYT2Jzdkpw8puSHwNW5mQcuY4E+ZEERFsW8q1raPSgYVk5rM8TiVdTD5x
+UwQG7ADidVSVTR5KGV8wmonbuDu9b61GgU7VBslEK9Lsd92FO0/u09FF6DgCcICD
+yRJ0nM0o9S9FI1lqKoxAdgWyFwrKkBVZ05IDHw+jS6r7D/2irYetPJmVO/dvi87r
+aaLvyvzU3nz4LiWHmSl2AgDanjDcxtjGoWR2cc+J0v3r/+VEOctrc5EuAnlhl9Yj
+kFcfZDfqthCNb49qNGDgQMGrKTt+Dvrka/ZZxXBN3v96L3K4O32j9c97sZ8LEFaI
+RHl91Mx2okjrT9q2+Gef2UR9XEzuFEBRBqxktltlrIQIncn6Xx/TjEvIhf2Dgo4W
+isaAwTazyZrzATTWR1JS6Hdm7Eb/nCEHAFmVUbQm0d5BDfKmnRsNaaZK
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/gcr/tests/test-data/pem-rsa-enc.key b/gcr/tests/test-data/pem-rsa-enc.key
new file mode 100644
index 00000000..65439fe6
--- /dev/null
+++ b/gcr/tests/test-data/pem-rsa-enc.key
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,24522D4CE5F5CD7B
+
+2H/8j0HYUya7LWEUxpgjp/LVcCX7yZB7SoREdoJdcqJEBUMWVxU/2OfVB8EZupmy
+7YHcnn5v1JwwtmAXAtqM9JGlvNWaRr1m4zDrhJn1fY3tu8YGtMR49IOZmOUBK+X+
+IxWAwaFDqLntuGZZnAmRJtgFVYVABEs5yM9zgoCGDaU4WMK3caD7Jnw8jH5m0nqQ
+XiQ1y1dHxFJmAgG0b5h2z7zjQTmmXd3IhXqSqsE/9ryruCCYa0Z7aAN5oAmO89I9
+gOyy3J4h76mTNFfF5btV4Jllwd4LkgGOmm69UxAyUTGzwYJ5gxgB3xFzGBwpVlcu
+72PrQCrjZqZ6rj6cTPGUYzcyMtEw3Xd6mFhApqJpVRZwNWUAMMJwHl2oWwKcIxfV
+y+OftRX6kc+cunrxCkl9aKuHDoJPEq+/Uh+AEXqir+942Vull0WPyuWUjaPKR1xJ
+poYsNfHRWq+klKCggQQL6jwuVbDLhbaXfgaNBQO1XMracgfmnO1PQPw8JSQ5iOkm
+Ybt2oHAEnrEWxZGn1PfRq6Z8HAbBlQpfmG7SMJZdQjlndKA6GR+tN5krKfpj6uak
+0eklm0Nb0YcDzJ3qqHXxIimK3Kh/WRZ1hVTnX4mS9u3HNQMo5Ov6z8OQN+Q45ffi
+ZDFkVwUTEJ+iwmCG7XnxX0v8Bv5LZmAnPu95KQTp4Ds0AZ6Sp+RqxvhnCO25cgWj
++N5jHGzsDk9/Jw7rAHz8pnl3sziNBWdAk5ASPA28HCQQo5peWnWajM3Pk98+/wHY
+blTh7gw77gTake6hpiegnhNUXwGm6BXEqmyu7mPW0z5XFRb9W7bpog==
+-----END RSA PRIVATE KEY-----
diff --git a/gcr/tests/test-data/test-x509-swiss.p7b b/gcr/tests/test-data/test-x509-swiss.p7b
new file mode 100755
index 00000000..d45b9e0e
--- /dev/null
+++ b/gcr/tests/test-data/test-x509-swiss.p7b
Binary files differ
diff --git a/gcr/tests/test-data/unclient.p12 b/gcr/tests/test-data/unclient.p12
new file mode 100644
index 00000000..321b4475
--- /dev/null
+++ b/gcr/tests/test-data/unclient.p12
Binary files differ
diff --git a/gcr/tests/unit-test-parser.c b/gcr/tests/unit-test-parser.c
new file mode 100644
index 00000000..53af5c12
--- /dev/null
+++ b/gcr/tests/unit-test-parser.c
@@ -0,0 +1,136 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* unit-test-pkix-parser.c: Test PKIX parser
+
+ Copyright (C) 2007 Stefan Walter
+
+ The Gnome Keyring Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Keyring Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Stef Walter <stef@memberwebs.com>
+*/
+
+#include "config.h"
+
+#include "run-auto-test.h"
+
+#include "egg/egg-secure-memory.h"
+
+#include "gcr/gcr-parser.h"
+
+#include <glib.h>
+#include <gcrypt.h>
+#include <libtasn1.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/*
+ * Each test looks like (on one line):
+ * void unit_test_xxxxx (CuTest* cu)
+ *
+ * Each setup looks like (on one line):
+ * void unit_setup_xxxxx (void);
+ *
+ * Each teardown looks like (on one line):
+ * void unit_teardown_xxxxx (void);
+ *
+ * Tests be run in the order specified here.
+ */
+
+static GcrParser *parser = NULL;
+static const gchar* filedesc = NULL;
+
+static void
+parsed_item (GcrParser *par, gpointer user_data)
+{
+ GP11Attributes *attrs;
+ const gchar *description;
+ const gchar *label;
+
+ g_assert (GCR_IS_PARSER (par));
+ g_assert (par == parser);
+ g_assert (par == user_data);
+
+ attrs = gcr_parser_get_parsed_attributes (parser);
+ description = gcr_parser_get_parsed_description (parser);
+ label = gcr_parser_get_parsed_label (parser);
+
+ g_print ("parsed %s '%s' at: %s\n", description, label, filedesc);
+}
+
+static gboolean
+authenticate (GcrParser *par, gint state, gpointer user_data)
+{
+ g_assert (GCR_IS_PARSER (par));
+ g_assert (par == parser);
+ g_assert (par == user_data);
+
+ switch (state) {
+ case 0:
+ gcr_parser_add_password (parser, "booo");
+ return TRUE;
+ default:
+ g_printerr ("decryption didn't work for: %s", filedesc);
+ g_assert (FALSE);
+ };
+}
+
+DEFINE_SETUP(parser)
+{
+ parser = gcr_parser_new ();
+ g_signal_connect (parser, "parsed", G_CALLBACK (parsed_item), parser);
+ g_signal_connect (parser, "authenticate", G_CALLBACK (authenticate), parser);
+}
+
+DEFINE_TEARDOWN(parser)
+{
+ g_object_unref (parser);
+ parser = NULL;
+}
+
+DEFINE_TEST(parse_all)
+{
+ guchar *contents;
+ GError *err = NULL;
+ gboolean result;
+ const gchar *filename;
+ gsize len;
+ GDir *dir;
+
+ dir = g_dir_open ("test-data", 0, NULL);
+ g_assert (dir);
+
+ for (;;) {
+ filename = g_dir_read_name (dir);
+ if (!filename)
+ break;
+ if (filename[0] == '.')
+ continue;
+
+ filedesc = filename;
+ contents = test_read_testdata (filename, &len);
+
+ result = gcr_parser_parse_data (parser, contents, len, &err);
+ if (!result) {
+ g_warning ("couldn't parse file data: %s: %s",
+ filename, err && err->message ? err->message : "");
+ g_error_free (err);
+ g_assert (FALSE);
+ }
+ }
+
+ g_dir_close (dir);
+}
diff --git a/gp11/gp11.h b/gp11/gp11.h
index 65638ddd..eed9601c 100644
--- a/gp11/gp11.h
+++ b/gp11/gp11.h
@@ -21,8 +21,8 @@
Author: Stef Walter <nielsen@memberwebs.com>
*/
-#ifndef GP11_H_
-#define GP11_H_
+#ifndef GP11_H
+#define GP11_H
#include <glib.h>
#include <glib-object.h>
@@ -1431,4 +1431,4 @@ guchar* gp11_processor_close_finish (GP11Processor *proce
G_END_DECLS
-#endif /*GP11_H_*/
+#endif /*GP11_H*/
diff --git a/pkcs11/gck/Makefile.am b/pkcs11/gck/Makefile.am
index 0fc9bc3c..8b1d8103 100644
--- a/pkcs11/gck/Makefile.am
+++ b/pkcs11/gck/Makefile.am
@@ -24,8 +24,6 @@ libgck_la_SOURCES = \
gck-data-asn1.c gck-data-asn1.h \
gck-data-der.c gck-data-der.h \
gck-data-file.c gck-data-file.h \
- gck-data-openssl.c gck-data-openssl.h \
- gck-data-pem.c gck-data-pem.h \
gck-data-types.h \
gck-factory.c gck-factory.h \
gck-file-tracker.c gck-file-tracker.h \
diff --git a/pkcs11/gck/gck-crypto.c b/pkcs11/gck/gck-crypto.c
index 2e1af6ea..7a0d76e9 100644
--- a/pkcs11/gck/gck-crypto.c
+++ b/pkcs11/gck/gck-crypto.c
@@ -977,520 +977,6 @@ gck_crypto_rsa_unpad_two (guint bits, const guchar *padded,
return unpad_rsa_pkcs1 (0x02, bits, padded, n_padded, n_raw);
}
-/* -----------------------------------------------------------------------------
- * PASSWORD TO KEY/IV
- */
-
-gboolean
-gck_crypto_symkey_generate_simple (int cipher_algo, int hash_algo,
- const gchar *password, gssize n_password,
- const guchar *salt, gsize n_salt, int iterations,
- guchar **key, guchar **iv)
-{
- gcry_md_hd_t mdh;
- gcry_error_t gcry;
- guchar *digest;
- guchar *digested;
- guint n_digest;
- gint pass, i;
- gint needed_iv, needed_key;
- guchar *at_iv, *at_key;
-
- g_assert (cipher_algo);
- g_assert (hash_algo);
-
- g_return_val_if_fail (iterations >= 1, FALSE);
-
- if (!password)
- n_password = 0;
- if (n_password == -1)
- n_password = strlen (password);
-
- /*
- * If cipher algo needs more bytes than hash algo has available
- * then the entire hashing process is done again (with the previous
- * hash bytes as extra input), and so on until satisfied.
- */
-
- needed_key = gcry_cipher_get_algo_keylen (cipher_algo);
- needed_iv = gcry_cipher_get_algo_blklen (cipher_algo);
-
- gcry = gcry_md_open (&mdh, hash_algo, 0);
- if (gcry) {
- g_warning ("couldn't create '%s' hash context: %s",
- gcry_md_algo_name (hash_algo), gcry_strerror (gcry));
- return FALSE;
- }
-
- n_digest = gcry_md_get_algo_dlen (hash_algo);
- g_return_val_if_fail (n_digest > 0, FALSE);
-
- digest = gcry_calloc_secure (n_digest, 1);
- g_return_val_if_fail (digest, FALSE);
- if (key) {
- *key = gcry_calloc_secure (needed_key, 1);
- g_return_val_if_fail (*key, FALSE);
- }
- if (iv)
- *iv = g_new0 (guchar, needed_iv);
-
- at_key = key ? *key : NULL;
- at_iv = iv ? *iv : NULL;
-
- for (pass = 0; TRUE; ++pass) {
- gcry_md_reset (mdh);
-
- /* Hash in the previous buffer on later passes */
- if (pass > 0)
- gcry_md_write (mdh, digest, n_digest);
-
- if (password)
- gcry_md_write (mdh, password, n_password);
- if (salt && n_salt)
- gcry_md_write (mdh, salt, n_salt);
- gcry_md_final (mdh);
- digested = gcry_md_read (mdh, 0);
- g_return_val_if_fail (digested, FALSE);
- memcpy (digest, digested, n_digest);
-
- for (i = 1; i < iterations; ++i) {
- gcry_md_reset (mdh);
- gcry_md_write (mdh, digest, n_digest);
- gcry_md_final (mdh);
- digested = gcry_md_read (mdh, 0);
- g_return_val_if_fail (digested, FALSE);
- memcpy (digest, digested, n_digest);
- }
-
- /* Copy as much as possible into the destinations */
- i = 0;
- while (needed_key && i < n_digest) {
- if (at_key)
- *(at_key++) = digest[i];
- needed_key--;
- i++;
- }
- while (needed_iv && i < n_digest) {
- if (at_iv)
- *(at_iv++) = digest[i];
- needed_iv--;
- i++;
- }
-
- if (needed_key == 0 && needed_iv == 0)
- break;
- }
-
- gcry_free (digest);
- gcry_md_close (mdh);
-
- return TRUE;
-}
-
-gboolean
-gck_crypto_symkey_generate_pbe (int cipher_algo, int hash_algo, const gchar *password,
- gssize n_password, const guchar *salt, gsize n_salt, int iterations,
- guchar **key, guchar **iv)
-{
- gcry_md_hd_t mdh;
- gcry_error_t gcry;
- guchar *digest;
- guchar *digested;
- guint i, n_digest;
- gint needed_iv, needed_key;
-
- g_assert (cipher_algo);
- g_assert (hash_algo);
-
- g_return_val_if_fail (iterations >= 1, FALSE);
-
- if (!password)
- n_password = 0;
- if (n_password == -1)
- n_password = strlen (password);
-
- /*
- * We only do one pass here.
- *
- * The key ends up as the first needed_key bytes of the hash buffer.
- * The iv ends up as the last needed_iv bytes of the hash buffer.
- *
- * The IV may overlap the key (which is stupid) if the wrong pair of
- * hash/cipher algorithms are chosen.
- */
-
- n_digest = gcry_md_get_algo_dlen (hash_algo);
- g_return_val_if_fail (n_digest > 0, FALSE);
-
- needed_key = gcry_cipher_get_algo_keylen (cipher_algo);
- needed_iv = gcry_cipher_get_algo_blklen (cipher_algo);
- if (needed_iv + needed_key > 16 || needed_iv + needed_key > n_digest) {
- g_warning ("using PBE symkey generation with %s using an algorithm that needs "
- "too many bytes of key and/or IV: %s",
- gcry_cipher_algo_name (hash_algo),
- gcry_cipher_algo_name (cipher_algo));
- return FALSE;
- }
-
- gcry = gcry_md_open (&mdh, hash_algo, 0);
- if (gcry) {
- g_warning ("couldn't create '%s' hash context: %s",
- gcry_md_algo_name (hash_algo), gcry_strerror (gcry));
- return FALSE;
- }
-
- digest = gcry_calloc_secure (n_digest, 1);
- g_return_val_if_fail (digest, FALSE);
- if (key) {
- *key = gcry_calloc_secure (needed_key, 1);
- g_return_val_if_fail (*key, FALSE);
- }
- if (iv)
- *iv = g_new0 (guchar, needed_iv);
-
- if (password)
- gcry_md_write (mdh, password, n_password);
- if (salt && n_salt)
- gcry_md_write (mdh, salt, n_salt);
- gcry_md_final (mdh);
- digested = gcry_md_read (mdh, 0);
- g_return_val_if_fail (digested, FALSE);
- memcpy (digest, digested, n_digest);
-
- for (i = 1; i < iterations; ++i)
- gcry_md_hash_buffer (hash_algo, digest, digest, n_digest);
-
- /* The first x bytes are the key */
- if (key) {
- g_assert (needed_key <= n_digest);
- memcpy (*key, digest, needed_key);
- }
-
- /* The last 16 - x bytes are the iv */
- if (iv) {
- g_assert (needed_iv <= n_digest && n_digest >= 16);
- memcpy (*iv, digest + (16 - needed_iv), needed_iv);
- }
-
- gcry_free (digest);
- gcry_md_close (mdh);
-
- return TRUE;
-}
-
-static gboolean
-generate_pkcs12 (int hash_algo, int type, const gchar *utf8_password,
- gssize n_password, const guchar *salt, gsize n_salt,
- int iterations, guchar *output, gsize n_output)
-{
- gcry_mpi_t num_b1, num_ij;
- guchar *hash, *buf_i, *buf_b;
- const gchar *end_password;
- gcry_md_hd_t mdh;
- const gchar *p2;
- guchar *p;
- gsize n_hash, i;
- gunichar unich;
- gcry_error_t gcry;
-
- num_b1 = num_ij = NULL;
-
- n_hash = gcry_md_get_algo_dlen (hash_algo);
- g_return_val_if_fail (n_hash > 0, FALSE);
-
- if (!utf8_password)
- n_password = 0;
- if (n_password == -1)
- end_password = utf8_password + strlen (utf8_password);
- else
- end_password = utf8_password + n_password;
-
- gcry = gcry_md_open (&mdh, hash_algo, 0);
- if (gcry) {
- g_warning ("couldn't create '%s' hash context: %s",
- gcry_md_algo_name (hash_algo), gcry_strerror (gcry));
- return FALSE;
- }
-
- /* Reqisition me a buffer */
- hash = gcry_calloc_secure (n_hash, 1);
- buf_i = gcry_calloc_secure (1, 128);
- buf_b = gcry_calloc_secure (1, 64);
- g_return_val_if_fail (hash && buf_i && buf_b, FALSE);
-
- /* Bring in the salt */
- p = buf_i;
- if (salt) {
- for (i = 0; i < 64; ++i)
- *(p++) = salt[i % n_salt];
- } else {
- memset (p, 0, 64);
- p += 64;
- }
-
- /* Bring in the password, as 16bits per character BMP string, ie: UCS2 */
- if (utf8_password) {
- p2 = utf8_password;
- for (i = 0; i < 64; i += 2) {
-
- /* Get a character from the string */
- if (p2 < end_password) {
- unich = g_utf8_get_char (p2);
- p2 = g_utf8_next_char (p2);
-
- /* Get zero null terminator, and loop back to beginning */
- } else {
- unich = 0;
- p2 = utf8_password;
- }
-
- /* Encode the bytes received */
- *(p++) = (unich & 0xFF00) >> 8;
- *(p++) = (unich & 0xFF);
- }
- } else {
- memset (p, 0, 64);
- p += 64;
- }
-
- /* Hash and bash */
- for (;;) {
- gcry_md_reset (mdh);
-
- /* Put in the PKCS#12 type of key */
- for (i = 0; i < 64; ++i)
- gcry_md_putc (mdh, type);
-
- /* Bring in the password */
- gcry_md_write (mdh, buf_i, utf8_password ? 128 : 64);
-
- /* First iteration done */
- memcpy (hash, gcry_md_read (mdh, hash_algo), n_hash);
-
- /* All the other iterations */
- for (i = 1; i < iterations; i++)
- gcry_md_hash_buffer (hash_algo, hash, hash, n_hash);
-
- /* Take out as much as we need */
- for (i = 0; i < n_hash && n_output; ++i) {
- *(output++) = hash[i];
- --n_output;
- }
-
- /* Is that enough generated keying material? */
- if (!n_output)
- break;
-
- /* Need more bytes, do some voodoo */
- for (i = 0; i < 64; ++i)
- buf_b[i] = hash[i % n_hash];
- gcry = gcry_mpi_scan (&num_b1, GCRYMPI_FMT_USG, buf_b, 64, NULL);
- g_return_val_if_fail (gcry == 0, FALSE);
- gcry_mpi_add_ui (num_b1, num_b1, 1);
- for (i = 0; i < 128; i += 64) {
- gcry = gcry_mpi_scan (&num_ij, GCRYMPI_FMT_USG, buf_i + i, 64, NULL);
- g_return_val_if_fail (gcry == 0, FALSE);
- gcry_mpi_add (num_ij, num_ij, num_b1);
- gcry_mpi_clear_highbit (num_ij, 64 * 8);
- gcry = gcry_mpi_print (GCRYMPI_FMT_USG, buf_i + i, 64, NULL, num_ij);
- g_return_val_if_fail (gcry == 0, FALSE);
- gcry_mpi_release (num_ij);
- }
- }
-
- gcry_free (buf_i);
- gcry_free (buf_b);
- gcry_free (hash);
- gcry_mpi_release (num_b1);
- gcry_md_close (mdh);
-
- return TRUE;
-}
-
-gboolean
-gck_crypto_symkey_generate_pkcs12 (int cipher_algo, int hash_algo, const gchar *password,
- gssize n_password, const guchar *salt, gsize n_salt,
- int iterations, guchar **key, guchar **iv)
-{
- gsize n_block, n_key;
- gboolean ret = TRUE;
-
- g_return_val_if_fail (cipher_algo, FALSE);
- g_return_val_if_fail (hash_algo, FALSE);
- g_return_val_if_fail (iterations > 0, FALSE);
-
- n_key = gcry_cipher_get_algo_keylen (cipher_algo);
- n_block = gcry_cipher_get_algo_blklen (cipher_algo);
-
- if (password && !g_utf8_validate (password, n_password, NULL)) {
- g_warning ("invalid non-UTF8 password");
- g_return_val_if_reached (FALSE);
- }
-
- if (key)
- *key = NULL;
- if (iv)
- *iv = NULL;
-
- /* Generate us an key */
- if (key) {
- *key = gcry_calloc_secure (n_key, 1);
- g_return_val_if_fail (*key != NULL, FALSE);
- ret = generate_pkcs12 (hash_algo, 1, password, n_password, salt, n_salt,
- iterations, *key, n_key);
- }
-
- /* Generate us an iv */
- if (ret && iv) {
- if (n_block > 1) {
- *iv = g_malloc (n_block);
- ret = generate_pkcs12 (hash_algo, 2, password, n_password, salt, n_salt,
- iterations, *iv, n_block);
- } else {
- *iv = NULL;
- }
- }
-
- /* Cleanup in case of failure */
- if (!ret) {
- g_free (iv ? *iv : NULL);
- gcry_free (key ? *key : NULL);
- }
-
- return ret;
-}
-
-static gboolean
-generate_pbkdf2 (int hash_algo, const gchar *password, gsize n_password,
- const guchar *salt, gsize n_salt, guint iterations,
- guchar *output, gsize n_output)
-{
- gcry_md_hd_t mdh;
- guint u, l, r, i, k;
- gcry_error_t gcry;
- guchar *U, *T, *buf;
- gsize n_buf, n_hash;
-
- g_return_val_if_fail (hash_algo > 0, FALSE);
- g_return_val_if_fail (iterations > 0, FALSE);
- g_return_val_if_fail (n_output > 0, FALSE);
- g_return_val_if_fail (n_output < G_MAXUINT32, FALSE);
-
- n_hash = gcry_md_get_algo_dlen (hash_algo);
- g_return_val_if_fail (n_hash > 0, FALSE);
-
- gcry = gcry_md_open (&mdh, hash_algo, GCRY_MD_FLAG_HMAC);
- if (gcry != 0) {
- g_warning ("couldn't create '%s' hash context: %s",
- gcry_md_algo_name (hash_algo), gcry_strerror (gcry));
- return FALSE;
- }
-
- /* Get us a temporary buffers */
- T = gcry_calloc_secure (n_hash, 1);
- U = gcry_calloc_secure (n_hash, 1);
- n_buf = n_salt + 4;
- buf = gcry_calloc_secure (n_buf, 1);
- g_return_val_if_fail (buf && T && U, FALSE);
-
- /* n_hash blocks in output, rounding up */
- l = ((n_output - 1) / n_hash) + 1;
-
- /* number of bytes in last, rounded up, n_hash block */
- r = n_output - (l - 1) * n_hash;
-
- memcpy (buf, salt, n_salt);
- for (i = 1; i <= l; i++) {
- memset (T, 0, n_hash);
- for (u = 1; u <= iterations; u++) {
- gcry_md_reset (mdh);
-
- gcry = gcry_md_setkey (mdh, password, n_password);
- g_return_val_if_fail (gcry == 0, FALSE);
-
- /* For first iteration on each block add 4 extra bytes */
- if (u == 1) {
- buf[n_salt + 0] = (i & 0xff000000) >> 24;
- buf[n_salt + 1] = (i & 0x00ff0000) >> 16;
- buf[n_salt + 2] = (i & 0x0000ff00) >> 8;
- buf[n_salt + 3] = (i & 0x000000ff) >> 0;
-
- gcry_md_write (mdh, buf, n_buf);
-
- /* Other iterations, any block */
- } else {
- gcry_md_write (mdh, U, n_hash);
- }
-
- memcpy (U, gcry_md_read (mdh, hash_algo), n_hash);
-
- for (k = 0; k < n_hash; k++)
- T[k] ^= U[k];
- }
-
- memcpy (output + (i - 1) * n_hash, T, i == l ? r : n_hash);
- }
-
- gcry_free (T);
- gcry_free (U);
- gcry_free (buf);
- gcry_md_close (mdh);
- return TRUE;
-}
-
-gboolean
-gck_crypto_symkey_generate_pbkdf2 (int cipher_algo, int hash_algo,
- const gchar *password, gssize n_password,
- const guchar *salt, gsize n_salt, int iterations,
- guchar **key, guchar **iv)
-{
- gsize n_key, n_block;
- gboolean ret = TRUE;
-
- g_return_val_if_fail (hash_algo, FALSE);
- g_return_val_if_fail (cipher_algo, FALSE);
- g_return_val_if_fail (iterations > 0, FALSE);
-
- n_key = gcry_cipher_get_algo_keylen (cipher_algo);
- n_block = gcry_cipher_get_algo_blklen (cipher_algo);
-
- if (key)
- *key = NULL;
- if (iv)
- *iv = NULL;
-
- if (!password)
- n_password = 0;
- if (n_password == -1)
- n_password = strlen (password);
-
- /* Generate us an key */
- if (key) {
- *key = gcry_calloc_secure (n_key, 1);
- g_return_val_if_fail (*key != NULL, FALSE);
- ret = generate_pbkdf2 (hash_algo, password, n_password, salt, n_salt,
- iterations, *key, n_key);
- }
-
- /* Generate us an iv */
- if (ret && iv) {
- if (n_block > 1) {
- *iv = g_malloc (n_block);
- gcry_create_nonce (*iv, n_block);
- } else {
- *iv = NULL;
- }
- }
-
- /* Cleanup in case of failure */
- if (!ret) {
- g_free (iv ? *iv : NULL);
- gcry_free (key ? *key : NULL);
- }
-
- return ret;
-}
-
/* --------------------------------------------------------------------------
* INITIALIZATION
*/
diff --git a/pkcs11/gck/gck-crypto.h b/pkcs11/gck/gck-crypto.h
index b7714b69..623b5448 100644
--- a/pkcs11/gck/gck-crypto.h
+++ b/pkcs11/gck/gck-crypto.h
@@ -159,44 +159,4 @@ guchar* gck_crypto_rsa_unpad_two (guint bi
gsize n_padded,
gsize *n_raw);
-gboolean gck_crypto_symkey_generate_simple (int cipher_algo,
- int hash_algo,
- const gchar *password,
- gssize n_password,
- const guchar *salt,
- gsize n_salt,
- int iterations,
- guchar **key,
- guchar **iv);
-
-gboolean gck_crypto_symkey_generate_pbe (int cipher_algo,
- int hash_algo,
- const gchar *password,
- gssize n_password,
- const guchar *salt,
- gsize n_salt,
- int iterations,
- guchar **key,
- guchar **iv);
-
-gboolean gck_crypto_symkey_generate_pkcs12 (int cipher_algo,
- int hash_algo,
- const gchar *password,
- gssize n_password,
- const guchar *salt,
- gsize n_salt,
- int iterations,
- guchar **key,
- guchar **iv);
-
-gboolean gck_crypto_symkey_generate_pbkdf2 (int cipher_algo,
- int hash_algo,
- const gchar *password,
- gssize n_password,
- const guchar *salt,
- gsize n_salt,
- int iterations,
- guchar **key,
- guchar **iv);
-
#endif /* GCKCRYPTO_H_ */
diff --git a/pkcs11/gck/gck-data-der.c b/pkcs11/gck/gck-data-der.c
index 3ba74197..48e6a940 100644
--- a/pkcs11/gck/gck-data-der.c
+++ b/pkcs11/gck/gck-data-der.c
@@ -29,6 +29,7 @@
#include "gck-data-types.h"
#include "egg/egg-secure-memory.h"
+#include "egg/egg-symkey.h"
#include <glib.h>
#include <gcrypt.h>
@@ -40,27 +41,7 @@
static GQuark OID_PKIX1_RSA;
static GQuark OID_PKIX1_DSA;
-
-static GQuark OID_PBE_MD2_DES_CBC;
-static GQuark OID_PBE_MD5_DES_CBC;
-static GQuark OID_PBE_MD2_RC2_CBC;
-static GQuark OID_PBE_MD5_RC2_CBC;
-static GQuark OID_PBE_SHA1_DES_CBC;
-static GQuark OID_PBE_SHA1_RC2_CBC;
-static GQuark OID_PBES2;
-static GQuark OID_PBKDF2;
-
-static GQuark OID_DES_CBC;
-static GQuark OID_DES_RC2_CBC;
-static GQuark OID_DES_EDE3_CBC;
-static GQuark OID_DES_RC5_CBC;
-
-static GQuark OID_PKCS12_PBE_ARCFOUR_SHA1;
-static GQuark OID_PKCS12_PBE_RC4_40_SHA1;
static GQuark OID_PKCS12_PBE_3DES_SHA1;
-static GQuark OID_PKCS12_PBE_2DES_SHA1;
-static GQuark OID_PKCS12_PBE_RC2_128_SHA1;
-static GQuark OID_PKCS12_PBE_RC2_40_SHA1;
static void
init_quarks (void)
@@ -74,29 +55,7 @@ init_quarks (void)
QUARK (OID_PKIX1_RSA, "1.2.840.113549.1.1.1");
QUARK (OID_PKIX1_DSA, "1.2.840.10040.4.1");
-
- QUARK (OID_PBE_MD2_DES_CBC, "1.2.840.113549.1.5.1");
- QUARK (OID_PBE_MD5_DES_CBC, "1.2.840.113549.1.5.3");
- QUARK (OID_PBE_MD2_RC2_CBC, "1.2.840.113549.1.5.4");
- QUARK (OID_PBE_MD5_RC2_CBC, "1.2.840.113549.1.5.6");
- QUARK (OID_PBE_SHA1_DES_CBC, "1.2.840.113549.1.5.10");
- QUARK (OID_PBE_SHA1_RC2_CBC, "1.2.840.113549.1.5.11");
-
- QUARK (OID_PBES2, "1.2.840.113549.1.5.13");
-
- QUARK (OID_PBKDF2, "1.2.840.113549.1.5.12");
-
- QUARK (OID_DES_CBC, "1.3.14.3.2.7");
- QUARK (OID_DES_RC2_CBC, "1.2.840.113549.3.2");
- QUARK (OID_DES_EDE3_CBC, "1.2.840.113549.3.7");
- QUARK (OID_DES_RC5_CBC, "1.2.840.113549.3.9");
-
- QUARK (OID_PKCS12_PBE_ARCFOUR_SHA1, "1.2.840.113549.1.12.1.1");
- QUARK (OID_PKCS12_PBE_RC4_40_SHA1, "1.2.840.113549.1.12.1.2");
QUARK (OID_PKCS12_PBE_3DES_SHA1, "1.2.840.113549.1.12.1.3");
- QUARK (OID_PKCS12_PBE_2DES_SHA1, "1.2.840.113549.1.12.1.4");
- QUARK (OID_PKCS12_PBE_RC2_128_SHA1, "1.2.840.113549.1.12.1.5");
- QUARK (OID_PKCS12_PBE_RC2_40_SHA1, "1.2.840.113549.1.12.1.6");
#undef QUARK
@@ -638,7 +597,7 @@ gck_data_der_read_private_pkcs8_crypted (const guchar *data, gsize n_data, const
/*
* Parse the encryption stuff into a cipher.
*/
- r = gck_data_der_read_cipher (scheme, password, n_password, params, n_params, &cih);
+ r = egg_symkey_read_cipher (scheme, password, n_password, params, n_params, &cih);
if (r == GCK_DATA_UNRECOGNIZED) {
ret = GCK_DATA_FAILURE;
goto done;
@@ -1031,7 +990,7 @@ prepare_and_encode_pkcs8_cipher (ASN1_TYPE asn, const gchar *password,
*n_block = gcry_cipher_get_algo_blklen (GCRY_MD_SHA1);
g_return_val_if_fail (n_key && *n_block, NULL);
- if (!gck_crypto_symkey_generate_pkcs12 (GCRY_CIPHER_3DES, GCRY_MD_SHA1,
+ if (!egg_symkey_generate_pkcs12 (GCRY_CIPHER_3DES, GCRY_MD_SHA1,
password, n_password, salt,
sizeof (salt), iterations, &key, &iv))
g_return_val_if_reached (NULL);
@@ -1322,437 +1281,3 @@ gck_data_der_write_certificate (ASN1_TYPE asn1, gsize *n_data)
return egg_asn1_encode (asn1, "", n_data, NULL);
}
-
-/* -----------------------------------------------------------------------------
- * CIPHER/KEY DESCRIPTIONS
- */
-
-GckDataResult
-gck_data_der_read_cipher (GQuark oid_scheme, const gchar *password, gsize n_password,
- const guchar *data, gsize n_data, gcry_cipher_hd_t *cih)
-{
- GckDataResult ret = GCK_DATA_UNRECOGNIZED;
-
- g_return_val_if_fail (oid_scheme != 0, GCK_DATA_FAILURE);
- g_return_val_if_fail (cih != NULL, GCK_DATA_FAILURE);
- g_return_val_if_fail (data != NULL && n_data != 0, GCK_DATA_FAILURE);
-
- init_quarks ();
-
- /* PKCS#5 PBE */
- if (oid_scheme == OID_PBE_MD2_DES_CBC)
- ret = gck_data_der_read_cipher_pkcs5_pbe (GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC,
- GCRY_MD_MD2, password, n_password, data, n_data, cih);
-
- else if (oid_scheme == OID_PBE_MD2_RC2_CBC)
- /* RC2-64 has no implementation in libgcrypt */
- ret = GCK_DATA_UNRECOGNIZED;
- else if (oid_scheme == OID_PBE_MD5_DES_CBC)
- ret = gck_data_der_read_cipher_pkcs5_pbe (GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC,
- GCRY_MD_MD5, password, n_password, data, n_data, cih);
- else if (oid_scheme == OID_PBE_MD5_RC2_CBC)
- /* RC2-64 has no implementation in libgcrypt */
- ret = GCK_DATA_UNRECOGNIZED;
- else if (oid_scheme == OID_PBE_SHA1_DES_CBC)
- ret = gck_data_der_read_cipher_pkcs5_pbe (GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC,
- GCRY_MD_SHA1, password, n_password, data, n_data, cih);
- else if (oid_scheme == OID_PBE_SHA1_RC2_CBC)
- /* RC2-64 has no implementation in libgcrypt */
- ret = GCK_DATA_UNRECOGNIZED;
-
-
- /* PKCS#5 PBES2 */
- else if (oid_scheme == OID_PBES2)
- ret = gck_data_der_read_cipher_pkcs5_pbes2 (password, n_password, data, n_data, cih);
-
-
- /* PKCS#12 PBE */
- else if (oid_scheme == OID_PKCS12_PBE_ARCFOUR_SHA1)
- ret = gck_data_der_read_cipher_pkcs12_pbe (GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM,
- password, n_password, data, n_data, cih);
- else if (oid_scheme == OID_PKCS12_PBE_RC4_40_SHA1)
- /* RC4-40 has no implementation in libgcrypt */;
-
- else if (oid_scheme == OID_PKCS12_PBE_3DES_SHA1)
- ret = gck_data_der_read_cipher_pkcs12_pbe (GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC,
- password, n_password, data, n_data, cih);
- else if (oid_scheme == OID_PKCS12_PBE_2DES_SHA1)
- /* 2DES has no implementation in libgcrypt */;
-
- else if (oid_scheme == OID_PKCS12_PBE_RC2_128_SHA1)
- ret = gck_data_der_read_cipher_pkcs12_pbe (GCRY_CIPHER_RFC2268_128, GCRY_CIPHER_MODE_CBC,
- password, n_password, data, n_data, cih);
-
- else if (oid_scheme == OID_PKCS12_PBE_RC2_40_SHA1)
- ret = gck_data_der_read_cipher_pkcs12_pbe (GCRY_CIPHER_RFC2268_40, GCRY_CIPHER_MODE_CBC,
- password, n_password, data, n_data, cih);
-
- if (ret == GCK_DATA_UNRECOGNIZED)
- g_message ("unsupported or unrecognized cipher oid: %s", g_quark_to_string (oid_scheme));
- return ret;
-}
-
-GckDataResult
-gck_data_der_read_cipher_pkcs5_pbe (int cipher_algo, int cipher_mode, int hash_algo,
- const gchar *password, gsize n_password, const guchar *data,
- gsize n_data, gcry_cipher_hd_t *cih)
-{
- ASN1_TYPE asn = ASN1_TYPE_EMPTY;
- gcry_error_t gcry;
- GckDataResult ret;
- const guchar *salt;
- gsize n_salt;
- gsize n_block, n_key;
- guint iterations;
- guchar *key = NULL;
- guchar *iv = NULL;
-
- g_return_val_if_fail (cipher_algo != 0 && cipher_mode != 0, GCK_DATA_FAILURE);
- g_return_val_if_fail (cih != NULL, GCK_DATA_FAILURE);
- g_return_val_if_fail (data != NULL && n_data != 0, GCK_DATA_FAILURE);
-
- *cih = NULL;
- ret = GCK_DATA_UNRECOGNIZED;
-
- /* Check if we can use this algorithm */
- if (gcry_cipher_algo_info (cipher_algo, GCRYCTL_TEST_ALGO, NULL, 0) != 0 ||
- gcry_md_test_algo (hash_algo) != 0)
- goto done;
-
- asn = egg_asn1_decode ("PKIX1.pkcs-5-PBE-params", data, n_data);
- if (!asn)
- goto done;
-
- ret = GCK_DATA_FAILURE;
-
- salt = egg_asn1_read_content (asn, data, n_data, "salt", &n_salt);
- if (!salt)
- goto done;
- if (!egg_asn1_read_uint (asn, "iterationCount", &iterations))
- iterations = 1;
-
- n_key = gcry_cipher_get_algo_keylen (cipher_algo);
- g_return_val_if_fail (n_key > 0, GCK_DATA_FAILURE);
- n_block = gcry_cipher_get_algo_blklen (cipher_algo);
-
- if (!gck_crypto_symkey_generate_pbe (cipher_algo, hash_algo, password, n_password, salt,
- n_salt, iterations, &key, n_block > 1 ? &iv : NULL))
- goto done;
-
- gcry = gcry_cipher_open (cih, cipher_algo, cipher_mode, 0);
- if (gcry != 0) {
- g_warning ("couldn't create cipher: %s", gcry_strerror (gcry));
- goto done;
- }
-
- if (iv)
- gcry_cipher_setiv (*cih, iv, n_block);
- gcry_cipher_setkey (*cih, key, n_key);
-
- ret = GCK_DATA_SUCCESS;
-
-done:
- gcry_free (iv);
- gcry_free (key);
-
- if (asn)
- asn1_delete_structure (&asn);
-
- return ret;
-}
-
-static gboolean
-setup_pkcs5_rc2_params (const guchar *data, guchar n_data, gcry_cipher_hd_t cih)
-{
- ASN1_TYPE asn = ASN1_TYPE_EMPTY;
- gcry_error_t gcry;
- const guchar *iv;
- gsize n_iv;
- guint version;
-
- g_assert (data);
-
- asn = egg_asn1_decode ("PKIX1.pkcs-5-rc2-CBC-params", data, n_data);
- if (!asn)
- return GCK_DATA_UNRECOGNIZED;
-
- if (!egg_asn1_read_uint (asn, "rc2ParameterVersion", &version))
- return GCK_DATA_FAILURE;
-
- iv = egg_asn1_read_content (asn, data, n_data, "iv", &n_iv);
- asn1_delete_structure (&asn);
-
- if (!iv)
- return GCK_DATA_FAILURE;
-
- gcry = gcry_cipher_setiv (cih, iv, n_iv);
-
- if (gcry != 0) {
- g_message ("couldn't set %lu byte iv on cipher", (gulong)n_iv);
- return GCK_DATA_FAILURE;
- }
-
- return GCK_DATA_SUCCESS;
-}
-
-static gboolean
-setup_pkcs5_des_params (const guchar *data, guchar n_data, gcry_cipher_hd_t cih)
-{
- ASN1_TYPE asn = ASN1_TYPE_EMPTY;
- gcry_error_t gcry;
- const guchar *iv;
- gsize n_iv;
-
- g_assert (data);
-
- asn = egg_asn1_decode ("PKIX1.pkcs-5-des-EDE3-CBC-params", data, n_data);
- if (!asn)
- asn = egg_asn1_decode ("PKIX1.pkcs-5-des-CBC-params", data, n_data);
- if (!asn)
- return GCK_DATA_UNRECOGNIZED;
-
- iv = egg_asn1_read_content (asn, data, n_data, "", &n_iv);
- asn1_delete_structure (&asn);
-
- if (!iv)
- return GCK_DATA_FAILURE;
-
- gcry = gcry_cipher_setiv (cih, iv, n_iv);
-
- if (gcry != 0) {
- g_message ("couldn't set %lu byte iv on cipher", (gulong)n_iv);
- return GCK_DATA_FAILURE;
- }
-
- return GCK_DATA_SUCCESS;
-}
-
-static GckDataResult
-setup_pkcs5_pbkdf2_params (const gchar *password, gsize n_password, const guchar *data,
- gsize n_data, int cipher_algo, gcry_cipher_hd_t cih)
-{
- ASN1_TYPE asn = ASN1_TYPE_EMPTY;
- GckDataResult ret;
- gcry_error_t gcry;
- guchar *key = NULL;
- const guchar *salt;
- gsize n_salt, n_key;
- guint iterations;
-
- g_assert (cipher_algo);
- g_assert (data);
-
- ret = GCK_DATA_UNRECOGNIZED;
-
- asn = egg_asn1_decode ("PKIX1.pkcs-5-PBKDF2-params", data, n_data);
- if (!asn)
- goto done;
-
- ret = GCK_DATA_FAILURE;
-
- if (!egg_asn1_read_uint (asn, "iterationCount", &iterations))
- iterations = 1;
- salt = egg_asn1_read_content (asn, data, n_data, "salt.specified", &n_salt);
- if (!salt)
- goto done;
-
- if (!gck_crypto_symkey_generate_pbkdf2 (cipher_algo, GCRY_MD_SHA1, password, n_password,
- salt, n_salt, iterations, &key, NULL))
- goto done;
-
- n_key = gcry_cipher_get_algo_keylen (cipher_algo);
- g_return_val_if_fail (n_key > 0, GCK_DATA_FAILURE);
-
- gcry = gcry_cipher_setkey (cih, key, n_key);
- if (gcry != 0) {
- g_message ("couldn't set %lu byte key on cipher", (gulong)n_key);
- goto done;
- }
-
- ret = GCK_DATA_SUCCESS;
-
-done:
- gcry_free (key);
- if (asn)
- asn1_delete_structure (&asn);
- return ret;
-}
-
-GckDataResult
-gck_data_der_read_cipher_pkcs5_pbes2 (const gchar *password, gsize n_password, const guchar *data,
- gsize n_data, gcry_cipher_hd_t *cih)
-{
- ASN1_TYPE asn = ASN1_TYPE_EMPTY;
- GckDataResult r, ret;
- GQuark key_deriv_algo, enc_oid;
- gcry_error_t gcry;
- int algo, mode;
- int beg, end, res;
-
- g_return_val_if_fail (cih != NULL, GCK_DATA_FAILURE);
- g_return_val_if_fail (data != NULL && n_data != 0, GCK_DATA_FAILURE);
-
- init_quarks ();
-
- *cih = NULL;
- ret = GCK_DATA_UNRECOGNIZED;
-
- asn = egg_asn1_decode ("PKIX1.pkcs-5-PBES2-params", data, n_data);
- if (!asn)
- goto done;
-
- res = GCK_DATA_FAILURE;
- algo = mode = 0;
-
- /* Read in all the encryption type */
- enc_oid = egg_asn1_read_oid (asn, "encryptionScheme.algorithm");
- if (!enc_oid)
- goto done;
- if (enc_oid == OID_DES_EDE3_CBC)
- algo = GCRY_CIPHER_3DES;
- else if (enc_oid == OID_DES_CBC)
- algo = GCRY_CIPHER_DES;
- else if (enc_oid == OID_DES_RC2_CBC)
- algo = GCRY_CIPHER_RFC2268_128;
- else if (enc_oid == OID_DES_RC5_CBC)
- /* RC5 doesn't exist in libgcrypt */;
-
- /* Unsupported? */
- if (algo == 0 || gcry_cipher_algo_info (algo, GCRYCTL_TEST_ALGO, NULL, 0) != 0) {
- ret = GCK_DATA_UNRECOGNIZED;
- goto done;
- }
-
- /* Instantiate our cipher */
- gcry = gcry_cipher_open (cih, algo, GCRY_CIPHER_MODE_CBC, 0);
- if (gcry != 0) {
- g_warning ("couldn't create cipher: %s", gcry_cipher_algo_name (algo));
- goto done;
- }
-
- /* Read out the parameters */
- if (asn1_der_decoding_startEnd (asn, data, n_data, "encryptionScheme.parameters",
- &beg, &end) != ASN1_SUCCESS)
- goto done;
-
- switch (algo) {
- case GCRY_CIPHER_3DES:
- case GCRY_CIPHER_DES:
- r = setup_pkcs5_des_params (data + beg, end - beg + 1, *cih);
- break;
- case GCRY_CIPHER_RFC2268_128:
- r = setup_pkcs5_rc2_params (data + beg, end - beg + 1, *cih);
- break;
- default:
- /* Should have been caught on the oid check above */
- g_assert_not_reached ();
- r = GCK_DATA_UNRECOGNIZED;
- break;
- };
-
- if (r != GCK_DATA_SUCCESS) {
- ret = r;
- goto done;
- }
-
- /* Read out the key creation paramaters */
- key_deriv_algo = egg_asn1_read_oid (asn, "keyDerivationFunc.algorithm");
- if (!key_deriv_algo)
- goto done;
- if (key_deriv_algo != OID_PBKDF2) {
- g_message ("unsupported key derivation algorithm: %s", g_quark_to_string (key_deriv_algo));
- ret = GCK_DATA_UNRECOGNIZED;
- goto done;
- }
-
- if (asn1_der_decoding_startEnd (asn, data, n_data, "keyDerivationFunc.parameters",
- &beg, &end) != ASN1_SUCCESS)
- goto done;
-
- ret = setup_pkcs5_pbkdf2_params (password, n_password, data + beg, end - beg + 1, algo, *cih);
-
-done:
- if (ret != GCK_DATA_SUCCESS && *cih) {
- gcry_cipher_close (*cih);
- *cih = NULL;
- }
-
- if (asn)
- asn1_delete_structure (&asn);
-
- return ret;
-}
-
-GckDataResult
-gck_data_der_read_cipher_pkcs12_pbe (int cipher_algo, int cipher_mode, const gchar *password,
- gsize n_password, const guchar *data, gsize n_data,
- gcry_cipher_hd_t *cih)
-{
- ASN1_TYPE asn = ASN1_TYPE_EMPTY;
- gcry_error_t gcry;
- GckDataResult ret;
- const guchar *salt;
- gsize n_salt;
- gsize n_block, n_key;
- guint iterations;
- guchar *key = NULL;
- guchar *iv = NULL;
-
- g_return_val_if_fail (cipher_algo != 0 && cipher_mode != 0, GCK_DATA_FAILURE);
- g_return_val_if_fail (cih != NULL, GCK_DATA_FAILURE);
- g_return_val_if_fail (data != NULL && n_data != 0, GCK_DATA_FAILURE);
-
- *cih = NULL;
- ret = GCK_DATA_UNRECOGNIZED;
-
- /* Check if we can use this algorithm */
- if (gcry_cipher_algo_info (cipher_algo, GCRYCTL_TEST_ALGO, NULL, 0) != 0)
- goto done;
-
- asn = egg_asn1_decode ("PKIX1.pkcs-12-PbeParams", data, n_data);
- if (!asn)
- goto done;
-
- ret = GCK_DATA_FAILURE;
-
- salt = egg_asn1_read_content (asn, data, n_data, "salt", &n_salt);
- if (!salt)
- goto done;
- if (!egg_asn1_read_uint (asn, "iterations", &iterations))
- goto done;
-
- n_block = gcry_cipher_get_algo_blklen (cipher_algo);
- n_key = gcry_cipher_get_algo_keylen (cipher_algo);
-
- /* Generate IV and key using salt read above */
- if (!gck_crypto_symkey_generate_pkcs12 (cipher_algo, GCRY_MD_SHA1, password,
- n_password, salt, n_salt, iterations, &key,
- n_block > 1 ? &iv : NULL))
- goto done;
-
- gcry = gcry_cipher_open (cih, cipher_algo, cipher_mode, 0);
- if (gcry != 0) {
- g_warning ("couldn't create encryption cipher: %s", gcry_strerror (gcry));
- goto done;
- }
-
- if (iv)
- gcry_cipher_setiv (*cih, iv, n_block);
- gcry_cipher_setkey (*cih, key, n_key);
-
- ret = GCK_DATA_SUCCESS;
-
-done:
- if (ret != GCK_DATA_SUCCESS && *cih) {
- gcry_cipher_close (*cih);
- *cih = NULL;
- }
-
- gcry_free (iv);
- gcry_free (key);
-
- if (asn)
- asn1_delete_structure (&asn);
-
- return ret;
-}
diff --git a/pkcs11/gck/gck-data-der.h b/pkcs11/gck/gck-data-der.h
index fe9e5004..26801e8f 100644
--- a/pkcs11/gck/gck-data-der.h
+++ b/pkcs11/gck/gck-data-der.h
@@ -118,24 +118,4 @@ GckDataResult gck_data_der_read_enhanced_usage (const guchar *data,
guchar* gck_data_der_write_certificate (ASN1_TYPE asn1, gsize *n_data);
-/* -----------------------------------------------------------------------------
- * CIPHERS
- */
-
-GckDataResult gck_data_der_read_cipher (GQuark oid_scheme, const gchar *password,
- gsize n_password, const guchar *data, gsize n_data,
- gcry_cipher_hd_t *cih);
-
-GckDataResult gck_data_der_read_cipher_pkcs5_pbe (int cipher_algo, int cipher_mode,
- int hash_algo, const gchar *password, gsize n_password,
- const guchar *data, gsize n_data,
- gcry_cipher_hd_t *cih);
-
-GckDataResult gck_data_der_read_cipher_pkcs5_pbes2 (const gchar *password, gsize n_password, const guchar *data,
- gsize n_data, gcry_cipher_hd_t *cih);
-
-GckDataResult gck_data_der_read_cipher_pkcs12_pbe (int cipher_algo, int cipher_mode,
- const gchar *password, gsize n_password, const guchar *data,
- gsize n_data, gcry_cipher_hd_t *cih);
-
#endif /*GKRPKIXDER_H_*/
diff --git a/pkcs11/gck/gck-data-file.c b/pkcs11/gck/gck-data-file.c
index b08648a5..5616fd0d 100644
--- a/pkcs11/gck/gck-data-file.c
+++ b/pkcs11/gck/gck-data-file.c
@@ -30,6 +30,7 @@
#include "egg/egg-buffer.h"
#include "egg/egg-secure-memory.h"
+#include "egg/egg-symkey.h"
#include <glib/gstdio.h>
@@ -361,7 +362,7 @@ create_cipher (GckLogin *login, int calgo, int halgo, const guchar *salt,
password = gck_login_get_password (login, &n_password);
- if (!gck_crypto_symkey_generate_simple (calgo, halgo, password, n_password,
+ if (!egg_symkey_generate_simple (calgo, halgo, password, n_password,
salt, n_salt, iterations, &key, &iv)) {
gcry_free (key);
g_free (iv);
diff --git a/pkcs11/gck/gck-data-openssl.h b/pkcs11/gck/gck-data-openssl.h
deleted file mode 100644
index 994f9cb7..00000000
--- a/pkcs11/gck/gck-data-openssl.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
-/* gck-data-openssl.h - OpenSSL compatibility functionality
-
- Copyright (C) 2007 Stefan Walter
-
- The Gnome Keyring Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- The Gnome Keyring Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with the Gnome Library; see the file COPYING.LIB. If not,
- write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
-
- Author: Stef Walter <stef@memberwebs.com>
-*/
-
-#ifndef GCKDATAOPENSSL_H_
-#define GCKDATAOPENSSL_H_
-
-#include "gck-data-types.h"
-
-int gck_data_openssl_parse_algo (const gchar *name, int *mode);
-
-gboolean gck_data_openssl_encrypt_block (const gchar *dekinfo, const gchar *password,
- gssize n_password, const guchar *data, gsize n_data,
- guchar **encrypted, gsize *n_encrypted);
-
-GckDataResult gck_data_openssl_decrypt_block (const gchar *dekinfo, const gchar *password,
- gssize n_password, const guchar *data, gsize n_data,
- guchar **decrypted, gsize *n_decrypted);
-
-const gchar* gck_data_openssl_get_dekinfo (GHashTable *headers);
-
-const gchar* gck_data_openssl_prep_dekinfo (GHashTable *headers);
-
-#endif /* GCKDATAOPENSSL_H_ */
diff --git a/pkcs11/gck/gck-data-pem.c b/pkcs11/gck/gck-data-pem.c
deleted file mode 100644
index c2cfcf9b..00000000
--- a/pkcs11/gck/gck-data-pem.c
+++ /dev/null
@@ -1,345 +0,0 @@
-/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
-/* gkr-pkix-pem.c - PEM base64 encoding helper routines
-
- Copyright (C) 2007 Stefan Walter
-
- The Gnome Keyring Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- The Gnome Keyring Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with the Gnome Library; see the file COPYING.LIB. If not,
- write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
-
- Author: Stef Walter <stef@memberwebs.com>
-*/
-
-#include "config.h"
-
-#include "gck-data-pem.h"
-
-#include <glib.h>
-
-#include <gcrypt.h>
-
-#include <ctype.h>
-#include <string.h>
-
-/*
- * PEM looks like:
- *
- * -----BEGIN RSA PRIVATE KEY-----
- * Proc-Type: 4,ENCRYPTED
- * DEK-Info: DES-EDE3-CBC,704CFFD62FBA03E9
- *
- * 4AV/g0BiTeb07hzo4/Ct47HGhHEshMhBPGJ843QzuAinpZBbg3OxwPsQsLgoPhJL
- * Bg6Oxyz9M4UN1Xlx6Lyo2lRT908mBP6dl/OItLsVArqAzM+e29KHQVNjV1h7xN9F
- * u84tOgZftKun+ZkQUOoRvMLLu4yV4CUraks9tgyXquugGba/tbeyj2MYsC8wwSJX
- * ....
- * -----END RSA PRIVATE KEY-----
- */
-
-#define PEM_SUFF "-----"
-#define PEM_SUFF_L 5
-#define PEM_PREF_BEGIN "-----BEGIN "
-#define PEM_PREF_BEGIN_L 11
-#define PEM_PREF_END "-----END "
-#define PEM_PREF_END_L 9
-
-static void
-parse_header_lines (const gchar *hbeg, const gchar *hend, GHashTable **result)
-{
- gchar **lines, **l;
- gchar *line, *name, *value;
- gchar *copy;
-
- copy = g_strndup (hbeg, hend - hbeg);
- lines = g_strsplit (copy, "\n", 0);
- g_free (copy);
-
- for (l = lines; l && *l; ++l) {
- line = *l;
- g_strstrip (line);
-
- /* Look for the break between name: value */
- value = strchr (line, ':');
- if (value == NULL)
- continue;
-
- *value = 0;
- value = g_strdup (value + 1);
- g_strstrip (value);
-
- name = g_strdup (line);
- g_strstrip (name);
-
- if (!*result)
- *result = gck_data_pem_headers_new ();
- g_hash_table_replace (*result, name, value);
- }
-
- g_strfreev (lines);
-}
-
-static const gchar*
-pem_find_begin (const gchar *data, gsize n_data, GQuark *type)
-{
- const gchar *pref, *suff;
- gchar *stype;
-
- /* Look for a prefix */
- pref = g_strstr_len ((gchar*)data, n_data, PEM_PREF_BEGIN);
- if (!pref)
- return NULL;
-
- n_data -= (pref - data) + PEM_PREF_BEGIN_L;
- data = pref + PEM_PREF_BEGIN_L;
-
- /* Look for the end of that begin */
- suff = g_strstr_len ((gchar*)data, n_data, PEM_SUFF);
- if (!suff)
- return NULL;
-
- /* Make sure on the same line */
- if (memchr (pref, '\n', suff - pref))
- return NULL;
-
- if (type) {
- *type = 0;
- pref += PEM_PREF_BEGIN_L;
- g_assert (suff > pref);
- stype = g_alloca (suff - pref + 1);
- memcpy (stype, pref, suff - pref);
- stype[suff - pref] = 0;
- *type = g_quark_from_string (stype);
- }
-
- /* The byte after this ---BEGIN--- */
- return suff + PEM_SUFF_L;
-}
-
-static const gchar*
-pem_find_end (const gchar *data, gsize n_data, GQuark type)
-{
- const gchar *stype;
- const gchar *pref;
- gsize n_type;
-
- /* Look for a prefix */
- pref = g_strstr_len (data, n_data, PEM_PREF_END);
- if (!pref)
- return NULL;
-
- n_data -= (pref - data) + PEM_PREF_END_L;
- data = pref + PEM_PREF_END_L;
-
- /* Next comes the type string */
- stype = g_quark_to_string (type);
- n_type = strlen (stype);
- if (strncmp ((gchar*)data, stype, n_type) != 0)
- return NULL;
-
- n_data -= n_type;
- data += n_type;
-
- /* Next comes the suffix */
- if (strncmp ((gchar*)data, PEM_SUFF, PEM_SUFF_L) != 0)
- return NULL;
-
- /* The beginning of this ---END--- */
- return pref;
-}
-
-static gboolean
-pem_parse_block (const gchar *data, gsize n_data, guchar **decoded, gsize *n_decoded,
- GHashTable **headers)
-{
- const gchar *x, *hbeg, *hend;
- const gchar *p, *end;
- gint state = 0;
- guint save = 0;
-
- g_assert (data);
- g_assert (n_data);
-
- g_assert (decoded);
- g_assert (n_decoded);
-
- p = data;
- end = p + n_data;
-
- hbeg = hend = NULL;
-
- /* Try and find a pair of blank lines with only white space between */
- while (hend == NULL) {
- x = memchr (p, '\n', end - p);
- if (!x)
- break;
- ++x;
- while (isspace (*x)) {
- /* Found a second line, with only spaces between */
- if (*x == '\n') {
- hbeg = data;
- hend = x;
- break;
- /* Found a space between two lines */
- } else {
- ++x;
- }
- }
-
- /* Try next line */
- p = x;
- }
-
- /* Headers found? */
- if (hbeg && hend) {
- data = hend;
- n_data = end - data;
- }
-
- *n_decoded = (n_data * 3) / 4 + 1;
- if (gcry_is_secure (data))
- *decoded = gcry_calloc_secure (*n_decoded, 1);
- else
- *decoded = gcry_calloc (*n_decoded, 1);
- g_return_val_if_fail (*decoded, FALSE);
-
- *n_decoded = g_base64_decode_step (data, n_data, *decoded, &state, &save);
- if (!*n_decoded) {
- gcry_free (*decoded);
- return FALSE;
- }
-
- if (headers && hbeg && hend)
- parse_header_lines (hbeg, hend, headers);
-
- return TRUE;
-}
-
-GHashTable*
-gck_data_pem_headers_new (void)
-{
- return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-}
-
-guint
-gck_data_pem_parse (const guchar *data, gsize n_data,
- GckDataPemCallback callback, gpointer user_data)
-{
- const gchar *beg, *end;
- guint nfound = 0;
- guchar *decoded = NULL;
- gsize n_decoded = 0;
- GHashTable *headers = NULL;
- GQuark type;
-
- g_return_val_if_fail (data, 0);
- g_return_val_if_fail (n_data, 0);
- g_return_val_if_fail (callback, 0);
-
- while (n_data > 0) {
-
- /* This returns the first character after the PEM BEGIN header */
- beg = pem_find_begin ((const gchar*)data, n_data, &type);
- if (!beg)
- break;
-
- g_assert (type);
-
- /* This returns the character position before the PEM END header */
- end = pem_find_end ((const gchar*)beg, n_data - ((const guchar*)beg - data), type);
- if (!end)
- break;
-
- if (beg != end) {
- if (pem_parse_block (beg, end - beg, &decoded, &n_decoded, &headers)) {
- (callback) (type, decoded, n_decoded, headers, user_data);
- ++nfound;
- gcry_free (decoded);
- if (headers)
- g_hash_table_remove_all (headers);
- }
- }
-
- /* Try for another block */
- end += PEM_SUFF_L;
- n_data -= (const guchar*)end - data;
- data = (const guchar*)end;
- }
-
- if (headers)
- g_hash_table_destroy (headers);
-
- return nfound;
-}
-
-#ifdef UNTESTED_CODE
-
-static void
-append_each_header (gpointer key, gpointer value, gpointer user_data)
-{
- GString *string = (GString*)user_data;
-
- g_string_append (string, (gchar*)key);
- g_string_append (string, ": ");
- g_string_append (string, (gchar*)value);
- g_string_append_c (string, '\n');
-}
-
-guchar*
-gck_data_pem_write (const guchar *data, gsize n_data, GQuark type,
- GHashTable *headers, gsize *n_result)
-{
- GString *string;
- gint state, save;
- gsize length, n_prefix;
-
- g_return_val_if_fail (data || !n_data, NULL);
- g_return_val_if_fail (type, NULL);
- g_return_val_if_fail (n_result, NULL);
-
- string = g_string_sized_new (4096);
-
- /* The prefix */
- g_string_append_len (string, PEM_PREF_BEGIN, PEM_PREF_BEGIN_L);
- g_string_append (string, g_quark_to_string (type));
- g_string_append_len (string, PEM_SUFF, PEM_SUFF_L);
- g_string_append_c (string, '\n');
-
- /* The headers */
- if (headers && g_hash_table_size (headers) > 0) {
- g_hash_table_foreach (headers, append_each_header, string);
- g_string_append_c (string, '\n');
- }
-
- /* Resize string to fit the base64 data. Algorithm from Glib reference */
- length = n_data * 4 / 3 + n_data * 4 / (3 * 72) + 7;
- n_prefix = string->len;
- g_string_set_size (string, n_prefix + length);
-
- /* The actual base64 data */
- state = save = 0;
- length = g_base64_encode_step (data, n_data, TRUE,
- string->str + string->len, &state, &save);
- g_string_set_size (string, n_prefix + length);
-
- /* The suffix */
- g_string_append_c (string, '\n');
- g_string_append_len (string, PEM_PREF_END, PEM_PREF_END_L);
- g_string_append (string, g_quark_to_string (type));
- g_string_append_len (string, PEM_SUFF, PEM_SUFF_L);
- g_string_append_c (string, '\n');
-
- *n_result = string->len;
- return (guchar*)g_string_free (string, FALSE);
-}
-
-#endif /* UNTESTED_CODE */
diff --git a/pkcs11/gck/gck-data-pem.h b/pkcs11/gck/gck-data-pem.h
deleted file mode 100644
index 48268f3a..00000000
--- a/pkcs11/gck/gck-data-pem.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
-/* gkr-pkix-pem.h - PEM base64 helper routines
-
- Copyright (C) 2007 Stefan Walter
-
- The Gnome Keyring Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- The Gnome Keyring Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with the Gnome Library; see the file COPYING.LIB. If not,
- write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
-
- Author: Stef Walter <stef@memberwebs.com>
-*/
-
-#ifndef GCK_DATA_PEM_H_
-#define GCK_DATA_PEM_H_
-
-#include <glib.h>
-
-typedef void (*GckDataPemCallback) (GQuark type, const guchar *data, gsize n_data,
- GHashTable *headers, gpointer user_data);
-
-GHashTable* gck_data_pem_headers_new (void);
-
-guint gck_data_pem_parse (const guchar *data, gsize n_data,
- GckDataPemCallback callback,
- gpointer user_data);
-
-guchar* gck_data_pem_write (const guchar *data, gsize n_data,
- GQuark type, GHashTable *headers,
- gsize *n_result);
-
-#endif /* GCK_DATA_PEM_H_ */
diff --git a/pkcs11/gck/gck-util.c b/pkcs11/gck/gck-util.c
index 98c877ab..6e807fd2 100644
--- a/pkcs11/gck/gck-util.c
+++ b/pkcs11/gck/gck-util.c
@@ -29,8 +29,6 @@
/* Only access using atomic operations */
static gint next_handle = 0x00000010;
-static const char HEXC[] = "0123456789ABCDEF";
-
gulong*
gck_util_ulong_alloc (gulong value)
{
@@ -89,79 +87,3 @@ gck_util_next_handle (void)
{
return (CK_ULONG)g_atomic_int_exchange_and_add (&next_handle, 1);
}
-
-guchar*
-gck_util_hex_decode (const gchar *data, gssize n_data, gsize *n_decoded)
-{
- guchar *result;
- guchar *decoded;
- gushort j;
- gint state = 0;
- const gchar* pos;
-
- g_return_val_if_fail (data || !n_data, NULL);
- g_return_val_if_fail (n_decoded, NULL);
-
- if (n_data == -1)
- n_data = strlen (data);
-
- decoded = result = g_malloc0 ((n_data / 2) + 1);
- *n_decoded = 0;
-
- while (n_data > 0) {
- if (!g_ascii_isspace (*data)) {
-
- /* Find the position */
- pos = strchr (HEXC, g_ascii_toupper (*data));
- if (pos == 0)
- break;
-
- j = pos - HEXC;
- if(!state) {
- *decoded = (j & 0xf) << 4;
- state = 1;
- } else {
- *decoded |= (j & 0xf);
- (*n_decoded)++;
- decoded++;
- state = 0;
- }
- }
-
- ++data;
- --n_data;
- }
-
- /* Parsing error */
- if (state != 0) {
- g_free (result);
- result = NULL;
- }
-
- return result;
-}
-
-gchar*
-gck_util_hex_encode (const guchar *data, gsize n_data)
-{
- gchar *result, *encoded;
- guchar j;
-
- g_return_val_if_fail (data || !n_data, NULL);
-
- encoded = result = g_malloc0 (n_data * 2 + 1);
-
- while(n_data > 0) {
- j = *(data) >> 4 & 0xf;
- *(encoded++) = HEXC[j];
-
- j = *(data++) & 0xf;
- *(encoded++) = HEXC[j];
-
- n_data--;
- }
-
- /* Make sure still null terminated */
- g_assert (encoded[n_data * 2] == 0);
- return result;
-}
diff --git a/pkcs11/gck/gck-util.h b/pkcs11/gck/gck-util.h
index 3afb34fa..97e35560 100644
--- a/pkcs11/gck/gck-util.h
+++ b/pkcs11/gck/gck-util.h
@@ -47,11 +47,4 @@ CK_RV gck_attribute_set_mpi (CK_ATTRIBUTE_
CK_ULONG gck_util_next_handle (void);
-guchar* gck_util_hex_decode (const gchar *data,
- gssize n_data,
- gsize *n_decoded);
-
-gchar* gck_util_hex_encode (const guchar *data,
- gsize n_data);
-
#endif /* GCKUTIL_H_ */
diff --git a/pkcs11/gck/tests/Makefile.am b/pkcs11/gck/tests/Makefile.am
index e75438fd..c0ed0df8 100644
--- a/pkcs11/gck/tests/Makefile.am
+++ b/pkcs11/gck/tests/Makefile.am
@@ -7,11 +7,9 @@ asn1-def-test.h: test.asn
# Test files should be listed in order they need to run
UNIT_AUTO = \
- unit-test-util.c \
unit-test-crypto.c \
unit-test-data-asn1.c \
unit-test-data-der.c \
- unit-test-data-openssl.c \
unit-test-transaction.c \
unit-test-store.c \
unit-test-memory-store.c \
diff --git a/pkcs11/gck/tests/unit-test-crypto.c b/pkcs11/gck/tests/unit-test-crypto.c
index 737ad674..457306f4 100644
--- a/pkcs11/gck/tests/unit-test-crypto.c
+++ b/pkcs11/gck/tests/unit-test-crypto.c
@@ -72,190 +72,6 @@ DEFINE_TEARDOWN(crypto_setup)
dsakey = NULL;
}
-const static struct {
- const gchar *password;
- int cipher_algo;
- int hash_algo;
- int iterations;
- const gchar *salt;
-
- const gchar *result_simple;
- const gchar *result_pkcs12;
- const gchar *result_pbkdf2;
- const gchar *result_pbe;
-} all_generation_tests[] = {
-
- { /* 24 byte output */
- "booo", GCRY_CIPHER_3DES, GCRY_MD_MD5, 1,
- "\x70\x4C\xFF\xD6\x2F\xBA\x03\xE9",
- "\x84\x12\xBB\x34\x94\x8C\x40\xAD\x97\x57\x96\x74\x5B\x6A\xFB\xF8\xD6\x61\x33\x51\xEA\x8C\xCF\xD8",
- NULL,
- NULL,
- NULL
- },
-
- { /* 5 byte output */
- "booo", GCRY_CIPHER_RFC2268_40, GCRY_MD_SHA1, 2048,
- "\x8A\x58\xC2\xE8\x7C\x1D\x80\x11",
- NULL,
- "\xD6\xA6\xF0\x76\x66",
- NULL,
- NULL
- },
-
- { /* Null Password, 5 byte output */
- NULL, GCRY_CIPHER_RFC2268_40, GCRY_MD_SHA1, 2000,
- "\x04\xE0\x1C\x3E\xF8\xF2\xE9\xFD",
- NULL,
- "\x98\x7F\x20\x97\x1E",
- NULL,
- NULL
- },
-
- { /* 24 byte output */
- "booo", GCRY_CIPHER_3DES, GCRY_MD_SHA1, 2048,
- "\xBD\xEE\x0B\xC6\xCF\x43\xAC\x25",
- NULL,
- "\x3F\x38\x1B\x0E\x87\xEB\x19\xBE\xD1\x39\xDC\x5B\xC2\xD2\xB3\x3C\x35\xA8\xB8\xF9\xEE\x66\x48\x94",
- "\x20\x25\x90\xD8\xD6\x98\x3E\x71\x10\x17\x1F\x51\x49\x87\x27\xCA\x97\x27\xD1\xC9\x72\xF8\x11\xBB",
- NULL
- },
-
- { /* Empty password, 24 byte output */
- "", GCRY_CIPHER_3DES, GCRY_MD_SHA1, 2048,
- "\xF7\xCF\xD9\xCF\x1F\xF3\xAD\xF6",
- NULL,
- NULL,
- "\x53\xE3\x35\x9E\x5D\xC1\x85\x1A\x71\x3A\x67\x4E\x80\x56\x13\xD6\x4E\x3E\x89\x43\xB7\x1D\x5F\x7F",
- NULL
- },
-
- { /* Empty password, 24 byte output */
- "", GCRY_CIPHER_3DES, GCRY_MD_SHA1, 2048,
- "\xD9\xB3\x2E\xC7\xBA\x1A\x8E\x15",
- NULL,
- "\x39\x70\x75\x7C\xF5\xE2\x13\x0B\x5D\xC2\x9D\x96\x8B\x71\xC7\xFC\x5B\x97\x1F\x79\x9F\x06\xFC\xA2",
- NULL,
- NULL
- },
-
- { /* 8 byte output */
- "booo", GCRY_CIPHER_DES, GCRY_MD_MD5, 2048,
- "\x93\x4C\x3D\x29\xA2\x42\xB0\xF5",
- NULL,
- NULL,
- NULL,
- "\x8C\x67\x19\x7F\xB9\x23\xE2\x8D"
- }
-};
-
-#define N_GENERATION_TESTS (sizeof (all_generation_tests) / sizeof (all_generation_tests[0]))
-
-DEFINE_TEST(generate_key_simple)
-{
- int i;
- gboolean ret;
- guchar *key;
-
- for (i = 0; i < N_GENERATION_TESTS; ++i) {
-
- if (!all_generation_tests[i].result_simple)
- continue;
-
- ret = gck_crypto_symkey_generate_simple (all_generation_tests[i].cipher_algo,
- all_generation_tests[i].hash_algo,
- all_generation_tests[i].password, -1,
- (guchar*)all_generation_tests[i].salt, 8,
- all_generation_tests[i].iterations,
- &key, NULL);
- g_assert (ret && "key generation failed");
-
- ret = (memcmp (key, all_generation_tests[i].result_simple,
- gcry_cipher_get_algo_keylen (all_generation_tests[i].cipher_algo)) == 0);
-
- g_assert (ret && "invalid simple key generated");
- }
-}
-
-DEFINE_TEST(generate_key_pkcs12)
-{
- int i;
- gboolean ret;
- guchar *key;
-
- for (i = 0; i < N_GENERATION_TESTS; ++i) {
-
- if (!all_generation_tests[i].result_pkcs12)
- continue;
-
- ret = gck_crypto_symkey_generate_pkcs12 (all_generation_tests[i].cipher_algo,
- all_generation_tests[i].hash_algo,
- all_generation_tests[i].password, -1,
- (guchar*)all_generation_tests[i].salt, 8,
- all_generation_tests[i].iterations,
- &key, NULL);
- g_assert ("failed to generate pkcs12 key" && ret);
-
- ret = (memcmp (key, all_generation_tests[i].result_pkcs12,
- gcry_cipher_get_algo_keylen (all_generation_tests[i].cipher_algo)) == 0);
-
- g_assert ("invalid pkcs12 key generated" && ret);
- }
-}
-
-DEFINE_TEST(generate_key_pbkdf2)
-{
- int i;
- gboolean ret;
- guchar *key;
-
- for (i = 0; i < N_GENERATION_TESTS; ++i) {
-
- if (!all_generation_tests[i].result_pbkdf2)
- continue;
-
- ret = gck_crypto_symkey_generate_pbkdf2 (all_generation_tests[i].cipher_algo,
- all_generation_tests[i].hash_algo,
- all_generation_tests[i].password, -1,
- (guchar*)all_generation_tests[i].salt, 8,
- all_generation_tests[i].iterations,
- &key, NULL);
- g_assert ("failed to generate pbkdf2 key" && ret);
-
- ret = (memcmp (key, all_generation_tests[i].result_pbkdf2,
- gcry_cipher_get_algo_keylen (all_generation_tests[i].cipher_algo)) == 0);
-
- g_assert ("invalid pbkdf2 key generated" && ret);
- }
-}
-
-DEFINE_TEST(generate_key_pbe)
-{
- int i;
- gboolean ret;
- guchar *key;
-
- for (i = 0; i < N_GENERATION_TESTS; ++i) {
-
- if (!all_generation_tests[i].result_pbe)
- continue;
-
- ret = gck_crypto_symkey_generate_pbe (all_generation_tests[i].cipher_algo,
- all_generation_tests[i].hash_algo,
- all_generation_tests[i].password, -1,
- (guchar*)all_generation_tests[i].salt, 8,
- all_generation_tests[i].iterations,
- &key, NULL);
- g_assert ("failed to generate pbe key" && ret);
-
- ret = (memcmp (key, all_generation_tests[i].result_pbe,
- gcry_cipher_get_algo_keylen (all_generation_tests[i].cipher_algo)) == 0);
-
- g_assert ("invalid pbe key generated" && ret);
-
- }
-}
-
DEFINE_TEST(parse_key)
{
gcry_sexp_t sexp = NULL;
diff --git a/pkcs11/roots-store/gck-roots-module.c b/pkcs11/roots-store/gck-roots-module.c
index 5deb246c..7d930d2d 100644
--- a/pkcs11/roots-store/gck-roots-module.c
+++ b/pkcs11/roots-store/gck-roots-module.c
@@ -25,10 +25,11 @@
#include "gck-roots-module.h"
#include "gck-roots-certificate.h"
-#include "gck/gck-data-pem.h"
#include "gck/gck-file-tracker.h"
#include "gck/gck-serializable.h"
+#include "egg/egg-openssl.h"
+
#include <string.h>
struct _GckRootsModule {
@@ -204,7 +205,7 @@ file_load (GckFileTracker *tracker, const gchar *path, GckRootsModule *self)
g_list_free (objects);
/* Try and parse the PEM */
- num = gck_data_pem_parse (data, n_data, parsed_pem_block, &ctx);
+ num = egg_openssl_pem_parse (data, n_data, parsed_pem_block, &ctx);
/* If no PEM data, try to parse directly as DER */
if (ctx.count == 0) {
diff --git a/pkcs11/ssh-store/gck-ssh-openssh.c b/pkcs11/ssh-store/gck-ssh-openssh.c
index 6e239280..b425061a 100644
--- a/pkcs11/ssh-store/gck-ssh-openssh.c
+++ b/pkcs11/ssh-store/gck-ssh-openssh.c
@@ -3,11 +3,10 @@
#include "gck/gck-data-asn1.h"
#include "gck/gck-data-der.h"
-#include "gck/gck-data-openssl.h"
-#include "gck/gck-data-pem.h"
#include "gck/gck-data-types.h"
#include "egg/egg-buffer.h"
+#include "egg/egg-openssl.h"
typedef struct _ParsePrivate {
gcry_sexp_t sexp;
@@ -163,7 +162,7 @@ load_encrypted_key (const guchar *data, gsize n_data, const gchar *dekinfo,
gint length;
/* Decrypt, this will result in garble if invalid password */
- res = gck_data_openssl_decrypt_block (dekinfo, password, n_password,
+ res = egg_openssl_decrypt_block (dekinfo, password, n_password,
data, n_data, &decrypted, &n_decrypted);
if (!res)
return FALSE;
@@ -214,7 +213,7 @@ parsed_pem_block (GQuark type, const guchar *data, gsize n_data,
return;
/* If it's encrypted ... */
- dekinfo = gck_data_openssl_get_dekinfo (headers);
+ dekinfo = egg_openssl_get_dekinfo (headers);
if (dekinfo) {
ctx->result = load_encrypted_key (data, n_data, dekinfo, ctx->password,
ctx->n_password, &ctx->sexp);
@@ -346,7 +345,7 @@ gck_ssh_openssh_parse_private_key (const guchar *data, gsize n_data,
ctx.password = password;
ctx.n_password = n_password;
- num = gck_data_pem_parse (data, n_data, parsed_pem_block, &ctx);
+ num = egg_openssl_pem_parse (data, n_data, parsed_pem_block, &ctx);
/* Didn't find any private key there */
if (num == 0 || !ctx.seen) {
diff --git a/pkcs11/user-store/gck-user-storage.c b/pkcs11/user-store/gck-user-storage.c
index 318772fe..5c995f9e 100644
--- a/pkcs11/user-store/gck-user-storage.c
+++ b/pkcs11/user-store/gck-user-storage.c
@@ -33,6 +33,8 @@
#include "gck/gck-serializable.h"
#include "gck/gck-util.h"
+#include "egg/egg-hex.h"
+
#include <glib/gstdio.h>
#include <libtasn1.h>
@@ -175,7 +177,7 @@ identifier_for_object (GckObject *object)
if (name == NULL) {
data = gck_object_get_attribute_data (object, CKA_ID, &n_data);
if (data && n_data)
- name = gck_util_hex_encode (data, n_data);
+ name = egg_hex_encode (data, n_data);
g_free (data);
}