From 59cf00a16fb0a6604c18ba80a9d5670bcf07acce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakob=20M=C3=B6ller?= <jakob.moeller@sap.com>
Date: Sun, 7 Jun 2020 17:20:06 +0200
Subject: [PATCH] Setup Intermediate Pin (#217)

* Setup Intermediate Pin

Signed-off-by: d067928 <jakob.moeller@sap.com>

* Setup Trust Anchors

Signed-off-by: d067928 <jakob.moeller@sap.com>

* Service Factory Merge Resolve

Signed-off-by: d067928 <jakob.moeller@sap.com>

* Add URLs for Submission and Distribution

Signed-off-by: d067928 <jakob.moeller@sap.com>
---
 .../src/main/assets/pins.properties           |  13 ---
 .../exception/CwaWebSecurityException.kt      |   8 ++
 .../http/CertificatePinnerFactory.kt          |  23 ----
 .../rki/coronawarnapp/http/ServiceFactory.kt  | 109 ++++++++++++++----
 .../http/{ => config}/DynamicURLs.kt          |   2 +-
 .../http/config/HTTPVariables.kt              |  45 ++++++++
 .../OfflineCacheInterceptor.kt                |   2 +-
 .../{ => interceptor}/RetryInterceptor.kt     |   2 +-
 .../WebSecurityVerificationInterceptor.kt     |  14 +++
 .../rki/coronawarnapp/ui/LauncherActivity.kt  |   2 +-
 .../de/rki/coronawarnapp/util/IndexHelper.kt  |  79 -------------
 .../rki/coronawarnapp/util/PropertyLoader.kt  |  28 -----
 .../main/res/xml/network_security_config.xml  |  27 ++++-
 13 files changed, 185 insertions(+), 169 deletions(-)
 delete mode 100644 Corona-Warn-App/src/main/assets/pins.properties
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/CwaWebSecurityException.kt
 delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/CertificatePinnerFactory.kt
 rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/{ => config}/DynamicURLs.kt (92%)
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/config/HTTPVariables.kt
 rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/{ => interceptor}/OfflineCacheInterceptor.kt (97%)
 rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/{ => interceptor}/RetryInterceptor.kt (93%)
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/interceptor/WebSecurityVerificationInterceptor.kt
 delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/IndexHelper.kt
 delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/PropertyLoader.kt

diff --git a/Corona-Warn-App/src/main/assets/pins.properties b/Corona-Warn-App/src/main/assets/pins.properties
deleted file mode 100644
index 198e2e9bb..000000000
--- a/Corona-Warn-App/src/main/assets/pins.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-# TODO add certificate pinning
-#
-# Intermediates will be encoded like this:
-# openssl x509 -in CERTNAME -pubkey -noout | \
-# openssl pkey -pubin -outform der | \
-# openssl dgst -sha256 -binary | \
-# openssl enc -base64
-#
-# Format is sha256/BASE64ENCODED
-# Pins are delimited by ","
-SUBMISSION_PINS=
-DISTRIBUTION_PINS=
-VERIFICATION_PINS=
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/CwaWebSecurityException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/CwaWebSecurityException.kt
new file mode 100644
index 000000000..6dbbbca8d
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/CwaWebSecurityException.kt
@@ -0,0 +1,8 @@
+package de.rki.coronawarnapp.exception
+
+import okio.IOException
+
+class CwaWebSecurityException(cause: Throwable) : IOException(
+    "an error occurred while trying to establish a secure connection to the server",
+    cause
+)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/CertificatePinnerFactory.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/CertificatePinnerFactory.kt
deleted file mode 100644
index 7a103bea0..000000000
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/CertificatePinnerFactory.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package de.rki.coronawarnapp.http
-
-import de.rki.coronawarnapp.util.PropertyLoader
-import okhttp3.CertificatePinner
-
-class CertificatePinnerFactory {
-    fun getCertificatePinner(): CertificatePinner = PropertyLoader().run {
-        CertificatePinner.Builder()
-            .add(
-                DynamicURLs.DOWNLOAD_CDN_URL.removePrefix(DynamicURLs.PATTERN_PREFIX_HTTPS),
-                *this.getDistributionPins()
-            )
-            .add(
-                DynamicURLs.SUBMISSION_CDN_URL.removePrefix(DynamicURLs.PATTERN_PREFIX_HTTPS),
-                *this.getSubmissionPins()
-            )
-            .add(
-                DynamicURLs.VERIFICATION_CDN_URL.removePrefix(DynamicURLs.PATTERN_PREFIX_HTTPS),
-                *this.getVerificationPins()
-            )
-            .build()
-    }
-}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/ServiceFactory.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/ServiceFactory.kt
index 42604a156..94c3fd63c 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/ServiceFactory.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/ServiceFactory.kt
@@ -4,15 +4,22 @@ import android.webkit.URLUtil
 import de.rki.coronawarnapp.BuildConfig
 import de.rki.coronawarnapp.CoronaWarnApplication
 import de.rki.coronawarnapp.exception.http.ServiceFactoryException
+import de.rki.coronawarnapp.http.config.DynamicURLs
+import de.rki.coronawarnapp.http.config.HTTPVariables
+import de.rki.coronawarnapp.http.interceptor.OfflineCacheInterceptor
+import de.rki.coronawarnapp.http.interceptor.WebSecurityVerificationInterceptor
+import de.rki.coronawarnapp.http.interceptor.RetryInterceptor
 import de.rki.coronawarnapp.http.service.DistributionService
 import de.rki.coronawarnapp.http.service.SubmissionService
 import de.rki.coronawarnapp.http.service.VerificationService
 import de.rki.coronawarnapp.risk.TimeVariables
 import okhttp3.Cache
+import okhttp3.CipherSuite
 import okhttp3.ConnectionPool
 import okhttp3.ConnectionSpec
 import okhttp3.Interceptor
 import okhttp3.OkHttpClient
+import okhttp3.TlsVersion
 import okhttp3.logging.HttpLoggingInterceptor
 import retrofit2.Retrofit
 import retrofit2.converter.gson.GsonConverterFactory
@@ -37,10 +44,13 @@ class ServiceFactory {
      * List of interceptors, e.g. logging
      */
     private val mInterceptors: List<Interceptor> = listOf(
+        WebSecurityVerificationInterceptor(),
         HttpLoggingInterceptor().also {
             if (BuildConfig.DEBUG) it.setLevel(HttpLoggingInterceptor.Level.BODY)
         },
-        OfflineCacheInterceptor(CoronaWarnApplication.getAppContext()),
+        OfflineCacheInterceptor(
+            CoronaWarnApplication.getAppContext()
+        ),
         RetryInterceptor(),
         HttpErrorParser()
     )
@@ -64,41 +74,98 @@ class ServiceFactory {
     private val okHttpClient by lazy {
         val clientBuilder = OkHttpClient.Builder()
 
-        val timeoutMs = TimeVariables.getTransactionTimeout()
-        clientBuilder.connectTimeout(timeoutMs, TimeUnit.MILLISECONDS)
-        clientBuilder.readTimeout(timeoutMs, TimeUnit.MILLISECONDS)
-        clientBuilder.writeTimeout(timeoutMs, TimeUnit.MILLISECONDS)
-        clientBuilder.callTimeout(timeoutMs, TimeUnit.MILLISECONDS)
+        clientBuilder.connectTimeout(
+            HTTPVariables.getHTTPConnectionTimeout(),
+            TimeUnit.MILLISECONDS
+        )
+        clientBuilder.readTimeout(
+            HTTPVariables.getHTTPReadTimeout(),
+            TimeUnit.MILLISECONDS
+        )
+        clientBuilder.writeTimeout(
+            HTTPVariables.getHTTPWriteTimeout(),
+            TimeUnit.MILLISECONDS
+        )
+        clientBuilder.callTimeout(
+            TimeVariables.getTransactionTimeout(),
+            TimeUnit.MILLISECONDS
+        )
 
         clientBuilder.connectionPool(conPool)
 
         cache.evictAll()
         clientBuilder.cache(cache)
 
-        val spec: ConnectionSpec = ConnectionSpec.Builder(ConnectionSpec.RESTRICTED_TLS)
-            .allEnabledCipherSuites() // TODO clarify more concrete Ciphers
-            .build()
-
-        clientBuilder.connectionSpecs(listOf(spec))
-
-        CertificatePinnerFactory().getCertificatePinner().run {
-            if (this.pins.isNotEmpty()) {
-                clientBuilder.certificatePinner(this)
-            }
-        }
-
         mInterceptors.forEach { clientBuilder.addInterceptor(it) }
 
         clientBuilder.build()
     }
 
+    /**
+     * For the CDN we want to ensure maximum Compatibility.
+     */
+    private fun getCDNSpecs(): List<ConnectionSpec> = listOf(
+        ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
+            .tlsVersions(
+                TlsVersion.TLS_1_0,
+                TlsVersion.TLS_1_1,
+                TlsVersion.TLS_1_2,
+                TlsVersion.TLS_1_3
+            )
+            .allEnabledCipherSuites()
+            .build()
+    )
+
+    /**
+     * For Submission and Verification we want to limit our specifications for TLS.
+     */
+    private fun getRestrictedSpecs(): List<ConnectionSpec> = listOf(
+        ConnectionSpec.Builder(ConnectionSpec.RESTRICTED_TLS)
+            .tlsVersions(
+                TlsVersion.TLS_1_2,
+                TlsVersion.TLS_1_3
+            )
+            .cipherSuites(
+                // TLS 1.2 with Perfect Forward Secrecy (BSI TR-02102-2)
+                CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+                CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+                CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+                CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+                CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
+                CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
+                CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,
+                CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+                CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+                CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+                // TLS 1.3 (BSI TR-02102-2)
+                CipherSuite.TLS_AES_128_GCM_SHA256,
+                CipherSuite.TLS_AES_256_GCM_SHA384,
+                CipherSuite.TLS_AES_128_CCM_SHA256
+            )
+            .build()
+    )
+
+    /**
+     * Helper function to create a new client from an existent Client with New Specs.
+     *
+     * @param specs
+     */
+    private fun OkHttpClient.buildClientWithNewSpecs(specs: List<ConnectionSpec>) =
+        this.newBuilder().connectionSpecs(specs).build()
+
     private val downloadCdnUrl
         get() = getValidUrl(DynamicURLs.DOWNLOAD_CDN_URL)
 
     fun distributionService(): DistributionService = distributionService
     private val distributionService by lazy {
         Retrofit.Builder()
-            .client(okHttpClient)
+            .client(okHttpClient.buildClientWithNewSpecs(getCDNSpecs()))
             .baseUrl(downloadCdnUrl)
             .addConverterFactory(gsonConverterFactory)
             .build()
@@ -111,7 +178,7 @@ class ServiceFactory {
     fun verificationService(): VerificationService = verificationService
     private val verificationService by lazy {
         Retrofit.Builder()
-            .client(okHttpClient)
+            .client(okHttpClient.buildClientWithNewSpecs(getRestrictedSpecs()))
             .baseUrl(verificationCdnUrl)
             .addConverterFactory(gsonConverterFactory)
             .build()
@@ -124,7 +191,7 @@ class ServiceFactory {
     fun submissionService(): SubmissionService = submissionService
     private val submissionService by lazy {
         Retrofit.Builder()
-            .client(okHttpClient)
+            .client(okHttpClient.buildClientWithNewSpecs(getRestrictedSpecs()))
             .baseUrl(submissionCdnUrl)
             .addConverterFactory(protoConverterFactory)
             .addConverterFactory(gsonConverterFactory)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/DynamicURLs.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/config/DynamicURLs.kt
similarity index 92%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/DynamicURLs.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/config/DynamicURLs.kt
index 20dd17814..cc05f85af 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/DynamicURLs.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/config/DynamicURLs.kt
@@ -1,4 +1,4 @@
-package de.rki.coronawarnapp.http
+package de.rki.coronawarnapp.http.config
 
 import de.rki.coronawarnapp.BuildConfig
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/config/HTTPVariables.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/config/HTTPVariables.kt
new file mode 100644
index 000000000..4939bf675
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/config/HTTPVariables.kt
@@ -0,0 +1,45 @@
+package de.rki.coronawarnapp.http.config
+
+object HTTPVariables {
+    /**
+     * The maximal runtime of a transaction
+     * In milliseconds
+     */
+    private const val HTTP_CONNECTION_TIMEOUT = 10000L
+
+    /**
+     * Getter function for [HTTP_CONNECTION_TIMEOUT]
+     *
+     * @return timeout in milliseconds
+     */
+    fun getHTTPConnectionTimeout(): Long =
+        HTTP_CONNECTION_TIMEOUT
+
+    /**
+     * The maximal runtime of a transaction
+     * In milliseconds
+     */
+    private const val HTTP_READ_TIMEOUT = 10000L
+
+    /**
+     * Getter function for [HTTP_READ_TIMEOUT]
+     *
+     * @return timeout in milliseconds
+     */
+    fun getHTTPReadTimeout(): Long =
+        HTTP_READ_TIMEOUT
+
+    /**
+     * The maximal runtime of a transaction
+     * In milliseconds
+     */
+    private const val HTTP_WRITE_TIMEOUT = 10000L
+
+    /**
+     * Getter function for [HTTP_WRITE_TIMEOUT]
+     *
+     * @return timeout in milliseconds
+     */
+    fun getHTTPWriteTimeout(): Long =
+        HTTP_WRITE_TIMEOUT
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/OfflineCacheInterceptor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/interceptor/OfflineCacheInterceptor.kt
similarity index 97%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/OfflineCacheInterceptor.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/interceptor/OfflineCacheInterceptor.kt
index 15bf62893..e8d14fab8 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/OfflineCacheInterceptor.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/interceptor/OfflineCacheInterceptor.kt
@@ -1,4 +1,4 @@
-package de.rki.coronawarnapp.http
+package de.rki.coronawarnapp.http.interceptor
 
 import android.content.Context
 import de.rki.coronawarnapp.util.ConnectivityHelper.isNetworkEnabled
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/RetryInterceptor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/interceptor/RetryInterceptor.kt
similarity index 93%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/RetryInterceptor.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/interceptor/RetryInterceptor.kt
index c4bfa179b..91167efe8 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/RetryInterceptor.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/interceptor/RetryInterceptor.kt
@@ -1,4 +1,4 @@
-package de.rki.coronawarnapp.http
+package de.rki.coronawarnapp.http.interceptor
 
 import android.util.Log
 import okhttp3.Interceptor
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/interceptor/WebSecurityVerificationInterceptor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/interceptor/WebSecurityVerificationInterceptor.kt
new file mode 100644
index 000000000..2af724e76
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/http/interceptor/WebSecurityVerificationInterceptor.kt
@@ -0,0 +1,14 @@
+package de.rki.coronawarnapp.http.interceptor
+
+import de.rki.coronawarnapp.exception.CwaWebSecurityException
+import okhttp3.Interceptor
+import okhttp3.Response
+import javax.net.ssl.SSLException
+
+class WebSecurityVerificationInterceptor : Interceptor {
+    override fun intercept(chain: Interceptor.Chain): Response {
+        try { return chain.proceed(chain.request()) } catch (e: SSLException) {
+            throw CwaWebSecurityException(e)
+        }
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/LauncherActivity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/LauncherActivity.kt
index 7a96494ec..938f0c302 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/LauncherActivity.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/LauncherActivity.kt
@@ -6,7 +6,7 @@ import android.os.Bundle
 import android.widget.Toast
 import androidx.appcompat.app.AppCompatActivity
 import androidx.lifecycle.lifecycleScope
-import de.rki.coronawarnapp.http.DynamicURLs
+import de.rki.coronawarnapp.http.config.DynamicURLs
 import de.rki.coronawarnapp.storage.LocalData
 import de.rki.coronawarnapp.ui.main.MainActivity
 import de.rki.coronawarnapp.ui.onboarding.OnboardingActivity
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/IndexHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/IndexHelper.kt
deleted file mode 100644
index 1ea82db66..000000000
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/IndexHelper.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/******************************************************************************
- * Corona-Warn-App                                                            *
- *                                                                            *
- * SAP SE and all other contributors /                                        *
- * copyright owners license this file to you 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.                                                         *
- ******************************************************************************/
-
-package de.rki.coronawarnapp.util
-
-import android.util.Log
-import com.google.common.base.Splitter
-import de.rki.coronawarnapp.BuildConfig
-import java.util.regex.Pattern
-
-@Suppress("ComplexCondition", "TooGenericExceptionThrown")
-object IndexHelper {
-    private val TAG: String? = IndexHelper::class.simpleName
-    private val WHITESPACE_SPLITTER: Splitter =
-        Splitter.onPattern("\\s+").trimResults().omitEmptyStrings()
-
-    private val INDEX_ELEMENT_PATTERN: Pattern = Pattern.compile("Cwa-([A-Z]{2})/([0-9]{10})-([0-9]+).zip")
-
-    private const val GROUP_COUNT = 3
-    private const val COUNTRY_CODE_GROUP = 1
-    private const val TIMESTAMP_GROUP = 2
-    private const val BATCH_NUMBER_GROUP = 3
-
-    /**
-     * Converts a String to an index according to the Delimiter defined in [WHITESPACE_SPLITTER] and the
-     * Regular Expression of an index element defined in [INDEX_ELEMENT_PATTERN]. The Pattern has to define
-     * 3 Groups.
-     *  1. Country Code Information
-     *  2. Creation Epoch of the Batch
-     *  3. Batch Number
-     *
-     * @return Map of Batch Number to File Names (from the Index)
-     */
-    fun String.convertToIndex(): Map<Long, String> = WHITESPACE_SPLITTER.splitToList(this).also {
-        if (BuildConfig.DEBUG) Log.d(TAG, "Index(${it.size} Elements):$it")
-    }.associateBy { indexElement ->
-        val matcher = INDEX_ELEMENT_PATTERN.matcher(indexElement)
-        if (
-            !matcher.matches() ||
-            matcher.groupCount() != GROUP_COUNT ||
-            matcher.group(COUNTRY_CODE_GROUP) != null ||
-            matcher.group(TIMESTAMP_GROUP) != null ||
-            matcher.group(BATCH_NUMBER_GROUP) != null
-        ) throw RuntimeException("Failed to parse batch from $indexElement")
-        val isoCountryCode =
-            matcher.group(COUNTRY_CODE_GROUP)
-                ?: throw NullPointerException("Batch Regex Group 1 (Country Code) must not be null")
-        val timestampString =
-            matcher.group(TIMESTAMP_GROUP)
-                ?: throw NullPointerException("Batch Regex Group 2 (Timestamp) must not be null")
-        val batchNumberString =
-            matcher.group(BATCH_NUMBER_GROUP)
-                ?: throw NullPointerException("Batch Regex Group 3 (Batch Number) must not be null")
-
-        if (BuildConfig.DEBUG) Log.d(
-            TAG, "index element " +
-                    "$indexElement=(Timestamp:$timestampString, BatchNum:$batchNumberString, " +
-                    "ISOCountryCode:$isoCountryCode"
-        )
-
-        timestampString.toLong()
-    }
-}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/PropertyLoader.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/PropertyLoader.kt
deleted file mode 100644
index c162ac72f..000000000
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/PropertyLoader.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package de.rki.coronawarnapp.util
-
-import android.util.Log
-import de.rki.coronawarnapp.CoronaWarnApplication
-import java.util.Properties
-
-class PropertyLoader {
-    companion object {
-        private const val PIN_PROPERTIES_FILE_NAME = "pins.properties"
-        private const val PIN_FILE_DELIMITER = ","
-        private const val DISTRIBUTION_PIN_PROPERTY_NAME = "DISTRIBUTION_PINS"
-        private const val SUBMISSION_PINS_PROPERTY_NAME = "SUBMISSION_PINS"
-        private const val VERIFICATION_PINS_PROPERTY_NAME = "VERIFICATION_PINS"
-    }
-
-    fun getDistributionPins() = getCertificatePins(DISTRIBUTION_PIN_PROPERTY_NAME)
-    fun getSubmissionPins() = getCertificatePins(SUBMISSION_PINS_PROPERTY_NAME)
-    fun getVerificationPins() = getCertificatePins(VERIFICATION_PINS_PROPERTY_NAME)
-
-    private fun getCertificatePins(key: String): Array<String> = Properties().run {
-        this.load(CoronaWarnApplication.getAppContext().assets.open(PIN_PROPERTIES_FILE_NAME))
-        this.getProperty(key)
-            .split(PIN_FILE_DELIMITER)
-            .filter { it.isNotEmpty() }
-            .also { Log.v(key, it.toString()) }
-            .toTypedArray()
-    }
-}
diff --git a/Corona-Warn-App/src/main/res/xml/network_security_config.xml b/Corona-Warn-App/src/main/res/xml/network_security_config.xml
index ce472cd51..6ec4e91c1 100644
--- a/Corona-Warn-App/src/main/res/xml/network_security_config.xml
+++ b/Corona-Warn-App/src/main/res/xml/network_security_config.xml
@@ -1,2 +1,27 @@
 <?xml version="1.0" encoding="utf-8"?>
-<network-security-config></network-security-config>
\ No newline at end of file
+<network-security-config xmlns:tools="http://schemas.android.com/tools">
+
+    <domain-config cleartextTrafficPermitted="false">
+        <!-- TODO Add Production Domain for Verification Service-->
+        <domain includeSubdomains="true">submission.coronawarn.app</domain>
+        <domain includeSubdomains="true">prodVerify.com</domain>
+        <pin-set expiration="2024-02-12" tools:ignore="MissingBackupPin">
+            <pin digest="SHA-256">c3jf+L8VIAFQnJJDM6Mfb4MtI1JnhVS8JwZHMwJj28M=</pin>
+        </pin-set>
+        <trust-anchors>
+            <certificates
+                src="system"
+                overridePins="false" />
+        </trust-anchors>
+    </domain-config>
+
+    <domain-config cleartextTrafficPermitted="false">
+        <domain includeSubdomains="true">svc90.main.px.t-online.de</domain>
+        <trust-anchors>
+            <certificates
+                src="system"
+                overridePins="false" />
+        </trust-anchors>
+    </domain-config>
+
+</network-security-config>
\ No newline at end of file
-- 
GitLab