@@ -15,14 +15,20 @@ import kotlinx.coroutines.*
15
15
import okhttp3.CertificatePinner
16
16
import okhttp3.OkHttpClient
17
17
import okhttp3.Request
18
+ import okio.ByteString.Companion.decodeBase64
19
+ import okio.ByteString.Companion.toByteString
18
20
import java.io.BufferedInputStream
21
+ import java.io.BufferedReader
22
+ import java.io.InputStreamReader
23
+ import java.io.PrintWriter
19
24
import java.net.URL
20
25
import java.security.KeyStore
26
+ import java.security.cert.Certificate
21
27
import java.security.cert.CertificateFactory
22
- import javax.net.ssl.HttpsURLConnection
23
- import javax.net.ssl.SSLContext
24
- import javax.net.ssl.TrustManagerFactory
28
+ import java.security.cert.X509Certificate
29
+ import javax.net.ssl.*
25
30
31
+ const val BADSSL_UNTRUSTED_ROOT_SHA256 = " sr2tjak7H6QRi8o0fyIXGWdPiU32rDsczcIEAqA+s4g="
26
32
27
33
class MainActivity : AppCompatActivity () {
28
34
override fun onCreate (savedInstanceState : Bundle ? ) {
@@ -120,15 +126,14 @@ class MainActivity : AppCompatActivity() {
120
126
try {
121
127
val hostname = " badssl.com"
122
128
val certificatePinner = CertificatePinner .Builder ()
123
- // DigiCert SHA2 Secure Server CA (valid until March 2023)
124
- .add(hostname, " sha256/5kJvNEMw0KjrCAu7eXY5HZdvyCS13BbA0VJG1RSP91w=" )
129
+ .add(hostname, " sha256/${BADSSL_UNTRUSTED_ROOT_SHA256 } " )
125
130
.build()
126
131
127
132
val client = OkHttpClient .Builder ()
128
133
.certificatePinner(certificatePinner)
129
134
.build()
130
135
val request = Request .Builder ()
131
- .url(" https://badssl.com" )
136
+ .url(" https://untrusted-root. badssl.com" )
132
137
.build();
133
138
134
139
client.newCall(request).execute().use { response ->
@@ -212,4 +217,56 @@ class MainActivity : AppCompatActivity() {
212
217
}
213
218
}
214
219
}
220
+
221
+ fun sendManuallyCustomPinned (view : View ) {
222
+ GlobalScope .launch(Dispatchers .IO ) {
223
+ onStart(R .id.manually_pinned)
224
+ try {
225
+ // Disable trust manager checks - we'll check the certificate manually ourselves later
226
+ val trustManager = arrayOf<TrustManager >(object : X509TrustManager {
227
+ override fun getAcceptedIssuers (): Array <X509Certificate ?>? {
228
+ return null
229
+ }
230
+
231
+ override fun checkClientTrusted (certs : Array <X509Certificate ?>? , authType : String? ) {}
232
+ override fun checkServerTrusted (certs : Array <X509Certificate ?>? , authType : String? ) {}
233
+ })
234
+
235
+ val context = SSLContext .getInstance(" TLS" )
236
+ context.init (null , trustManager, null )
237
+
238
+ val socket = context.socketFactory.createSocket(" untrusted-root.badssl.com" , 443 ) as SSLSocket
239
+
240
+ val certs = socket.session.peerCertificates
241
+
242
+ if (! certs.any { cert -> doesCertMatchPin(BADSSL_UNTRUSTED_ROOT_SHA256 , cert) }) {
243
+ socket.close() // Close the socket immediately without sending a request
244
+ throw Error (" Unrecognized cert hash." )
245
+ }
246
+
247
+ // Send a real request, just to make it clear that we trust the connection:
248
+ val pw = PrintWriter (socket.outputStream)
249
+ pw.println (" GET / HTTP/1.1" )
250
+ pw.println (" Host: untrusted-root.badssl.com" )
251
+ pw.println (" " )
252
+ pw.flush()
253
+
254
+ val br = BufferedReader (InputStreamReader (socket.inputStream))
255
+ val responseLine = br.readLine()
256
+
257
+ println (" Response was: $responseLine " )
258
+ socket.close()
259
+
260
+ onSuccess(R .id.manually_pinned)
261
+ } catch (e: Throwable ) {
262
+ println (e)
263
+ onError(R .id.manually_pinned, e.toString())
264
+ }
265
+ }
266
+ }
267
+
268
+ private fun doesCertMatchPin (pin : String , cert : Certificate ): Boolean {
269
+ val certHash = cert.publicKey.encoded.toByteString().sha256()
270
+ return certHash == pin.decodeBase64()
271
+ }
215
272
}
0 commit comments