Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/bitfireAT/cert4android.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRicki Hirner <hirner@bitfire.at>2020-10-25 22:10:24 +0300
committerRicki Hirner <hirner@bitfire.at>2020-10-25 22:10:24 +0300
commit37984d0d0ba234c7111297496d771f8915b02b2e (patch)
tree255c274972c51e1d81b60ec5f1e7d4669793d9e3
parent047e48f9b5dddbc726d36b3677c01d223b6ad2dd (diff)
Use MD5 fingerprint instead of CN as key to store trusted certificates (so that multiple certs with the same CN can be stored)
-rw-r--r--src/main/java/at/bitfire/cert4android/CertUtils.kt16
-rw-r--r--src/main/java/at/bitfire/cert4android/CustomCertService.kt8
-rw-r--r--src/main/java/at/bitfire/cert4android/TrustCertificateActivity.kt19
-rw-r--r--src/test/java/at/bitfire/cert4android/CertUtilsTest.kt27
4 files changed, 47 insertions, 23 deletions
diff --git a/src/main/java/at/bitfire/cert4android/CertUtils.kt b/src/main/java/at/bitfire/cert4android/CertUtils.kt
index 5947fa3..128d6c8 100644
--- a/src/main/java/at/bitfire/cert4android/CertUtils.kt
+++ b/src/main/java/at/bitfire/cert4android/CertUtils.kt
@@ -10,6 +10,7 @@ package at.bitfire.cert4android
import java.security.GeneralSecurityException
import java.security.KeyStore
+import java.security.MessageDigest
import java.security.cert.X509Certificate
import java.util.logging.Level
import javax.net.ssl.TrustManagerFactory
@@ -17,6 +18,11 @@ import javax.net.ssl.X509TrustManager
object CertUtils {
+ fun fingerprint(cert: X509Certificate, algorithm: String): String {
+ val md = MessageDigest.getInstance(algorithm)
+ return hexString(md.digest(cert.encoded))
+ }
+
fun getTrustManager(keyStore: KeyStore?): X509TrustManager? {
try {
val tmf = TrustManagerFactory.getInstance("X509")
@@ -37,4 +43,14 @@ object CertUtils {
return str.toString()
}
+ fun hexString(data: ByteArray): String {
+ val str = StringBuilder()
+ for ((idx, b) in data.withIndex()) {
+ if (idx != 0)
+ str.append(':')
+ str.append(String.format("%02x", b).toUpperCase())
+ }
+ return str.toString()
+ }
+
}
diff --git a/src/main/java/at/bitfire/cert4android/CustomCertService.kt b/src/main/java/at/bitfire/cert4android/CustomCertService.kt
index 1591f53..b62b53d 100644
--- a/src/main/java/at/bitfire/cert4android/CustomCertService.kt
+++ b/src/main/java/at/bitfire/cert4android/CustomCertService.kt
@@ -14,6 +14,7 @@ import android.content.Context
import android.content.Intent
import android.util.Log
import android.widget.Toast
+import androidx.annotation.MainThread
import androidx.core.app.NotificationCompat
import org.conscrypt.Conscrypt
import java.io.ByteArrayInputStream
@@ -118,6 +119,7 @@ class CustomCertService: Service() {
// started service
+ @MainThread
override fun onStartCommand(intent: Intent?, flags: Int, id: Int): Int {
Constants.log.fine("Received command: $intent")
@@ -156,7 +158,11 @@ class CustomCertService: Service() {
untrustedCerts.remove(cert)
try {
- trustedKeyStore.setCertificateEntry(cert.subjectDN.name, cert)
+ // This is the key which is used to store the certificate. If the CN is used,
+ // there can be only one certificate per CN (which is not always desired),
+ // so we use the MD5 fingerprint.
+ val certKey = CertUtils.fingerprint(cert, "MD5")
+ trustedKeyStore.setCertificateEntry(certKey, cert)
saveKeyStore()
} catch(e: KeyStoreException) {
Constants.log.log(Level.SEVERE, "Couldn't add certificate into key store", e)
diff --git a/src/main/java/at/bitfire/cert4android/TrustCertificateActivity.kt b/src/main/java/at/bitfire/cert4android/TrustCertificateActivity.kt
index 8285c1d..e6653a7 100644
--- a/src/main/java/at/bitfire/cert4android/TrustCertificateActivity.kt
+++ b/src/main/java/at/bitfire/cert4android/TrustCertificateActivity.kt
@@ -18,14 +18,12 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import at.bitfire.cert4android.databinding.ActivityTrustCertificateBinding
import java.io.ByteArrayInputStream
-import java.security.MessageDigest
import java.security.cert.CertificateFactory
import java.security.cert.CertificateParsingException
import java.security.cert.X509Certificate
import java.security.spec.MGF1ParameterSpec.SHA1
import java.security.spec.MGF1ParameterSpec.SHA256
import java.text.DateFormat
-import java.util.*
import java.util.logging.Level
import kotlin.concurrent.thread
@@ -114,8 +112,8 @@ class TrustCertificateActivity: AppCompatActivity() {
validFrom.postValue(formatter.format(cert.notBefore))
validTo.postValue(formatter.format(cert.notAfter))
- sha1.postValue(fingerprint(cert, SHA1.digestAlgorithm))
- sha256.postValue(fingerprint(cert, SHA256.digestAlgorithm))
+ sha1.postValue("SHA1: " + CertUtils.fingerprint(cert, SHA1.digestAlgorithm))
+ sha256.postValue("SHA256: " + CertUtils.fingerprint(cert, SHA256.digestAlgorithm))
} catch(e: CertificateParsingException) {
Constants.log.log(Level.WARNING, "Couldn't parse certificate", e)
@@ -124,19 +122,6 @@ class TrustCertificateActivity: AppCompatActivity() {
}
}
- private fun fingerprint(cert: X509Certificate, algorithm: String) =
- try {
- val md = MessageDigest.getInstance(algorithm)
- "$algorithm: ${hexString(md.digest(cert.encoded))}"
- } catch(e: Exception) {
- e.message ?: "Couldn't create message digest"
- }
-
- private fun hexString(data: ByteArray): String {
- val str = data.mapTo(LinkedList()) { String.format("%02x", it) }
- return str.joinToString(":")
- }
-
}
} \ No newline at end of file
diff --git a/src/test/java/at/bitfire/cert4android/CertUtilsTest.kt b/src/test/java/at/bitfire/cert4android/CertUtilsTest.kt
index d508d1f..cd33b18 100644
--- a/src/test/java/at/bitfire/cert4android/CertUtilsTest.kt
+++ b/src/test/java/at/bitfire/cert4android/CertUtilsTest.kt
@@ -13,20 +13,31 @@ import org.junit.Assert.assertNotNull
import org.junit.Test
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
+import java.security.spec.MGF1ParameterSpec
class CertUtilsTest {
+ val certFactory = CertificateFactory.getInstance("X.509")
+
+
@Test
- fun getTrustManagerSystem() {
- assertNotNull(CertUtils.getTrustManager(null))
+ fun testFingerprint() {
+ javaClass.classLoader!!.getResourceAsStream("davdroid-web.crt").use { stream ->
+ val cert = certFactory.generateCertificate(stream) as X509Certificate
+ assertEquals("8D:E5:74:B2:AA:3E:5C:EE:62:84:4A:3B:78:71:B6:C3", CertUtils.fingerprint(cert, "MD5"))
+ assertEquals("6C:83:A0:12:1A:F5:55:BF:C2:BC:23:DA:78:E4:5F:88:6E:01:0A:BC", CertUtils.fingerprint(cert, MGF1ParameterSpec.SHA1.digestAlgorithm))
+ }
}
@Test
- fun getTag() {
- val factory = CertificateFactory.getInstance("X.509")
+ fun testGetTrustManagerSystem() {
+ assertNotNull(CertUtils.getTrustManager(null))
+ }
+ @Test
+ fun testGetTag() {
javaClass.classLoader!!.getResourceAsStream("davdroid-web.crt").use { stream ->
- val cert = factory.generateCertificate(stream) as X509Certificate
+ val cert = certFactory.generateCertificate(stream) as X509Certificate
assertNotNull(cert)
assertEquals("276126a80783ee16b84811e1e96e977a" +
@@ -48,4 +59,10 @@ class CertUtilsTest {
}
}
+ @Test
+ fun testHexString() {
+ assertEquals("", CertUtils.hexString(ByteArray(0)))
+ assertEquals("00:01:02:03:04:05:06:07:08:09:0A:0B:0C:0D:0E:0F:10", CertUtils.hexString(ByteArray(17) { i -> i.toByte() }))
+ }
+
}