1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package com.vauth.foxyvpn.vpn.upstream
import android.content.Context
import com.vauth.foxyvpn.R
import com.vauth.foxyvpn.data.AppLogger
import java.io.ByteArrayInputStream
import java.security.KeyStore
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import javax.net.ssl.TrustManagerFactory
private const val TAG = "UpstreamTrust"
private val PEM_BLOCK = Regex("-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----", RegexOption.DOT_MATCHES_ALL)
object UpstreamTrust {
@Volatile private var appContext: Context? = null
@Volatile private var cached: TrustManagerFactory? = null
@Volatile private var built = false
private val lock = Any()
fun init(context: Context) {
appContext = context.applicationContext
}
fun trustManagerFactory(): TrustManagerFactory? {
if (built) return cached
synchronized(lock) {
if (!built) {
cached = runCatching { build() }
.onFailure { AppLogger.e(TAG, "could not build the merged trust store; using the system default", it) }
.getOrNull()
built = true
}
return cached
}
}
private fun build(): TrustManagerFactory? {
val merged = KeyStore.getInstance(KeyStore.getDefaultType()).apply { load(null, null) }
var systemCount = 0
runCatching {
val system = KeyStore.getInstance("AndroidCAStore").apply { load(null, null) }
for (alias in system.aliases()) {
val cert = system.getCertificate(alias) as? X509Certificate ?: continue
merged.setCertificateEntry("system:$alias", cert)
systemCount++
}
}.onFailure {
AppLogger.w(TAG, "could not read the system CA store; relying on the bundled roots only", it)
}
var bundledCount = 0
val context = appContext
if (context == null) {
AppLogger.w(TAG, "no application context; the bundled roots were not loaded")
} else {
runCatching {
val pem = context.resources.openRawResource(R.raw.globalsign_roots).use { it.readBytes() }
.toString(Charsets.US_ASCII)
val factory = CertificateFactory.getInstance("X.509")
for (block in PEM_BLOCK.findAll(pem)) {
runCatching {
val cert = factory.generateCertificate(
ByteArrayInputStream(block.value.toByteArray(Charsets.US_ASCII)),
) as X509Certificate
merged.setCertificateEntry("bundled:$bundledCount", cert)
bundledCount++
}
}
}.onFailure {
AppLogger.w(TAG, "could not load the bundled GlobalSign roots", it)
}
}
if (systemCount == 0 && bundledCount == 0) return null
AppLogger.i(TAG, "upstream TLS trust: $systemCount system root(s) + $bundledCount bundled root(s)")
return TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply { init(merged) }
}
}