Skip to content

Commit 8411df9

Browse files
committed
Cover 3 more Appmattus cases & split domains to scope config explicitly
The goal with the config is to ensure that it's the explicit pinning parameters provided inline within each implementation that's doing the pinning, not some other background check due to more config (which would confuse things and maybe avoid testing some unpinning techniques).
1 parent 1faec0f commit 8411df9

File tree

3 files changed

+170
-15
lines changed

3 files changed

+170
-15
lines changed

app/src/main/java/tech/httptoolkit/pinning_demo/MainActivity.kt

Lines changed: 124 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import android.graphics.drawable.Drawable
44
import android.net.http.SslError
55
import android.os.Bundle
66
import android.view.View
7-
import android.webkit.SslErrorHandler
8-
import android.webkit.WebView
9-
import android.webkit.WebViewClient
7+
import android.webkit.*
108
import android.widget.Button
119
import android.widget.Toast
1210
import androidx.annotation.IdRes
@@ -17,7 +15,10 @@ import com.android.volley.toolbox.BasicNetwork
1715
import com.android.volley.toolbox.HurlStack
1816
import com.android.volley.toolbox.NoCache
1917
import com.android.volley.toolbox.StringRequest
18+
import com.appmattus.certificatetransparency.certificateTransparencyHostnameVerifier
2019
import com.appmattus.certificatetransparency.certificateTransparencyInterceptor
20+
import com.appmattus.certificatetransparency.certificateTransparencyTrustManager
21+
import com.appmattus.certificatetransparency.installCertificateTransparencyProvider
2122
import com.datatheorem.android.trustkit.TrustKit
2223
import kotlinx.coroutines.Dispatchers
2324
import kotlinx.coroutines.GlobalScope
@@ -46,6 +47,14 @@ class MainActivity : AppCompatActivity() {
4647
setContentView(R.layout.activity_main)
4748

4849
TrustKit.initializeWithNetworkSecurityConfiguration(this@MainActivity)
50+
51+
// Appmattus global setup:
52+
installCertificateTransparencyProvider {
53+
// Match only our single Appmattus test domain:
54+
-"*.httptoolkit.tech"
55+
-"*.badssl.com"
56+
+"rsa4096.badssl.com"
57+
}
4958
}
5059

5160
private fun onStart(@IdRes id: Int) {
@@ -96,7 +105,7 @@ class MainActivity : AppCompatActivity() {
96105
GlobalScope.launch(Dispatchers.IO) {
97106
onStart(R.id.unpinned)
98107
try {
99-
val mURL = URL("https://httptoolkit.com")
108+
val mURL = URL("https://amiusing.httptoolkit.tech")
100109
with(mURL.openConnection() as HttpsURLConnection) {
101110
println("URL: ${this.url}")
102111
println("Response Code: ${this.responseCode}")
@@ -116,7 +125,7 @@ class MainActivity : AppCompatActivity() {
116125

117126
var connectionFailed = false
118127

119-
webView.loadUrl("https://sha256.badssl.com")
128+
webView.loadUrl("https://amiusing.httptoolkit.tech")
120129
webView.webViewClient = object : WebViewClient() {
121130
override fun onReceivedSslError(
122131
view: WebView?,
@@ -161,7 +170,7 @@ class MainActivity : AppCompatActivity() {
161170
onStart(R.id.okhttp_pinned)
162171

163172
try {
164-
val hostname = "sha256.badssl.com"
173+
val hostname = "ecc384.badssl.com"
165174
val certificatePinner = CertificatePinner.Builder()
166175
.add(hostname, "sha256/${LETS_ENCRYPT_ROOT_SHA256}")
167176
.build()
@@ -170,7 +179,7 @@ class MainActivity : AppCompatActivity() {
170179
.certificatePinner(certificatePinner)
171180
.build()
172181
val request = Request.Builder()
173-
.url("https://sha256.badssl.com")
182+
.url("https://ecc384.badssl.com")
174183
.build()
175184

176185
client.newCall(request).execute().use { response ->
@@ -216,7 +225,7 @@ class MainActivity : AppCompatActivity() {
216225
// Make a request using that client:
217226
val stringRequest = StringRequest(
218227
com.android.volley.Request.Method.GET,
219-
"https://sha256.badssl.com",
228+
"https://ecc384.badssl.com",
220229
{ _ ->
221230
println("Volley success")
222231
this@MainActivity.onSuccess(R.id.volley_pinned)
@@ -258,6 +267,26 @@ class MainActivity : AppCompatActivity() {
258267
fun sendAppmattusCTChecked(view: View) {
259268
GlobalScope.launch(Dispatchers.IO) {
260269
onStart(R.id.appmattus_ct_checked)
270+
271+
try {
272+
val mURL = URL("https://sha256.badssl.com")
273+
with(mURL.openConnection() as HttpsURLConnection) {
274+
this.hostnameVerifier = certificateTransparencyHostnameVerifier(this.hostnameVerifier)
275+
println("URL: ${this.url}")
276+
println("Response Code: ${this.responseCode}")
277+
}
278+
279+
onSuccess(R.id.appmattus_ct_checked)
280+
} catch (e: Throwable) {
281+
println(e)
282+
onError(R.id.appmattus_ct_checked, e.toString())
283+
}
284+
}
285+
}
286+
287+
fun sendAppmattusOkHttpCTChecked(view: View) {
288+
GlobalScope.launch(Dispatchers.IO) {
289+
onStart(R.id.appmattus_okhttp_ct_checked)
261290
try {
262291
val appmattusInterceptor = certificateTransparencyInterceptor()
263292
val client = OkHttpClient.Builder().apply {
@@ -272,10 +301,92 @@ class MainActivity : AppCompatActivity() {
272301
println("Response Code: ${response.code}")
273302
}
274303

275-
onSuccess(R.id.appmattus_ct_checked)
304+
onSuccess(R.id.appmattus_okhttp_ct_checked)
276305
} catch (e: Throwable) {
277306
println(e)
278-
onError(R.id.appmattus_ct_checked, e.toString())
307+
onError(R.id.appmattus_okhttp_ct_checked, e.toString())
308+
}
309+
}
310+
}
311+
312+
fun sendAppmattusRawCTChecked(view: View) {
313+
GlobalScope.launch(Dispatchers.IO) {
314+
onStart(R.id.appmattus_raw_ct_checked)
315+
316+
val cf = CertificateFactory.getInstance("X.509")
317+
val caStream = BufferedInputStream(resources.openRawResource(R.raw.lets_encrypt_isrg_root))
318+
val caCertificate = cf.generateCertificate(caStream)
319+
320+
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType())
321+
keyStore.load(null)
322+
keyStore.setCertificateEntry("ca", caCertificate)
323+
324+
val trustManagerFactory = TrustManagerFactory
325+
.getInstance(TrustManagerFactory.getDefaultAlgorithm())
326+
trustManagerFactory.init(keyStore)
327+
328+
val originalTrustManagers = trustManagerFactory.trustManagers;
329+
330+
// Wrap the native trust managers with Appmattus's CT implementation:
331+
val ctWrappedTrustManagers = originalTrustManagers.map { tm ->
332+
certificateTransparencyTrustManager(tm as X509TrustManager)
333+
}.toTypedArray()
334+
335+
try {
336+
val context = SSLContext.getInstance("TLS")
337+
context.init(null, ctWrappedTrustManagers, null)
338+
339+
val mURL = URL("https://ecc384.badssl.com")
340+
with(mURL.openConnection() as HttpsURLConnection) {
341+
this.sslSocketFactory = context.socketFactory
342+
343+
println("URL: ${this.url}")
344+
println("Response Code: ${this.responseCode}")
345+
}
346+
onSuccess(R.id.appmattus_raw_ct_checked)
347+
} catch (e: Throwable) {
348+
println(e)
349+
onError(R.id.appmattus_raw_ct_checked, e.toString())
350+
}
351+
}
352+
}
353+
354+
// Pinned by global setup from installCertificateTransparencyProvider
355+
// call in onCreate():
356+
fun sendAppmattusCTWebView(view: View) {
357+
onStart(R.id.appmattus_webview_ct_checked)
358+
val webView = WebView(this@MainActivity)
359+
360+
var connectionFailed = false
361+
362+
webView.loadUrl("https://rsa4096.badssl.com")
363+
webView.webViewClient = object : WebViewClient() {
364+
override fun onReceivedSslError(
365+
view: WebView?,
366+
handler: SslErrorHandler?,
367+
error: SslError?
368+
) {
369+
println("Appmattus webview SSL error: " + error.toString())
370+
onError(R.id.appmattus_webview_ct_checked, error.toString())
371+
connectionFailed = true
372+
handler?.cancel()
373+
}
374+
375+
override fun onReceivedError(
376+
view: WebView?,
377+
request: WebResourceRequest?,
378+
error: WebResourceError?
379+
) {
380+
println("Appmattus webview error: " + error.toString())
381+
onError(R.id.appmattus_webview_ct_checked, error.toString())
382+
connectionFailed = true
383+
}
384+
385+
override fun onPageFinished(view: WebView?, url: String?) {
386+
if (connectionFailed) return
387+
388+
println("Appmattus WebView loaded OK")
389+
onSuccess(R.id.appmattus_webview_ct_checked)
279390
}
280391
}
281392
}
@@ -302,7 +413,7 @@ class MainActivity : AppCompatActivity() {
302413
val context = SSLContext.getInstance("TLS")
303414
context.init(null, trustManagerFactory.trustManagers, null)
304415

305-
val mURL = URL("https://sha256.badssl.com")
416+
val mURL = URL("https://ecc384.badssl.com")
306417
with(mURL.openConnection() as HttpsURLConnection) {
307418
this.sslSocketFactory = context.socketFactory
308419

@@ -338,7 +449,7 @@ class MainActivity : AppCompatActivity() {
338449
val context = SSLContext.getInstance("TLS")
339450
context.init(null, trustManager, null)
340451

341-
val socket = context.socketFactory.createSocket("sha256.badssl.com", 443) as SSLSocket
452+
val socket = context.socketFactory.createSocket("ecc384.badssl.com", 443) as SSLSocket
342453

343454
val certs = socket.session.peerCertificates
344455

@@ -350,7 +461,7 @@ class MainActivity : AppCompatActivity() {
350461
// Send a real request, just to make it clear that we trust the connection:
351462
val pw = PrintWriter(socket.outputStream)
352463
pw.println("GET / HTTP/1.1")
353-
pw.println("Host: sha256.badssl.com")
464+
pw.println("Host: ecc384.badssl.com")
354465
pw.println("")
355466
pw.flush()
356467

app/src/main/res/layout/activity_main.xml

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,28 @@
6363
android:layout_width="match_parent"
6464
android:layout_height="wrap_content"
6565
android:onClick="sendAppmattusCTChecked"
66-
android:text="Appmattus CT-checked request" />
66+
android:text="Appmattus CT request" />
67+
68+
<Button
69+
android:id="@+id/appmattus_okhttp_ct_checked"
70+
android:layout_width="match_parent"
71+
android:layout_height="wrap_content"
72+
android:onClick="sendAppmattusOkHttpCTChecked"
73+
android:text="Appmattus+OkHttp CT request" />
74+
75+
<Button
76+
android:id="@+id/appmattus_raw_ct_checked"
77+
android:layout_width="match_parent"
78+
android:layout_height="wrap_content"
79+
android:onClick="sendAppmattusRawCTChecked"
80+
android:text="Appmattus+raw TLS CT" />
81+
82+
<Button
83+
android:id="@+id/appmattus_webview_ct_checked"
84+
android:layout_width="match_parent"
85+
android:layout_height="wrap_content"
86+
android:onClick="sendAppmattusCTWebView"
87+
android:text="Appmattus+WebView CT" />
6788

6889
<Button
6990
android:id="@+id/custom_context_pinned"
@@ -77,7 +98,7 @@
7798
android:layout_width="match_parent"
7899
android:layout_height="wrap_content"
79100
android:onClick="sendCustomRawSocketPinned"
80-
android:text="Custom manually pinned request" />
101+
android:text="Custom manually pinned raw request" />
81102

82103
</LinearLayout>
83104

app/src/main/res/xml/network_security_config.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,30 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<network-security-config>
3+
4+
<!-- We use amiusing.httptoolkit.tech for unpinned requests -->
5+
6+
<!-- We use ecc384.badssl.com for manual pinning, defined in code -->
7+
8+
<!-- We use sha256.badssl.com for normal config-defined pinning: -->
39
<domain-config>
410
<domain includeSubdomains="false">sha256.badssl.com</domain>
11+
<pin-set>
12+
<pin digest="SHA-256">C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=</pin>
13+
14+
<!-- TrustKit rejects the config if we use only one pin, so we add a dud: -->
15+
<pin digest="SHA-256">ABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFA</pin>
16+
</pin-set>
17+
<trust-anchors>
18+
<certificates src="@raw/lets_encrypt_isrg_root" />
19+
</trust-anchors>
20+
<trustkit-config enforcePinning="true">
21+
<report-uri>http://trustkit-report-url.test</report-uri>
22+
</trustkit-config>
23+
</domain-config>
24+
25+
<!-- We use rsa4096.badssl.com for Appmattus's global config-defined pinning: -->
26+
<domain-config>
27+
<domain includeSubdomains="false">rsa4096.badssl.com</domain>
528
<pin-set>
629
<pin digest="SHA-256">C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=</pin>
730
</pin-set>

0 commit comments

Comments
 (0)