@@ -15,14 +15,20 @@ import kotlinx.coroutines.*
1515import okhttp3.CertificatePinner
1616import okhttp3.OkHttpClient
1717import okhttp3.Request
18+ import okio.ByteString.Companion.decodeBase64
19+ import okio.ByteString.Companion.toByteString
1820import java.io.BufferedInputStream
21+ import java.io.BufferedReader
22+ import java.io.InputStreamReader
23+ import java.io.PrintWriter
1924import java.net.URL
2025import java.security.KeyStore
26+ import java.security.cert.Certificate
2127import 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.*
2530
31+ const val BADSSL_UNTRUSTED_ROOT_SHA256 = " sr2tjak7H6QRi8o0fyIXGWdPiU32rDsczcIEAqA+s4g="
2632
2733class MainActivity : AppCompatActivity () {
2834 override fun onCreate (savedInstanceState : Bundle ? ) {
@@ -120,15 +126,14 @@ class MainActivity : AppCompatActivity() {
120126 try {
121127 val hostname = " badssl.com"
122128 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 } " )
125130 .build()
126131
127132 val client = OkHttpClient .Builder ()
128133 .certificatePinner(certificatePinner)
129134 .build()
130135 val request = Request .Builder ()
131- .url(" https://badssl.com" )
136+ .url(" https://untrusted-root. badssl.com" )
132137 .build();
133138
134139 client.newCall(request).execute().use { response ->
@@ -212,4 +217,56 @@ class MainActivity : AppCompatActivity() {
212217 }
213218 }
214219 }
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+ }
215272}
0 commit comments