1
1
// SPDX-FileCopyrightText: 2017-2023 Alexey Rochev <[email protected] >
2
+ // SPDX-FileCopyrightText: 2019 Thunderberry
2
3
//
3
4
// SPDX-License-Identifier: GPL-3.0-or-later
4
5
5
6
package org.equeim.tremotesf.torrentfile.rpc
6
7
8
+ import android.annotation.SuppressLint
9
+ import android.content.Context
10
+ import android.net.http.X509TrustManagerExtensions
11
+ import android.os.Build
7
12
import android.util.Base64
13
+ import androidx.annotation.Keep
14
+ import org.equeim.tremotesf.rpc.R
15
+ import java.io.InputStream
16
+ import java.security.InvalidAlgorithmParameterException
8
17
import java.security.KeyFactory
9
18
import java.security.KeyStore
10
- import java.security.SecureRandom
11
19
import java.security.cert.Certificate
20
+ import java.security.cert.CertificateException
12
21
import java.security.cert.CertificateFactory
22
+ import java.security.cert.X509Certificate
13
23
import java.security.spec.PKCS8EncodedKeySpec
24
+ import javax.net.ssl.HostnameVerifier
14
25
import javax.net.ssl.KeyManager
15
26
import javax.net.ssl.KeyManagerFactory
16
27
import javax.net.ssl.SSLContext
17
28
import javax.net.ssl.SSLSocketFactory
18
29
import javax.net.ssl.TrustManagerFactory
19
30
import javax.net.ssl.X509TrustManager
20
31
32
+
21
33
class TlsConfiguration (
22
34
val sslSocketFactory : SSLSocketFactory ,
23
35
val trustManager : X509TrustManager ,
36
+ val hostnameVerifier : HostnameVerifier ? ,
24
37
val clientCertificates : List <Certificate >,
25
38
)
26
39
@@ -30,59 +43,168 @@ class TlsConfiguration(
30
43
internal fun createTlsConfiguration (
31
44
clientCertificatesString : String? ,
32
45
selfSignedCertificatesString : String? ,
33
- ): TlsConfiguration = try {
34
- val certificateFactory = CertificateFactory .getInstance(" X.509" )
35
- val trustManager = createTrustManager(selfSignedCertificatesString, certificateFactory)
36
- val clientCertificates: List <Certificate >
37
- val keyManager: KeyManager ?
38
- if (clientCertificatesString != null ) {
39
- clientCertificates = parseCertificates(certificateFactory, clientCertificatesString, " client" )
40
- keyManager = createKeyManager(clientCertificates, clientCertificatesString)
41
- } else {
42
- clientCertificates = emptyList()
43
- keyManager = null
46
+ serverHostname : String ,
47
+ context : Context ,
48
+ ): TlsConfiguration ? {
49
+ // We need to set up ISRG Root X1 certificate for Android < 7.1
50
+ if (clientCertificatesString == null && selfSignedCertificatesString == null && Build .VERSION .SDK_INT >= Build .VERSION_CODES .N_MR1 ) {
51
+ return null
52
+ }
53
+ return try {
54
+ val certificateFactory = CertificateFactory .getInstance(" X.509" )
55
+
56
+ val trustManager = selfSignedCertificatesString?.let {
57
+ createTrustManagerForSelfSignedCertificates(it, certificateFactory)
58
+ } ? : createDefaultTrustManager(certificateFactory, context)
59
+
60
+ val hostnameVerifier = if (selfSignedCertificatesString != null ) {
61
+ HostnameVerifier { hostname, _ -> hostname == serverHostname }
62
+ } else {
63
+ null
64
+ }
65
+
66
+ val clientCertificates: List <Certificate >
67
+ val keyManager: KeyManager ?
68
+ if (clientCertificatesString != null ) {
69
+ clientCertificates = try {
70
+ certificateFactory.generateCertificates(clientCertificatesString.byteInputStream()).toList()
71
+ } catch (e: Exception ) {
72
+ throw RuntimeException (" Failed to parse client's certificate chain" , e)
73
+ }
74
+ keyManager = createKeyManagerForClientCertificate(clientCertificates, clientCertificatesString)
75
+ } else {
76
+ clientCertificates = emptyList()
77
+ keyManager = null
78
+ }
79
+ val sslContext = SSLContext .getInstance(" TLS" )
80
+ sslContext.init (keyManager?.let { arrayOf(it) }, arrayOf(trustManager), null )
81
+ TlsConfiguration (
82
+ sslSocketFactory = sslContext.socketFactory,
83
+ trustManager = trustManager,
84
+ hostnameVerifier = hostnameVerifier,
85
+ clientCertificates = clientCertificates,
86
+ )
87
+ } catch (e: Exception ) {
88
+ throw RuntimeException (" Failed to set up TLS configuration" , e)
44
89
}
45
- val sslContext = SSLContext .getInstance(" TLS" )
46
- sslContext.init (keyManager?.let { arrayOf(it) }, arrayOf(trustManager), SecureRandom ())
47
- TlsConfiguration (
48
- sslSocketFactory = sslContext.socketFactory,
49
- trustManager = trustManager,
50
- clientCertificates = clientCertificates,
51
- )
52
- } catch (e: Exception ) {
53
- throw RuntimeException (" Failed to set up TLS configuration" , e)
54
90
}
55
91
56
- private fun parseCertificates (factory : CertificateFactory , certificatesString : String , who : String ): List <Certificate > {
57
- return try {
58
- factory.generateCertificates(certificatesString.byteInputStream()).toList()
92
+ private fun createTrustManagerForSelfSignedCertificates (
93
+ selfSignedCertificatesString : String ,
94
+ certificateFactory : CertificateFactory ,
95
+ ): X509TrustManager =
96
+ try {
97
+ createTrustManagerForCertificateChain(selfSignedCertificatesString.byteInputStream(), certificateFactory)
59
98
} catch (e: Exception ) {
60
- throw RuntimeException (" Failed to parse $who 's certificates" )
99
+ throw RuntimeException (" Failed to create TrustManager for server's self signed certificate chain" , e)
100
+ }
101
+
102
+ private fun createDefaultTrustManager (
103
+ certificateFactory : CertificateFactory ,
104
+ context : Context ,
105
+ ): X509TrustManager = try {
106
+ val defaultTrustManager = TrustManagerFactory .getInstance(TrustManagerFactory .getDefaultAlgorithm()).run {
107
+ init (null as KeyStore ? )
108
+ trustManagers.single() as X509TrustManager
109
+ }
110
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .N_MR1 ) {
111
+ defaultTrustManager
112
+ } else {
113
+ CompositeX509TrustManager (
114
+ defaultTrustManager,
115
+ createTrustManagerForCertificateChain(
116
+ context.resources.openRawResource(R .raw.isrgrootx1),
117
+ certificateFactory
118
+ )
119
+ )
61
120
}
121
+ } catch (e: Exception ) {
122
+ throw RuntimeException (" Failed to create default TrustManager" , e)
62
123
}
63
124
64
- private fun createTrustManager (selfSignedCertificatesString : String? , certificateFactory : CertificateFactory ): X509TrustManager {
65
- val keyStore = selfSignedCertificatesString?.let {
66
- val selfSignedCertificates = parseCertificates(certificateFactory, it, " server" )
67
- val keyStore = KeyStore .getInstance(KeyStore .getDefaultType())
68
- keyStore.load(null , null )
69
- selfSignedCertificates.forEachIndexed { index, cert ->
70
- keyStore.setCertificateEntry(index.toString(), cert)
71
- }
72
- keyStore
125
+ private fun createTrustManagerForCertificateChain (
126
+ certificatesStream : InputStream ,
127
+ certificateFactory : CertificateFactory ,
128
+ ): X509TrustManager {
129
+ val certificates = certificatesStream.use(certificateFactory::generateCertificates)!!
130
+ if (certificates.isEmpty()) {
131
+ throw RuntimeException (" Did not read any certificates" )
132
+ }
133
+ val keyStore = KeyStore .getInstance(KeyStore .getDefaultType())
134
+ keyStore.load(null , null )
135
+ certificates.forEachIndexed { index, cert ->
136
+ keyStore.setCertificateEntry(index.toString(), cert)
73
137
}
74
138
val trustManagerFactory = TrustManagerFactory .getInstance(TrustManagerFactory .getDefaultAlgorithm())
75
139
trustManagerFactory.init (keyStore)
76
140
return trustManagerFactory.trustManagers.single() as X509TrustManager
77
141
}
78
142
143
+ @SuppressLint(" CustomX509TrustManager" )
144
+ private class CompositeX509TrustManager (vararg trustManagers : X509TrustManager ) : X509TrustManager {
145
+ private class TrustManagerAndExtensions (
146
+ val trustManager : X509TrustManager ,
147
+ val extensions : X509TrustManagerExtensions ,
148
+ ) {
149
+ constructor (trustManager: X509TrustManager ) : this (trustManager, X509TrustManagerExtensions (trustManager))
150
+ }
151
+
152
+ private val trustManagers: List <TrustManagerAndExtensions > = trustManagers.map(::TrustManagerAndExtensions )
153
+
154
+ override fun checkClientTrusted (chain : Array <out X509Certificate >? , authType : String? ) {
155
+ checkTrusted { trustManager.checkClientTrusted(chain, authType) }
156
+ }
157
+
158
+ override fun checkServerTrusted (chain : Array <out X509Certificate >? , authType : String? ) {
159
+ checkTrusted { trustManager.checkServerTrusted(chain, authType) }
160
+ }
161
+
162
+ override fun getAcceptedIssuers (): Array <X509Certificate > =
163
+ trustManagers.asSequence().flatMap { it.trustManager.acceptedIssuers.asSequence() }.toList().toTypedArray()
164
+
165
+ @Suppress(" unused" )
166
+ @Keep
167
+ fun checkServerTrusted (
168
+ chain : Array <X509Certificate ?>? , authType : String? , host : String? ,
169
+ ): List <X509Certificate > {
170
+ return checkTrusted { extensions.checkServerTrusted(chain, authType, host) }
171
+ }
172
+
173
+ private fun <T > checkTrusted (check : TrustManagerAndExtensions .() -> T ): T {
174
+ val certificateExceptions = mutableListOf<CertificateException >()
175
+ for (trustManager in trustManagers) {
176
+ try {
177
+ return trustManager.check()
178
+ } catch (e: CertificateException ) {
179
+ certificateExceptions.add(e)
180
+ } catch (e: RuntimeException ) {
181
+ val cause = e.cause
182
+ if (cause is InvalidAlgorithmParameterException ) {
183
+ // Handling of [InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty]
184
+ //
185
+ // This is most likely a result of using a TrustManager created from an empty KeyStore.
186
+ // The exception will be thrown during the SSL Handshake. It is safe to suppress
187
+ // and can be bundle with the other exceptions to proceed validating the counterparty with
188
+ // the remaining TrustManagers.
189
+ certificateExceptions.add(CertificateException (cause))
190
+ } else {
191
+ throw e
192
+ }
193
+ }
194
+ }
195
+ val certificateException = CertificateException (" None of the TrustManagers trust this certificate chain" )
196
+ certificateExceptions.forEach(certificateException::addSuppressed)
197
+ throw certificateException
198
+ }
199
+ }
200
+
79
201
private val PRIVATE_KEY_REGEX =
80
202
Regex (
81
203
""" .*-----BEGIN PRIVATE KEY-----([A-Za-z0-9+/=\n]+)-----END PRIVATE KEY-----.*""" ,
82
204
setOf (RegexOption .MULTILINE , RegexOption .DOT_MATCHES_ALL )
83
205
)
84
206
85
- private fun createKeyManager (clientCertificates : List <Certificate >, clientCertificatesString : String ): KeyManager {
207
+ private fun createKeyManagerForClientCertificate (clientCertificates : List <Certificate >, clientCertificatesString : String ): KeyManager {
86
208
val clientKey = try {
87
209
val spec = PKCS8EncodedKeySpec (
88
210
Base64 .decode(
0 commit comments