ktorio / ktor

Framework for quickly creating connected applications in Kotlin with minimal effort
https://ktor.io
Apache License 2.0
12.86k stars 1.04k forks source link

SSL pinning for iOS #1431

Closed Rjmaurya13 closed 4 years ago

Rjmaurya13 commented 4 years ago

How to make SSL pinning for iOS. As I am getting response but for other thing its not working.

Getting crash near this code
val remoteCertificateData : NSData = SecCertificateCopyData(certificate) as NSData

This is the error.

Uncaught Kotlin exception: kotlin.TypeCastException

Here is my code.

override fun URLSession(
                session: NSURLSession,
                didReceiveChallenge: NSURLAuthenticationChallenge,
                completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Unit
            ) {
                val serverTrust = didReceiveChallenge.protectionSpace.serverTrust
                val certificate = SecTrustGetCertificateAtIndex(serverTrust,0)

                var result: SecTrustResultType = 0u

                memScoped{
                    val nativeResult = alloc<SecTrustResultTypeVar>()
                    nativeResult.value = result

                    SecTrustEvaluate(serverTrust!!, nativeResult.ptr)
                }

                val remoteCertificateData : NSData = SecCertificateCopyData(certificate) as NSData

                val bundle = NSBundle.bundleForClass(objc_getRequiredClass("IosClientEngine"))
                Logger.debug("pathToCert","$bundle")

                val pathToCert = bundle.pathForResource("MyCertificate","cer")

                val localCertificate : NSData = NSData.dataWithContentsOfFile(pathToCert!!)!!

                if (localCertificate == remoteCertificateData) {
                    completionHandler(NSURLSessionAuthChallengeUseCredential,NSURLCredential.create(serverTrust))

                } else {
                    completionHandler(NSURLSessionAuthChallengeUseCredential, null)

                }
            }
danailalexiev commented 4 years ago

Any suggestions for a workaround for self-signed certificate handling on iOS?

alistairsykes commented 4 years ago

I recently had to tackle implementing iOS certificate pinning. Here is a blog post I just published detailing my solution. Feel free to use it to help you work out your solution. Hope it helps someone.

https://medium.com/@alistairsykes/kotlin-multiplatform-ios-certificate-pinning-fd1abba5ca8f

hardysim commented 4 years ago

When I get it right, I need to add your CertificatePinner and use it like so:


HttpClient(Ios) {
 engine {
        challengeHandler = CertificatePinner.Builder()
            .add("publicobject.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
            .build()
    }
}

This would be great and easy to use but when coping the class from your article, the imports are missing and there are many unresolved references.

You're mentioning that a fix for our workaround will be available with f992dd3cad59d8d4262c0c924c59154ce03c0181 which is now merged in ktor 1.3.2.

@alistairsykes Would you please update your article with an example how to add certificate pinning for iOS using this new version?

alistairsykes commented 4 years ago

I have update my blog @hardysim to better reflect using this with version 1.3.2.

The api changed slightly since writing my blog and 1.3.2 release. The implementation will now look more like this:

HttpClient(Ios) {

    // ...

    engine {
        val builder = CertificatePinner.Builder()
            .add("publicobject.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
        handleChallenge(builder.build())
    }
}

Not sure which imports you'd be missing. Recommend checking out: https://ktor.io/clients/http-client/quick-start/client.html https://ktor.io/clients/http-client/multiplatform.html

Maybe you are looking for:

import io.ktor.client.HttpClient
import io.ktor.client.engine.ios.Ios

In terms of imports for the CertificatePinner itself, it depends where you copy this class to, and what package name you use. You will see in my blog that I have set package as package com.example.util, but recommend you package it wherever best suits your project.

Also I found this, really helpful when it comes to imports.

Hope that was helpful, shout if you'd like more information on my post.

Blog link again for reference

hardysim commented 4 years ago

The blog post seems not to be updated. At least, the signature of invoke() does not match the one from ktor 1.3.2. Maybe you can post the class here as well?

The imports for are missing for toNSData() and SecCertificateRef.getPublicKeyBytes() complains about the wrong return type (requires ByteArray? but found Unit). Maybe because toByteArray() is missing as well?!

alistairsykes commented 4 years ago

Sorry about that @hardysim . Didn't update the gist correctly.

Updated main gist:

Updated cutils gist:

Try again. Really sorry about that.

hardysim commented 4 years ago

Yeah, getting closer 🎉.

Still missing imports for @VisibleForTesting (but I can just remove that for now), NSMutableDataand ByteVar.

And I get two errors on base64EncodedStringWithOptions():

The integer literal does not conform to the expected type NSDataBase64EncodingOptions / = ULong /

hardysim commented 4 years ago

I can use import platform.Foundation.NSMutableData but it's still missing appendBytes() and reinterpret().

alistairsykes commented 4 years ago

main gist:

cutils gist:

I'm not seeing the base64EncodedStringWithOptions() error. Could this be due to the missing imports.

Hopefully that resolves it. Sorry again @hardysim.

e5l commented 4 years ago

Hi @alistairsykes, could you make the PR with the CertificatePinner? It would be nice to support it out of the box.

hardysim commented 4 years ago

@alistairsykes it seems to compile now 👍 But I still get those 2 errors:

image

And yes, a PR would be great.

alistairsykes commented 4 years ago

Hi @alistairsykes, could you make the PR with the CertificatePinner? It would be nice to support it out of the box.

I'd love to.

pejaab commented 4 years ago

@alistairsykes Thanks for providing this implementation. Unfortunately, I see the same error on base64EncodedStringWithOptions() as @hardysim. Additionally, when setting up the CertificatePinner for initial failure, the request fails as expected, but with "Server trust is invalid", which does not give the certificate chain to be used. This happens on the ios simulator, if it makes a difference?!

e5l commented 4 years ago

Could you try using 0.convert()?. It looks like the type is platform dependent.

alistairsykes commented 4 years ago

I included a fun on the builder validateTrust which might help with the emulator issue.

pejaab commented 4 years ago

@e5l thanks, this works! Beforehand I had used 0u.

@alistairsykes this actually helps as well, but only partially: validateTrust does the trick I receive

HttpClient: Certificate pinning failure!
      Peer certificate chain:
        sha256/UIUWcLlepHcfOtECdRs0Hzrnu8QpOsZkFHXNOP9t2vw=: *.cognitive.microsoft.com
      Pinned certificates for text-recognizer.cognitiveservices.azure.com:
        sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

but when I replace sha256/AAA... with the above sha256/UIUW..., it looks to me like I am back to square one without the CertificatePinner:

Exception in http request: Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “text-recognizer.cognitiveservices.azure.com” which could put your confidential information at risk." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
        "<cert(0x7fb49303aa00) s: *.cognitive.microsoft.com i: Microsoft IT TLS CA 5>",
        "<cert(0x7fb493034200) s: Microsoft IT TLS CA 5 i: Baltimore CyberTrust Root>"
    ), NSErrorClientCertificateStateKey=0,

Any chance you have a suggestion?

pejaab commented 4 years ago

@alistairsykes I finally got around to test this on an actual iPhone and it looks like this is only an issue on the ios simulator?! Just wanted to let you know. Thanks for the advice and the great work

Rjmaurya13 commented 4 years ago

Here is the code that worked for me. https://stackoverflow.com/questions/58777854/ktor-multiplatform-ssl-pinning-for-ios-in-kotlin

I hope it will help.

override fun URLSession(
    session: NSURLSession,
    didReceiveChallenge: NSURLAuthenticationChallenge,
    completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Unit) {

    val serverTrust = didReceiveChallenge.protectionSpace.serverTrust
    var result: SecTrustResultType = 0u

    memScoped{
        val nativeResult = alloc<SecTrustResultTypeVar>()
        nativeResult.value = result
        SecTrustEvaluate(serverTrust!!, nativeResult.ptr)
    }

    val serverCertificate = SecTrustGetCertificateAtIndex(serverTrust,0)
    val serverCertificateData = SecCertificateCopyData(serverCertificate)
    val data = CFDataGetBytePtr(serverCertificateData)
    val size = CFDataGetLength(serverCertificateData)

    val cert1 = NSData.dataWithBytes(data,size.toULong())
    val pathToCert = NSBundle.mainBundle.pathForResource("Your Certificate","cer")

    val localCertificate : NSData = NSData.dataWithContentsOfFile(pathToCert!!)!!

    if (localCertificate == cert1) {
        completionHandler(NSURLSessionAuthChallengeUseCredential,NSURLCredential.create(serverTrust))
    } else {
        completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, null)
    }
}
ptsiogas commented 3 years ago

@alistairsykes is there a way to perform pinning only for the root and intermediate certificate? Based on tries I can see that I must perform pinning in the whole certificate chain in order to get a succesful response.

e5l commented 3 years ago

After the fix, you need to pin only a single certificate in the chain

ptsiogas commented 3 years ago

@e5l in which version I can find the fix? I suppose 1.5.2 ?

e5l commented 3 years ago

Sure, 1.5.2 on the last week of Feb

ptsiogas commented 3 years ago

@e5l is there an alpha version I can test;

e5l commented 3 years ago

Sure, the latest EAP 22: https://ktor.io/eap/