summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorxuegao <xg353y@intl.att.com>2019-11-28 15:13:18 +0100
committerxuegao <xg353y@intl.att.com>2019-11-29 16:23:22 +0100
commit1ebfe6b467e5a6a42c756f225397da76f9e3dfc2 (patch)
tree29c55623caf2373cd51f71ceb80d80e513a1c330 /src
parent876d1a49367b4614680954913590372d773ec8ec (diff)
Merge ssl password
Use the aaf encrypted ssl password fot server.ssl parameters Issue-ID: CLAMP-339 Change-Id: I8869bb527f2851c1d298cd03e45327791a8acfab Signed-off-by: xuegao <xg353y@intl.att.com>
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/onap/clamp/clds/Application.java14
-rw-r--r--src/main/java/org/onap/clamp/clds/config/CamelConfiguration.java17
-rw-r--r--src/main/java/org/onap/clamp/clds/config/SslConfig.java97
-rw-r--r--src/main/java/org/onap/clamp/clds/filter/ClampCadiFilter.java9
-rw-r--r--src/main/java/org/onap/clamp/util/PassDecoder.java74
-rw-r--r--src/main/resources/application-noaaf.properties12
-rw-r--r--src/main/resources/application.properties15
-rw-r--r--src/test/java/org/onap/clamp/util/PassDecoderTest.java52
-rw-r--r--src/test/resources/clds/aaf/org.onap.clamp.keyfile27
9 files changed, 294 insertions, 23 deletions
diff --git a/src/main/java/org/onap/clamp/clds/Application.java b/src/main/java/org/onap/clamp/clds/Application.java
index efc4b128c..e41140f5c 100644
--- a/src/main/java/org/onap/clamp/clds/Application.java
+++ b/src/main/java/org/onap/clamp/clds/Application.java
@@ -29,6 +29,7 @@ import com.att.eelf.configuration.EELFLogger;
import com.att.eelf.configuration.EELFManager;
import java.io.IOException;
+import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@@ -39,6 +40,7 @@ import java.util.Enumeration;
import org.apache.catalina.connector.Connector;
import org.onap.clamp.clds.util.ClampVersioning;
import org.onap.clamp.clds.util.ResourceFileUtil;
+import org.onap.clamp.util.PassDecoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
@@ -135,6 +137,8 @@ public class Application extends SpringBootServletInitializer {
return tomcat;
}
+
+
private Connector createRedirectConnector(int redirectSecuredPort) {
if (redirectSecuredPort <= 0) {
eelfLogger.warn("HTTP port redirection to HTTPS is disabled because the HTTPS port is 0 (random port) or -1"
@@ -155,10 +159,12 @@ public class Application extends SpringBootServletInitializer {
if (env.getProperty("server.ssl.key-store") != null) {
KeyStore keystore = KeyStore.getInstance(env.getProperty("server.ssl.key-store-type"));
- keystore.load(
- ResourceFileUtil.getResourceAsStream(
- env.getProperty("server.ssl.key-store").replaceAll("classpath:", "")),
- env.getProperty("server.ssl.key-store-password").toCharArray());
+ String password = PassDecoder.decode(env.getProperty("server.ssl.key-store-password"),
+ env.getProperty("clamp.config.keyFile"));
+ String keyStore = env.getProperty("server.ssl.key-store");
+ InputStream is = ResourceFileUtil.getResourceAsStream(keyStore.replaceAll("classpath:", ""));
+ keystore.load(is, password.toCharArray());
+
Enumeration<String> aliases = keystore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
diff --git a/src/main/java/org/onap/clamp/clds/config/CamelConfiguration.java b/src/main/java/org/onap/clamp/clds/config/CamelConfiguration.java
index 271dc84ff..949ff1eb4 100644
--- a/src/main/java/org/onap/clamp/clds/config/CamelConfiguration.java
+++ b/src/main/java/org/onap/clamp/clds/config/CamelConfiguration.java
@@ -48,6 +48,7 @@ import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.onap.clamp.clds.util.ClampVersioning;
+import org.onap.clamp.util.PassDecoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@@ -61,18 +62,24 @@ public class CamelConfiguration extends RouteBuilder {
@Autowired
private Environment env;
- private void configureDefaultSslProperties() {
+ private void configureDefaultSslProperties() throws IOException {
if (env.getProperty("server.ssl.trust-store") != null) {
URL storeResource = Thread.currentThread().getContextClassLoader()
.getResource(env.getProperty("server.ssl.trust-store").replaceAll("classpath:", ""));
System.setProperty("javax.net.ssl.trustStore", storeResource.getPath());
- System.setProperty("javax.net.ssl.trustStorePassword", env.getProperty("server.ssl.trust-store-password"));
+ String keyFile = env.getProperty("clamp.config.keyFile");
+ String trustStorePass = PassDecoder.decode(env.getProperty("server.ssl.trust-store-password"),
+ keyFile);
+ System.setProperty("javax.net.ssl.trustStorePassword", trustStorePass);
System.setProperty("javax.net.ssl.trustStoreType", "jks");
System.setProperty("ssl.TrustManagerFactory.algorithm", "PKIX");
storeResource = Thread.currentThread().getContextClassLoader()
.getResource(env.getProperty("server.ssl.key-store").replaceAll("classpath:", ""));
System.setProperty("javax.net.ssl.keyStore", storeResource.getPath());
- System.setProperty("javax.net.ssl.keyStorePassword", env.getProperty("server.ssl.key-store-password"));
+
+ String keyStorePass = PassDecoder.decode(env.getProperty("server.ssl.key-store-password"),
+ keyFile);
+ System.setProperty("javax.net.ssl.keyStorePassword", keyStorePass);
System.setProperty("javax.net.ssl.keyStoreType", env.getProperty("server.ssl.key-store-type"));
}
}
@@ -81,10 +88,12 @@ public class CamelConfiguration extends RouteBuilder {
throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException, CertificateException, IOException {
if (env.getProperty("server.ssl.trust-store") != null) {
KeyStore truststore = KeyStore.getInstance("JKS");
+ String keyFile = env.getProperty("clamp.config.keyFile");
+ String password = PassDecoder.decode(env.getProperty("server.ssl.trust-store-password"), keyFile);
truststore.load(
Thread.currentThread().getContextClassLoader()
.getResourceAsStream(env.getProperty("server.ssl.trust-store").replaceAll("classpath:", "")),
- env.getProperty("server.ssl.trust-store-password").toCharArray());
+ password.toCharArray());
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance("PKIX");
trustFactory.init(truststore);
diff --git a/src/main/java/org/onap/clamp/clds/config/SslConfig.java b/src/main/java/org/onap/clamp/clds/config/SslConfig.java
new file mode 100644
index 000000000..7c7433e96
--- /dev/null
+++ b/src/main/java/org/onap/clamp/clds/config/SslConfig.java
@@ -0,0 +1,97 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+
+package org.onap.clamp.clds.config;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+
+import org.onap.clamp.clds.util.ResourceFileUtil;
+import org.onap.clamp.util.PassDecoder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+import org.springframework.boot.web.server.Ssl;
+import org.springframework.boot.web.server.SslStoreProvider;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.env.Environment;
+import org.springframework.core.io.ResourceLoader;
+
+@Configuration
+@Profile("clamp-ssl-config")
+public class SslConfig {
+ @Autowired
+ private Environment env;
+
+ @Bean
+ WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer(ServerProperties serverProperties,
+ ResourceLoader resourceLoader) {
+ return (tomcat) -> tomcat.setSslStoreProvider(new SslStoreProvider() {
+ @Override
+ public KeyStore getKeyStore() throws KeyStoreException,
+ NoSuchAlgorithmException, CertificateException, IOException {
+ KeyStore keystore = KeyStore.getInstance(env.getProperty("server.ssl.key-store-type"));
+ String password = PassDecoder.decode(env.getProperty("server.ssl.key-store-password"),
+ env.getProperty("clamp.config.keyFile"));
+ String keyStore = env.getProperty("server.ssl.key-store");
+ InputStream is = ResourceFileUtil.getResourceAsStream(keyStore.replaceAll("classpath:", ""));
+ keystore.load(is, password.toCharArray());
+ return keystore;
+ }
+
+ @Override
+ public KeyStore getTrustStore() throws KeyStoreException,
+ NoSuchAlgorithmException, CertificateException, IOException {
+ KeyStore truststore = KeyStore.getInstance("JKS");
+ String password = PassDecoder.decode(env.getProperty("server.ssl.trust-store-password"),
+ env.getProperty("clamp.config.keyFile"));
+ truststore.load(
+ Thread.currentThread().getContextClassLoader()
+ .getResourceAsStream(env.getProperty("server.ssl.trust-store")
+ .replaceAll("classpath:", "")),
+ password.toCharArray());
+ return truststore;
+ }
+ });
+ }
+
+ @Bean
+ WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatSslCustomizer(ServerProperties serverProperties,
+ ResourceLoader resourceLoader) {
+ return (tomcat) -> tomcat.setSsl(new Ssl() {
+ @Override
+ public String getKeyPassword() {
+ String password = PassDecoder.decode(env.getProperty("server.ssl.key-password"),
+ env.getProperty("clamp.config.keyFile"));
+ return password;
+ }
+ });
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/onap/clamp/clds/filter/ClampCadiFilter.java b/src/main/java/org/onap/clamp/clds/filter/ClampCadiFilter.java
index 68544de67..9e04bd084 100644
--- a/src/main/java/org/onap/clamp/clds/filter/ClampCadiFilter.java
+++ b/src/main/java/org/onap/clamp/clds/filter/ClampCadiFilter.java
@@ -60,19 +60,19 @@ public class ClampCadiFilter extends CadiFilter {
@Value("${server.ssl.key-store:#{null}}")
private String keyStore;
- @Value("${clamp.config.cadi.cadiKeystorePassword:#{null}}")
+ @Value("${server.ssl.key-store-password:#{null}}")
private String keyStorePass;
@Value("${server.ssl.trust-store:#{null}}")
private String trustStore;
- @Value("${clamp.config.cadi.cadiTruststorePassword:#{null}}")
+ @Value("${server.ssl.trust-store-password:#{null}}")
private String trustStorePass;
@Value("${server.ssl.key-alias:clamp@clamp.onap.org}")
private String alias;
- @Value("${clamp.config.cadi.keyFile:#{null}}")
+ @Value("${clamp.config.keyFile:#{null}}")
private String keyFile;
@Value("${clamp.config.cadi.cadiLoglevel:#{null}}")
@@ -152,7 +152,8 @@ public class ClampCadiFilter extends CadiFilter {
.generateCertificate(new ByteArrayInputStream(
URLDecoder.decode(certHeader, StandardCharsets.UTF_8.toString()).getBytes()));
X509Certificate caCert = (X509Certificate) certificateFactory
- .generateCertificate(new ByteArrayInputStream(ResourceFileUtil.getResourceAsString("clds/aaf/ssl/ca-certs.pem").getBytes()));
+ .generateCertificate(new ByteArrayInputStream(
+ ResourceFileUtil.getResourceAsString("clds/aaf/ssl/ca-certs.pem").getBytes()));
X509Certificate[] certifArray = ((X509Certificate[]) request
.getAttribute("javax.servlet.request.X509Certificate"));
diff --git a/src/main/java/org/onap/clamp/util/PassDecoder.java b/src/main/java/org/onap/clamp/util/PassDecoder.java
new file mode 100644
index 000000000..70a47477e
--- /dev/null
+++ b/src/main/java/org/onap/clamp/util/PassDecoder.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ */
+
+package org.onap.clamp.util;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import org.onap.aaf.cadi.Symm;
+import org.onap.clamp.clds.util.ResourceFileUtil;
+
+/**
+ * PassDecoder for decrypting the truststore and keystore password.
+ */
+public class PassDecoder {
+ /**
+ * Used to log PassDecoder class.
+ */
+ private static final EELFLogger logger = EELFManager.getInstance().getLogger(PassDecoder.class);
+
+ /**
+ * Decode the password.
+ * @param encryptedPass The encrypted password
+ * @param keyFileIs The key file in InputStream format
+ */
+ public static String decode(String encryptedPass, String keyFile) {
+ if (null == keyFile) {
+ logger.debug("Key file is not defined, thus password will not be decrypted");
+ return encryptedPass;
+ }
+ if (null == encryptedPass) {
+ logger.error("Encrypted password is not defined");
+ return null;
+ }
+ try {
+ InputStream is;
+ if (keyFile.contains("classpath:")) {
+ is = ResourceFileUtil.getResourceAsStream(keyFile.replaceAll("classpath:", ""));
+ } else {
+ File key = new File(keyFile);
+ is = new FileInputStream(key);
+ }
+ Symm symm = Symm.obtain(is);
+
+ return symm.depass(encryptedPass);
+ } catch (IOException e) {
+ logger.error("Exception occurred during the key decryption", e);
+ return null;
+ }
+ }
+}
diff --git a/src/main/resources/application-noaaf.properties b/src/main/resources/application-noaaf.properties
index 79466c89f..d389b211c 100644
--- a/src/main/resources/application-noaaf.properties
+++ b/src/main/resources/application-noaaf.properties
@@ -55,21 +55,25 @@ server.port=8443
## Config part for Server certificates
# Can be a classpath parameter instead of file:/
server.ssl.key-store=classpath:/clds/aaf/org.onap.clamp.p12
-server.ssl.key-store-password=China in the Spring
-server.ssl.key-password=China in the Spring
+server.ssl.key-store-password=enc:WWCxchk4WGBNSvuzLq3MLjMs5ObRybJtts5AI0XD1Vc
+server.ssl.key-password=enc:WWCxchk4WGBNSvuzLq3MLjMs5ObRybJtts5AI0XD1Vc
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=clamp@clamp.onap.org
## Config part for Client certificates
server.ssl.client-auth=want
server.ssl.trust-store=classpath:/clds/aaf/truststoreONAPall.jks
-server.ssl.trust-store-password=changeit
+server.ssl.trust-store-password=enc:iDnPBBLq_EMidXlMa1FEuBR8TZzYxrCg66vq_XfLHdJ
+
+# The key file used to decode the key store and trust store password
+# If not defined, the key store and trust store password will not be decrypted
+clamp.config.keyFile=classpath:/clds/aaf/org.onap.clamp.keyfile
#server.http-to-https-redirection.port=8080
server.servlet.context-path=/
#Modified engine-rest applicationpath
-spring.profiles.active=clamp-default,clamp-default-user,clamp-sdc-controller-new
+spring.profiles.active=clamp-default,clamp-default-user,clamp-sdc-controller-new,clamp-ssl-config
spring.http.converters.preferred-json-mapper=gson
#The max number of active threads in this pool
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 3ac6fa255..b97d64364 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -60,21 +60,25 @@ server.port=8443
## Config part for Server certificates
# Can be a classpath parameter instead of file:/
server.ssl.key-store=classpath:/clds/aaf/org.onap.clamp.p12
-server.ssl.key-store-password=China in the Spring
-server.ssl.key-password=China in the Spring
+server.ssl.key-store-password=enc:WWCxchk4WGBNSvuzLq3MLjMs5ObRybJtts5AI0XD1Vc
+server.ssl.key-password=enc:WWCxchk4WGBNSvuzLq3MLjMs5ObRybJtts5AI0XD1Vc
server.ssl.key-store-type=PKCS12
server.ssl.key-alias=clamp@clamp.onap.org
+# The key file used to decode the key store and trust store password
+# If not defined, the key store and trust store password will not be decrypted
+clamp.config.keyFile=classpath:/clds/aaf/org.onap.clamp.keyfile
+
## Config part for Client certificates
server.ssl.client-auth=want
server.ssl.trust-store=classpath:/clds/aaf/truststoreONAPall.jks
-server.ssl.trust-store-password=changeit
+server.ssl.trust-store-password=enc:iDnPBBLq_EMidXlMa1FEuBR8TZzYxrCg66vq_XfLHdJ
#server.http-to-https-redirection.port=8080
server.servlet.context-path=/
#Modified engine-rest applicationpath
-spring.profiles.active=clamp-default,clamp-aaf-authentication,clamp-sdc-controller-new
+spring.profiles.active=clamp-default,clamp-aaf-authentication,clamp-sdc-controller-new,clamp-ssl-config
spring.http.converters.preferred-json-mapper=gson
#The max number of active threads in this pool
@@ -240,13 +244,10 @@ clamp.config.security.permission.instance=dev
clamp.config.security.authentication.class=org.onap.aaf.cadi.principal.X509Principal
#AAF related parameters
-clamp.config.cadi.keyFile=classpath:/clds/aaf/org.onap.clamp.keyfile
clamp.config.cadi.cadiLoglevel=DEBUG
clamp.config.cadi.cadiLatitude=10
clamp.config.cadi.cadiLongitude=10
clamp.config.cadi.aafLocateUrl=https://aaf-locate:8095
-clamp.config.cadi.cadiKeystorePassword=enc:WWCxchk4WGBNSvuzLq3MLjMs5ObRybJtts5AI0XD1Vc
-clamp.config.cadi.cadiTruststorePassword=enc:iDnPBBLq_EMidXlMa1FEuBR8TZzYxrCg66vq_XfLHdJ
clamp.config.cadi.oauthTokenUrl= https://AAF_LOCATE_URL/locate/onap.org.osaaf.aaf.token:2.1/token
clamp.config.cadi.oauthIntrospectUrll=https://AAF_LOCATE_URL/locate/onap.org.osaaf.aaf.introspect:2.1/introspect
clamp.config.cadi.aafEnv=DEV
diff --git a/src/test/java/org/onap/clamp/util/PassDecoderTest.java b/src/test/java/org/onap/clamp/util/PassDecoderTest.java
new file mode 100644
index 000000000..56443e31d
--- /dev/null
+++ b/src/test/java/org/onap/clamp/util/PassDecoderTest.java
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+
+package org.onap.clamp.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+
+public class PassDecoderTest {
+
+ private final String encrypted = "enc:WWCxchk4WGBNSvuzLq3MLjMs5ObRybJtts5AI0XD1Vc";
+
+ @Test
+ public final void testDecryptionNoKeyfile() throws Exception {
+ String decodedPass = PassDecoder.decode(encrypted, null);
+ assertEquals(decodedPass, encrypted);
+ }
+
+ @Test
+ public final void testDecryptionNoPassword() throws Exception {
+ String decodedPass = PassDecoder.decode(null, "src/test/resources/clds/aaf/org.onap.clamp.keyfile");
+ assertNull(decodedPass);
+ }
+
+ @Test
+ public final void testDecryption() throws Exception {
+ String decodedPass = PassDecoder.decode(encrypted, "src/test/resources/clds/aaf/org.onap.clamp.keyfile");
+ assertEquals(decodedPass, "China in the Spring");
+ }
+}
diff --git a/src/test/resources/clds/aaf/org.onap.clamp.keyfile b/src/test/resources/clds/aaf/org.onap.clamp.keyfile
new file mode 100644
index 000000000..c2521fc81
--- /dev/null
+++ b/src/test/resources/clds/aaf/org.onap.clamp.keyfile
@@ -0,0 +1,27 @@
+kzJMxgphAoBxJz1_vYjxx-V87fahDQdYUqBIyWhZp8ojXdNpmB-96T9CvgJScJynbLcqw2Cj2CYx
+wd97vFOYhlyz5zK3tSyIuydOkVGJsJ1S4PviTtjhiJvNourJNDHgtas1Y1y2fQ5_8aVxj-s4W72N
+MNYhkeTinaQx_d_5hkBPABJlgCxKLnmxHo2jAJktnZYa5t5h48m7KiUx_RVEkQVtEvux-7vgXaC4
+ymTXj6zI9XoMTVxM0OAl4y7kBiUoOUaxS4tVKV34RJYNNqBjiUTQa_ag-KeUacRABk1ozfwzpvE5
+Sjz8WCy0L-LtCQnapkhKLt04ndCZtw8LDJ-Zz0ZgR2PVIPpTgs9VnVuOi5jf4LzTrtUatvOWkKB9
+drXKzp6cNXnZ0jkD3vV1BzqzhynKnZR2o_ilZv5CTTdpGUt906N_DwZuX6LfcV_7yvjX42bTfeIR
+ycPtodFPXlqqn9VUyh5nOauJlnOHAQmSDzjMEgjy17nQX3Ad7s4BfvujzUl-d0MqB_HCKbaW32UT
+xcY-0JfI1Y-2IdYfIkUdhVmxop6sSg0jAobWzgCRoRQkP3a2iIlKdfMyskshoWKIDVtlr-3fkDEb
+x_b_o1rRoUfzUzxEdphaUAq80Sc0i77ZLT3KF9vJOhyU_pBnApYFxVk7Hkk3VRxJKS7jyL4H7k1x
+2m5-2G8fB9XbYZT82xmAquNx4oBdpwj3_ncGF9YRF94K6NZgqemT5iWhpXMoelSU1blASgT3qlTm
+B6YgbD5owExNHwRVd8KeRsYrOnBWUiktsIhXFhNZmDUNWMFGQ2KxEcOt1tJwsQDehJFgY_l1JQ0d
+643wJ7rTJkGkYX309cydRQUX4Z0ckSQS9LhMd9stxF5XOHlvHdbW0pXNS7SaLbzKCVldUgncvI6z
+KWkwrWbftrZK2RT1UZKNngQDMGOk9OhbHAs7YzhFNFARZoRNobIv5tZVDomy-YgJb9-mD1UTkRBL
+WXOyoryDlgKrgFsgHclGDI1UFO5N-JfebPKxbP505f4924hxF2r8bspvVW8ZtHQo_SJmhauOX8n_
+eN_LK43LB9k53WAHZ_utvs0s6wGf7I73oj_N7DIFaHTDSm_MhDsFDLVG_wUzCpZ5FP2uL3nnqMkF
+Ob-l1fywfmfOmrz1BY6g4sRPPeWXuclYTnRnDRu5VQyc7_aBEVkyt3zw0JEex0vJNFUJl3pYjS55
+GplAB6p7VbS9ceZEtc5Z3qFIVHEzKWZxT190E23t_LlMuEoQ1zaqdHynNaMs61-q_A2aHRiTqlRm
+7FahVB3RX4AVLl23mu4u3A9ZDXc40nzjs9mwOVsuKlPvQ2rteDUG1njr2R1_V_MyQuoJjdfbIkPG
+4eF0QzlSMdbkeprdQxSfV5YT-yPpkBxSsCMMM43sKm4Hy7_CUdvp4Iayrp3vtK3oYMuCGi6qTadz
+KzxfTf8meKan3eMZW4RLByyniH5nQnX_KGfBly05AmFyVH_j0fyOg-48kDhtEKeqmDnP4C01jOID
+Ip_AKaB6e0GwsHzVTLZOklHwu_qzsaTzchBOG_dJJju7bxY7qv78Pa92wZIP311gSCVbc-gxxbsR
+qI555twmYEoasFm4xz10OYDOkvM1E1Rtxu3ymRLZpe6AoyFBVzEW7Dncdw7O98dKcgrp8ZlQ_8Wg
+5zZH0Cic7xnIZ0bNZyQXw56CSUiXVWuwVY3e0djXP3F-FO5gP8VTxbpW4C0t6McXAOlvSEfFKxN7
+u6OBeOKwjrtHaJk2ghF8MUcpDXanhbAgHez9larGlscCkgvoRLNaRH9GIdSVgY3HtNhJRaJIq01S
+OGeBjC5J4o-nTrqRFkwyDAYcPL373eYX1dBFFVHR-4q50H9m_zMxZHXETafxzV4DT3Qi8Sxh3uaS
+ZX7mRaNaOE0uC1n87_IZ9WhrwIQaZng2lnd9yZ-4rx8fB8WA8KQzifzvHAcMb_HV10JWGaz5A2Rm
+EXDsfexQC6CqYg5rdzzlNWDPNlHy5ubyz7fRXZ99uIwBY9aJcvCXCiEXJkC6utj3NcXQrJmk \ No newline at end of file