-
Notifications
You must be signed in to change notification settings - Fork 74
Expand file tree
/
Copy pathHttpConnectionManagement.kt
More file actions
164 lines (147 loc) · 6.15 KB
/
HttpConnectionManagement.kt
File metadata and controls
164 lines (147 loc) · 6.15 KB
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package io.github.benoitduffez.cupsprint
import android.content.Context
import android.util.Base64
import io.github.benoitduffez.cupsprint.app.BasicAuthActivity
import io.github.benoitduffez.cupsprint.ssl.AdditionalKeyManager
import io.github.benoitduffez.cupsprint.ssl.AdditionalKeyStoresSSLSocketFactory
import io.github.benoitduffez.cupsprint.ssl.AndroidCupsHostnameVerifier
import timber.log.Timber
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.io.UnsupportedEncodingException
import java.net.HttpURLConnection
import java.net.URL
import java.security.*
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.KeyManager
private const val KEYSTORE_FILE = "cupsprint-trustfile"
private const val KEYSTORE_PASSWORD = "i6:[(mW*xh~=Ni;S|?8lz8eZ;!SU(S"
object HttpConnectionManagement {
/**
* Will handle SSL related stuff to this connection so that certs are properly managed
*
* @param connection The target https connection
*/
fun handleHttpsUrlConnection(context: Context, connection: HttpsURLConnection) {
connection.hostnameVerifier = AndroidCupsHostnameVerifier(context)
try {
val trustStore = loadKeyStore(context) ?: return
var keyManager: KeyManager? = null
try {
keyManager = AdditionalKeyManager.fromAlias(context)
} catch (e: CertificateException) {
Timber.e(e, "Couldn't load system key store: ${e.localizedMessage}")
}
connection.sslSocketFactory = AdditionalKeyStoresSSLSocketFactory(keyManager, trustStore)
} catch (e: NoSuchAlgorithmException) {
Timber.e(e, "Couldn't handle SSL URL connection: ${e.localizedMessage}")
} catch (e: UnrecoverableKeyException) {
Timber.e(e, "Couldn't handle SSL URL connection: ${e.localizedMessage}")
} catch (e: KeyStoreException) {
Timber.e(e, "Couldn't handle SSL URL connection: ${e.localizedMessage}")
} catch (e: KeyManagementException) {
Timber.e(e, "Couldn't handle SSL URL connection: ${e.localizedMessage}")
}
}
/**
* Try to get the contents of the local key store
*
* @return A valid KeyStore object if nothing went wrong, null otherwise
*/
private fun loadKeyStore(context: Context): KeyStore? {
val trustStore: KeyStore
try {
trustStore = KeyStore.getInstance(KeyStore.getDefaultType())
} catch (e: KeyStoreException) {
Timber.e(e, "Couldn't open local key store")
return null
}
// Load the local keystore into memory
try {
val fis = context.openFileInput(KEYSTORE_FILE)
trustStore.load(fis, KEYSTORE_PASSWORD.toCharArray())
return trustStore
} catch (e: FileNotFoundException) {
// This one can be ignored safely - at least not sent to crashlytics
Timber.e("Couldn't open local key store: ${e.localizedMessage}")
} catch (e: IOException) {
Timber.e(e, "Couldn't open local key store")
} catch (e: NoSuchAlgorithmException) {
Timber.e(e, "Couldn't open local key store")
} catch (e: CertificateException) {
Timber.e(e, "Couldn't open local key store")
}
// if we couldn't load local keystore file, create an new empty one
try {
trustStore.load(null, null)
} catch (e: IOException) {
Timber.e(e, "Couldn't create new key store")
} catch (e: NoSuchAlgorithmException) {
Timber.e(e, "Couldn't create new key store")
} catch (e: CertificateException) {
Timber.e(e, "Couldn't create new key store")
}
return trustStore
}
/**
* Add certs to the keystore (thus trusting them)
*
* @param chain The chain of certs to trust
* @return true if it was saved, false otherwise
*/
fun saveCertificates(context: Context, chain: Array<X509Certificate>): Boolean {
// Load existing certs
val trustStore = loadKeyStore(context) ?: return false
// Add new certs
try {
for (c in chain) {
trustStore.setCertificateEntry(c.subjectDN.toString(), c)
}
} catch (e: KeyStoreException) {
Timber.e(e, "Couldn't store cert chain into key store")
return false
}
// Save new keystore
var fos: FileOutputStream? = null
try {
fos = context.openFileOutput(KEYSTORE_FILE, Context.MODE_PRIVATE)
trustStore.store(fos, KEYSTORE_PASSWORD.toCharArray())
fos!!.close()
} catch (e: Exception) {
Timber.e(e, "Unable to save key store")
} finally {
if (fos != null) {
try {
fos.close()
} catch (e: IOException) {
Timber.e(e, "Couldn't close key store")
}
}
}
return true
}
/**
* See if there are some basic auth credentials saved, and configure the connection
*
* @param url URL we're about to request
* @param connection The connection to be configured
*/
fun handleBasicAuth(context: Context, url: URL, connection: HttpURLConnection) {
val prefs = context.getSharedPreferences(BasicAuthActivity.CREDENTIALS_FILE, Context.MODE_PRIVATE)
val id = BasicAuthActivity.findSavedCredentialsId(url.toString(), prefs)
if (id < 0) {
return
}
val username = prefs.getString(BasicAuthActivity.KEY_BASIC_AUTH_LOGIN + id, "")
val password = prefs.getString(BasicAuthActivity.KEY_BASIC_AUTH_PASSWORD + id, "")
try {
val encoded = Base64.encodeToString(("$username:$password").toByteArray(charset("UTF-8")), Base64.NO_WRAP)
connection.setRequestProperty("Authorization", "Basic $encoded")
} catch (e: UnsupportedEncodingException) {
Timber.e(e, "Couldn't base64 encode basic auth credentials")
}
}
}