diff --git a/jams-common/src/main/java/module-info.java b/jams-common/src/main/java/module-info.java index 37e5ce3059229a0f59d880bf050ae093cc3da22e..862c4913fba34cbd43aa2039ba80ad43b628f9e3 100644 --- a/jams-common/src/main/java/module-info.java +++ b/jams-common/src/main/java/module-info.java @@ -52,6 +52,7 @@ module jams.common { exports net.jami.jams.common.authentication.local; exports net.jami.jams.common.objects.responses; exports net.jami.jams.common.cryptoengineapi.ocsp; + exports net.jami.jams.common.updater; requires jdk.crypto.cryptoki; requires java.base; requires java.sql; diff --git a/jams-common/src/main/java/net/jami/jams/common/updater/AppUpdater.java b/jams-common/src/main/java/net/jami/jams/common/updater/AppUpdater.java new file mode 100644 index 0000000000000000000000000000000000000000..56b431a20e8f43ef2ad37e70cedcab06e37542c4 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/updater/AppUpdater.java @@ -0,0 +1,11 @@ +package net.jami.jams.common.updater; + +public interface AppUpdater { + + String getCurrentVersion(); + String getLatestVersion(); + boolean downloadUpdates(); + + + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/updater/subscription/SubscriptionStatusResponse.java b/jams-common/src/main/java/net/jami/jams/common/updater/subscription/SubscriptionStatusResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..9776ca254be99c5bceaf2e4d1ae66d5c9eea6504 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/updater/subscription/SubscriptionStatusResponse.java @@ -0,0 +1,12 @@ +package net.jami.jams.common.updater.subscription; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class SubscriptionStatusResponse { + private Boolean subscribed; + private SubscriptionType subscriptionType; + private Long expiryDate; +} diff --git a/jams-common/src/main/java/net/jami/jams/common/updater/subscription/SubscriptionType.java b/jams-common/src/main/java/net/jami/jams/common/updater/subscription/SubscriptionType.java new file mode 100644 index 0000000000000000000000000000000000000000..21467d99f190e03f3f09000da0d534acd1d21a99 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/updater/subscription/SubscriptionType.java @@ -0,0 +1,5 @@ +package net.jami.jams.common.updater.subscription; + +public enum SubscriptionType { + COMMUNITY +} diff --git a/jams-server/src/main/java/net/jami/jams/server/Server.java b/jams-server/src/main/java/net/jami/jams/server/Server.java index a4ae6537c442e9932961641c925a3bcf1ff7b33a..65f9f406d5d069cf18aff38a9531311e0a4ba444 100644 --- a/jams-server/src/main/java/net/jami/jams/server/Server.java +++ b/jams-server/src/main/java/net/jami/jams/server/Server.java @@ -10,12 +10,14 @@ import net.jami.jams.common.cryptoengineapi.CertificateAuthority; import net.jami.jams.common.jami.NameServer; import net.jami.jams.common.serialization.JsoniterRegistry; import net.jami.jams.common.server.ServerSettings; +import net.jami.jams.common.updater.AppUpdater; import net.jami.jams.common.utils.LibraryLoader; import net.jami.jams.nameserver.LocalNameServer; import net.jami.jams.nameserver.PublicNameServer; import net.jami.jams.server.core.TomcatLauncher; import net.jami.jams.server.startup.AuthModuleLoader; import net.jami.jams.server.startup.CryptoEngineLoader; +import net.jami.jams.server.startup.UpdaterLoader; import java.io.File; import java.io.FileInputStream; @@ -35,6 +37,7 @@ public class Server { //This one gets loaded via JAR, to make it more flexible. public static CertificateAuthority certificateAuthority; public static AuthenticationModule userAuthenticationModule; + public static AppUpdater appUpdater; public static NameServer nameServer; private static TomcatLauncher tomcatLauncher = null; @@ -77,6 +80,7 @@ public class Server { else nameServer = new LocalNameServer(dataStore,userAuthenticationModule,serverSettings.getServerPublicURI()); } else nameServer = new LocalNameServer(dataStore,userAuthenticationModule,serverSettings.getServerPublicURI()); + appUpdater = UpdaterLoader.loadUpdater(); log.info("All services are UP and ready for use..."); } catch (Exception e){ diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/ActivateSubscriptionWorkflow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/ActivateSubscriptionWorkflow.java new file mode 100644 index 0000000000000000000000000000000000000000..601a7e8ee76cd4bcd8505b5a8cd4bd475bf84fad --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/ActivateSubscriptionWorkflow.java @@ -0,0 +1,35 @@ +package net.jami.jams.server.core.workflows; + +import lombok.extern.slf4j.Slf4j; + +import java.io.FileOutputStream; +import java.security.KeyStore; + +@Slf4j +public class ActivateSubscriptionWorkflow { + + public static boolean activateSubscription(String data){ + try { + //TODO: Decode the the data into a certificate and private key. + + //TODO: Verify that the certificate has really been signed by SavoirFaireLinux and is valid. + + //Build a keystore from the data. + KeyStore ks = KeyStore.getInstance("JKS"); + char[] password = "changeit".toCharArray(); + ks.load(null, password); + ks.setKeyEntry("license",null,null); + FileOutputStream fos = new FileOutputStream("license.jks"); + ks.store(fos, password); + fos.close(); + log.info("Succesfully activated your license!"); + return true; + } + catch (Exception e){ + log.error("The activation process failed with error: {}",e.getMessage()); + return false; + } + } + + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/SubscriptionServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/SubscriptionServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..67239407bd82517d61f4ddd67e39167ec0d81b96 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/SubscriptionServlet.java @@ -0,0 +1,29 @@ +package net.jami.jams.server.servlets.api.update; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +@WebServlet("/api/subscription") +public class SubscriptionServlet extends HttpServlet { + + //Get the subscription status (see: SubscriptionStatusResponse.class) + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doGet(req, resp); + } + + //Upload the license here, which is really just uploading a base64 representation of the keypair - and store it + //somewhere. + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + //Create the keystore based on the uploadaded keypair. + + + super.doPost(req, resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/UpdateServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/UpdateServlet.java index f274802961fe80cc5709206eee3c385574611c16..3d5464613c9184b396022e96448d36426cf3d246 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/UpdateServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/update/UpdateServlet.java @@ -8,7 +8,7 @@ import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; -@WebServlet("/api/update/status") +@WebServlet("/api/update") public class UpdateServlet extends HttpServlet { //Return the current version number and the available version number. diff --git a/jams-server/src/main/java/net/jami/jams/server/startup/UpdaterLoader.java b/jams-server/src/main/java/net/jami/jams/server/startup/UpdaterLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..9187be4570331e46068ba97c734ca63079766bd3 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/startup/UpdaterLoader.java @@ -0,0 +1,22 @@ +package net.jami.jams.server.startup; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.updater.AppUpdater; +import net.jami.jams.common.utils.LibraryLoader; + +@Slf4j +public class UpdaterLoader { + + public static AppUpdater loadUpdater() { + try { + Class<?> cls = LibraryLoader.classLoader.loadClass("net.jami.jams.common.updater.AppUpdater"); + log.info("Updater service started..."); + return (AppUpdater) cls.getConstructor().newInstance(); + } + catch (Exception e){ + log.error("Could not load update module..."); + return null; + } + } + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/update/SFLTrustStore.java b/jams-server/src/main/java/net/jami/jams/server/update/SFLTrustStore.java deleted file mode 100644 index 8726727769689d6b17fc7952eb2662cd299456ce..0000000000000000000000000000000000000000 --- a/jams-server/src/main/java/net/jami/jams/server/update/SFLTrustStore.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.jami.jams.server.update; - -import javax.net.ssl.X509TrustManager; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -public class SFLTrustStore implements X509TrustManager { - - - @Override - public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { - //Doesn't need to be implemented. - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { - //Needs to be implemented - } - - //Implement this. - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } -} diff --git a/pom.xml b/pom.xml index 8f7fdbe87ed5623f7a5545dbf90f7bd320bdc8c9..684e2615a36a605ab2967139332d627471897f28 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,7 @@ <module>jami-dht</module> <module>authentication-module</module> <module>jami-nameserver</module> + <module>updater</module> </modules> <properties> diff --git a/updater/pom.xml b/updater/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..fc45cafa89560c6bbb9fbaea72ba50efa10db524 --- /dev/null +++ b/updater/pom.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>jams3-parent</artifactId> + <groupId>net.jami</groupId> + <version>${revision}</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>updater</artifactId> + <dependencies> + <dependency> + <groupId>net.jami</groupId> + <artifactId>jams-common</artifactId> + <version>${revision}</version> + <scope>compile</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>${maven.shade.version}</version> + <executions> + <!-- Run shade goal on package phase --> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <outputFile>../jams/libs/${project.artifactId}.jar</outputFile> + <filters> + <filter> + <artifact>*:*</artifact> + <excludes> + <exclude>META-INF/*.SF</exclude> + <exclude>META-INF/*.DSA</exclude> + <exclude>META-INF/*.RSA</exclude> + </excludes> + </filter> + </filters> + <transformers> + <!-- add Main-Class to manifest file --> + <transformer + implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <manifestEntries> + <Main-Class>net.jami.jams.server.Server</Main-Class> + <Implementation-Title>${project.artifactId}</Implementation-Title> + <Implementation-Version>${project.version}</Implementation-Version> + <Class-Path>.</Class-Path> + </manifestEntries> + </transformer> + <transformer + implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/> + </transformers> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> \ No newline at end of file diff --git a/updater/src/main/java/module-info.java b/updater/src/main/java/module-info.java new file mode 100644 index 0000000000000000000000000000000000000000..1ac87ad45e5d283e339fc567447f5b35347c98f0 --- /dev/null +++ b/updater/src/main/java/module-info.java @@ -0,0 +1,6 @@ +module updater { + requires jams.common; + requires lombok; + requires org.slf4j; + +} \ No newline at end of file diff --git a/updater/src/main/java/net/jami/jams/updater/JAMSUpdater.java b/updater/src/main/java/net/jami/jams/updater/JAMSUpdater.java new file mode 100644 index 0000000000000000000000000000000000000000..e97ee8c06c78a0a9a9c3b793cc4768175b77dabe --- /dev/null +++ b/updater/src/main/java/net/jami/jams/updater/JAMSUpdater.java @@ -0,0 +1,23 @@ +package net.jami.jams.updater; + +import net.jami.jams.common.updater.AppUpdater; + +public class JAMSUpdater implements AppUpdater { + + UpdateDownloader updateDownloader = new UpdateDownloader(); + + @Override + public String getCurrentVersion() { + return null; + } + + @Override + public String getLatestVersion() { + return null; + } + + @Override + public boolean downloadUpdates() { + return updateDownloader.doUpdate(); + } +} diff --git a/updater/src/main/java/net/jami/jams/updater/SFLTrustStore.java b/updater/src/main/java/net/jami/jams/updater/SFLTrustStore.java new file mode 100644 index 0000000000000000000000000000000000000000..3f7b5cc2a32221b34608c992cd13d59d1bfe3916 --- /dev/null +++ b/updater/src/main/java/net/jami/jams/updater/SFLTrustStore.java @@ -0,0 +1,51 @@ +package net.jami.jams.updater; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.utils.X509Utils; + +import javax.net.ssl.X509TrustManager; +import java.io.InputStream; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +@Slf4j +public class SFLTrustStore implements X509TrustManager { + + X509Certificate[] sflCertificate = new X509Certificate[1]; + + //TODO: This just returns the SavoirFaireLinux CA everywhere - get this from the OEM resources folder. + public SFLTrustStore() { + try { + InputStream is = SFLTrustStore.class.getClassLoader().getResourceAsStream("ca.crt"); + X509Certificate certificate = X509Utils.getCertificateFromPEMString(new String(is.readAllBytes())); + sflCertificate[0] = certificate; + } + catch (Exception e){ + log.error("Could not load the SavoirFaireLinux certificate with error: {}",e.getMessage()); + } + } + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + boolean failedCheck = false; + for(int i=0; i < x509Certificates.length; i++){ + try { + x509Certificates[i].verify(sflCertificate[0].getPublicKey()); + } + catch (Exception e){ + throw new CertificateException("Failed to verify the server's identity..."); + } + } + } + + //Implement this. + @Override + public X509Certificate[] getAcceptedIssuers() { + return sflCertificate; + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/update/UpdateCheckTask.java b/updater/src/main/java/net/jami/jams/updater/UpdateCheckTask.java similarity index 92% rename from jams-server/src/main/java/net/jami/jams/server/update/UpdateCheckTask.java rename to updater/src/main/java/net/jami/jams/updater/UpdateCheckTask.java index 5c47e9db0bbd23922d24963f6bb3de4f089999e9..d41fd0d60aeea9fb7bfe828e808f26c1f2e8264c 100644 --- a/jams-server/src/main/java/net/jami/jams/server/update/UpdateCheckTask.java +++ b/updater/src/main/java/net/jami/jams/updater/UpdateCheckTask.java @@ -1,4 +1,4 @@ -package net.jami.jams.server.update; +package net.jami.jams.updater; import lombok.extern.slf4j.Slf4j; @@ -7,7 +7,7 @@ import java.net.URL; import java.util.List; import java.util.TimerTask; -import static net.jami.jams.server.update.UpdateDaemon.UPDATE_SERVER_URI; +import static net.jami.jams.updater.UpdateDaemon.UPDATE_SERVER_URI; @Slf4j public class UpdateCheckTask extends TimerTask { diff --git a/jams-server/src/main/java/net/jami/jams/server/update/UpdateDaemon.java b/updater/src/main/java/net/jami/jams/updater/UpdateDaemon.java similarity index 83% rename from jams-server/src/main/java/net/jami/jams/server/update/UpdateDaemon.java rename to updater/src/main/java/net/jami/jams/updater/UpdateDaemon.java index 5353182e014fee9e86457c18785755df3985bf5d..5eeb87147bf3f8a2a831abfc7e4ed4990be6e077 100644 --- a/jams-server/src/main/java/net/jami/jams/server/update/UpdateDaemon.java +++ b/updater/src/main/java/net/jami/jams/updater/UpdateDaemon.java @@ -1,4 +1,4 @@ -package net.jami.jams.server.update; +package net.jami.jams.updater; import lombok.Getter; import lombok.Setter; @@ -11,6 +11,7 @@ import java.util.Timer; @Setter public class UpdateDaemon extends Timer { + //TODO: This should come from the resources file. public static final String UPDATE_SERVER_URI = "https://jami.net"; public static final List<String> updateFiles = new ArrayList<>(); diff --git a/jams-server/src/main/java/net/jami/jams/server/update/UpdateDownloader.java b/updater/src/main/java/net/jami/jams/updater/UpdateDownloader.java similarity index 74% rename from jams-server/src/main/java/net/jami/jams/server/update/UpdateDownloader.java rename to updater/src/main/java/net/jami/jams/updater/UpdateDownloader.java index 1f1e837454c36cfef588f20b6fd52916a3b2b0d8..a66798c1d23ebb97aa0058add2f4f557f3b4c650 100644 --- a/jams-server/src/main/java/net/jami/jams/server/update/UpdateDownloader.java +++ b/updater/src/main/java/net/jami/jams/updater/UpdateDownloader.java @@ -1,16 +1,14 @@ -package net.jami.jams.server.update; +package net.jami.jams.updater; import lombok.extern.slf4j.Slf4j; import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import java.net.URL; -import java.security.KeyStore; -import static net.jami.jams.server.update.UpdateDaemon.UPDATE_SERVER_URI; -import static net.jami.jams.server.update.UpdateDaemon.updateFiles; +import static net.jami.jams.updater.UpdateDaemon.UPDATE_SERVER_URI; +import static net.jami.jams.updater.UpdateDaemon.updateFiles; @Slf4j public class UpdateDownloader { @@ -23,12 +21,12 @@ public class UpdateDownloader { } - public void doUpdate(){ + public boolean doUpdate(){ try { if (!loadLicense()) { log.warn("This server does not have a valid license, no files will be download and no update" + " will take place..."); - return; + return false; } for(String file : UpdateDaemon.updateFiles) { URL url = new URL(UPDATE_SERVER_URI); @@ -47,29 +45,25 @@ public class UpdateDownloader { log.info("Successfully downloaded file..."); } else { log.info("An error has occurred while trying to download a file: {} ", con.getResponseCode()); + return false; } } + return true; } catch (Exception e){ log.error("Could not check for updates with error: {}",e.getMessage()); + return false; } } private boolean loadLicense(){ try { - KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); - ks.load(null, null); - char[] password = "changeit".toCharArray(); - ks.load(null, password); - - //Set the license. - ks.setKeyEntry("license",null,null); - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks,"".toCharArray()); - + //We assume the keystore already exists, because it gets created upon upload. + //Basically just load the JKS keystore here. + //TODO: Load keystore from file. //Initialize the SSL context & load the SFL trust store. SSLContext sslContext = SSLContext.getInstance("SSL"); - sslContext.init(kmf.getKeyManagers(), new SFLTrustStore[]{new SFLTrustStore()},null); + //sslContext.init(kmf.getKeyManagers(), new SFLTrustStore[]{new SFLTrustStore()},null); log.info("License loaded successfully!"); sslSocketFactory = sslContext.getSocketFactory(); return true; diff --git a/updater/src/main/resources/ca.crt b/updater/src/main/resources/ca.crt new file mode 100644 index 0000000000000000000000000000000000000000..4e4a1fdcdc732799485e301a3686560a26e1ef96 --- /dev/null +++ b/updater/src/main/resources/ca.crt @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIGJTCCBA2gAwIBAgIBATANBgkqhkiG9w0BAQsFADCBmzELMAkGA1UEBhMCQ0Ex +CzAJBgNVBAgTAlFDMREwDwYDVQQHEwhNb250cmVhbDEgMB4GA1UEChMXU2F2b2ly +LWZhaXJlIExpbnV4IEluYy4xDTALBgNVBAsTBEpBTVMxGjAYBgNVBAMTEUpBTVMg +TGljZW5zaW5nIENBMR8wHQYJKoZIhvcNAQkBFhBzdXBwb3J0QGphbWkubmV0MB4X +DTIwMDIxNzIzNDQwMFoXDTMwMDIxNzIzNDQwMFowgZsxCzAJBgNVBAYTAkNBMQsw +CQYDVQQIEwJRQzERMA8GA1UEBxMITW9udHJlYWwxIDAeBgNVBAoTF1Nhdm9pci1m +YWlyZSBMaW51eCBJbmMuMQ0wCwYDVQQLEwRKQU1TMRowGAYDVQQDExFKQU1TIExp +Y2Vuc2luZyBDQTEfMB0GCSqGSIb3DQEJARYQc3VwcG9ydEBqYW1pLm5ldDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANwPgPJLFvPUrP6e2H+OzZZuF3+3 +EqJ5/2a2khT+VziqkEwwm0DUQP6sABuNnZq9VeA8dBknLCCJpPCzmXjjn75hR2kB +B1jDKjJMBIs2rlXdy/EZ2668ndt3bi06I0GWBJUKzbchTtAW+J75SXtdCSiBR0pM +LnvhfyEQEF2tMO8xFqtEfjDxi5TFXoKZyZXgJ+Q6JAC2eRxdOFdP0V+FDArXnAkY +Y7/psBb45nKWut2EQP9fJacP8TWat7oXNgJ3c8JD+0NqwE6qZWVnC5ggS0PEFeo+ +MhQENoJua0UEVliKDHnXCms1AbjZu4/DLuuN1HqgrqTowGQ8DQf7WXa944u++ZLa +G8BJ3jCDoOvUpEkGwC81rmto5ehVq8y+TaElpjHR2btUcDpQ4c/dleSxBm8OkDLA +mkernsOsyIgfAy/sXKoCUZwpZsCy0+NUoCkKcljNh9YgI8apPg3fQ2r5/bheJZGe +evrCn0/ZB4KEN0VzdEiWR89AUsgw+tez6HIngKNPft2fmKR8rAbgs/Ls/pqItlkG +yOJw0DVhwHXtfMHk0P9AeaBqtvcjLbn4ZLEB2+rsceef/2quCenlGJ9V2xga2kpk +sfbosSF0qx/1IsVw8NlqRQCJ1ys5hQ94UF/QQt/+v3qw8eqWtmoIdPpo1UBQdQog +1PMJdcZaumsihKIhAgMBAAGjcjBwMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FLpkHueN4c6HBF0E2tgSa+sviwNIMAsGA1UdDwQEAwIBBjARBglghkgBhvhCAQEE +BAMCAAcwHgYJYIZIAYb4QgENBBEWD3hjYSBjZXJ0aWZpY2F0ZTANBgkqhkiG9w0B +AQsFAAOCAgEAMtqlnIed+BaKhH9b1qJBRnSQqLLIYCa1NrWVRZ1J6ynxF+/amBZE +X6BsgnjAXFk4U4LnMgYV9ZunhawZiOnh8YxUFGKNtlToh08GyjYAi+2br06plWaL +0bmJk2QSybBjfU1H7XgaDGHJy4AsRkpL+7vhSFLqsczEJRo0k4yG6MdMsJD0tc8O +pukUF0f2dsQyg9Br8EOiVF4jz4aKAOHRPURbb9V7FssnIHBvgWfbQGYuK3eVorek +MIdmzYliUbJc0MuHPwhRYgw7lrwQKGnJNJP9+5WBawP4IFJt5TlAyFyXm7W0Vfui +5szsy+aEAp3TPbNJ16gZKRzT/1kT4HaPiiKo5PJmBIonvT6A0XTIJvHIBAoGSORG +bNMf894jG299Xtavz7O3jxqGqkBFB6O/KLa4loVQn7G0mpDnStRP7xHEVEx/hNyA +PnRIap6ymiYx6anEr96wTpRcbhIX2XSTQk4Boz9og5AMv046bS/othVtAwk6BlXF ++RLe6XB3P7tiLIU0c8x5FdDZjid2igUDiTHWFmLT5SFTo3aCaSb7QVXO+YAxomBz +6RFQ2Hto+9kSyiU/4fgWdQAngDSupI4dTNBfp7EDEStqoa3ewgD3f4dWoeh0VIxN +Rl2PC6898uZF35FBrXOWjh8sx8tlCaflFOAdIfizVdDez2dDZtZlREY= +-----END CERTIFICATE-----