tls config changes

This commit is contained in:
Jonathan Fisher 2020-11-11 12:22:48 +00:00
parent 50cf425a5b
commit ab6635af69
3 changed files with 112 additions and 53 deletions

View File

@ -11,24 +11,27 @@ object DummyTrustManager {
fun get(): Array<TrustManager> { fun get(): Array<TrustManager> {
return arrayOf( return arrayOf(
object : X509TrustManager { object : X509TrustManager {
@Throws(CertificateException::class)
override fun checkClientTrusted( override fun checkClientTrusted(
chain: Array<X509Certificate?>?, chain: Array<out X509Certificate>?,
authType: String? authType: String?
) { ) {
} }
@Throws(CertificateException::class)
override fun checkServerTrusted( override fun checkServerTrusted(
chain: Array<X509Certificate?>?, chain: Array<out X509Certificate>?,
authType: String? authType: String?
) { ) {
println("checkServerTrusted()")
println("checkServerTrusted() authType: $authType")
chain?.forEach { cert ->
println("checkServerTrusted() cert: ${cert.subjectDN}")
}
} }
override fun getAcceptedIssuers(): Array<X509Certificate?>? { override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf() return arrayOf()
} }
} })
)
} }
} }

View File

@ -22,7 +22,8 @@ const val GEMINI_SCHEME = "gemini"
* *
*/ */
class GeminiDatasource( class GeminiDatasource(
private val context: Context): Datasource { private val context: Context
): Datasource {
private val prefs = PreferenceManager.getDefaultSharedPreferences(context) private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
private var last: URI? = null private var last: URI? = null
@ -38,7 +39,7 @@ class GeminiDatasource(
when (uri.scheme) { when (uri.scheme) {
GEMINI_SCHEME -> { GEMINI_SCHEME -> {
val cached = RuntimeCache.get(uri) val cached = RuntimeCache.get(uri)
if(cached != null){ if (cached != null) {
last = uri last = uri
onUpdate( onUpdate(
GemState.ResponseGemtext( GemState.ResponseGemtext(
@ -48,7 +49,7 @@ class GeminiDatasource(
) )
) )
return return
}else{ } else {
onUpdate(GemState.Requesting(uri)) onUpdate(GemState.Requesting(uri))
GlobalScope.launch { GlobalScope.launch {
@ -71,7 +72,10 @@ class GeminiDatasource(
!address.contains("://") -> { !address.contains("://") -> {
//looks like a relative link //looks like a relative link
val lastAddress = last.toString() val lastAddress = last.toString()
val relAddress = "${lastAddress.substring(0, lastAddress.lastIndexOf("/") + 1)}$address" val relAddress = "${lastAddress.substring(
0,
lastAddress.lastIndexOf("/") + 1
)}$address"
URI.create(relAddress) URI.create(relAddress)
} }
else -> { else -> {
@ -104,26 +108,6 @@ class GeminiDatasource(
https://framagit.org/waweic/gemini-client/-/blob/master/app/src/main/java/rocks/ism/decentral/geminiclient/GeminiConnection.kt https://framagit.org/waweic/gemini-client/-/blob/master/app/src/main/java/rocks/ism/decentral/geminiclient/GeminiConnection.kt
* *
*/ */
private val trustAllCerts: Array<TrustManager> = arrayOf(object : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
}
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
println("checkServerTrusted()")
println("checkServerTrusted() authType: $authType")
chain?.forEach { cert ->
println("checkServerTrusted() cert: ${cert.subjectDN}")
}
}
override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf()
}
})
private fun geminiRequest(uri: URI, onUpdate: (state: GemState) -> Unit){ private fun geminiRequest(uri: URI, onUpdate: (state: GemState) -> Unit){
last = uri last = uri
val port = if(uri.port == -1) 1965 else uri.port val port = if(uri.port == -1) 1965 else uri.port
@ -132,23 +116,28 @@ class GeminiDatasource(
println("REQ_PROTOCOL: $protocol") println("REQ_PROTOCOL: $protocol")
val sslContext = SSLContext.getInstance(protocol) //todo - extract and reuse this
sslContext.init(null, trustAllCerts, SecureRandom()) val sslContext = if(protocol == "TLS_ALL"){
SSLContext.getInstance("TLS")
}else{
SSLContext.getInstance(protocol)
}
sslContext.init(null, DummyTrustManager.get(), null)
val factory: SSLSocketFactory = sslContext.socketFactory val factory: SSLSocketFactory = sslContext.socketFactory
val socket: SSLSocket? val socket: SSLSocket?
try { try {
socket = factory.createSocket(uri.host, port) as SSLSocket socket = factory.createSocket(uri.host, port) as SSLSocket
socket.enabledCipherSuites = factory.supportedCipherSuites
when (protocol) { when (protocol) {
"TLS" -> socket.enabledProtocols = socket.supportedProtocols "TLS" -> {}//Use default enabled protocols
"TLS_ALL" -> socket.enabledProtocols = socket.supportedProtocols
else -> socket.enabledProtocols = arrayOf(protocol) else -> socket.enabledProtocols = arrayOf(protocol)
} }
socket.startHandshake() socket.startHandshake()
}catch(ce: ConnectException){ }catch (ce: ConnectException){
println("socket error: $ce") println("socket error: $ce")
onUpdate( onUpdate(
GemState.ResponseError( GemState.ResponseError(
@ -159,7 +148,7 @@ class GeminiDatasource(
) )
) )
return return
}catch(she: SSLHandshakeException){ }catch (she: SSLHandshakeException){
println("socket error: $she") println("socket error: $she")
onUpdate( onUpdate(
GemState.ResponseError( GemState.ResponseError(
@ -209,7 +198,12 @@ class GeminiDatasource(
header.code == GeminiResponse.INPUT -> onUpdate(GemState.ResponseInput(uri, header)) header.code == GeminiResponse.INPUT -> onUpdate(GemState.ResponseInput(uri, header))
header.code == GeminiResponse.REDIRECT -> request(URI.create(header.meta), onUpdate) header.code == GeminiResponse.REDIRECT -> request(URI.create(header.meta), onUpdate)
header.code != GeminiResponse.SUCCESS -> onUpdate(GemState.ResponseError(header)) header.code != GeminiResponse.SUCCESS -> onUpdate(GemState.ResponseError(header))
header.meta.startsWith("text/gemini") -> getGemtext(bufferedReader, uri, header, onUpdate) header.meta.startsWith("text/gemini") -> getGemtext(
bufferedReader,
uri,
header,
onUpdate
)
header.meta.startsWith("text/") -> getString(socket, uri, header, onUpdate) header.meta.startsWith("text/") -> getString(socket, uri, header, onUpdate)
header.meta.startsWith("image/") -> getBinary(socket, uri, header, onUpdate) header.meta.startsWith("image/") -> getBinary(socket, uri, header, onUpdate)
header.meta.startsWith("audio/") -> getBinary(socket, uri, header, onUpdate) header.meta.startsWith("audio/") -> getBinary(socket, uri, header, onUpdate)
@ -228,7 +222,12 @@ class GeminiDatasource(
socket.close() socket.close()
} }
private fun getGemtext(reader: BufferedReader, uri: URI, header: GeminiResponse.Header, onUpdate: (state: GemState) -> Unit){ private fun getGemtext(
reader: BufferedReader,
uri: URI,
header: GeminiResponse.Header,
onUpdate: (state: GemState) -> Unit
){
val lines = mutableListOf<String>() val lines = mutableListOf<String>()
@ -243,22 +242,53 @@ class GeminiDatasource(
onUpdate(GemState.ResponseGemtext(uri, header, processed)) onUpdate(GemState.ResponseGemtext(uri, header, processed))
} }
private fun getString(socket: SSLSocket?, uri: URI, header: GeminiResponse.Header, onUpdate: (state: GemState) -> Unit){ private fun getString(
socket: SSLSocket?,
uri: URI,
header: GeminiResponse.Header,
onUpdate: (state: GemState) -> Unit
){
val content = socket?.inputStream?.bufferedReader().use { reader -> reader?.readText() } val content = socket?.inputStream?.bufferedReader().use { reader -> reader?.readText() }
socket?.close() socket?.close()
onUpdate(GemState.ResponseText(uri, header, content ?: "Error fetching content")) onUpdate(GemState.ResponseText(uri, header, content ?: "Error fetching content"))
} }
private fun getBinary(socket: SSLSocket?, uri: URI, header: GeminiResponse.Header, onUpdate: (state: GemState) -> Unit){ private fun getBinary(
socket: SSLSocket?,
uri: URI,
header: GeminiResponse.Header,
onUpdate: (state: GemState) -> Unit
){
val filenameRegex = Regex("[^A-Za-z0-9]") var filename: String? = null
val cacheFile = File(context.cacheDir, filenameRegex.replace(uri.path, "_")) val fileSegmentIndex: Int = uri.path.lastIndexOf('/')
if (fileSegmentIndex != -1) {
filename = uri.path.substring(fileSegmentIndex + 1)
}
val host = uri.host.replace(".", "_")
val cacheName = "${host}_$filename"
println("Caching file: $filename from uri: $uri, cacheName: $cacheName")
val cacheFile = File(context.cacheDir, cacheName)
when { when {
cacheFile.exists() -> { cacheFile.exists() -> {
when { when {
header.meta.startsWith("image/") -> onUpdate(GemState.ResponseImage(uri, header, cacheFile.toUri())) header.meta.startsWith("image/") -> onUpdate(
header.meta.startsWith("audio/") -> onUpdate(GemState.ResponseAudio(uri, header, cacheFile.toUri())) GemState.ResponseImage(
uri,
header,
cacheFile.toUri()
)
)
header.meta.startsWith("audio/") -> onUpdate(
GemState.ResponseAudio(
uri,
header,
cacheFile.toUri()
)
)
} }
} }
@ -270,8 +300,20 @@ class GeminiDatasource(
} }
when { when {
header.meta.startsWith("image/") -> onUpdate(GemState.ResponseImage(uri, header, cacheFile.toUri())) header.meta.startsWith("image/") -> onUpdate(
header.meta.startsWith("audio/") -> onUpdate(GemState.ResponseAudio(uri, header, cacheFile.toUri())) GemState.ResponseImage(
uri,
header,
cacheFile.toUri()
)
)
header.meta.startsWith("audio/") -> onUpdate(
GemState.ResponseAudio(
uri,
header,
cacheFile.toUri()
)
)
} }
} }
} }

View File

@ -19,6 +19,9 @@ class SettingsFragment: PreferenceFragmentCompat(), Preference.OnPreferenceChang
val context = preferenceManager.context val context = preferenceManager.context
val screen = preferenceManager.createPreferenceScreen(context) val screen = preferenceManager.createPreferenceScreen(context)
/**
* TLS Settings
*/
val tlsCategory = PreferenceCategory(context) val tlsCategory = PreferenceCategory(context)
tlsCategory.key = "tls_category" tlsCategory.key = "tls_category"
tlsCategory.title = "TLS Config" tlsCategory.title = "TLS Config"
@ -30,6 +33,12 @@ class SettingsFragment: PreferenceFragmentCompat(), Preference.OnPreferenceChang
tlsDefaultPreference.onPreferenceChangeListener = this tlsDefaultPreference.onPreferenceChangeListener = this
tlsCategory.addPreference(tlsDefaultPreference) tlsCategory.addPreference(tlsDefaultPreference)
val tlsAllSupportedPreference = SwitchPreferenceCompat(context)
tlsAllSupportedPreference.key = "tls_All_Supported"
tlsAllSupportedPreference.title = "TLS Enable all supported"
tlsAllSupportedPreference.onPreferenceChangeListener = this
tlsCategory.addPreference(tlsAllSupportedPreference)
val sslContext = SSLContext.getInstance("TLS") val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, null, SecureRandom()) sslContext.init(null, null, SecureRandom())
val factory: SSLSocketFactory = sslContext.socketFactory val factory: SSLSocketFactory = sslContext.socketFactory
@ -55,6 +64,10 @@ class SettingsFragment: PreferenceFragmentCompat(), Preference.OnPreferenceChang
val default = preferenceScreen.findPreference<SwitchPreferenceCompat>("tls_Default") val default = preferenceScreen.findPreference<SwitchPreferenceCompat>("tls_Default")
default?.isChecked = false default?.isChecked = false
} }
if(key != "tls_All_Supported"){
val default = preferenceScreen.findPreference<SwitchPreferenceCompat>("tls_All_Supported")
default?.isChecked = false
}
protocols.forEach { protocol -> protocols.forEach { protocol ->
val tlsSwitchKey = "tls_${protocol.toLowerCase(Locale.getDefault())}" val tlsSwitchKey = "tls_${protocol.toLowerCase(Locale.getDefault())}"
if (tlsSwitchKey != key) { if (tlsSwitchKey != key) {
@ -66,12 +79,13 @@ class SettingsFragment: PreferenceFragmentCompat(), Preference.OnPreferenceChang
} }
} }
val prefTitle = preference.title.toString() when (preference.key) {
"tls_Default" -> setTLSProtocol("TLS")
if(prefTitle.toLowerCase().contains("default")){ "tls_All_Supported" -> setTLSProtocol("TLS_ALL")
setTLSProtocol("TLS") else -> {
}else{ val prefTitle = preference.title.toString()
setTLSProtocol(prefTitle) setTLSProtocol(prefTitle)
}
} }
return true return true